From 1b6c2bdd7b57891ed35e3c067871d2c0bf282824 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 14 Jan 2026 07:21:25 +0000 Subject: [PATCH 001/126] 8375055: C2: Better dead loop detection printout Reviewed-by: chagedorn, qamai --- src/hotspot/share/opto/phaseX.cpp | 46 +++++++++++++++++++------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1e4cd42e09a..c4bdc5e8903 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -762,26 +762,36 @@ bool PhaseGVN::is_dominator_helper(Node *d, Node *n, bool linear_only) { //------------------------------dead_loop_check-------------------------------- // Check for a simple dead loop when a data node references itself directly // or through an other data node excluding cons and phis. -void PhaseGVN::dead_loop_check( Node *n ) { - // Phi may reference itself in a loop - if (n != nullptr && !n->is_dead_loop_safe() && !n->is_CFG()) { - // Do 2 levels check and only data inputs. - bool no_dead_loop = true; - uint cnt = n->req(); - for (uint i = 1; i < cnt && no_dead_loop; i++) { - Node *in = n->in(i); - if (in == n) { - no_dead_loop = false; - } else if (in != nullptr && !in->is_dead_loop_safe()) { - uint icnt = in->req(); - for (uint j = 1; j < icnt && no_dead_loop; j++) { - if (in->in(j) == n || in->in(j) == in) - no_dead_loop = false; - } +void PhaseGVN::dead_loop_check(Node* n) { + // Phi may reference itself in a loop. + if (n == nullptr || n->is_dead_loop_safe() || n->is_CFG()) { + return; + } + + // Do 2 levels check and only data inputs. + for (uint i = 1; i < n->req(); i++) { + Node* in = n->in(i); + if (in == n) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node references itself: %s (%d)", + n->Name(), n->_idx); + } + + if (in == nullptr || in->is_dead_loop_safe()) { + continue; + } + for (uint j = 1; j < in->req(); j++) { + if (in->in(j) == n) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node input references current node: %s (%d) -> %s (%d)", + in->Name(), in->_idx, n->Name(), n->_idx); + } + if (in->in(j) == in) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node input references itself: %s (%d)", + in->Name(), in->_idx); } } - if (!no_dead_loop) { n->dump_bfs(100, nullptr, ""); } - assert(no_dead_loop, "dead loop detected"); } } From 703665c13f754f3ba7858c4bb2549c76cbc22a62 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 14 Jan 2026 13:46:40 +0000 Subject: [PATCH 002/126] 8356684: jpackage error messages are not helpful Reviewed-by: almatvee --- .../jdk/jpackage/internal/Executor.java | 16 +- .../jdk/jpackage/internal/cli/Main.java | 64 +++++-- .../jpackage/internal/cli/StandardOption.java | 3 + .../jdk/jpackage/internal/cli/Utils.java | 4 +- ...xecutableAttributesWithCapturedOutput.java | 53 ++++++ .../internal/model/JPackageException.java | 3 +- .../model/SelfContainedException.java | 42 ++++ .../resources/MainResources.properties | 5 + .../internal/util/CommandOutputControl.java | 83 ++++++-- .../helpers/jdk/jpackage/test/Executor.java | 15 +- .../test/mock/CommandActionSpecs.java | 7 + .../jdk/jpackage/internal/cli/MainTest.java | 180 +++++++++++++++++- .../util/CommandOutputControlTest.java | 39 +++- 13 files changed, 440 insertions(+), 74 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java index ca7a630b6d1..53c587ab37c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java @@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.UnaryOperator; import java.util.spi.ToolProvider; import java.util.stream.Stream; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.util.CommandLineFormat; import jdk.jpackage.internal.util.CommandOutputControl; import jdk.jpackage.internal.util.CommandOutputControl.ProcessAttributes; @@ -211,17 +212,11 @@ final class Executor { throw new IllegalStateException("No target to execute"); } - PrintableOutputBuilder printableOutputBuilder; - if (dumpOutput()) { - printableOutputBuilder = new PrintableOutputBuilder(coc); - } else { - printableOutputBuilder = null; - } - if (dumpOutput()) { Log.verbose(String.format("Running %s", CommandLineFormat.DEFAULT.apply(List.of(commandLine().getFirst())))); } + var printableOutputBuilder = new PrintableOutputBuilder(coc); Result result; try { if (timeout == null) { @@ -233,11 +228,12 @@ final class Executor { throw ExceptionBox.toUnchecked(ex); } + var printableOutput = printableOutputBuilder.create(); if (dumpOutput()) { - log(result, printableOutputBuilder.create()); + log(result, printableOutput); } - return result; + return ExecutableAttributesWithCapturedOutput.augmentResultWithOutput(result, printableOutput); } Result executeExpectSuccess() throws IOException { @@ -309,7 +305,7 @@ final class Executor { pid.ifPresent(p -> { sb.append(" [PID: ").append(p).append("]"); }); - sb.append(":\n ").append(result.execAttrs()); + sb.append(":\n ").append(result.execAttrs().printableCommandLine()); Log.verbose(sb.toString()); if (!printableOutput.isEmpty()) { 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 270ba0c927f..562a0d2d3c1 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 @@ -29,10 +29,12 @@ import static jdk.jpackage.internal.cli.StandardOption.HELP; import static jdk.jpackage.internal.cli.StandardOption.VERBOSE; import static jdk.jpackage.internal.cli.StandardOption.VERSION; +import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; +import java.io.StringReader; import java.io.UncheckedIOException; import java.nio.file.NoSuchFileException; import java.util.Collection; @@ -48,7 +50,11 @@ import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.Globals; import jdk.jpackage.internal.Log; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.model.SelfContainedException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; import jdk.jpackage.internal.util.Slot; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -239,44 +245,60 @@ public final class Main { reportError(ex.getCause()); } else if (t instanceof UncheckedIOException ex) { reportError(ex.getCause()); + } else if (t instanceof UnexpectedResultException ex) { + printExternalCommandError(ex); } else { printError(t, Optional.empty()); } } - private void printError(Throwable t, Optional advice) { - var isAlienException = isAlienExceptionType(t); + private void printExternalCommandError(UnexpectedResultException ex) { + var result = ex.getResult(); + var commandOutput = ((ExecutableAttributesWithCapturedOutput)result.execAttrs()).printableOutput(); + var printableCommandLine = result.execAttrs().printableCommandLine(); - if (isAlienException || verbose) { + if (verbose) { + stackTracePrinter.accept(ex); + } + + String msg; + if (ex instanceof UnexpectedExitCodeException) { + msg = I18N.format("error.command-failed-unexpected-exit-code", result.getExitCode(), printableCommandLine); + } else if (result.exitCode().isPresent()) { + msg = I18N.format("error.command-failed-unexpected-output", printableCommandLine); + } else { + msg = I18N.format("error.command-failed-timed-out", printableCommandLine); + } + + messagePrinter.accept(I18N.format("message.error-header", msg)); + if (!verbose) { + messagePrinter.accept(I18N.format("message.failed-command-output-header")); + try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { + lines.forEach(messagePrinter); + } + } + } + + private void printError(Throwable t, Optional advice) { + var isSelfContained = isSelfContained(t); + + if (!isSelfContained || verbose) { stackTracePrinter.accept(t); } String msg; - if (isAlienException) { - msg = t.toString(); - } else { + if (isSelfContained) { msg = t.getMessage(); + } else { + msg = t.toString(); } messagePrinter.accept(I18N.format("message.error-header", msg)); advice.ifPresent(v -> messagePrinter.accept(I18N.format("message.advice-header", v))); } - private static boolean isAlienExceptionType(Throwable t) { - switch (t) { - case JPackageException _ -> { - return false; - } - case Utils.ParseException _ -> { - return false; - } - case StandardOption.AddLauncherIllegalArgumentException _ -> { - return false; - } - default -> { - return true; - } - } + private static boolean isSelfContained(Throwable t) { + return t.getClass().getAnnotation(SelfContainedException.class) != null; } } 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 9c828705a4d..fadd9bacb1c 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 @@ -61,6 +61,8 @@ import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.SelfContainedException; import jdk.jpackage.internal.util.SetBuilder; /** @@ -701,6 +703,7 @@ public final class StandardOption { } + @SelfContainedException static class AddLauncherIllegalArgumentException extends IllegalArgumentException { AddLauncherIllegalArgumentException(String message) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java index 0565a8f1235..a94de355284 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.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 java.util.function.IntPredicate; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.model.BundlingEnvironment; +import jdk.jpackage.internal.model.SelfContainedException; final class Utils { @@ -98,6 +99,7 @@ final class Utils { }); } + @SelfContainedException static final class ParseException extends RuntimeException { ParseException(String msg) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java new file mode 100644 index 00000000000..faed9d24d01 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.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. 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.model; + +import java.util.List; +import java.util.Objects; +import jdk.jpackage.internal.util.CommandOutputControl.ExecutableAttributes; +import jdk.jpackage.internal.util.CommandOutputControl.Result; + +/** + * {@link ExecutableAttributes} augmented with printable command output. + */ +public record ExecutableAttributesWithCapturedOutput(ExecutableAttributes execAttrs, String printableOutput) + implements ExecutableAttributes { + + public ExecutableAttributesWithCapturedOutput { + Objects.requireNonNull(execAttrs); + Objects.requireNonNull(printableOutput); + } + + @Override + public List commandLine() { + return execAttrs.commandLine(); + } + + public static Result augmentResultWithOutput(Result result, String output) { + var execAttrs = new ExecutableAttributesWithCapturedOutput(result.execAttrs(), output); + return result.copyWithExecutableAttributes(execAttrs); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java index 9727f21d2d7..3d24d293f18 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.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 @@ -30,6 +30,7 @@ import java.util.Objects; /** * Generic jpackage exception with non-null message. */ +@SelfContainedException public class JPackageException extends RuntimeException { public JPackageException(String msg) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java new file mode 100644 index 00000000000..c7d1b3016ac --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java @@ -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. 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.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for exceptions with self-contained error messages that don't + * require an additional context, like exception class name or a stack trace. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface SelfContainedException { +} 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 588a3702839..07460a18fe8 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 @@ -55,6 +55,11 @@ message.module-version=Using version "{0}" from module "{1}" as application vers message.error-header=Error: {0} message.advice-header=Advice to fix: {0} +message.failed-command-output-header=Command output: + +error.command-failed-unexpected-output=Unexpected output from executing the command {0} +error.command-failed-unexpected-exit-code=Unexpected exit code {0} from executing the command {1} +error.command-failed-timed-out=Timed-out command {0} error.version-string-empty=Version may not be empty string error.version-string-zero-length-component=Version [{0}] contains a zero length component diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java index e35439815b1..00fbdd71919 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java @@ -796,6 +796,10 @@ public final class CommandOutputControl { public interface ExecutableAttributes { List commandLine(); + + default String printableCommandLine() { + return CommandLineFormat.DEFAULT.apply(commandLine()); + } } public sealed interface Executable { @@ -812,11 +816,6 @@ public final class CommandOutputControl { Objects.requireNonNull(pid); commandLine.forEach(Objects::requireNonNull); } - - @Override - public String toString() { - return CommandLineFormat.DEFAULT.apply(commandLine()); - } } public record ToolProviderAttributes(String name, List args) implements ExecutableAttributes { @@ -825,11 +824,6 @@ public final class CommandOutputControl { args.forEach(Objects::requireNonNull); } - @Override - public String toString() { - return CommandLineFormat.DEFAULT.apply(commandLine()); - } - @Override public List commandLine() { return Stream.concat(Stream.of(name), args.stream()).toList(); @@ -837,14 +831,9 @@ public final class CommandOutputControl { } public static ExecutableAttributes EMPTY_EXECUTABLE_ATTRIBUTES = new ExecutableAttributes() { - @Override - public String toString() { - return ""; - } - @Override public List commandLine() { - return List.of(); + return List.of(""); } }; @@ -869,6 +858,51 @@ public final class CommandOutputControl { Objects.requireNonNull(execAttrs); } + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + public Result create() { + return new Result( + exitCode, + Optional.ofNullable(content).map(List::copyOf).map(StringListContent::new).map(c -> { + return new CommandOutput>(Optional.of(c), c.size(), true); + }), + Optional.empty(), + Optional.ofNullable(execAttrs).orElse(EMPTY_EXECUTABLE_ATTRIBUTES)); + } + + public Builder exitCode(int v) { + exitCode = Optional.of(v); + return this; + } + + public Builder noExitCode() { + exitCode = Optional.empty(); + return this; + } + + public Builder execAttrs(ExecutableAttributes v) { + execAttrs = v; + return this; + } + + public Builder content(List v) { + content = v; + return this; + } + + public Builder content(String... v) { + return content(List.of(v)); + } + + private ExecutableAttributes execAttrs; + private List content; + private Optional exitCode = Optional.empty(); + } + public Result(int exitCode) { this(Optional.of(exitCode), Optional.empty(), Optional.empty(), EMPTY_EXECUTABLE_ATTRIBUTES); } @@ -1019,7 +1053,8 @@ public final class CommandOutputControl { } private UnexpectedResultException(Result value) { - this(value, String.format("Unexpected result from executing the command %s", value.execAttrs())); + this(value, String.format("Unexpected result from executing the command %s", + value.execAttrs().printableCommandLine())); } public Result getResult() { @@ -1034,11 +1069,21 @@ public final class CommandOutputControl { public static final class UnexpectedExitCodeException extends UnexpectedResultException { public UnexpectedExitCodeException(Result value, String message) { - super(value, message); + super(requireExitCode(value), message); } public UnexpectedExitCodeException(Result value) { - this(value, String.format("Unexpected exit code %d from executing the command %s", value.getExitCode(), value.execAttrs())); + this(value, String.format("Unexpected exit code %d from executing the command %s", + requireExitCode(value).getExitCode(), value.execAttrs().printableCommandLine())); + } + + private static Result requireExitCode(Result v) { + Objects.requireNonNull(v); + if (v.exitCode().isPresent()) { + return v; + } else { + throw new IllegalArgumentException(); + } } private static final long serialVersionUID = 1L; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index d4833eb9736..b508d4f5ffe 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -283,11 +283,11 @@ public final class Executor extends CommandArguments { long expectedExitCode = expectedExitCodes.getFirst(); TKit.assertEquals(expectedExitCode, getExitCode(), String.format( "Check command %s exited with %d code", - base.execAttrs(), expectedExitCode)); + base.execAttrs().printableCommandLine(), expectedExitCode)); } default -> { TKit.assertTrue(expectedExitCodes.contains(getExitCode()), String.format( "Check command %s exited with one of %s codes", - base.execAttrs(), expectedExitCodes.stream().sorted().toList())); + base.execAttrs().printableCommandLine(), expectedExitCodes.stream().sorted().toList())); } } return this; @@ -302,7 +302,7 @@ public final class Executor extends CommandArguments { } public String getPrintableCommandLine() { - return base.execAttrs().toString(); + return base.execAttrs().printableCommandLine(); } } @@ -515,21 +515,16 @@ public final class Executor extends CommandArguments { return String.format(format, CommandLineFormat.DEFAULT.apply(cmdline), cmdline.size()); } - private record ExecutableAttributes(CommandOutputControl.ExecutableAttributes base, String toStringValue) + private record ExecutableAttributes(CommandOutputControl.ExecutableAttributes base, String printableCommandLine) implements CommandOutputControl.ExecutableAttributes { ExecutableAttributes { Objects.requireNonNull(base); - if (toStringValue.isBlank()) { + if (printableCommandLine.isBlank()) { throw new IllegalArgumentException(); } } - @Override - public String toString() { - return toStringValue; - } - @Override public List commandLine() { return base.commandLine(); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java index e89e458c02d..ce3aa6f80e1 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java @@ -115,6 +115,13 @@ public record CommandActionSpecs(List specs) { return printToStderr(List.of(str)); } + public Builder argsListener(Consumer> listener) { + Objects.requireNonNull(listener); + return action(CommandActionSpec.create("args-listener", context -> { + listener.accept(context.args()); + })); + } + public Builder exit(int exitCode) { return action(CommandActionSpec.create(String.format("exit(%d)", exitCode), () -> { return exitCode; 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 365a8a50b33..79648260274 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 @@ -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 @@ -22,6 +22,7 @@ */ package jdk.jpackage.internal.cli; +import static jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput.augmentResultWithOutput; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -43,19 +44,31 @@ import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.Globals; +import jdk.jpackage.internal.MockUtils; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.Annotations; +import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JUnitAdapter; import jdk.jpackage.test.TKit; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.mock.VerbatimCommandMock; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -public class MainTest extends JUnitAdapter.TestSrcInitializer { +public class MainTest extends JUnitAdapter { @ParameterizedTest @MethodSource @@ -78,6 +91,71 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { assertNotEquals(str, msg); } + /** + * Run app image packaging and simulate jlink failure. + *

+ * The test verifies that jpackage prints an error message with jlink exit code, + * command line, and output. + */ + @Annotations.Test + public void testFailedCommandOutput() { + + var jlinkMockExitCode = 17; + + var jlinkArgs = new ArrayList(); + + var jlinkMock = CommandActionSpecs.build() + .stdout("It").stderr("fell").stdout("apart") + .argsListener(jlinkArgs::addAll) + .exit(jlinkMockExitCode).create().toCommandMockBuilder().name("jlink-mock").create(); + + var script = Script.build() + // Replace jlink with the mock. + .map(Script.cmdlineStartsWith("jlink"), jlinkMock) + // Don't mock other external commands. + .map(_ -> true, VerbatimCommandMock.INSTANCE) + .createLoop(); + + var jpackageExitCode = 1; + + var jpackageToolProviderMock = new ToolProvider() { + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + var globalsMutator = MockUtils.buildJPackage().script(script).createGlobalsMutator(); + return Globals.main(() -> { + globalsMutator.accept(Globals.instance()); + + var result = ExecutionResult.create(args); + + var jlinkMockAttrs = new CommandOutputControl.ToolProviderAttributes(jlinkMock.name(), jlinkArgs); + + result.stdout().forEach(out::println); + result.stderr().forEach(err::println); + + assertEquals(jpackageExitCode, result.exitCode()); + assertEquals(List.of(), result.stdout()); + assertEquals(List.of( + I18N.format("message.error-header", I18N.format("error.command-failed-unexpected-exit-code", + jlinkMockExitCode, jlinkMockAttrs.printableCommandLine())), + I18N.format("message.failed-command-output-header"), + "It", "fell", "apart"), result.stderr()); + + return jpackageExitCode; + }); + } + + @Override + public String name() { + return "jpackage-mock"; + } + }; + + JPackageCommand.helloAppImage() + .ignoreDefaultVerbose(true) + .useToolProvider(jpackageToolProviderMock) + .execute(jpackageExitCode); + } + private static Collection testOutput() { return Stream.of( // Print the tool version @@ -165,6 +243,49 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); } } + + var execAttrs = new CommandOutputControl.ProcessAttributes(Optional.of(12345L), List.of("foo", "--bar")); + for (var makeCause : List.>of( + ex -> ex, + ExceptionBox::toUnchecked + )) { + + for (var expect : List.of( + Map.entry( + augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).execAttrs(execAttrs).create(), + "The quick brown fox\njumps over the lazy dog" + ).unexpected("Kaput!"), + ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE + ), + Map.entry( + new UnexpectedExitCodeException(augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).create(), + "The quick brown fox\njumps" + )), + ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE + ), + Map.entry( + augmentResultWithOutput( + CommandOutputControl.Result.build().create(), + "The quick brown fox\njumps" + ).unexpected("Timed out!"), + ExceptionFormatter.FAILED_COMMAND_TIMEDOUT_MESSAGE + ) + )) { + var cause = makeCause.apply(expect.getKey()); + var expectedOutput = new ArrayList(); + if (verbose) { + expectedOutput.add(ExceptionFormatter.STACK_TRACE); + } + expectedOutput.add(expect.getValue()); + if (!verbose) { + expectedOutput.add(ExceptionFormatter.FAILED_COMMAND_OUTPUT); + } + data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); + } + } + } return data; @@ -350,7 +471,24 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { ex.printStackTrace(pw); } return sink.toString(); - }) + }), + FAILED_COMMAND_OUTPUT(ex -> { + var result = failedCommandResult(ex); + var commandOutput = ((ExecutableAttributesWithCapturedOutput)result.execAttrs()).printableOutput(); + + var sink = new StringWriter(); + var pw = new PrintWriter(sink); + + pw.println(I18N.format("message.failed-command-output-header")); + try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { + lines.forEach(pw::println); + } + + return sink.toString(); + }), + FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE(errorMessage(CommandFailureType.UNEXPECTED_OUTPUT::getMessage)), + FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE(errorMessage(CommandFailureType.UNEXPECTED_EXIT_CODE::getMessage)), + FAILED_COMMAND_TIMEDOUT_MESSAGE(errorMessage(CommandFailureType.TIMEDOUT::getMessage)), ; ExceptionFormatter(Function formatter) { @@ -369,6 +507,42 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { }; } + private static CommandOutputControl.Result failedCommandResult(Exception ex) { + Objects.requireNonNull(ex); + if (ex instanceof UnexpectedResultException urex) { + return urex.getResult(); + } else { + throw new IllegalArgumentException(); + } + } + + private enum CommandFailureType { + UNEXPECTED_OUTPUT, + UNEXPECTED_EXIT_CODE, + TIMEDOUT, + ; + + String getMessage(Exception ex) { + var result = failedCommandResult(ex); + var printableCommandLine = result.execAttrs().printableCommandLine(); + switch (this) { + case TIMEDOUT -> { + return I18N.format("error.command-failed-timed-out", printableCommandLine); + } + case UNEXPECTED_EXIT_CODE -> { + return I18N.format("error.command-failed-unexpected-exit-code", result.getExitCode(), printableCommandLine); + } + case UNEXPECTED_OUTPUT -> { + return I18N.format("error.command-failed-unexpected-output", printableCommandLine); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + } + private final Function formatter; } 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 f1d8c142eb9..d71cf7c4d41 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 @@ -203,11 +203,11 @@ public class CommandOutputControlTest { exec = coc.createExecutable(new ProcessBuilder("runme", "--foo", "--baz=10")); } - assertEquals("runme --foo --baz=10", exec.attributes().toString()); + assertEquals("runme --foo --baz=10", exec.attributes().printableCommandLine()); } @Test - public void test_Result_no_args_ctor() { + public void test_Result_single_arg_ctor() { var result = new CommandOutputControl.Result(7); assertFalse(result.findContent().isPresent()); assertFalse(result.findStdout().isPresent()); @@ -216,6 +216,18 @@ public class CommandOutputControlTest { assertSame(Objects.requireNonNull(CommandOutputControl.EMPTY_EXECUTABLE_ATTRIBUTES), result.execAttrs()); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Result_unexpected(boolean customMessage) throws IOException { + var result = new CommandOutputControl.Result(7); + + if (customMessage) { + assertEquals("Kaput!", result.unexpected("Kaput!").getMessage()); + } else { + assertEquals("Unexpected result from executing the command ", result.unexpected().getMessage()); + } + } + @Test public void test_Result_expectExitCode() throws IOException { var result = new CommandOutputControl.Result(7); @@ -276,14 +288,9 @@ public class CommandOutputControlTest { var empty = new CommandOutputControl.Result(0); var execAttrs = new CommandOutputControl.ExecutableAttributes() { - @Override - public String toString() { - return "foo"; - } - @Override public List commandLine() { - return List.of(); + return List.of("foo"); } }; @@ -295,6 +302,20 @@ public class CommandOutputControlTest { assertSame(execAttrs, copy.execAttrs()); } + @Test + public void test_UnexpectedExitCodeException_no_exit_code() { + + var resultWithoutExitCode = CommandOutputControl.Result.build().create(); + + assertThrowsExactly(IllegalArgumentException.class, () -> { + new CommandOutputControl.UnexpectedExitCodeException(resultWithoutExitCode); + }); + + assertThrowsExactly(IllegalArgumentException.class, () -> { + new CommandOutputControl.UnexpectedExitCodeException(resultWithoutExitCode, "Kaput!"); + }); + } + @ParameterizedTest @EnumSource(ExecutableType.class) public void test_timeout_expires(ExecutableType mode) throws InterruptedException, IOException { @@ -1781,7 +1802,7 @@ public class CommandOutputControlTest { } } - private final static class InterruptibleToolProvider implements ToolProvider { + private static final class InterruptibleToolProvider implements ToolProvider { InterruptibleToolProvider(ToolProvider impl) { this.impl = impl; From 20bd178b997b8bbf895877774d55d1a9e87c3038 Mon Sep 17 00:00:00 2001 From: Roger Calnan Date: Wed, 14 Jan 2026 14:08:21 +0000 Subject: [PATCH 003/126] 8373836: add anchors to the java options in the java man page Reviewed-by: jwilhelm, iris --- src/java.base/share/man/java.md | 494 ++++++++++++++++---------------- 1 file changed, 247 insertions(+), 247 deletions(-) diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 30661a3f387..4343df663e6 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ To launch a source-file program: : Specifies the name of the class to be launched. Command-line entries following `classname` are the arguments for the main method. -`-jar` *jarfile* +[`-jar`]{#-jar} *jarfile* : Executes a program encapsulated in a JAR file. The *jarfile* argument is the name of a JAR file with a manifest that contains a line in the form `Main-Class:`*classname* that defines the class with the @@ -70,7 +70,7 @@ To launch a source-file program: is the source of all user classes, and other class path settings are ignored. If you're using JAR files, then see [jar](jar.html). -`-m` or `--module` *module*\[`/`*mainclass*\] +[`-m`]{#-m} or `--module` *module*\[`/`*mainclass*\] : Executes the main class in a module specified by *mainclass* if it is given, or, if it is not given, the value in the *module*. In other words, *mainclass* can be used when it is not specified by the module, or to @@ -370,7 +370,7 @@ the JVM. > **Note:** To specify an argument for a long option, you can use either `--`*name*`=`*value* or `--`*name* *value*. -`-agentlib:`*libname*\[`=`*options*\] +[`-agentlib:`]{#-agentlib}*libname*\[`=`*options*\] : Loads the specified native agent library. After the library name, a comma-separated list of options specific to the library can be used. If the option `-agentlib:foo` is specified, then the JVM attempts to @@ -393,12 +393,12 @@ the JVM. > `-agentlib:jdwp=transport=dt_socket,server=y,address=8000` -`-agentpath:`*pathname*\[`=`*options*\] +[`-agentpath:`]{#-agentpath}*pathname*\[`=`*options*\] : Loads the native agent library specified by the absolute path name. This option is equivalent to `-agentlib` but uses the full path and file name of the library. -`--class-path` *classpath*, `-classpath` *classpath*, or `-cp` *classpath* +[`--class-path`]{#--class-path} *classpath*, `-classpath` *classpath*, or `-cp` *classpath* : Specifies a list of directories, JAR files, and ZIP archives to search for class files. @@ -424,15 +424,15 @@ the JVM. expanded except by querying the environment, such as by calling `System.getenv("CLASSPATH")`. -`--disable-@files` +[`--disable-@files`]{#--disable-@files} : Can be used anywhere on the command line, including in an argument file, to prevent further `@filename` expansion. This option stops expanding `@`-argfiles after the option. -`--enable-preview` +[`--enable-preview`]{#--enable-preview} : Allows classes to depend on [preview features](https://docs.oracle.com/en/java/javase/12/language/index.html#JSLAN-GUID-5A82FE0E-0CA4-4F1F-B075-564874FE2823) of the release. -`--enable-native-access` *module*\[`,`*module*...\] +[`--enable-native-access`]{#--enable-native-access} *module*\[`,`*module*...\] : Native access involves access to code or data outside the Java runtime. This is generally unsafe and, if done incorrectly, might crash the JVM or result in memory corruption. Native access can occur as a result of calling a method that @@ -465,7 +465,7 @@ the JVM. run it with `--illegal-native-access=deny` along with any necessary `--enable-native-access` options. -`--enable-final-field-mutation` *module*\[,*module*...\] +[`--enable-final-field-mutation`]{#--enable-final-field-mutation} *module*\[,*module*...\] : Mutation of final fields is possible with the reflection API of the Java Platform. However, it compromises safety and performance in all programs. This option allows code in the specified modules to mutate final fields by reflection. @@ -498,13 +498,13 @@ the JVM. run it with `--illegal-final-field-mutation=deny` along with any necessary `--enable-final-field-mutation` options. -`--finalization=`*value* +[`--finalization=`]{#--finalization}*value* : Controls whether the JVM performs finalization of objects. Valid values are "enabled" and "disabled". Finalization is enabled by default, so the value "enabled" does nothing. The value "disabled" disables finalization, so that no finalizers are invoked. -`--module-path` *modulepath*... or `-p` *modulepath* +[`--module-path`]{#--module-path} *modulepath*... or `-p` *modulepath* : Specifies where to find application modules with a list of path elements. The elements of a module path can be a file path to a module or a directory containing modules. Each module is either a modular JAR or an @@ -513,7 +513,7 @@ the JVM. On Windows, semicolons (`;`) separate path elements in this list; on other platforms it is a colon (`:`). -`--upgrade-module-path` *modulepath*... +[`--upgrade-module-path`]{#--upgrade-module-path} *modulepath*... : Specifies where to find module replacements of upgradeable modules in the runtime image with a list of path elements. The elements of a module path can be a file path to a module or a directory @@ -523,33 +523,33 @@ the JVM. On Windows, semicolons (`;`) separate path elements in this list; on other platforms it is a colon (`:`). -`--add-modules` *module*\[`,`*module*...\] +[`--add-modules`]{#--add-modules} *module*\[`,`*module*...\] : Specifies the root modules to resolve in addition to the initial module. *module* can also be `ALL-DEFAULT`, `ALL-SYSTEM`, and `ALL-MODULE-PATH`. -`--list-modules` +[`--list-modules`]{#--list-modules} : Lists the observable modules and then exits. -`-d` *module\_name* or `--describe-module` *module\_name* +[`-d`]{#-d} *module\_name* or `--describe-module` *module\_name* : Describes a specified module and then exits. -`--dry-run` +[`--dry-run`]{#--dry-run} : Creates the VM but doesn't execute the main method. This `--dry-run` option might be useful for validating the command-line options such as the module system configuration. -`--validate-modules` +[`--validate-modules`]{#--validate-modules} : Validates all modules and exit. This option is helpful for finding conflicts and other errors with modules on the module path. -`-D`*property*`=`*value* +[`-D`]{#-D}*property*`=`*value* : Sets a system property value. The *property* variable is a string with no spaces that represents the name of the property. The *value* variable is a string that represents the value of the property. If *value* is a string with spaces, then enclose it in quotation marks (for example `-Dfoo="foo bar"`). -`-disableassertions`\[`:`\[*packagename*\]...\|`:`*classname*\] or `-da`\[`:`\[*packagename*\]...\|`:`*classname*\] +[`-disableassertions`]{#-disableassertions}\[`:`\[*packagename*\]...\|`:`*classname*\] or `-da`\[`:`\[*packagename*\]...\|`:`*classname*\] : Disables assertions. By default, assertions are disabled in all packages and classes. With no arguments, `-disableassertions` (`-da`) disables assertions in all packages and classes. With the *packagename* argument @@ -574,10 +574,10 @@ the JVM. > `java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat MyClass` -`-disablesystemassertions` or `-dsa` +[`-disablesystemassertions`]{#-disablesystemassertions} or `-dsa` : Disables assertions in all system classes. -`-enableassertions`\[`:`\[*packagename*\]...\|`:`*classname*\] or `-ea`\[`:`\[*packagename*\]...\|`:`*classname*\] +[`-enableassertions`]{#-enableassertions}\[`:`\[*packagename*\]...\|`:`*classname*\] or `-ea`\[`:`\[*packagename*\]...\|`:`*classname*\] : Enables assertions. By default, assertions are disabled in all packages and classes. With no arguments, `-enableassertions` (`-ea`) enables assertions in all packages and classes. With the *packagename* argument ending in @@ -604,28 +604,28 @@ the JVM. > `java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat MyClass` -`-enablesystemassertions` or `-esa` +[`-enablesystemassertions`]{#-enablesystemassertions} or `-esa` : Enables assertions in all system classes. -`-help`, `-h`, or `-?` +[`-help`]{#-help}, `-h`, or `-?` : Prints the help message to the error stream. -`--help` +[`--help`]{#--help} : Prints the help message to the output stream. -`-javaagent:`*jarpath*\[`=`*options*\] +[`-javaagent:`]{#-javaagent_}*jarpath*\[`=`*options*\] : Loads the specified Java programming language agent. See `java.lang.instrument`. -`--show-version` +[`--show-version`]{#--show-version} : Prints the product version to the output stream and continues. -`-showversion` +[`-showversion`]{#-showversion} : Prints the product version to the error stream and continues. -`--show-module-resolution` +[`--show-module-resolution`]{#--show-module-resolution} : Shows module resolution output during startup. -`-splash:`*imagepath* +[`-splash:`]{#-splash_}*imagepath* : Shows the splash screen with the image specified by *imagepath*. HiDPI scaled images are automatically supported and used if available. The unscaled image file name, such as `image.ext`, should always be passed as @@ -639,29 +639,29 @@ the JVM. See the SplashScreen API documentation for more information. -`-verbose:class` +[`-verbose:class`]{#-verbose_class} : Displays information about each loaded class. -`-verbose:gc` +[`-verbose:gc`]{#-verbose_gc} : Displays information about each garbage collection (GC) event. -`-verbose:jni` +[`-verbose:jni`]{#-verbose_jni} : Displays information about the use of native methods and other Java Native Interface (JNI) activity. -`-verbose:module` +[`-verbose:module`]{#-verbose_module} : Displays information about the modules in use. -`--version` +[`--version`]{#--version} : Prints product version to the output stream and exits. -`-version` +[`-version`]{#-version} : Prints product version to the error stream and exits. -`-X` +[`-X`]{#-X} : Prints the help on extra options to the error stream. -`--help-extra` +[`--help-extra`]{#--help-extra} : Prints the help on extra options to the output stream. `@`*argfile* @@ -688,7 +688,7 @@ the JVM. The following `java` options are general purpose options that are specific to the Java HotSpot Virtual Machine. -`-Xbatch` +[`-Xbatch`]{#-Xbatch} : Disables background compilation. By default, the JVM compiles the method as a background task, running the method in interpreter mode until the background compilation is finished. The `-Xbatch` flag disables background @@ -696,14 +696,14 @@ the Java HotSpot Virtual Machine. task until completed. This option is equivalent to `-XX:-BackgroundCompilation`. -`-Xbootclasspath/a:`*directories*\|*zip*\|*JAR-files* +[`-Xbootclasspath/a:`]{#-Xbootclasspath}*directories*\|*zip*\|*JAR-files* : Specifies a list of directories, JAR files, and ZIP archives to append to the end of the default bootstrap class path. On Windows, semicolons (`;`) separate entities in this list; on other platforms it is a colon (`:`). -`-Xcheck:jni` +[`-Xcheck:jni`]{#-Xcheck_jni} : Performs additional checks for Java Native Interface (JNI) functions. The following checks are considered indicative of significant problems @@ -739,22 +739,22 @@ the Java HotSpot Virtual Machine. Expect a performance degradation when this option is used. -`-Xcomp` +[`-Xcomp`]{#-Xcomp} : Testing mode to exercise JIT compilers. This option should not be used in production environments. -`-Xdebug` +[`-Xdebug`]{#-Xdebug} : Does nothing; deprecated for removal in a future release. -`-Xdiag` +[`-Xdiag`]{#-Xdiag} : Shows additional diagnostic messages. -`-Xint` +[`-Xint`]{#-Xint} : Runs the application in interpreted-only mode. Compilation to native code is disabled, and all bytecode is executed by the interpreter. The performance benefits offered by the just-in-time (JIT) compiler aren't present in this mode. -`-Xinternalversion` +[`-Xinternalversion`]{#-Xinternalversion} : Displays more detailed JVM version information than the `-version` option, and then exits. @@ -763,11 +763,11 @@ the Java HotSpot Virtual Machine. logging framework. See [Enable Logging with the JVM Unified Logging Framework]. -`-Xmixed` +[`-Xmixed`]{#-Xmixed} : Executes all bytecode by the interpreter except for hot methods, which are compiled to native code. On by default. Use `-Xint` to switch off. -`-Xmn` *size* +[`-Xmn`]{#-Xmn} *size* : Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery) in the generational collectors. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or @@ -793,7 +793,7 @@ the Java HotSpot Virtual Machine. the heap for the young generation, you can use `-XX:NewSize` to set the initial size and `-XX:MaxNewSize` to set the maximum size. -`-Xms` *size* +[`-Xms`]{#-Xms} *size* : Sets the minimum and the initial size (in bytes) of the heap. This value must be a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -815,7 +815,7 @@ the Java HotSpot Virtual Machine. initial heap size. If it appears after `-Xms` on the command line, then the initial heap size gets set to the value specified with `-XX:InitialHeapSize`. -`-Xmx` *size* +[`-Xmx`]{#-Xmx} *size* : Specifies the maximum size (in bytes) of the heap. This value must be a multiple of 1024 and greater than 2 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -832,7 +832,7 @@ the Java HotSpot Virtual Machine. The `-Xmx` option is equivalent to `-XX:MaxHeapSize`. -`-Xnoclassgc` +[`-Xnoclassgc`]{#-Xnoclassgc} : Disables garbage collection (GC) of classes. This can save some GC time, which shortens interruptions during the application run. When you specify `-Xnoclassgc` at startup, the class objects in the application are left @@ -840,7 +840,7 @@ the Java HotSpot Virtual Machine. more memory being permanently occupied which, if not used carefully, throws an out-of-memory exception. -`-Xrs` +[`-Xrs`]{#-Xrs} : Reduces the use of operating system signals by the JVM. Shutdown hooks enable the orderly shutdown of a Java application by running user cleanup code (such as closing database connections) at shutdown, even if the JVM @@ -890,7 +890,7 @@ the Java HotSpot Virtual Machine. User code is responsible for causing shutdown hooks to run, for example, by calling `System.exit()` when the JVM is to be terminated. -`-Xshare:`*mode* +[`-Xshare:`]{#-Xshare_}*mode* : Sets the class data sharing (CDS) mode. Possible *mode* arguments for this option include the following: @@ -910,10 +910,10 @@ the Java HotSpot Virtual Machine. `off` : Do not attempt to use shared class data. -`-XshowSettings` +[`-XshowSettings`]{#-XshowSettings} : Shows all settings and then continues. -`-XshowSettings:`*category* +[`-XshowSettings:`]{#-XshowSettings_}*category* : Shows settings and continues. Possible *category* arguments for this option include the following: @@ -942,7 +942,7 @@ the Java HotSpot Virtual Machine. `system` : **Linux only:** Shows host system or container configuration and continues. -`-Xss` *size* +[`-Xss`]{#-Xss} *size* : Sets the thread stack size (in bytes). Append the letter `k` or `K` to indicate KB, `m` or `M` to indicate MB, or `g` or `G` to indicate GB. The actual size may be rounded up to a multiple of the system page size as @@ -970,32 +970,32 @@ the Java HotSpot Virtual Machine. This option is similar to `-XX:ThreadStackSize`. -`--add-reads` *module*`=`*target-module*(`,`*target-module*)\* +[`--add-reads`]{#--add-reads} *module*`=`*target-module*(`,`*target-module*)\* : Updates *module* to read the *target-module*, regardless of the module declaration. *target-module* can be `ALL-UNNAMED` to read all unnamed modules. -`--add-exports` *module*`/`*package*`=`*target-module*(`,`*target-module*)\* +[`--add-exports`]{#--add-exports} *module*`/`*package*`=`*target-module*(`,`*target-module*)\* : Updates *module* to export *package* to *target-module*, regardless of module declaration. *target-module* can be `ALL-UNNAMED` to export to all unnamed modules. -`--add-opens` *module*`/`*package*`=`*target-module*(`,`*target-module*)\* +[`--add-opens`]{#--add-opens} *module*`/`*package*`=`*target-module*(`,`*target-module*)\* : Updates *module* to open *package* to *target-module*, regardless of module declaration. -`--limit-modules` *module*\[`,`*module*...\] +[`--limit-modules`]{#--limit-modules} *module*\[`,`*module*...\] : Specifies the limit of the universe of observable modules. -`--patch-module` *module*`=`*file*(`;`*file*)\* +[`--patch-module`]{#--patch-module} *module*`=`*file*(`;`*file*)\* : Overrides or augments a module with classes and resources in JAR files or directories. -`--source` *version* +[`--source`]{#--source} *version* : Sets the version of the source in source-file mode. -`--sun-misc-unsafe-memory-access=` *value* +[`--sun-misc-unsafe-memory-access=`]{#--sun-misc-unsafe-memory-access} *value* : Allow or deny usage of unsupported API `sun.misc.Unsafe`. *value* is one of: `allow` @@ -1021,20 +1021,20 @@ the Java HotSpot Virtual Machine. The following extra options are macOS specific. -`-XstartOnFirstThread` +[`-XstartOnFirstThread`]{#-XstartOnFirstThread} : Runs the `main()` method on the first (AppKit) thread. -`-Xdock:name=`*application\_name* +[`-Xdock:name=`]{#-Xdock_name}*application\_name* : Overrides the default application name displayed in dock. -`-Xdock:icon=`*path\_to\_icon\_file* +[`-Xdock:icon=`]{#-Xdock_icon}*path\_to\_icon\_file* : Overrides the default icon displayed in dock. ## Advanced Options for Java These `java` options can be used to enable other advanced options. -`-XX:+UnlockDiagnosticVMOptions` +[`-XX:+UnlockDiagnosticVMOptions`]{#-XX__UnlockDiagnosticVMOptions} : Unlocks the options intended for diagnosing the JVM. By default, this option is disabled and diagnostic options aren't available. @@ -1046,7 +1046,7 @@ These `java` options can be used to enable other advanced options. of these options may be removed or their behavior changed without any warning. -`-XX:+UnlockExperimentalVMOptions` +[`-XX:+UnlockExperimentalVMOptions`]{#-XX__UnlockExperimentalVMOptions} : Unlocks the options that provide experimental features in the JVM. By default, this option is disabled and experimental features aren't available. @@ -1054,7 +1054,7 @@ These `java` options can be used to enable other advanced options. These `java` options control the runtime behavior of the Java HotSpot VM. -`-XX:ActiveProcessorCount=`*x* +[`-XX:ActiveProcessorCount=`]{#-XX_ActiveProcessorCount}*x* : Overrides the number of CPUs that the VM will use to calculate the size of thread pools it will use for various operations such as Garbage Collection and ForkJoinPool. @@ -1066,7 +1066,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. `-XX:-UseContainerSupport` for a description of enabling and disabling container support. -`-XX:AllocateHeapAt=`*path* +[`-XX:AllocateHeapAt=`]{#-XX_AllocateHeapAt}*path* : Takes a path to the file system and uses memory mapping to allocate the object heap on the memory device. Using this option enables the HotSpot VM to allocate the Java object heap on an alternative memory device, such as @@ -1084,7 +1084,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. The existing heap related flags (such as `-Xmx` and `-Xms`) and garbage-collection related flags continue to work as before. -`-XX:-CompactStrings` +[`-XX:-CompactStrings`]{#-XX__CompactStrings} : Disables the Compact Strings feature. By default, this option is enabled. When this option is enabled, Java Strings containing only single-byte characters are internally represented and stored as @@ -1107,7 +1107,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. In both of these scenarios, disabling Compact Strings makes sense. -`-XX:ErrorFile=`*filename* +[`-XX:ErrorFile=`]{#-XX_ErrorFile}*filename* : Specifies the path and file name to which error data is written when an irrecoverable error occurs. By default, this file is created in the current working directory and named `hs_err_pid`*pid*`.log` where *pid* is the @@ -1139,7 +1139,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. `TMP` environment variable; if that environment variable isn't defined, then the value of the `TEMP` environment variable is used. -`-XX:+ExtensiveErrorReports` +[`-XX:+ExtensiveErrorReports`]{#-XX__ExtensiveErrorReports} : Enables the reporting of more extensive error information in the `ErrorFile`. This option can be turned on in environments where maximal information is desired - even if the resulting logs may be quite large and/or contain @@ -1147,7 +1147,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. from release to release, and across different platforms. By default this option is disabled. -`-XX:FlightRecorderOptions=`*parameter*`=`*value* (or) `-XX:FlightRecorderOptions:`*parameter*`=`*value* +[`-XX:FlightRecorderOptions=`]{#-XX_FlightRecorderOptions}*parameter*`=`*value* (or) `-XX:FlightRecorderOptions:`*parameter*`=`*value* : Sets the parameters that control the behavior of JFR. Multiple parameters can be specified by separating them with a comma. @@ -1207,7 +1207,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. 4 kilobytes. Overriding this parameter could reduce performance and is not recommended. -`-XX:LargePageSizeInBytes=`*size* +[`-XX:LargePageSizeInBytes=`]{#-XX_LargePageSizeInBytes}*size* : Sets the maximum large page size (in bytes) used by the JVM. The *size* argument must be a valid page size supported by the environment to have any effect. Append the letter `k` or `K` to indicate kilobytes, @@ -1221,7 +1221,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. > `-XX:LargePageSizeInBytes=1g` -`-XX:MaxDirectMemorySize=`*size* +[`-XX:MaxDirectMemorySize=`]{#-XX_MaxDirectMemorySize}*size* : Sets the maximum total size (in bytes) of the `java.nio` package, direct-buffer allocations. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate @@ -1237,14 +1237,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. -XX:MaxDirectMemorySize=1048576 ``` -`-XX:-MaxFDLimit` +[`-XX:-MaxFDLimit`]{#-XX__MaxFDLimit} : Disables the attempt to set the soft limit for the number of open file descriptors to the hard limit. By default, this option is enabled on all platforms, but is ignored on Windows. The only time that you may need to disable this is on macOS, where its use imposes a maximum of 10240, which is lower than the actual system maximum. -`-XX:NativeMemoryTracking=`*mode* +[`-XX:NativeMemoryTracking=`]{#-XX_NativeMemoryTracking}*mode* : Specifies the mode for tracking JVM native memory usage. Possible *mode* arguments for this option include the following: @@ -1261,7 +1261,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. usage by individual `CallSite`, individual virtual memory region and its committed regions. -`-XX:TrimNativeHeapInterval=`*millis* +[`-XX:TrimNativeHeapInterval=`]{#-XX_TrimNativeHeapInterval}*millis* : Interval, in ms, at which the JVM will trim the native heap. Lower values will reclaim memory more eagerly at the cost of higher overhead. A value of 0 (default) disables native heap trimming. @@ -1269,7 +1269,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is only supported on Linux with GNU C Library (glibc). -`-XX:ObjectAlignmentInBytes=`*alignment* +[`-XX:ObjectAlignmentInBytes=`]{#-XX_ObjectAlignmentInBytes}*alignment* : Sets the memory alignment of Java objects (in bytes). By default, the value is set to 8 bytes. The specified value should be a power of 2, and must be within the range of 8 and 256 (inclusive). This option makes it possible to @@ -1283,7 +1283,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. increases. As a result, you may not realize any benefits from using compressed pointers with large Java heap sizes. -`-XX:OnError=`*string* +[`-XX:OnError=`]{#-XX_OnError}*string* : Sets a custom command or a series of semicolon-separated commands to run when an irrecoverable error occurs. If the string contains spaces, then it must be enclosed in quotation marks. @@ -1304,7 +1304,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. > `-XX:OnError="userdump.exe %p"` -`-XX:OnOutOfMemoryError=`*string* +[`-XX:OnOutOfMemoryError=`]{#-XX_OnOutOfMemoryError}*string* : Sets a custom command or a series of semicolon-separated commands to run when an `OutOfMemoryError` exception is first thrown by the JVM. If the string @@ -1316,31 +1316,31 @@ These `java` options control the runtime behavior of the Java HotSpot VM. directly from Java code, nor by the JVM for other types of resource exhaustion (such as native thread creation errors). -`-XX:+PrintCommandLineFlags` +[`-XX:+PrintCommandLineFlags`]{#-XX__PrintCommandLineFlags} : Enables printing of ergonomically selected JVM flags that appeared on the command line. It can be useful to know the ergonomic values set by the JVM, such as the heap space size and the selected garbage collector. By default, this option is disabled and flags aren't printed. -`-XX:+PreserveFramePointer` +[`-XX:+PreserveFramePointer`]{#-XX__PreserveFramePointer} : Selects between using the RBP register as a general purpose register (`-XX:-PreserveFramePointer`) and using the RBP register to hold the frame pointer of the currently executing method (`-XX:+PreserveFramePointer`). If the frame pointer is available, then external profiling tools (for example, Linux perf) can construct more accurate stack traces. -`-XX:+PrintNMTStatistics` +[`-XX:+PrintNMTStatistics`]{#-XX__PrintNMTStatistics} : Enables printing of collected native memory tracking data at JVM exit when native memory tracking is enabled (see `-XX:NativeMemoryTracking`). By default, this option is disabled and native memory tracking data isn't printed. -`-XX:SharedArchiveFile=`*path* +[`-XX:SharedArchiveFile=`]{#-XX_SharedArchiveFile}*path* : Specifies the path and name of the class data sharing (CDS) archive file See [Application Class Data Sharing]. -`-XX:+VerifySharedSpaces` +[`-XX:+VerifySharedSpaces`]{#-XX__VerifySharedSpaces} : If this option is specified, the JVM will load a CDS archive file only if it passes an integrity check based on CRC32 checksums. The purpose of this flag is to check for unintentional damage to CDS archive files in transmission or storage. @@ -1348,10 +1348,10 @@ These `java` options control the runtime behavior of the Java HotSpot VM. ensure that the CDS archive files used by Java applications cannot be modified without proper authorization. -`-XX:SharedArchiveConfigFile=`*shared\_config\_file* +[`-XX:SharedArchiveConfigFile=`]{#-XX_SharedArchiveConfigFile}*shared\_config\_file* : Specifies additional shared data added to the archive file. -`-XX:SharedClassListFile=`*file\_name* +[`-XX:SharedClassListFile=`]{#-XX_SharedClassListFile}*file\_name* : Specifies the text file that contains the names of the classes to store in the class data sharing (CDS) archive. This file contains the full name of one class per line, except slashes (`/`) replace dots (`.`). For example, @@ -1369,7 +1369,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. See [Application Class Data Sharing]. -`-XX:+ShowCodeDetailsInExceptionMessages` +[`-XX:+ShowCodeDetailsInExceptionMessages`]{#-XX__ShowCodeDetailsInExceptionMessages} : Enables printing of improved `NullPointerException` messages. When an application throws a `NullPointerException`, the option enables the JVM to analyze the program's bytecode instructions to determine precisely which reference is `null`, @@ -1378,13 +1378,13 @@ These `java` options control the runtime behavior of the Java HotSpot VM. and will be printed as the exception message along with the method, filename, and line number. By default, this option is enabled. -`-XX:+ShowMessageBoxOnError` +[`-XX:+ShowMessageBoxOnError`]{#-XX__ShowMessageBoxOnError} : Enables the display of a dialog box when the JVM experiences an irrecoverable error. This prevents the JVM from exiting and keeps the process active so that you can attach a debugger to it to investigate the cause of the error. By default, this option is disabled. -`-XX:StartFlightRecording:`*parameter*`=`*value* +[`-XX:StartFlightRecording:`]{#-XX_StartFlightRecording_}*parameter*`=`*value* : Starts a JFR recording for the Java application. This option is equivalent to the `JFR.start` diagnostic command that starts a recording during runtime. `-XX:StartFlightRecording:help` prints available options and @@ -1503,7 +1503,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. To only see warnings and errors from JFR during startup set -Xlog:jfr+startup=warning. -`-XX:ThreadStackSize=`*size* +[`-XX:ThreadStackSize=`]{#-XX_ThreadStackSize}*size* : Sets the Java thread stack size (in kilobytes). Use of a scaling suffix, such as `k`, results in the scaling of the kilobytes value so that `-XX:ThreadStackSize=1k` sets the Java thread stack size to 1024\*1024 @@ -1529,7 +1529,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is similar to `-Xss`. -`-XX:+UseCompactObjectHeaders` +[`-XX:+UseCompactObjectHeaders`]{#-XX__UseCompactObjectHeaders} : Enables compact object headers. By default, this option is disabled. Enabling this option reduces memory footprint in the Java heap by 4 bytes per object (on average) and often improves performance. @@ -1538,7 +1538,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. In a future release it is expected to be enabled by default, and eventually will be the only mode of operation. -`-XX:-UseCompressedOops` +[`-XX:-UseCompressedOops`]{#-XX__UseCompressedOops} : Disables the use of compressed pointers. By default, this option is enabled, and compressed pointers are used. This will automatically limit the maximum ergonomically determined Java heap size to the maximum amount @@ -1553,7 +1553,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. It's possible to use compressed pointers with Java heap sizes greater than 32 GB. See the `-XX:ObjectAlignmentInBytes` option. -`-XX:-UseContainerSupport` +[`-XX:-UseContainerSupport`]{#-XX__UseContainerSupport} : **Linux only:** The VM now provides automatic container detection support, which allows the VM to determine the amount of memory and number of processors that are available to a Java process running in docker containers. It uses this @@ -1568,28 +1568,28 @@ These `java` options control the runtime behavior of the Java HotSpot VM. information. See [Enable Logging with the JVM Unified Logging Framework] for a description of using Unified Logging. -`-XX:+UseLargePages` +[`-XX:+UseLargePages`]{#-XX__UseLargePages} : Enables the use of large page memory. By default, this option is disabled and large page memory isn't used. See [Large Pages]. -`-XX:+UseTransparentHugePages` +[`-XX:+UseTransparentHugePages`]{#-XX__UseTransparentHugePages} : **Linux only:** Enables the use of large pages that can dynamically grow or shrink. This option is disabled by default. You may encounter performance problems with transparent huge pages as the OS moves other pages around to create huge pages; this option is made available for experimentation. -`-XX:+AllowUserSignalHandlers` +[`-XX:+AllowUserSignalHandlers`]{#-XX__AllowUserSignalHandlers} : **Non-Windows:** Enables installation of signal handlers by the application. By default, this option is disabled and the application isn't allowed to install signal handlers. -`-XX:VMOptionsFile=`*filename* +[`-XX:VMOptionsFile=`]{#-XX_VMOptionsFile}*filename* : Allows user to specify VM options in a file, for example, `java -XX:VMOptionsFile=/var/my_vm_options HelloWorld`. -`-XX:UseBranchProtection=`*mode* +[`-XX:UseBranchProtection=`]{#-XX_UseBranchProtection}*mode* : **Linux AArch64 only:** Specifies the branch protection mode. All options other than `none` require the VM to have been built with branch protection @@ -1613,14 +1613,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. These `java` options control the dynamic just-in-time (JIT) compilation performed by the Java HotSpot VM. -`-XX:AllocateInstancePrefetchLines=`*lines* +[`-XX:AllocateInstancePrefetchLines=`]{#-XX_AllocateInstancePrefetchLines}*lines* : Sets the number of lines to prefetch ahead of the instance allocation pointer. By default, the number of lines to prefetch is set to 1: > `-XX:AllocateInstancePrefetchLines=1` -`-XX:AllocatePrefetchDistance=`*size* +[`-XX:AllocatePrefetchDistance=`]{#-XX_AllocatePrefetchDistance}*size* : Sets the size (in bytes) of the prefetch distance for object allocation. Memory about to be written with the value of new objects is prefetched up to this distance starting from the address of the last allocated object. @@ -1636,7 +1636,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchDistance=1024` -`-XX:AllocatePrefetchInstr=`*instruction* +[`-XX:AllocatePrefetchInstr=`]{#-XX_AllocatePrefetchInstr}*instruction* : Sets the prefetch instruction to prefetch ahead of the allocation pointer. Possible values are from 0 to 3. The actual instructions behind the values depend on the platform. By default, the prefetch instruction is set to 0: @@ -1644,7 +1644,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchInstr=0` -`-XX:AllocatePrefetchLines=`*lines* +[`-XX:AllocatePrefetchLines=`]{#-XX_AllocatePrefetchLines}*lines* : Sets the number of cache lines to load after the last object allocation by using the prefetch instructions generated in compiled code. The default value is 1 if the last allocated object was an instance, and 3 if it was an @@ -1656,7 +1656,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchLines=5` -`-XX:AllocatePrefetchStepSize=`*size* +[`-XX:AllocatePrefetchStepSize=`]{#-XX_AllocatePrefetchStepSize}*size* : Sets the step size (in bytes) for sequential prefetch instructions. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, `g` or `G` to indicate gigabytes. By default, the step size is @@ -1665,7 +1665,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchStepSize=16` -`-XX:AllocatePrefetchStyle=`*style* +[`-XX:AllocatePrefetchStyle=`]{#-XX_AllocatePrefetchStyle}*style* : Sets the generated code style for prefetch instructions. The *style* argument is an integer from 0 to 3: @@ -1684,12 +1684,12 @@ performed by the Java HotSpot VM. : Generate one prefetch instruction per cache line. -`-XX:+BackgroundCompilation` +[`-XX:+BackgroundCompilation`]{#-XX__BackgroundCompilation} : Enables background compilation. This option is enabled by default. To disable background compilation, specify `-XX:-BackgroundCompilation` (this is equivalent to specifying `-Xbatch`). -`-XX:CICompilerCount=`*threads* +[`-XX:CICompilerCount=`]{#-XX_CICompilerCount}*threads* : Sets the number of compiler threads to use for compilation. By default, the number of compiler threads is selected automatically depending on the number of CPUs and memory available for compiled code. @@ -1697,11 +1697,11 @@ performed by the Java HotSpot VM. > `-XX:CICompilerCount=2` -`-XX:+UseDynamicNumberOfCompilerThreads` +[`-XX:+UseDynamicNumberOfCompilerThreads`]{#-XX__UseDynamicNumberOfCompilerThreads} : Dynamically create compiler thread up to the limit specified by `-XX:CICompilerCount`. This option is enabled by default. -`-XX:CompileCommand=`*command*`,`*method*\[`,`*option*\] +[`-XX:CompileCommand=`]{#-XX_CompileCommand}*command*`,`*method*\[`,`*option*\] : Specifies a *command* to perform on a *method*. For example, to exclude the `indexOf()` method of the `String` class from being compiled, use the following: @@ -1800,7 +1800,7 @@ performed by the Java HotSpot VM. You can suppress this by specifying the `-XX:CompileCommand=quiet` option before other `-XX:CompileCommand` options. -`-XX:CompileCommandFile=`*filename* +[`-XX:CompileCommandFile=`]{#-XX_CompileCommandFile}*filename* : Sets the file from which JIT compiler commands are read. By default, the `.hotspot_compiler` file is used to store commands performed by the JIT compiler. @@ -1814,7 +1814,7 @@ performed by the Java HotSpot VM. If you're using commands for the JIT compiler to perform on methods, then see the `-XX:CompileCommand` option. -`-XX:CompilerDirectivesFile=`*file* +[`-XX:CompilerDirectivesFile=`]{#-XX_CompilerDirectivesFile}*file* : Adds directives from a file to the directives stack when a program starts. See [Compiler Control](https://docs.oracle.com/en/java/javase/12/vm/compiler-control1.html#GUID-94AD8194-786A-4F19-BFFF-278F8E237F3A). @@ -1822,14 +1822,14 @@ performed by the Java HotSpot VM. `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:+CompilerDirectivesPrint` +[`-XX:+CompilerDirectivesPrint`]{#-XX__CompilerDirectivesPrint} : Prints the directives stack when the program starts or when a new directive is added. The `-XX:+CompilerDirectivesPrint` option has to be used together with the `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:CompileOnly=`*methods* +[`-XX:CompileOnly=`]{#-XX_CompileOnly}*methods* : Sets the list of methods (separated by commas) to which compilation should be restricted. Only the specified methods are compiled. @@ -1841,7 +1841,7 @@ performed by the Java HotSpot VM. -XX:CompileCommand=compileonly,methodN ``` -`-XX:CompileThresholdScaling=`*scale* +[`-XX:CompileThresholdScaling=`]{#-XX_CompileThresholdScaling}*scale* : Provides unified control of first compilation. This option controls when methods are first compiled for both the tiered and the nontiered modes of operation. The `CompileThresholdScaling` option has a floating point value @@ -1851,11 +1851,11 @@ performed by the Java HotSpot VM. compilation while values greater than 1.0 delay compilation. Setting `CompileThresholdScaling` to 0 is equivalent to disabling compilation. -`-XX:+DoEscapeAnalysis` +[`-XX:+DoEscapeAnalysis`]{#-XX__DoEscapeAnalysis} : Enables the use of escape analysis. This option is enabled by default. To disable the use of escape analysis, specify `-XX:-DoEscapeAnalysis`. -`-XX:InitialCodeCacheSize=`*size* +[`-XX:InitialCodeCacheSize=`]{#-XX_InitialCodeCacheSize}*size* : Sets the initial code cache size (in bytes). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. The default value depends on the platform. The initial code @@ -1865,11 +1865,11 @@ performed by the Java HotSpot VM. > `-XX:InitialCodeCacheSize=32k` -`-XX:+Inline` +[`-XX:+Inline`]{#-XX__Inline} : Enables method inlining. This option is enabled by default to increase performance. To disable method inlining, specify `-XX:-Inline`. -`-XX:InlineSmallCode=`*size* +[`-XX:InlineSmallCode=`]{#-XX_InlineSmallCode}*size* : Sets the maximum code size (in bytes) for already compiled methods that may be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, @@ -1879,7 +1879,7 @@ performed by the Java HotSpot VM. > `-XX:InlineSmallCode=1000` -`-XX:+LogCompilation` +[`-XX:+LogCompilation`]{#-XX__LogCompilation} : Enables logging of compilation activity to a file named `hotspot.log` in the current working directory. You can specify a different log file path and name using the `-XX:LogFile` option. @@ -1893,7 +1893,7 @@ performed by the Java HotSpot VM. `-XX:+PrintCompilation` option. -`-XX:FreqInlineSize=`*size* +[`-XX:FreqInlineSize=`]{#-XX_FreqInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a hot method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1903,7 +1903,7 @@ performed by the Java HotSpot VM. > `-XX:FreqInlineSize=325` -`-XX:MaxInlineSize=`*size* +[`-XX:MaxInlineSize=`]{#-XX_MaxInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a cold method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1912,7 +1912,7 @@ performed by the Java HotSpot VM. > `-XX:MaxInlineSize=35` -`-XX:C1MaxInlineSize=`*size* +[`-XX:C1MaxInlineSize=`]{#-XX_C1MaxInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a cold method to be inlined. This flag only applies to the C1 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1921,7 +1921,7 @@ performed by the Java HotSpot VM. > `-XX:MaxInlineSize=35` -`-XX:MaxTrivialSize=`*size* +[`-XX:MaxTrivialSize=`]{#-XX_MaxTrivialSize}*size* : Sets the maximum bytecode size (in bytes) of a trivial method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to @@ -1930,7 +1930,7 @@ performed by the Java HotSpot VM. > `-XX:MaxTrivialSize=6` -`-XX:C1MaxTrivialSize=`*size* +[`-XX:C1MaxTrivialSize=`]{#-XX_C1MaxTrivialSize}*size* : Sets the maximum bytecode size (in bytes) of a trivial method to be inlined. This flag only applies to the C1 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to @@ -1939,14 +1939,14 @@ performed by the Java HotSpot VM. > `-XX:MaxTrivialSize=6` -`-XX:MaxNodeLimit=`*nodes* +[`-XX:MaxNodeLimit=`]{#-XX_MaxNodeLimit}*nodes* : Sets the maximum number of nodes to be used during single method compilation. By default the value depends on the features enabled. In the following example the maximum number of nodes is set to 100,000: > `-XX:MaxNodeLimit=100000` -`-XX:NonNMethodCodeHeapSize=`*size* +[`-XX:NonNMethodCodeHeapSize=`]{#-XX_NonNMethodCodeHeapSize}*size* : Sets the size in bytes of the code segment containing nonmethod code. A nonmethod code segment containing nonmethod code, such as compiler @@ -1954,16 +1954,16 @@ performed by the Java HotSpot VM. cache forever. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:NonProfiledCodeHeapSize=`*size* +[`-XX:NonProfiledCodeHeapSize=`]{#-XX_NonProfiledCodeHeapSize}*size* : Sets the size in bytes of the code segment containing nonprofiled methods. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:+OptimizeStringConcat` +[`-XX:+OptimizeStringConcat`]{#-XX__OptimizeStringConcat} : Enables the optimization of `String` concatenation operations. This option is enabled by default. To disable the optimization of `String` concatenation operations, specify `-XX:-OptimizeStringConcat`. -`-XX:+PrintAssembly` +[`-XX:+PrintAssembly`]{#-XX__PrintAssembly} : Enables printing of assembly code for bytecoded and native methods by using the external `hsdis-.so` or `.dll` library. For 64-bit VM on Windows, it's `hsdis-amd64.dll`. This lets you to see the generated code, which may @@ -1973,11 +1973,11 @@ performed by the Java HotSpot VM. `-XX:+PrintAssembly` option has to be used together with the `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:ProfiledCodeHeapSize=`*size* +[`-XX:ProfiledCodeHeapSize=`]{#-XX_ProfiledCodeHeapSize}*size* : Sets the size in bytes of the code segment containing profiled methods. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:+PrintCompilation` +[`-XX:+PrintCompilation`]{#-XX__PrintCompilation} : Enables verbose diagnostic output from the JVM by printing a message to the console every time a method is compiled. This lets you to see which methods actually get compiled. By default, this option is disabled and diagnostic @@ -1986,7 +1986,7 @@ performed by the Java HotSpot VM. You can also log compilation activity to a file by using the `-XX:+LogCompilation` option. -`-XX:+PrintInlining` +[`-XX:+PrintInlining`]{#-XX__PrintInlining} : Enables printing of inlining decisions. This let's you see which methods are getting inlined. @@ -1995,7 +1995,7 @@ performed by the Java HotSpot VM. `-XX:+UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:ReservedCodeCacheSize=`*size* +[`-XX:ReservedCodeCacheSize=`]{#-XX_ReservedCodeCacheSize}*size* : Sets the maximum code cache size (in bytes) for JIT-compiled code. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. The default maximum code @@ -2005,7 +2005,7 @@ performed by the Java HotSpot VM. size shouldn't be less than the initial code cache size; see the option `-XX:InitialCodeCacheSize`. -`-XX:+SegmentedCodeCache` +[`-XX:+SegmentedCodeCache`]{#-XX__SegmentedCodeCache} : Enables segmentation of the code cache, without which the code cache consists of one large segment. With `-XX:+SegmentedCodeCache`, separate segments will be used for non-method, profiled method, and non-profiled @@ -2018,29 +2018,29 @@ performed by the Java HotSpot VM. (`-XX:+TieredCompilation` ) and the reserved code cache size (`-XX:ReservedCodeCacheSize`) is at least 240 MB. -`-XX:StartAggressiveSweepingAt=`*percent* +[`-XX:StartAggressiveSweepingAt=`]{#-XX_StartAggressiveSweepingAt}*percent* : Forces stack scanning of active methods to aggressively remove unused code when only the given percentage of the code cache is free. The default value is 10%. -`-XX:-TieredCompilation` +[`-XX:-TieredCompilation`]{#-XX__TieredCompilation} : Disables the use of tiered compilation. By default, this option is enabled. -`-XX:UseSSE=`*version* +[`-XX:UseSSE=`]{#-XX_UseSSE}*version* : Enables the use of SSE instruction set of a specified version. Is set by default to the highest supported version available (x86 only). -`-XX:UseAVX=`*version* +[`-XX:UseAVX=`]{#-XX_UseAVX}*version* : Enables the use of AVX instruction set of a specified version. Is set by default to the highest supported version available (x86 only). -`-XX:+UseAES` +[`-XX:+UseAES`]{#-XX__UseAES} : Enables hardware-based AES intrinsics for hardware that supports it. This option is on by default on hardware that has the necessary instructions. The `-XX:+UseAES` is used in conjunction with `UseAESIntrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAESIntrinsics` +[`-XX:+UseAESIntrinsics`]{#-XX__UseAESIntrinsics} : Enables AES intrinsics. Specifying `-XX:+UseAESIntrinsics` is equivalent to also enabling `-XX:+UseAES`. To disable hardware-based AES intrinsics, specify `-XX:-UseAES -XX:-UseAESIntrinsics`. For example, to enable hardware @@ -2051,47 +2051,47 @@ performed by the Java HotSpot VM. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAESCTRIntrinsics` +[`-XX:+UseAESCTRIntrinsics`]{#-XX__UseAESCTRIntrinsics} : Analogous to `-XX:+UseAESIntrinsics` enables AES/CTR intrinsics. -`-XX:+UseGHASHIntrinsics` +[`-XX:+UseGHASHIntrinsics`]{#-XX__UseGHASHIntrinsics} : Controls the use of GHASH intrinsics. Enabled by default on platforms that support the corresponding instructions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseChaCha20Intrinsics` +[`-XX:+UseChaCha20Intrinsics`]{#-XX__UseChaCha20Intrinsics} : Enable ChaCha20 intrinsics. This option is on by default for supported platforms. To disable ChaCha20 intrinsics, specify `-XX:-UseChaCha20Intrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UsePoly1305Intrinsics` +[`-XX:+UsePoly1305Intrinsics`]{#-XX__UsePoly1305Intrinsics} : Enable Poly1305 intrinsics. This option is on by default for supported platforms. To disable Poly1305 intrinsics, specify `-XX:-UsePoly1305Intrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseBASE64Intrinsics` +[`-XX:+UseBASE64Intrinsics`]{#-XX__UseBASE64Intrinsics} : Controls the use of accelerated BASE64 encoding routines for `java.util.Base64`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAdler32Intrinsics` +[`-XX:+UseAdler32Intrinsics`]{#-XX__UseAdler32Intrinsics} : Controls the use of Adler32 checksum algorithm intrinsic for `java.util.zip.Adler32`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCRC32Intrinsics` +[`-XX:+UseCRC32Intrinsics`]{#-XX__UseCRC32Intrinsics} : Controls the use of CRC32 intrinsics for `java.util.zip.CRC32`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCRC32CIntrinsics` +[`-XX:+UseCRC32CIntrinsics`]{#-XX__UseCRC32CIntrinsics} : Controls the use of CRC32C intrinsics for `java.util.zip.CRC32C`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA` +[`-XX:+UseSHA`]{#-XX__UseSHA} : Enables hardware-based intrinsics for SHA crypto hash functions for some hardware. The `UseSHA` option is used in conjunction with the `UseSHA1Intrinsics`, `UseSHA256Intrinsics`, and `UseSHA512Intrinsics` @@ -2108,26 +2108,26 @@ performed by the Java HotSpot VM. disable only a particular SHA intrinsic, use the appropriate corresponding option. For example: `-XX:-UseSHA256Intrinsics`. -`-XX:+UseSHA1Intrinsics` +[`-XX:+UseSHA1Intrinsics`]{#-XX__UseSHA1Intrinsics} : Enables intrinsics for SHA-1 crypto hash function. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA256Intrinsics` +[`-XX:+UseSHA256Intrinsics`]{#-XX__UseSHA256Intrinsics} : Enables intrinsics for SHA-224 and SHA-256 crypto hash functions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA512Intrinsics` +[`-XX:+UseSHA512Intrinsics`]{#-XX__UseSHA512Intrinsics} : Enables intrinsics for SHA-384 and SHA-512 crypto hash functions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseMathExactIntrinsics` +[`-XX:+UseMathExactIntrinsics`]{#-XX__UseMathExactIntrinsics} : Enables intrinsification of various `java.lang.Math.*Exact()` functions. Enabled by default. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseMultiplyToLenIntrinsic` +[`-XX:+UseMultiplyToLenIntrinsic`]{#-XX__UseMultiplyToLenIntrinsic} : Enables intrinsification of `BigInteger.multiplyToLen()`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. @@ -2152,44 +2152,44 @@ performed by the Java HotSpot VM. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCMoveUnconditionally` +[`-XX:+UseCMoveUnconditionally`]{#-XX__UseCMoveUnconditionally} : Generates CMove (scalar and vector) instructions regardless of profitability analysis. -`-XX:+UseCodeCacheFlushing` +[`-XX:+UseCodeCacheFlushing`]{#-XX__UseCodeCacheFlushing} : Enables flushing of the code cache before shutting down the compiler. This option is enabled by default. To disable flushing of the code cache before shutting down the compiler, specify `-XX:-UseCodeCacheFlushing`. -`-XX:+UseCondCardMark` +[`-XX:+UseCondCardMark`]{#-XX__UseCondCardMark} : Enables checking if the card is already marked before updating the card table. This option is disabled by default. It should be used only on machines with multiple sockets, where it increases the performance of Java applications that rely on concurrent operations. -`-XX:+UseCountedLoopSafepoints` +[`-XX:+UseCountedLoopSafepoints`]{#-XX__UseCountedLoopSafepoints} : Keeps safepoints in counted loops. Its default value depends on whether the selected garbage collector requires low latency safepoints. -`-XX:LoopStripMiningIter=`*number_of_iterations* +[`-XX:LoopStripMiningIter=`]{#-XX_LoopStripMiningIter}*number_of_iterations* : Controls the number of iterations in the inner strip mined loop. Strip mining transforms counted loops into two level nested loops. Safepoints are kept in the outer loop while the inner loop can execute at full speed. This option controls the maximum number of iterations in the inner loop. The default value is 1,000. -`-XX:LoopStripMiningIterShortLoop=`*number_of_iterations* +[`-XX:LoopStripMiningIterShortLoop=`]{#-XX_LoopStripMiningIterShortLoop}*number_of_iterations* : Controls loop strip mining optimization. Loops with the number of iterations less than specified will not have safepoints in them. Default value is 1/10th of `-XX:LoopStripMiningIter`. -`-XX:+UseFMA` +[`-XX:+UseFMA`]{#-XX__UseFMA} : Enables hardware-based FMA intrinsics for hardware where FMA instructions are available (such as, Intel and ARM64). FMA intrinsics are generated for the `java.lang.Math.fma(`*a*`,` *b*`,` *c*`)` methods that calculate the value of `(` *a* `*` *b* `+` *c* `)` expressions. -`-XX:+UseSuperWord` +[`-XX:+UseSuperWord`]{#-XX__UseSuperWord} : Enables the transformation of scalar operations into superword operations. Superword is a vectorization optimization. This option is enabled by default. To disable the transformation of scalar operations into superword @@ -2200,7 +2200,7 @@ performed by the Java HotSpot VM. These `java` options provide the ability to gather system information and perform extensive debugging. -`-XX:+DisableAttachMechanism` +[`-XX:+DisableAttachMechanism`]{#-XX__DisableAttachMechanism} : Disables the mechanism that lets tools attach to the JVM. By default, this option is disabled, meaning that the attach mechanism is enabled and you can use diagnostics and troubleshooting tools such as `jcmd`, `jstack`, @@ -2211,17 +2211,17 @@ perform extensive debugging. supported when using the tools from one JDK version to troubleshoot a different JDK version. -`-XX:+DTraceAllocProbes` +[`-XX:+DTraceAllocProbes`]{#-XX__DTraceAllocProbes} : **Linux and macOS:** Enable `dtrace` tool probes for object allocation. -`-XX:+DTraceMethodProbes` +[`-XX:+DTraceMethodProbes`]{#-XX__DTraceMethodProbes} : **Linux and macOS:** Enable `dtrace` tool probes for method-entry and method-exit. -`-XX:+DTraceMonitorProbes` +[`-XX:+DTraceMonitorProbes`]{#-XX__DTraceMonitorProbes} : **Linux and macOS:** Enable `dtrace` tool probes for monitor events. -`-XX:+HeapDumpOnOutOfMemoryError` +[`-XX:+HeapDumpOnOutOfMemoryError`]{#-XX__HeapDumpOnOutOfMemoryError} : Enables the dumping of the Java heap to a file in the current directory by using the heap profiler (HPROF) when a `java.lang.OutOfMemoryError` exception is thrown by the JVM. You can explicitly set the heap dump file path and @@ -2233,7 +2233,7 @@ perform extensive debugging. directly from Java code, nor by the JVM for other types of resource exhaustion (such as native thread creation errors). -`-XX:HeapDumpPath=`*path* +[`-XX:HeapDumpPath=`]{#-XX_HeapDumpPath}*path* : Sets the path and file name for writing the heap dump provided by the heap profiler (HPROF) when the `-XX:+HeapDumpOnOutOfMemoryError` option is set. By default, the file is created in the current working directory, and it's @@ -2253,7 +2253,7 @@ perform extensive debugging. > `-XX:HeapDumpPath=C:/log/java/java_heapdump.log` -`-XX:LogFile=`*path* +[`-XX:LogFile=`]{#-XX_LogFile}*path* : Sets the path and file name to where log data is written. By default, the file is created in the current working directory, and it's named `hotspot.log`. @@ -2268,7 +2268,7 @@ perform extensive debugging. > `-XX:LogFile=C:/log/java/hotspot.log` -`-XX:+PrintClassHistogram` +[`-XX:+PrintClassHistogram`]{#-XX__PrintClassHistogram} : Enables printing of a class instance histogram after one of the following events: @@ -2282,7 +2282,7 @@ perform extensive debugging. the `jcmd` *pid* `GC.class_histogram` command, where *pid* is the current Java process identifier. -`-XX:+PrintConcurrentLocks` +[`-XX:+PrintConcurrentLocks`]{#-XX__PrintConcurrentLocks} : Enables printing of `java.util.concurrent` locks after one of the following events: @@ -2296,12 +2296,12 @@ perform extensive debugging. `jcmd` *pid* `Thread.print -l` command, where *pid* is the current Java process identifier. -`-XX:+PrintFlagsRanges` +[`-XX:+PrintFlagsRanges`]{#-XX__PrintFlagsRanges} : Prints the range specified and allows automatic testing of the values. See [Validate Java Virtual Machine Flag Arguments]. -`-XX:+PerfDataSaveToFile` +[`-XX:+PerfDataSaveToFile`]{#-XX__PerfDataSaveToFile} : If enabled, saves [jstat](jstat.html) binary data when the Java application exits. This binary data is saved in a file named `hsperfdata_`*pid*, where *pid* is the process identifier of the Java application that you ran. Use @@ -2312,7 +2312,7 @@ perform extensive debugging. > `jstat -gc file:///`*path*`/hsperfdata_`*pid* -`-XX:+UsePerfData` +[`-XX:+UsePerfData`]{#-XX__UsePerfData} : Enables the `perfdata` feature. This option is enabled by default to allow JVM monitoring and performance testing. Disabling it suppresses the creation of the `hsperfdata_userid` directories. To disable the `perfdata` @@ -2323,13 +2323,13 @@ perform extensive debugging. These `java` options control how garbage collection (GC) is performed by the Java HotSpot VM. -`-XX:+AlwaysPreTouch` +[`-XX:+AlwaysPreTouch`]{#-XX__AlwaysPreTouch} : Requests the VM to touch every page on the Java heap after requesting it from the operating system and before handing memory out to the application. By default, this option is disabled and all pages are committed as the application uses the heap space. -`-XX:ConcGCThreads=`*threads* +[`-XX:ConcGCThreads=`]{#-XX_ConcGCThreads}*threads* : Sets the number of threads used for concurrent GC. Sets *`threads`* to approximately 1/4 of the number of parallel garbage collection threads. The default value depends on the number of CPUs available to the JVM. @@ -2339,24 +2339,24 @@ Java HotSpot VM. > `-XX:ConcGCThreads=2` -`-XX:+DisableExplicitGC` +[`-XX:+DisableExplicitGC`]{#-XX__DisableExplicitGC} : Enables the option that disables processing of calls to the `System.gc()` method. This option is disabled by default, meaning that calls to `System.gc()` are processed. If processing of calls to `System.gc()` is disabled, then the JVM still performs GC when necessary. -`-XX:+ExplicitGCInvokesConcurrent` +[`-XX:+ExplicitGCInvokesConcurrent`]{#-XX__ExplicitGCInvokesConcurrent} : Enables invoking of concurrent GC by using the `System.gc()` request. This option is disabled by default and can be enabled only with the `-XX:+UseG1GC` option. -`-XX:G1AdaptiveIHOPNumInitialSamples=`*number* +[`-XX:G1AdaptiveIHOPNumInitialSamples=`]{#-XX_G1AdaptiveIHOPNumInitialSamples}*number* : When `-XX:UseAdaptiveIHOP` is enabled, this option sets the number of completed marking cycles used to gather samples until G1 adaptively determines the optimum value of `-XX:InitiatingHeapOccupancyPercent`. Before, G1 uses the value of `-XX:InitiatingHeapOccupancyPercent` directly for this purpose. The default value is 3. -`-XX:G1HeapRegionSize=`*size* +[`-XX:G1HeapRegionSize=`]{#-XX_G1HeapRegionSize}*size* : Sets the size of the regions into which the Java heap is subdivided when using the garbage-first (G1) collector. The value is a power of 2 and can range from 1 MB to 32 MB. The default region size is determined @@ -2367,44 +2367,44 @@ Java HotSpot VM. > `-XX:G1HeapRegionSize=16m` -`-XX:G1HeapWastePercent=`*percent* +[`-XX:G1HeapWastePercent=`]{#-XX_G1HeapWastePercent}*percent* : Sets the percentage of heap that you're willing to waste. The Java HotSpot VM doesn't initiate the mixed garbage collection cycle when the reclaimable percentage is less than the heap waste percentage. The default is 5 percent. -`-XX:G1MaxNewSizePercent=`*percent* +[`-XX:G1MaxNewSizePercent=`]{#-XX_G1MaxNewSizePercent}*percent* : Sets the percentage of the heap size to use as the maximum for the young generation size. The default value is 60 percent of your Java heap. This is an experimental flag. This setting replaces the `-XX:DefaultMaxNewGenPercent` setting. -`-XX:G1MixedGCCountTarget=`*number* +[`-XX:G1MixedGCCountTarget=`]{#-XX_G1MixedGCCountTarget}*number* : Sets the target number of mixed garbage collections after a marking cycle to collect old regions with at most `G1MixedGCLIveThresholdPercent` live data. The default is 8 mixed garbage collections. The goal for mixed collections is to be within this target number. -`-XX:G1MixedGCLiveThresholdPercent=`*percent* +[`-XX:G1MixedGCLiveThresholdPercent=`]{#-XX_G1MixedGCLiveThresholdPercent}*percent* : Sets the occupancy threshold for an old region to be included in a mixed garbage collection cycle. The default occupancy is 85 percent. This is an experimental flag. This setting replaces the `-XX:G1OldCSetRegionLiveThresholdPercent` setting. -`-XX:G1NewSizePercent=`*percent* +[`-XX:G1NewSizePercent=`]{#-XX_G1NewSizePercent}*percent* : Sets the percentage of the heap to use as the minimum for the young generation size. The default value is 5 percent of your Java heap. This is an experimental flag. This setting replaces the `-XX:DefaultMinNewGenPercent` setting. -`-XX:G1OldCSetRegionThresholdPercent=`*percent* +[`-XX:G1OldCSetRegionThresholdPercent=`]{#-XX_G1OldCSetRegionThresholdPercent}*percent* : Sets an upper limit on the number of old regions to be collected during a mixed garbage collection cycle. The default is 10 percent of the Java heap. -`-XX:G1ReservePercent=`*percent* +[`-XX:G1ReservePercent=`]{#-XX_G1ReservePercent}*percent* : Sets the percentage of the heap (0 to 50) that's reserved as a false ceiling to reduce the possibility of promotion failure for the G1 collector. When you increase or decrease the percentage, ensure that you @@ -2415,7 +2415,7 @@ Java HotSpot VM. > `-XX:G1ReservePercent=20` -`-XX:+G1UseAdaptiveIHOP` +[`-XX:+G1UseAdaptiveIHOP`]{#-XX__G1UseAdaptiveIHOP} : Controls adaptive calculation of the old generation occupancy to start background work preparing for an old generation collection. If enabled, G1 uses `-XX:InitiatingHeapOccupancyPercent` for the first few times as @@ -2428,7 +2428,7 @@ Java HotSpot VM. The default is enabled. -`-XX:InitialHeapSize=`*size* +[`-XX:InitialHeapSize=`]{#-XX_InitialHeapSize}*size* : Sets the initial size (in bytes) of the memory allocation pool. This value must be either 0, or a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, @@ -2452,7 +2452,7 @@ Java HotSpot VM. command line, then the initial heap size gets set to the value specified with `-Xms`. -`-XX:InitialRAMPercentage=`*percent* +[`-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. @@ -2462,7 +2462,7 @@ Java HotSpot VM. > `-XX:InitialRAMPercentage=5` -`-XX:InitialSurvivorRatio=`*ratio* +[`-XX:InitialSurvivorRatio=`]{#-XX_InitialSurvivorRatio}*ratio* : Sets the initial survivor space ratio used by the throughput garbage collector (which is enabled by the `-XX:+UseParallelGC` option). Adaptive sizing is enabled by default with the throughput garbage collector by @@ -2491,7 +2491,7 @@ Java HotSpot VM. > `-XX:InitialSurvivorRatio=4` -`-XX:InitiatingHeapOccupancyPercent=`*percent* +[`-XX:InitiatingHeapOccupancyPercent=`]{#-XX_InitiatingHeapOccupancyPercent}*percent* : Sets the percentage of the old generation occupancy (0 to 100) at which to start the first few concurrent marking cycles for the G1 garbage collector. @@ -2506,7 +2506,7 @@ Java HotSpot VM. > `-XX:InitiatingHeapOccupancyPercent=75` -`-XX:MaxGCPauseMillis=`*time* +[`-XX:MaxGCPauseMillis=`]{#-XX_MaxGCPauseMillis}*time* : Sets a target for the maximum GC pause time (in milliseconds). This is a soft goal, and the JVM will make its best effort to achieve it. Only G1 and Parallel support a maximum GC pause time target. For G1, the default @@ -2517,7 +2517,7 @@ Java HotSpot VM. > `-XX:MaxGCPauseMillis=500` -`-XX:MaxHeapSize=`*size* +[`-XX:MaxHeapSize=`]{#-XX_MaxHeapSize}*size* : Sets the maximum size (in byes) of the memory allocation pool. This value must be a multiple of 1024 and greater than 2 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -2537,7 +2537,7 @@ Java HotSpot VM. The `-XX:MaxHeapSize` option is equivalent to `-Xmx`. -`-XX:MaxHeapFreeRatio=`*percent* +[`-XX:MaxHeapFreeRatio=`]{#-XX_MaxHeapFreeRatio}*percent* : Sets the maximum allowed percentage of free heap space (0 to 100) after a GC event. If free heap space expands above this value, then the heap is shrunk. By default, this value is set to 70%. @@ -2558,7 +2558,7 @@ Java HotSpot VM. description of using this option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:MaxMetaspaceSize=`*size* +[`-XX:MaxMetaspaceSize=`]{#-XX_MaxMetaspaceSize}*size* : Sets the maximum amount of native memory that can be allocated for class metadata. By default, the size isn't limited. The amount of metadata for an application depends on the application itself, other running applications, @@ -2569,11 +2569,11 @@ Java HotSpot VM. > `-XX:MaxMetaspaceSize=256m` -`-XX:MaxNewSize=`*size* +[`-XX:MaxNewSize=`]{#-XX_MaxNewSize}*size* : Sets the maximum size (in bytes) of the heap for the young generation (nursery). The default value is set ergonomically. -`-XX:MaxRAMPercentage=`*percent* +[`-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 @@ -2589,7 +2589,7 @@ Java HotSpot VM. > `-XX:MaxRAMPercentage=75` -`-XX:MinRAMPercentage=`*percent* +[`-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 @@ -2600,7 +2600,7 @@ Java HotSpot VM. > `-XX:MinRAMPercentage=75` -`-XX:MaxTenuringThreshold=`*threshold* +[`-XX:MaxTenuringThreshold=`]{#-XX_MaxTenuringThreshold}*threshold* : Sets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector. @@ -2610,13 +2610,13 @@ Java HotSpot VM. > `-XX:MaxTenuringThreshold=10` -`-XX:MetaspaceSize=`*size* +[`-XX:MetaspaceSize=`]{#-XX_MetaspaceSize}*size* : Sets the size of the allocated class metadata space that triggers a garbage collection the first time it's exceeded. This threshold for a garbage collection is increased or decreased depending on the amount of metadata used. The default size depends on the platform. -`-XX:MinHeapFreeRatio=`*percent* +[`-XX:MinHeapFreeRatio=`]{#-XX_MinHeapFreeRatio}*percent* : Sets the minimum allowed percentage of free heap space (0 to 100) after a GC event. If free heap space falls below this value, then the heap is expanded. By default, this value is set to 40%. @@ -2637,7 +2637,7 @@ Java HotSpot VM. description of using this option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:MinHeapSize=`*size* +[`-XX:MinHeapSize=`]{#-XX_MinHeapSize}*size* : Sets the minimum size (in bytes) of the memory allocation pool. This value must be either 0, or a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, @@ -2656,14 +2656,14 @@ Java HotSpot VM. If you set this option to 0, then the minimum size is set to the same value as the initial size. -`-XX:NewRatio=`*ratio* +[`-XX:NewRatio=`]{#-XX_NewRatio}*ratio* : Sets the ratio between young and old generation sizes. By default, this option is set to 2. The following example shows how to set the young-to-old ratio to 1: > `-XX:NewRatio=1` -`-XX:NewSize=`*size* +[`-XX:NewSize=`]{#-XX_NewSize}*size* : Sets the initial size (in bytes) of the heap for the young generation (nursery). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. @@ -2687,7 +2687,7 @@ Java HotSpot VM. The `-XX:NewSize` option is equivalent to `-Xmn`. -`-XX:ParallelGCThreads=`*threads* +[`-XX:ParallelGCThreads=`]{#-XX_ParallelGCThreads}*threads* : Sets the number of the stop-the-world (STW) worker threads. The default value depends on the number of CPUs available to the JVM and the garbage collector selected. @@ -2697,11 +2697,11 @@ Java HotSpot VM. > `-XX:ParallelGCThreads=2` -`-XX:+PrintAdaptiveSizePolicy` +[`-XX:+PrintAdaptiveSizePolicy`]{#-XX__PrintAdaptiveSizePolicy} : Enables printing of information about adaptive-generation sizing. By default, this option is disabled. -`-XX:SoftRefLRUPolicyMSPerMB=`*time* +[`-XX:SoftRefLRUPolicyMSPerMB=`]{#-XX_SoftRefLRUPolicyMSPerMB}*time* : Sets the amount of time (in milliseconds) a softly reachable object is kept active on the heap after the last time it was referenced. The default value is one second of lifetime per free megabyte in the heap. The @@ -2718,7 +2718,7 @@ Java HotSpot VM. `-XX:SoftRefLRUPolicyMSPerMB=2500` -`-XX:-ShrinkHeapInSteps` +[`-XX:-ShrinkHeapInSteps`]{#-XX__ShrinkHeapInSteps} : Incrementally reduces the Java heap to the target size, specified by the option `-XX:MaxHeapFreeRatio`. This option is enabled by default. If disabled, then it immediately reduces the Java heap to the target size @@ -2730,7 +2730,7 @@ Java HotSpot VM. `MaxHeapFreeRatio` option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:StringDeduplicationAgeThreshold=`*threshold* +[`-XX:StringDeduplicationAgeThreshold=`]{#-XX_StringDeduplicationAgeThreshold}*threshold* : Identifies `String` objects reaching the specified age that are considered candidates for deduplication. An object's age is a measure of how many times it has survived garbage collection. This is sometimes referred to as @@ -2741,14 +2741,14 @@ Java HotSpot VM. default value for this option is `3`. See the `-XX:+UseStringDeduplication` option. -`-XX:SurvivorRatio=`*ratio* +[`-XX:SurvivorRatio=`]{#-XX_SurvivorRatio}*ratio* : Sets the ratio between eden space size and survivor space size. By default, this option is set to 8. The following example shows how to set the eden/survivor space ratio to 4: > `-XX:SurvivorRatio=4` -`-XX:TargetSurvivorRatio=`*percent* +[`-XX:TargetSurvivorRatio=`]{#-XX_TargetSurvivorRatio}*percent* : Sets the desired percentage of survivor space (0 to 100) used after young garbage collection. By default, this option is set to 50%. @@ -2757,7 +2757,7 @@ Java HotSpot VM. > `-XX:TargetSurvivorRatio=30` -`-XX:TLABSize=`*size* +[`-XX:TLABSize=`]{#-XX_TLABSize}*size* : Sets the initial size (in bytes) of a thread-local allocation buffer (TLAB). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. If this option is @@ -2767,13 +2767,13 @@ Java HotSpot VM. > `-XX:TLABSize=512k` -`-XX:+UseAdaptiveSizePolicy` +[`-XX:+UseAdaptiveSizePolicy`]{#-XX__UseAdaptiveSizePolicy} : Enables the use of adaptive generation sizing. This option is enabled by default. To disable adaptive generation sizing, specify `-XX:-UseAdaptiveSizePolicy` and set the size of the memory allocation pool explicitly. See the `-XX:SurvivorRatio` option. -`-XX:+UseG1GC` +[`-XX:+UseG1GC`]{#-XX__UseG1GC} : Enables the use of the garbage-first (G1) garbage collector. It's a server-style garbage collector, targeted for multiprocessor machines with a large amount of RAM. This option meets GC pause time goals with high @@ -2783,7 +2783,7 @@ Java HotSpot VM. pause time below 0.5 seconds). By default, this option is enabled and G1 is used as the default garbage collector. -`-XX:+UseGCOverheadLimit` +[`-XX:+UseGCOverheadLimit`]{#-XX__UseGCOverheadLimit} : Enables the use of a policy that limits the proportion of time spent by the JVM on GC before an `OutOfMemoryError` exception is thrown. This option is enabled, by default, and the parallel GC will throw an `OutOfMemoryError` @@ -2793,26 +2793,26 @@ Java HotSpot VM. little or no progress. To disable this option, specify the option `-XX:-UseGCOverheadLimit`. -`-XX:+UseNUMA` +[`-XX:+UseNUMA`]{#-XX__UseNUMA} : Enables performance optimization of an application on a machine with nonuniform memory architecture (NUMA) by increasing the application's use of lower latency memory. The default value for this option depends on the garbage collector. -`-XX:+UseParallelGC` +[`-XX:+UseParallelGC`]{#-XX__UseParallelGC} : Enables the use of the parallel scavenge garbage collector (also known as the throughput collector) to improve the performance of your application by leveraging multiple processors. By default, this option is disabled and the default collector is used. -`-XX:+UseSerialGC` +[`-XX:+UseSerialGC`]{#-XX__UseSerialGC} : Enables the use of the serial garbage collector. This is generally the best choice for small and simple applications that don't require any special functionality from garbage collection. By default, this option is disabled and the default collector is used. -`-XX:+UseStringDeduplication` +[`-XX:+UseStringDeduplication`]{#-XX__UseStringDeduplication} : Enables string deduplication. By default, this option is disabled. To use this option, you must enable the garbage-first (G1) garbage collector. @@ -2822,34 +2822,34 @@ Java HotSpot VM. character array, identical `String` objects can point to and share the same character array. -`-XX:+UseTLAB` +[`-XX:+UseTLAB`]{#-XX__UseTLAB} : Enables the use of thread-local allocation blocks (TLABs) in the young generation space. This option is enabled by default. To disable the use of TLABs, specify the option `-XX:-UseTLAB`. -`-XX:+UseZGC` +[`-XX:+UseZGC`]{#-XX__UseZGC} : Enables the use of the Z garbage collector (ZGC). This is a low latency garbage collector, providing max pause times of a few milliseconds, at some throughput cost. Pause times are independent of what heap size is used. Supports heap sizes from 8MB to 16TB. -`-XX:ZAllocationSpikeTolerance=`*factor* +[`-XX:ZAllocationSpikeTolerance=`]{#-XX_ZAllocationSpikeTolerance}*factor* : Sets the allocation spike tolerance for ZGC. By default, this option is set to 2.0. This factor describes the level of allocation spikes to expect. For example, using a factor of 3.0 means the current allocation rate can be expected to triple at any time. -`-XX:ZCollectionInterval=`*seconds* +[`-XX:ZCollectionInterval=`]{#-XX_ZCollectionInterval}*seconds* : Sets the maximum interval (in seconds) between two GC cycles when using ZGC. By default, this option is set to 0 (disabled). -`-XX:ZFragmentationLimit=`*percent* +[`-XX:ZFragmentationLimit=`]{#-XX_ZFragmentationLimit}*percent* : Sets the maximum acceptable heap fragmentation (in percent) for ZGC. By default, this option is set to 25. Using a lower value will cause the heap to be compacted more aggressively, to reclaim more memory at the cost of using more CPU time. -`-XX:+ZProactive` +[`-XX:+ZProactive`]{#-XX__ZProactive} : Enables proactive GC cycles when using ZGC. By default, this option is enabled. ZGC will start a proactive GC cycle if doing so is expected to have minimal impact on the running application. This is useful if the @@ -2857,27 +2857,27 @@ Java HotSpot VM. want to keep the heap size down and allow reference processing to happen even when there are a lot of free space on the heap. -`-XX:+ZUncommit` +[`-XX:+ZUncommit`]{#-XX__ZUncommit} : Enables uncommitting of unused heap memory when using ZGC. By default, this option is enabled. Uncommitting unused heap memory will lower the memory footprint of the JVM, and make that memory available for other processes to use. -`-XX:ZUncommitDelay=`*seconds* +[`-XX:ZUncommitDelay=`]{#-XX_ZUncommitDelay}*seconds* : Sets the amount of time (in seconds) that heap memory must have been unused before being uncommitted. By default, this option is set to 300 (5 minutes). Committing and uncommitting memory are relatively expensive operations. Using a lower value will cause heap memory to be uncommitted earlier, at the risk of soon having to commit it again. -`-XX:+UseShenandoahGC` +[`-XX:+UseShenandoahGC`]{#-XX__UseShenandoahGC} : Enables the use of the Shenandoah garbage collector. This is a low pause time, concurrent garbage collector. Its pause times are not proportional to the size of the heap. Shenandoah garbage collector can work with compressed pointers. See `-XX:UseCompressedOops` for further information about compressed pointers. -`-XX:ShenandoahGCMode=`*mode* +[`-XX:ShenandoahGCMode=`]{#-XX_ShenandoahGCMode}*mode* : Sets the GC mode for Shenandoah GC to use. By default, this option is set to `satb`. Among other things, this defines which barriers are in use. Possible mode values include the following: @@ -2891,7 +2891,7 @@ Java HotSpot VM. generational. Please see [JEP 404](https://openjdk.org/jeps/404) and [JEP 521](https://openjdk.org/jeps/521) for its advantages and risks. -`-XX:ShenandoahGCHeuristics=`*heuristics* +[`-XX:ShenandoahGCHeuristics=`]{#-XX_ShenandoahGCHeuristics}*heuristics* : Sets the heuristics for Shenandoah GC to use. By default, this option is set to `adaptive`. This fine-tunes the GC mode selected, by choosing when to start the GC, how much to process on each cycle, and what other features @@ -2916,7 +2916,7 @@ These `java` options are deprecated and might be removed in a future JDK release. They're still accepted and acted upon, but a warning is issued when they're used. -`-Xloggc:`*filename* +[`-Xloggc:`]{#-Xloggc_}*filename* : Sets the file to which verbose GC events information should be redirected for logging. The `-Xloggc` option overrides `-verbose:gc` if both are given with the same java command. `-Xloggc:`*filename* is replaced by @@ -2927,11 +2927,11 @@ they're used. `-Xlog:gc:garbage-collection.log` -`-XX:+FlightRecorder` +[`-XX:+FlightRecorder`]{#-XX__FlightRecorder} : Enables the use of Java Flight Recorder (JFR) during the runtime of the application. Since JDK 8u40 this option has not been required to use JFR. -`-XX:+ParallelRefProcEnabled` +[`-XX:+ParallelRefProcEnabled`]{#-XX__ParallelRefProcEnabled} : Enables parallel reference processing. By default, collectors employing multiple threads perform parallel reference processing if the number of parallel threads to use is larger than one. @@ -2939,7 +2939,7 @@ they're used. (`-XX:+UseParallelGC` or `-XX:+UseG1GC`). Other collectors employing multiple threads always perform reference processing in parallel. -`-XX:MaxRAM=`*size* +[`-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. @@ -2958,13 +2958,13 @@ they're used. > `-XX:MaxRAM=2G` -`-XX:+AggressiveHeap` +[`-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 the configuration of the computer (RAM and CPU). By default, the option is disabled and the heap sizes are configured less aggressively. -`-XX:+NeverActAsServerClassMachine` +[`-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. @@ -2989,7 +2989,7 @@ they're used. These `java` options are still accepted but ignored, and a warning is issued when they're used. -`--illegal-access=`*parameter* +[`--illegal-access=`]{#--illegal-access}*parameter* : Controlled _relaxed strong encapsulation_, as defined in [JEP 261](https://openjdk.org/jeps/261#Relaxed-strong-encapsulation). This option was deprecated in JDK 16 by [JEP @@ -3283,7 +3283,7 @@ Flags to Xlog]. The following provides quick reference to the `-Xlog` command and syntax for options: -`-Xlog` +[`-Xlog`]{#-Xlog} : Enables JVM logging on an `info` level. `-Xlog:help` @@ -3570,7 +3570,7 @@ Legacy Runtime Flag Xlog Configuration Comment The following are `-Xlog` examples. -`-Xlog` +[`-Xlog`]{#-Xlog} : Logs all messages by using the `info` level to `stdout` with `uptime`, `levels`, and `tags` decorations. This is equivalent to using: @@ -4075,7 +4075,7 @@ The deployment of the AOT cache is divided into three phases: The AOT cache can be used with the following command-line options: -`-XX:AOTCache=`*cachefile* +[`-XX:AOTCache=`]{#-XX_AOTCache}*cachefile* : Specifies the location of the AOT cache. The standard extension for *cachefile* is `.aot`. This option cannot be used together with `-XX:AOTCacheOutput`. @@ -4084,13 +4084,13 @@ The AOT cache can be used with the following command-line options: The *cachefile* is written by AOT mode `create`. In that case, this option is equivalent to `-XX:AOTCacheOutput=`*cachefile*. -`-XX:AOTCacheOutput=`*cachefile* +[`-XX:AOTCacheOutput=`]{#-XX_AOTCacheOutput}*cachefile* : Specifies the location of the AOT cache to write. The standard extension for *cachefile* is `.aot`. This option cannot be used together with `-XX:AOTCache`. This option is compatible with `AOTMode` settings of `record`, `create`, or `auto` (the default). -`-XX:AOTConfiguration=`*configfile* +[`-XX:AOTConfiguration=`]{#-XX_AOTConfiguration}*configfile* : Specifies the AOT Configuration file for the JVM to write to or read from. The standard extension for *configfile* is `.aotconfig`. @@ -4098,7 +4098,7 @@ The AOT cache can be used with the following command-line options: The *configfile* is read by AOT mode `create`, and written by the other applicable modes. If the AOT mode is `auto`, then `AOTCacheOutput` must also be present. -`-XX:AOTMode=`*mode* +[`-XX:AOTMode=`]{#-XX_AOTMode}*mode* : Specifies the AOT Mode for this run. *mode* must be one of the following: `auto`, `off`, `record`, `create`, or `on`. @@ -4170,7 +4170,7 @@ The AOT cache can be used with the following command-line options: options are compatible with the AOT cache. An alternative is to run your application with `-XX:AOTMode=auto -Xlog:aot` to see if the AOT cache can be used or not. -`-XX:+AOTClassLinking` +[`-XX:+AOTClassLinking`]{#-XX__AOTClassLinking} : If this option is enabled, the JVM will perform more advanced optimizations (such as ahead-of-time resolution of invokedynamic instructions) when creating the AOT cache. As a result, the application will see further improvements From 56545328f849c3ebf062e3ff601224084fa3b46e Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Wed, 14 Jan 2026 16:54:24 +0000 Subject: [PATCH 004/126] 8375297: ZGC: Remove obsolete O_CLOEXEC definition Reviewed-by: tschatzl, eosterlund --- src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp index 25ffd0b8078..28159ae4801 100644 --- a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp @@ -66,9 +66,6 @@ #endif // open(2) flags -#ifndef O_CLOEXEC -#define O_CLOEXEC 02000000 -#endif #ifndef O_TMPFILE #define O_TMPFILE (020000000 | O_DIRECTORY) #endif From 60fbaf5b26d7d359b1258898d4c4dfd86010b8a5 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 14 Jan 2026 18:53:10 +0000 Subject: [PATCH 005/126] 8374828: Save load_barrier_on_oop_field_preloaded in aot CodeCache Reviewed-by: adinn, iklam, shade --- src/hotspot/share/code/aotCodeCache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 0314c5227d2..f51c068f1e7 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.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 @@ -1371,6 +1371,7 @@ void AOTCodeAddressTable::init_extrs() { SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow); #endif #if INCLUDE_ZGC + SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr()); SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); #if defined(AMD64) SET_ADDRESS(_extrs, &ZPointerLoadShift); From a7507ffa1dda403110a61c4b61143b76e8a7911e Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Wed, 14 Jan 2026 19:26:45 +0000 Subject: [PATCH 006/126] 8375237: Document existing exceptional behavior of divideUnsigned and remainderUnsigned Reviewed-by: rgiulietti --- src/java.base/share/classes/java/lang/Integer.java | 4 +++- src/java.base/share/classes/java/lang/Long.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index a9da1c32490..85ca80735f8 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1448,6 +1448,7 @@ public final class Integer extends Number * @param divisor the value doing the dividing * @return the unsigned quotient of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #remainderUnsigned * @since 1.8 */ @@ -1466,6 +1467,7 @@ public final class Integer extends Number * @param divisor the value doing the dividing * @return the unsigned remainder of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #divideUnsigned * @since 1.8 */ diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index c5cd9650f2d..5fa1b8fc2ea 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1411,6 +1411,7 @@ public final class Long extends Number * @param divisor the value doing the dividing * @return the unsigned quotient of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #remainderUnsigned * @since 1.8 */ @@ -1434,6 +1435,7 @@ public final class Long extends Number * @param divisor the value doing the dividing * @return the unsigned remainder of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #divideUnsigned * @since 1.8 */ From 3007365b73d400ee6a5ea9a9041899bb81cf357a Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Wed, 14 Jan 2026 19:27:10 +0000 Subject: [PATCH 007/126] 8373913: Refactor serialization tests to use JUnit Reviewed-by: jlu, naoto --- .../Serializable/GetField/ReadFieldsCNF.java | 34 ++-- .../class/NonSerializableTest.java | 75 +++++---- .../Serializable/records/RecordClassTest.java | 10 +- .../records/SerialVersionUIDTest.java | 12 +- .../serialFilter/CheckArrayTest.java | 28 ++-- .../serialFilter/CheckInputOrderTest.java | 23 +-- .../serialFilter/GlobalFilterTest.java | 71 ++++---- .../serialFilter/InvalidGlobalFilterTest.java | 40 ++--- .../serialFilter/MixedFiltersTest.java | 52 +++--- .../serialFilter/SerialFactoryExample.java | 33 ++-- .../serialFilter/SerialFactoryFaults.java | 52 +++--- .../serialFilter/SerialFilterFactoryTest.java | 76 +++++---- .../SerialFilterFunctionTest.java | 51 +++--- .../serialFilter/SerialFilterTest.java | 151 ++++++++---------- 14 files changed, 355 insertions(+), 353 deletions(-) diff --git a/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java b/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java index 7b91222d737..2b3f1c6aa45 100644 --- a/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java +++ b/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,8 @@ * @summary Verify that ObjectInputStream ReadFields correctly reports ClassNotFoundException * while getting the field value. The test uses Vector that calls ReadFields from its readObject. * @library /test/lib - * @run testng ReadFieldsCNF - * @run testng/othervm -Djdk.serialGetFieldCnfeReturnsNull=true ReadFieldsCNF + * @run junit ReadFieldsCNF + * @run junit/othervm -Djdk.serialGetFieldCnfeReturnsNull=true ReadFieldsCNF */ import java.io.ByteArrayInputStream; @@ -41,12 +41,12 @@ import java.io.StreamCorruptedException; import java.nio.charset.StandardCharsets; import java.util.Vector; -import org.testng.annotations.Test; -import org.testng.Assert; - import jdk.test.lib.hexdump.HexPrinter; import jdk.test.lib.hexdump.ObjectStreamPrinter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + public class ReadFieldsCNF { private static final boolean GETFIELD_CNFE_RETURNS_NULL = @@ -58,7 +58,7 @@ public class ReadFieldsCNF { * @throws IOException If any other exception occurs */ @Test - private static void testVectorWithRole() throws IOException { + void testVectorWithRole() throws IOException { System.out.println("Property GETFIELD_CNFE_RETURNS_NULL: " + GETFIELD_CNFE_RETURNS_NULL); Role role = new Role(); @@ -75,7 +75,7 @@ public class ReadFieldsCNF { System.out.printf("Role offset: %d (0x%x) : %s%n", off, off, Role.class.getName()); if (off < 0) { HexPrinter.simple().formatter(ObjectStreamPrinter.formatter()).format(bytes); - Assert.fail("classname not found"); + Assertions.fail("classname not found"); } bytes[off] = (byte) 'X'; // replace R with X -> Class not found @@ -85,18 +85,18 @@ public class ReadFieldsCNF { try { Object obj = in.readObject(); System.out.println("Read: " + obj); - Assert.fail("Should not reach here, an exception should always occur"); + Assertions.fail("Should not reach here, an exception should always occur"); } catch (ClassNotFoundException cnfe) { // Expected ClassNotFoundException String expected = "XeadFieldsCNF$Role"; - Assert.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); + Assertions.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); if (GETFIELD_CNFE_RETURNS_NULL) { - Assert.fail("Expected IOException got ClassNotFoundException", cnfe); + Assertions.fail("Expected IOException got ClassNotFoundException", cnfe); } System.out.println("Normal: OIS.readObject: " + cnfe); } catch (StreamCorruptedException ioe) { if (!GETFIELD_CNFE_RETURNS_NULL) { - Assert.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); + Assertions.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); } System.out.println("Normal: " + ioe); } @@ -108,7 +108,7 @@ public class ReadFieldsCNF { * @throws IOException If any other exception occurs */ @Test - private static void testHolderWithRole() throws IOException { + void testHolderWithRole() throws IOException { System.out.println("Property GETFIELD_CNFE_RETURNS_NULL: " + GETFIELD_CNFE_RETURNS_NULL); Role role = new Role(); Holder holder = new Holder(role); @@ -123,7 +123,7 @@ public class ReadFieldsCNF { System.out.printf("Role offset: %d (0x%x)%n", off, off); if (off < 0) { HexPrinter.simple().formatter(ObjectStreamPrinter.formatter()).format(bytes); - Assert.fail("classname found at index: " + off + " (0x" + Integer.toHexString(off) + ")"); + Assertions.fail("classname found at index: " + off + " (0x" + Integer.toHexString(off) + ")"); } bytes[off] = (byte) 'X'; // replace R with X -> Class not found @@ -133,15 +133,15 @@ public class ReadFieldsCNF { try { Holder obj = (Holder)in.readObject(); System.out.println("Read: " + obj); - Assert.fail("Should not reach here, an exception should always occur"); + Assertions.fail("Should not reach here, an exception should always occur"); } catch (ClassNotFoundException cnfe) { // Expected ClassNotFoundException String expected = "XeadFieldsCNF$Role"; - Assert.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); + Assertions.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); System.out.println("Normal: OIS.readObject: " + cnfe); } catch (StreamCorruptedException ioe) { if (!GETFIELD_CNFE_RETURNS_NULL) { - Assert.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); + Assertions.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); } System.out.println("Normal: " + ioe); } diff --git a/test/jdk/java/io/Serializable/class/NonSerializableTest.java b/test/jdk/java/io/Serializable/class/NonSerializableTest.java index fa81b3e3ce7..2a36380aae7 100644 --- a/test/jdk/java/io/Serializable/class/NonSerializableTest.java +++ b/test/jdk/java/io/Serializable/class/NonSerializableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, 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,79 +32,84 @@ * jdk.test.lib.JDKToolLauncher * jdk.test.lib.Platform * jdk.test.lib.process.* - * @run testng/timeout=300 NonSerializableTest + * @run junit/timeout=300 NonSerializableTest * @summary Enable serialize of nonSerializable Class descriptor. */ import java.nio.file.Paths; import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class NonSerializableTest { - @BeforeClass - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { boolean b = CompilerUtils.compile( Paths.get(System.getProperty("test.src"), "TestEntry.java"), Paths.get(System.getProperty("user.dir"))); assertTrue(b, "Compilation failed"); } - @DataProvider - public Object[][] provider() { - return new String[][][] { + // Test cases to compile and run + public static Stream> provider() { + return Stream.of( // Write NonSerial1, Read NonSerial1 - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-d"}}, + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-d"), // Write NonSerial1, Read NonSerial2 - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"NonSerialA_2", "-cp", ".", "TestEntry", "-d"}}, + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("NonSerialA_2", "-cp", ".", "TestEntry", "-d"), // Write NonSerial1, Read Serial1 - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_1", "-cp", ".", "TestEntry", "-d"}}, + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_1", "-cp", ".", "TestEntry", "-d"), // Write Serial1, Read NonSerial1 - {{"SerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-doe"}}, + List.of("SerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-doe"), // Write Serial1, Read Serial2 - {{"SerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_2", "-cp", ".", "TestEntry", "-d"}}, + List.of("SerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_2", "-cp", ".", "TestEntry", "-d"), // Write Serial2, Read Serial1 - {{"SerialA_2", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_1", "-cp", ".", "TestEntry", "-d"}}, + List.of("SerialA_2", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_1", "-cp", ".", "TestEntry", "-d"), // Write Serial1, Read Serial3 - {{"SerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_3", "-cp", ".", "TestEntry", "-de"}}, + List.of("SerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_3", "-cp", ".", "TestEntry", "-de"), // Write Serial3, Read Serial1 - {{"SerialA_3", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_1", "-cp", ".", "TestEntry", "-de"}}, - }; + List.of("SerialA_3", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_1", "-cp", ".", "TestEntry", "-de")); } - @Test(dataProvider="provider") - public void test(String[] args) throws Exception { + @ParameterizedTest + @MethodSource("provider") + public void test(List argList) throws Exception { + String[] args = argList.toArray(new String[0]); boolean b = CompilerUtils.compile(Paths.get(System.getProperty("test.src"), args[0]), Paths.get(System.getProperty("user.dir"))); assertTrue(b, "Compilation failed"); - String params[] = Arrays.copyOfRange(args, 1, args.length); + String[] params = Arrays.copyOfRange(args, 1, args.length); ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(params); - Process p = ProcessTools.startProcess("Serializable Test", pb); - int exitValue = p.waitFor(); - assertEquals(exitValue, 0, "Test failed"); + try (Process p = ProcessTools.startProcess("Serializable Test", pb)) { + int exitValue = p.waitFor(); + assertEquals(0, exitValue, "Test failed"); + } } } diff --git a/test/jdk/java/io/Serializable/records/RecordClassTest.java b/test/jdk/java/io/Serializable/records/RecordClassTest.java index ba920ce92e5..951dd2f44dc 100644 --- a/test/jdk/java/io/Serializable/records/RecordClassTest.java +++ b/test/jdk/java/io/Serializable/records/RecordClassTest.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 @@ -37,6 +37,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; +import java.io.Serial; import java.io.Serializable; import static java.lang.System.out; @@ -49,12 +50,12 @@ import org.junit.jupiter.params.provider.MethodSource; /** * Serializes and deserializes record classes. Ensures that the SUID is 0. */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class RecordClassTest { record Foo () implements Serializable { } record Bar (int x) implements Serializable { + @Serial private static final long serialVersionUID = 987654321L; } @@ -70,6 +71,7 @@ public class RecordClassTest { } record Wibble () implements ThrowingExternalizable { + @Serial private static final long serialVersionUID = 12345678L; } @@ -77,7 +79,7 @@ public class RecordClassTest { record Wubble (Wobble wobble, Wibble wibble, String s) implements ThrowingExternalizable { } - public Object[][] recordClasses() { + public static Object[][] recordClasses() { return new Object[][] { new Object[] { Foo.class , 0L }, new Object[] { Bar.class , 987654321L }, @@ -124,7 +126,7 @@ public class RecordClassTest { record NotSerializable3(T t) { } - public Object[][] notSerRecordClasses() { + public static Object[][] notSerRecordClasses() { return new Object[][] { new Object[] { NotSerializable1.class }, new Object[] { NotSerializable2.class }, diff --git a/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java b/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java index 2c133392dcb..f313e8a44a3 100644 --- a/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java +++ b/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.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 @@ -35,6 +35,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.Serial; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -44,18 +45,18 @@ import static java.lang.System.out; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class SerialVersionUIDTest { record R1 () implements Serializable { + @Serial private static final long serialVersionUID = 1L; } record R2 (int x, int y) implements Serializable { + @Serial private static final long serialVersionUID = 0L; } @@ -64,10 +65,11 @@ public class SerialVersionUIDTest { record R4 (String s) implements Serializable { } record R5 (long l) implements Serializable { + @Serial private static final long serialVersionUID = 5678L; } - public Object[][] recordObjects() { + public static Object[][] recordObjects() { return new Object[][] { new Object[] { new R1(), 1L }, new Object[] { new R2(1, 2), 0L }, @@ -103,7 +105,7 @@ public class SerialVersionUIDTest { assertEquals(expectedUID, dis.readLong()); } - public Object[][] recordClasses() { + public static Object[][] recordClasses() { List list = new ArrayList<>(); List> recordClasses = List.of(R1.class, R2.class, R3.class, R4.class, R5.class); LongStream.of(0L, 1L, 100L, 10_000L, 1_000_000L).forEach(suid -> diff --git a/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.java b/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.java index d00c670bd15..2081375ca59 100644 --- a/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.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 @@ -30,15 +30,15 @@ import java.io.InvalidClassException; import jdk.internal.access.SharedSecrets; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.Assert; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @build CheckArrayTest SerialFilterTest * @bug 8203368 * @modules java.base/jdk.internal.access - * @run testng CheckArrayTest + * @run junit CheckArrayTest * * @summary Test the SharedSecret access to ObjectInputStream.checkArray works * with overridden subclasses. @@ -53,8 +53,8 @@ import org.testng.Assert; */ public class CheckArrayTest { - @DataProvider(name = "Patterns") - Object[][] patterns() { + // Test patterns for arrays + private static Object[][] patterns() { return new Object[][]{ new Object[]{"maxarray=10", 10, new String[10]}, // successful new Object[]{"maxarray=10", 11, new String[11]}, // exception expected @@ -64,7 +64,8 @@ public class CheckArrayTest { /** * Test SharedSecrets checkArray with unmodified ObjectInputStream. */ - @Test(dataProvider = "Patterns") + @ParameterizedTest + @MethodSource("patterns") public void normalOIS(String pattern, int arraySize, Object[] array) throws IOException { ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); byte[] bytes = SerialFilterTest.writeObjects(array); @@ -75,10 +76,10 @@ public class CheckArrayTest { ois.setObjectInputFilter(filter); SharedSecrets.getJavaObjectInputStreamAccess() .checkArray(ois, array.getClass(), arraySize); - Assert.assertTrue(array.length >= arraySize, + Assertions.assertTrue(array.length >= arraySize, "Should have thrown InvalidClassException due to array size"); } catch (InvalidClassException ice) { - Assert.assertFalse(array.length > arraySize, + Assertions.assertFalse(array.length > arraySize, "Should NOT have thrown InvalidClassException due to array size"); } } @@ -88,7 +89,8 @@ public class CheckArrayTest { * Test SharedSecrets checkArray with an ObjectInputStream subclassed to * handle all input stream functions. */ - @Test(dataProvider = "Patterns") + @ParameterizedTest + @MethodSource("patterns") public void subclassedOIS(String pattern, int arraySize, Object[] array) throws IOException { byte[] bytes = SerialFilterTest.writeObjects(array); try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); @@ -98,10 +100,10 @@ public class CheckArrayTest { ois.setObjectInputFilter(filter); SharedSecrets.getJavaObjectInputStreamAccess() .checkArray(ois, array.getClass(), arraySize); - Assert.assertTrue(array.length >= arraySize, + Assertions.assertTrue(array.length >= arraySize, "Should have thrown InvalidClassException due to array size"); } catch (InvalidClassException ice) { - Assert.assertFalse(array.length > arraySize, + Assertions.assertFalse(array.length > arraySize, "Should NOT have thrown InvalidClassException due to array size"); } } diff --git a/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.java b/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.java index 0230eb653ce..d824c947ece 100644 --- a/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, 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,28 +25,28 @@ import java.io.ByteArrayInputStream; import java.io.InvalidClassException; import java.io.ObjectInputFilter; import java.io.ObjectInputStream; +import java.io.Serial; import java.io.Serializable; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @build CheckInputOrderTest SerialFilterTest - * @run testng/othervm CheckInputOrderTest + * @run junit/othervm CheckInputOrderTest * * @summary Test that when both global filter and specific filter are set, * global filter will not affect specific filter. */ public class CheckInputOrderTest implements Serializable { + @Serial private static final long serialVersionUID = 12345678901L; - @DataProvider(name="Patterns") - Object[][] patterns() { + // Test cases for serial filter strings + static Object[][] patterns() { return new Object[][] { new Object[] { SerialFilterTest.genTestObject("maxarray=1", true), "java.**;java.lang.*;java.lang.Long;maxarray=0", false }, new Object[] { SerialFilterTest.genTestObject("maxarray=1", true), "java.**;java.lang.*;java.lang.Long", true }, @@ -75,7 +75,8 @@ public class CheckInputOrderTest implements Serializable { * "global filter reject" + "specific ObjectInputStream filter is empty" => should reject * "global filter reject" + "specific ObjectInputStream filter allow" => should allow */ - @Test(dataProvider="Patterns") + @ParameterizedTest + @MethodSource("patterns") public void testRejectedInGlobal(Object toDeserialized, String pattern, boolean allowed) throws Exception { byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); diff --git a/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.java b/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.java index 9e38d6c499d..18bbf265e43 100644 --- a/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, 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,9 +21,8 @@ * questions. */ -import static org.testng.Assert.assertEquals; -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.assertTrue; import java.io.ByteArrayInputStream; import java.io.EOFException; @@ -35,39 +34,40 @@ import java.io.ObjectInputStream; import java.security.Security; import java.util.Objects; -import org.testng.Assert; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @bug 8231422 * @build GlobalFilterTest SerialFilterTest - * @run testng/othervm GlobalFilterTest - * @run testng/othervm -Djdk.serialFilter=java.** + * @run junit/othervm GlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=java.** * -Dexpected-jdk.serialFilter=java.** GlobalFilterTest * @summary Test Global Filters */ -@Test public class GlobalFilterTest { private static final String serialPropName = "jdk.serialFilter"; private static final String badSerialFilter = "java.lang.StringBuffer;!*"; private static final String origSerialFilterProperty = System.setProperty(serialPropName, badSerialFilter); + private static final String EXPECTED_GLOBAL_FILTER = System.getProperty("expected-" + serialPropName, + Security.getProperty(serialPropName)); + + static boolean hasGlobalFilter() { + return EXPECTED_GLOBAL_FILTER != null && !EXPECTED_GLOBAL_FILTER.isEmpty(); + } + /** * DataProvider of patterns and objects derived from the configured process-wide filter. * @return Array of arrays of pattern, object, allowed boolean, and API factory */ - @DataProvider(name="globalPatternElements") - Object[][] globalPatternElements() { - String globalFilter = - System.getProperty("expected-" + serialPropName, - Security.getProperty(serialPropName)); - if (globalFilter == null) { - return new Object[0][]; - } + static Object[][] globalPatternElements() { - String[] patterns = globalFilter.split(";"); + String[] patterns = EXPECTED_GLOBAL_FILTER.split(";"); Object[][] objects = new Object[patterns.length][]; for (int i = 0; i < patterns.length; i++) { @@ -83,7 +83,7 @@ public class GlobalFilterTest { ? SerialFilterTest.genTestObject(pattern, true) : SerialFilterTest.genTestObject(pattern.substring(1), false); - Assert.assertNotNull(o, "fail generation failed"); + Assertions.assertNotNull(o, "fail generation failed"); } objects[i] = new Object[3]; objects[i][0] = pattern; @@ -98,13 +98,13 @@ public class GlobalFilterTest { * and has the toString matching the configured pattern. */ @Test() - static void globalFilter() { + void globalFilter() { ObjectInputFilter filter = ObjectInputFilter.Config.getSerialFilter(); // Check that the System.setProperty(jdk.serialFilter) DOES NOT affect the filter. String asSetSystemProp = System.getProperty(serialPropName, Security.getProperty(serialPropName)); - Assert.assertNotEquals(Objects.toString(filter, null), asSetSystemProp, + Assertions.assertNotEquals(asSetSystemProp, Objects.toString(filter, null), "System.setProperty(\"jdk.serialfilter\", ...) should not change filter: " + asSetSystemProp); @@ -112,7 +112,7 @@ public class GlobalFilterTest { System.getProperty("expected-" + serialPropName, Security.getProperty(serialPropName)); System.out.printf("global pattern: %s, filter: %s%n", pattern, filter); - Assert.assertEquals(Objects.toString(filter, null), pattern, + assertEquals(pattern, Objects.toString(filter, null), "process-wide filter pattern does not match"); } @@ -120,16 +120,15 @@ public class GlobalFilterTest { * If the Global filter is already set, it should always refuse to be * set again. */ - @Test() - @SuppressWarnings("removal") - static void setGlobalFilter() { + @Test + void setGlobalFilter() { ObjectInputFilter filter = new SerialFilterTest.Validator(); ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter(); if (global != null) { // once set, can never be re-set try { ObjectInputFilter.Config.setSerialFilter(filter); - Assert.fail("set only once process-wide filter"); + Assertions.fail("set only once process-wide filter"); } catch (IllegalStateException ise) { // Normal, once set can never be re-set } @@ -141,7 +140,7 @@ public class GlobalFilterTest { try { // Try to set it again, expecting it to throw ObjectInputFilter.Config.setSerialFilter(filter); - Assert.fail("set only once process-wide filter"); + Assertions.fail("set only once process-wide filter"); } catch (IllegalStateException ise) { // Normal case } @@ -154,8 +153,10 @@ public class GlobalFilterTest { * * @param pattern a pattern extracted from the configured global pattern */ - @Test(dataProvider = "globalPatternElements") - static void globalFilterElements(String pattern, boolean allowed,Object obj) { + @ParameterizedTest + @EnabledIf("hasGlobalFilter") + @MethodSource("globalPatternElements") + void globalFilterElements(String pattern, boolean allowed,Object obj) { testGlobalPattern(pattern, obj, allowed); } @@ -177,15 +178,15 @@ public class GlobalFilterTest { } catch (EOFException eof) { // normal completion } catch (ClassNotFoundException cnf) { - Assert.fail("Deserializing", cnf); + Assertions.fail("Deserializing", cnf); } - Assert.assertTrue(allowed, "filter should have thrown an exception"); + assertTrue(allowed, "filter should have thrown an exception"); } catch (IllegalArgumentException iae) { - Assert.fail("bad format pattern", iae); + Assertions.fail("bad format pattern", iae); } catch (InvalidClassException ice) { - Assert.assertFalse(allowed, "filter should not have thrown an exception: " + ice); + Assertions.assertFalse(allowed, "filter should not have thrown an exception: " + ice); } catch (IOException ioe) { - Assert.fail("Unexpected IOException", ioe); + Assertions.fail("Unexpected IOException", ioe); } } } diff --git a/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.java b/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.java index a017354b103..b924a96c86c 100644 --- a/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.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,9 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -31,18 +28,22 @@ import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* * @test * @bug 8278087 * @summary Test that an invalid pattern value for the jdk.serialFilter system property causes an * exception to be thrown when an attempt is made to use the filter or deserialize. * A subset of invalid filter patterns is tested. - * @run testng/othervm -Djdk.serialFilter=.* InvalidGlobalFilterTest - * @run testng/othervm -Djdk.serialFilter=! InvalidGlobalFilterTest - * @run testng/othervm -Djdk.serialFilter=/ InvalidGlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=.* InvalidGlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=! InvalidGlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=/ InvalidGlobalFilterTest * */ -@Test public class InvalidGlobalFilterTest { private static final String serialPropName = "jdk.serialFilter"; private static final String serialFilter = System.getProperty(serialPropName); @@ -64,13 +65,13 @@ public class InvalidGlobalFilterTest { "java.base/", "Invalid jdk.serialFilter: class or package missing in: \"java.base/\"", "/", "Invalid jdk.serialFilter: module name is missing in: \"/\""); - @DataProvider(name = "MethodsToCall") - private Object[][] cases() { + // Test cases for exceptions + private static Object[][] cases() { return new Object[][] { - {serialFilter, "getSerialFilter", (Assert.ThrowingRunnable) () -> ObjectInputFilter.Config.getSerialFilter()}, - {serialFilter, "setSerialFilter", (Assert.ThrowingRunnable) () -> ObjectInputFilter.Config.setSerialFilter(new NoopFilter())}, - {serialFilter, "new ObjectInputStream(is)", (Assert.ThrowingRunnable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, - {serialFilter, "new OISSubclass()", (Assert.ThrowingRunnable) () -> new OISSubclass()}, + {serialFilter, "getSerialFilter", (Executable) () -> ObjectInputFilter.Config.getSerialFilter()}, + {serialFilter, "setSerialFilter", (Executable) () -> ObjectInputFilter.Config.setSerialFilter(new NoopFilter())}, + {serialFilter, "new ObjectInputStream(is)", (Executable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, + {serialFilter, "new OISSubclass()", (Executable) () -> new OISSubclass()}, }; } @@ -78,18 +79,19 @@ public class InvalidGlobalFilterTest { * Test each method that should throw IllegalStateException based on * the invalid arguments it was launched with. */ - @Test(dataProvider = "MethodsToCall") - public void initFaultTest(String pattern, String method, Assert.ThrowingRunnable runnable) { + @ParameterizedTest + @MethodSource("cases") + public void initFaultTest(String pattern, String method, Executable runnable) { - IllegalStateException ex = Assert.expectThrows(IllegalStateException.class, + IllegalStateException ex = Assertions.assertThrows(IllegalStateException.class, runnable); String expected = invalidMessages.get(serialFilter); if (expected == null) { - Assert.fail("No expected message for filter: " + serialFilter); + Assertions.fail("No expected message for filter: " + serialFilter); } System.out.println(ex.getMessage()); - Assert.assertEquals(ex.getMessage(), expected, "wrong message"); + Assertions.assertEquals(expected, ex.getMessage(), "wrong message"); } private static class NoopFilter implements ObjectInputFilter { diff --git a/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.java b/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.java index 8e24f27f598..5af5f4d15ed 100644 --- a/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, 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,20 +25,21 @@ import java.io.ByteArrayInputStream; import java.io.InvalidClassException; import java.io.ObjectInputFilter; import java.io.ObjectInputStream; +import java.io.Serial; import java.io.Serializable; import java.security.Security; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -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.api.condition.DisabledIf; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @build MixedFiltersTest SerialFilterTest - * @run testng/othervm -Djdk.serialFilter=!java.**;!java.lang.Long;maxdepth=5;maxarray=5;maxbytes=90;maxrefs=5 MixedFiltersTest - * @run testng/othervm -Djdk.serialFilter=java.**;java.lang.Long;maxdepth=1000;maxarray=1000;maxbytes=1000;maxrefs=1000 MixedFiltersTest + * @run junit/othervm -Djdk.serialFilter=!java.**;!java.lang.Long;maxdepth=5;maxarray=5;maxbytes=90;maxrefs=5 MixedFiltersTest + * @run junit/othervm -Djdk.serialFilter=java.**;java.lang.Long;maxdepth=1000;maxarray=1000;maxbytes=1000;maxrefs=1000 MixedFiltersTest * * @summary Test that when both global filter and specific filter are set, * global filter will not affect specific filter. @@ -46,23 +47,17 @@ import static org.testng.Assert.fail; public class MixedFiltersTest implements Serializable { + @Serial private static final long serialVersionUID = 1234567890L; + private static final String JDK_SERIAL_FILTER = System.getProperty("jdk.serialFilter", + Security.getProperty("jdk.serialFilter")); - boolean globalRejected; - - @BeforeClass - public void setup() { - String pattern = System.getProperty("jdk.serialFilter", - Security.getProperty("jdk.serialFilter")); - globalRejected = pattern.startsWith("!"); + private static boolean globalRejected() { + return JDK_SERIAL_FILTER.startsWith("!"); } - @DataProvider(name="RejectedInGlobal") - Object[][] rejectedInGlobal() { - if (!globalRejected) { - return new Object[0][]; - } + static Object[][] rejectedInGlobal() { return new Object[][] { new Object[] { Long.MAX_VALUE, "java.**" }, new Object[] { Long.MAX_VALUE, "java.lang.Long" }, @@ -79,7 +74,9 @@ public class MixedFiltersTest implements Serializable { * "global filter reject" + "specific ObjectInputStream filter is empty" => should reject * "global filter reject" + "specific ObjectInputStream filter allow" => should allow */ - @Test(dataProvider="RejectedInGlobal") + @ParameterizedTest + @EnabledIf("globalRejected") + @MethodSource("rejectedInGlobal") public void testRejectedInGlobal(Object toDeserialized, String pattern) throws Exception { byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); @@ -96,12 +93,7 @@ public class MixedFiltersTest implements Serializable { } } - @DataProvider(name="AllowedInGlobal") - Object[][] allowedInGlobal() { - if (globalRejected) { - return new Object[0][]; - } - + static Object[][] allowedInGlobal() { return new Object[][] { new Object[] { Long.MAX_VALUE, "!java.**" }, new Object[] { Long.MAX_VALUE, "!java.lang.Long" }, @@ -118,7 +110,9 @@ public class MixedFiltersTest implements Serializable { * "global filter allow" + "specific ObjectInputStream filter is empty" => should allow * "global filter allow" + "specific ObjectInputStream filter reject" => should reject */ - @Test(dataProvider="AllowedInGlobal") + @ParameterizedTest + @DisabledIf("globalRejected") + @MethodSource("allowedInGlobal") public void testAllowedInGlobal(Object toDeserialized, String pattern) throws Exception { byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.java b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.java index 45b1872bae0..271c22b917c 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.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,10 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -46,9 +42,13 @@ import static java.io.ObjectInputFilter.Status.ALLOWED; import static java.io.ObjectInputFilter.Status.REJECTED; import static java.io.ObjectInputFilter.Status.UNDECIDED; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test - * @run testng/othervm SerialFactoryExample - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryExample$FilterInThread SerialFactoryExample + * @run junit/othervm SerialFactoryExample + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryExample$FilterInThread SerialFactoryExample * @summary Test SerialFactoryExample */ @@ -76,10 +76,9 @@ import static java.io.ObjectInputFilter.Status.UNDECIDED; * * The `doWithSerialFilter` calls can be nested. When nested, the filters are concatenated. */ -@Test public class SerialFactoryExample { - @DataProvider(name = "Examples") + // Test cases for filters static Object[][] examples() { return new Object[][]{ {new Point(1, 2), null, @@ -108,7 +107,8 @@ public class SerialFactoryExample { } - @Test(dataProvider = "Examples") + @ParameterizedTest + @MethodSource("examples") void examples(Serializable obj, ObjectInputFilter filter, Status expected) { // Establish FilterInThread as the application-wide filter factory FilterInThread filterInThread; @@ -128,11 +128,11 @@ public class SerialFactoryExample { Object o = deserializeObject(bytes); }); if (expected.equals(REJECTED)) - Assert.fail("IllegalClassException should have occurred"); + Assertions.fail("IllegalClassException should have occurred"); } catch (UncheckedIOException uioe) { IOException ioe = uioe.getCause(); - Assert.assertEquals(ioe.getClass(), InvalidClassException.class, "Wrong exception"); - Assert.assertEquals(REJECTED, expected, "Exception should not have occurred"); + Assertions.assertEquals(InvalidClassException.class, ioe.getClass(), "Wrong exception"); + Assertions.assertEquals(expected, REJECTED, "Exception should not have occurred"); } } @@ -142,7 +142,8 @@ public class SerialFactoryExample { * @param filter a filter * @param expected status */ - @Test(dataProvider = "Examples") + @ParameterizedTest + @MethodSource("examples") void checkStatus(Serializable obj, ObjectInputFilter filter, Status expected) { // Establish FilterInThread as the application-wide filter factory FilterInThread filterInThread; @@ -166,12 +167,12 @@ public class SerialFactoryExample { System.out.println(" filter in effect: " + filterInThread.currFilter); if (compositeFilter != null) { Status actualStatus = compositeFilter.checkInput(info); - Assert.assertEquals(actualStatus, expected, "Wrong Status"); + Assertions.assertEquals(expected, actualStatus, "Wrong Status"); } }); } catch (Exception ex) { - Assert.fail("unexpected exception", ex); + Assertions.fail("unexpected exception", ex); } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.java b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.java index 1d35306a426..add9f3557da 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.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,9 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -32,15 +29,19 @@ import java.io.ObjectInputFilter.Config; import java.io.ObjectInputStream; import java.util.function.BinaryOperator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test - * @run testng/othervm -Djdk.serialFilterFactory=ForcedError_NoSuchClass SerialFactoryFaults - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$NoPublicConstructor SerialFactoryFaults - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$ConstructorThrows SerialFactoryFaults - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$FactorySetsFactory SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=ForcedError_NoSuchClass SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$NoPublicConstructor SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$ConstructorThrows SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$FactorySetsFactory SerialFactoryFaults * @summary Check cases where the Filter Factory initialization from properties fails */ -@Test public class SerialFactoryFaults { // Sample the serial factory class name @@ -52,13 +53,13 @@ public class SerialFactoryFaults { System.getProperty("test.src", ".") + "/logging.properties"); } - @DataProvider(name = "MethodsToCall") - private Object[][] cases() { + // Test cases of faults + private static Object[][] cases() { return new Object[][] { - {"getSerialFilterFactory", (Assert.ThrowingRunnable) () -> Config.getSerialFilterFactory()}, - {"setSerialFilterFactory", (Assert.ThrowingRunnable) () -> Config.setSerialFilterFactory(new NoopFactory())}, - {"new ObjectInputStream(is)", (Assert.ThrowingRunnable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, - {"new OISSubclass()", (Assert.ThrowingRunnable) () -> new OISSubclass()}, + {"getSerialFilterFactory", (Executable) () -> Config.getSerialFilterFactory()}, + {"setSerialFilterFactory", (Executable) () -> Config.setSerialFilterFactory(new NoopFactory())}, + {"new ObjectInputStream(is)", (Executable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, + {"new OISSubclass()", (Executable) () -> new OISSubclass()}, }; } @@ -66,26 +67,23 @@ public class SerialFactoryFaults { * Test each method that should throw IllegalStateException based on * the invalid arguments it was launched with. */ - @Test(dataProvider = "MethodsToCall") - public void initFaultTest(String name, Assert.ThrowingRunnable runnable) { - IllegalStateException ex = Assert.expectThrows(IllegalStateException.class, + @ParameterizedTest + @MethodSource("cases") + public void initFaultTest(String name, Executable runnable) { + IllegalStateException ex = Assertions.assertThrows(IllegalStateException.class, runnable); final String msg = ex.getMessage(); if (factoryName.equals("ForcedError_NoSuchClass")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: ForcedError_NoSuchClass: java.lang.ClassNotFoundException: ForcedError_NoSuchClass", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: ForcedError_NoSuchClass: java.lang.ClassNotFoundException: ForcedError_NoSuchClass", msg, "wrong exception"); } else if (factoryName.equals("SerialFactoryFaults$NoPublicConstructor")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: SerialFactoryFaults$NoPublicConstructor: java.lang.NoSuchMethodException: SerialFactoryFaults$NoPublicConstructor.()", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: SerialFactoryFaults$NoPublicConstructor: java.lang.NoSuchMethodException: SerialFactoryFaults$NoPublicConstructor.()", msg, "wrong exception"); } else if (factoryName.equals("SerialFactoryFaults$ConstructorThrows")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: SerialFactoryFaults$ConstructorThrows: java.lang.RuntimeException: constructor throwing a runtime exception", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: SerialFactoryFaults$ConstructorThrows: java.lang.RuntimeException: constructor throwing a runtime exception", msg, "wrong exception"); } else if (factoryName.equals("SerialFactoryFaults$FactorySetsFactory")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: SerialFactoryFaults$FactorySetsFactory: java.lang.IllegalStateException: Serial filter factory initialization incomplete", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: SerialFactoryFaults$FactorySetsFactory: java.lang.IllegalStateException: Serial filter factory initialization incomplete", msg, "wrong exception"); } else { - Assert.fail("No test for filter factory: " + factoryName); + Assertions.fail("No test for filter factory: " + factoryName); } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.java b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.java index 6842eabe9b0..f34f95a72b3 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.java @@ -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. * 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,9 +22,6 @@ */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -38,17 +35,26 @@ import java.io.Serial; import java.io.Serializable; import java.util.function.BinaryOperator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test * @build SerialFilterFactoryTest - * @run testng/othervm SerialFilterFactoryTest - * @run testng/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$PropertyFilterFactory + * @run junit/othervm SerialFilterFactoryTest + * @run junit/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$PropertyFilterFactory * -Djava.util.logging.config.file=${test.src}/logging.properties SerialFilterFactoryTest - * @run testng/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$NotMyFilterFactory + * @run junit/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$NotMyFilterFactory * -Djava.util.logging.config.file=${test.src}/logging.properties SerialFilterFactoryTest * * @summary Test Context-specific Deserialization Filters */ -@Test +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class SerialFilterFactoryTest { // A stream with just the header, enough to create a OIS @@ -70,7 +76,7 @@ public class SerialFilterFactoryTest { ois.writeObject(new Dummy("Here")); return boas.toByteArray(); } catch (IOException ioe) { - Assert.fail("unexpected IOE", ioe); + Assertions.fail("unexpected IOE", ioe); } throw new RuntimeException("should not reach here"); } @@ -109,7 +115,7 @@ public class SerialFilterFactoryTest { return !(ObjectInputFilter.Config.getSerialFilterFactory() instanceof NotMyFilterFactory); } - @DataProvider(name="FilterCases") + // Test cases for filter factory static Object[][] filterCases() { if (isValidFilterFactory()) { return new Object[][]{ @@ -124,9 +130,11 @@ public class SerialFilterFactoryTest { } // Setting the filter factory to null is not allowed. - @Test(expectedExceptions=NullPointerException.class) + @Test void testNull() { - Config.setSerialFilterFactory(null); + Assertions.assertThrows(NullPointerException.class, () -> { + Config.setSerialFilterFactory(null); + }); } /** @@ -136,7 +144,6 @@ public class SerialFilterFactoryTest { * Try to set it again, the second should throw. */ @Test - @SuppressWarnings("removal") void testSecondSetShouldThrow() { var currFF = Config.getSerialFilterFactory(); if (currFF.getClass().getClassLoader() == null) { @@ -145,14 +152,14 @@ public class SerialFilterFactoryTest { Config.setSerialFilterFactory(contextFilterFactory); currFF = contextFilterFactory; } catch (IllegalStateException ise) { - Assert.fail("First setSerialFilterFactory should not throw"); + Assertions.fail("First setSerialFilterFactory should not throw"); } } // Setting it again will throw - Assert.expectThrows(IllegalStateException.class, + Assertions.assertThrows(IllegalStateException.class, () -> Config.setSerialFilterFactory(new MyFilterFactory("f11"))); var resetFF = Config.getSerialFilterFactory(); - Assert.assertEquals(resetFF, currFF, "Setting again should not change filter factory"); + Assertions.assertEquals(currFF, resetFF, "Setting again should not change filter factory"); } /** @@ -167,8 +174,10 @@ public class SerialFilterFactoryTest { * @throws IOException if an I/O error occurs (should not occur) * @throws ClassNotFoundException for class not found (should not occur) */ - @Test(dataProvider="FilterCases") - @SuppressWarnings("removal") + @ParameterizedTest + @EnabledIf("isValidFilterFactory") + @MethodSource("filterCases") + @Order(1) void testCase(MyFilterFactory dynFilterFactory, Validator dynFilter, Validator streamFilter) throws IOException, ClassNotFoundException { @@ -182,32 +191,32 @@ public class SerialFilterFactoryTest { InputStream is = new ByteArrayInputStream(simpleStream); ObjectInputStream ois = new ObjectInputStream(is); - Assert.assertNull(factory.current(), "initially current should be null"); - Assert.assertEquals(factory.next(), configFilter, "initially next should be the configured filter"); + Assertions.assertNull(factory.current(), "initially current should be null"); + Assertions.assertEquals(configFilter, factory.next(), "initially next should be the configured filter"); var currFilter = ois.getObjectInputFilter(); if (currFilter != null && currFilter.getClass().getClassLoader() == null) { // Builtin loader; defaults to configured filter - Assert.assertEquals(currFilter, configFilter, "getObjectInputFilter should be configured filter"); + Assertions.assertEquals(configFilter, currFilter, "getObjectInputFilter should be configured filter"); } else { - Assert.assertEquals(currFilter, configFilter, "getObjectInputFilter should be null"); + Assertions.assertEquals(configFilter, currFilter, "getObjectInputFilter should be null"); } if (streamFilter != null) { ois.setObjectInputFilter(streamFilter); // MyFilterFactory is called when the stream filter is changed; verify values passed it - Assert.assertEquals(factory.current(), currFilter, "when setObjectInputFilter, current should be current filter"); - Assert.assertEquals(factory.next(), streamFilter, "next should be stream specific filter"); + Assertions.assertEquals(currFilter, factory.current(), "when setObjectInputFilter, current should be current filter"); + Assertions.assertEquals(streamFilter, factory.next(), "next should be stream specific filter"); // Check the OIS filter after the factory has updated it. currFilter = ois.getObjectInputFilter(); - Assert.assertEquals(currFilter, streamFilter, "getObjectInputFilter should be set"); + Assertions.assertEquals(streamFilter, currFilter, "getObjectInputFilter should be set"); // Verify that it can not be set again - Assert.assertThrows(IllegalStateException.class, () -> ois.setObjectInputFilter(streamFilter)); + Assertions.assertThrows(IllegalStateException.class, () -> ois.setObjectInputFilter(streamFilter)); } if (currFilter instanceof Validator validator) { validator.reset(); Object o = ois.readObject(); // Invoke only for the side effect of calling the Filter - Assert.assertEquals(validator.count, 1, "Wrong number of calls to the stream filter"); + Assertions.assertEquals(1, validator.count, "Wrong number of calls to the stream filter"); } else { Object o = ois.readObject(); // Invoke only for the side effect of calling the filter } @@ -217,18 +226,19 @@ public class SerialFilterFactoryTest { @Test void testPropertyFilterFactory() { if (jdkSerialFilterFactoryProp != null) { - Assert.assertEquals(jdkSerialFilterFactory.getClass().getName(), jdkSerialFilterFactoryProp, + Assertions.assertEquals(jdkSerialFilterFactoryProp, jdkSerialFilterFactory.getClass().getName(), "jdk.serialFilterFactory property classname mismatch"); } } // Test that setting the filter factory after any deserialization (any testCase) // throws IllegalStateException with the specific message - @Test(dependsOnMethods="testCase") + @Test + @Order(99) void testSetFactoryAfterDeserialization() { BinaryOperator factory = Config.getSerialFilterFactory(); - IllegalStateException ise = Assert.expectThrows(IllegalStateException.class, () -> Config.setSerialFilterFactory(factory)); - Assert.assertTrue(ise.getMessage().startsWith("Cannot replace filter factory: ")); + IllegalStateException ise = Assertions.assertThrows(IllegalStateException.class, () -> Config.setSerialFilterFactory(factory)); + Assertions.assertTrue(ise.getMessage().startsWith("Cannot replace filter factory: ")); } @@ -243,11 +253,11 @@ public class SerialFilterFactoryTest { // Try to set the filter to null ois.setObjectInputFilter(null); if (curr != null) { - Assert.fail("setting filter to null after a non-null filter should throw"); + Assertions.fail("setting filter to null after a non-null filter should throw"); } } catch (IllegalStateException ise) { if (curr == null) { - Assert.fail("setting filter to null after a null filter should not throw"); + Assertions.fail("setting filter to null after a null filter should not throw"); } } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.java b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.java index 41832d583c2..8930c2dfa9d 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.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,11 +21,6 @@ * questions. */ - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.ObjectInputFilter; import java.io.ObjectInputFilter.FilterInfo; import java.util.function.Predicate; @@ -35,12 +30,16 @@ import static java.io.ObjectInputFilter.Status.ALLOWED; import static java.io.ObjectInputFilter.Status.REJECTED; import static java.io.ObjectInputFilter.Status.UNDECIDED; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test - * @run testng/othervm -Djava.util.logging.config.file=${test.src}/logging.properties + * @run junit/othervm -Djava.util.logging.config.file=${test.src}/logging.properties * SerialFilterFunctionTest * @summary ObjectInputFilter.Config Function Tests */ -@Test public class SerialFilterFunctionTest { @Test @@ -53,10 +52,10 @@ public class SerialFilterFunctionTest { ObjectInputFilter filter2 = getFilter(st2); ObjectInputFilter f = ObjectInputFilter.merge(filter1, filter2); Status r = f.checkInput(info); - Assert.assertEquals(merge(st1, st2), r, "merge"); + Assertions.assertEquals(r, merge(st1, st2), "merge"); } - Assert.assertSame(ObjectInputFilter.merge(filter1, null), filter1, "merge with null fail"); - Assert.assertThrows(NullPointerException.class, () -> ObjectInputFilter.merge(null, filter1)); + Assertions.assertSame(ObjectInputFilter.merge(filter1, null), filter1, "merge with null fail"); + Assertions.assertThrows(NullPointerException.class, () -> ObjectInputFilter.merge(null, filter1)); } } @@ -84,7 +83,7 @@ public class SerialFilterFunctionTest { return (cl) -> cl.equals(Integer.class); } - @DataProvider(name = "AllowPredicateCases") + // Test cases of filter strings static Object[][] allowPredicateCases() { return new Object[][]{ { Integer.class, isInteger(), REJECTED, ALLOWED}, @@ -95,18 +94,17 @@ public class SerialFilterFunctionTest { }; } - @Test(dataProvider = "AllowPredicateCases") + @ParameterizedTest + @MethodSource("allowPredicateCases") void testAllowPredicates(Class clazz, Predicate> predicate, Status otherStatus, Status expected) { ObjectInputFilter.FilterInfo info = new SerialInfo(clazz); if (predicate == null || expected == null) { - Assert.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); + Assertions.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); } else { - Assert.assertEquals(ObjectInputFilter.allowFilter(predicate, otherStatus).checkInput(info), - expected, "Predicate result"); + Assertions.assertEquals( expected, ObjectInputFilter.allowFilter(predicate, otherStatus).checkInput(info), "Predicate result"); } } - @DataProvider(name = "RejectPredicateCases") static Object[][] rejectPredicateCases() { return new Object[][]{ { Integer.class, isInteger(), REJECTED, REJECTED}, @@ -117,14 +115,15 @@ public class SerialFilterFunctionTest { }; } - @Test(dataProvider = "RejectPredicateCases") + @ParameterizedTest + @MethodSource("rejectPredicateCases") void testRejectPredicates(Class clazz, Predicate> predicate, Status otherStatus, Status expected) { ObjectInputFilter.FilterInfo info = new SerialInfo(clazz); if (predicate == null || expected == null) { - Assert.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); + Assertions.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); } else { - Assert.assertEquals(ObjectInputFilter.rejectFilter(predicate, otherStatus) - .checkInput(info), expected, "Predicate result"); + Assertions.assertEquals(expected, ObjectInputFilter.rejectFilter(predicate, otherStatus) + .checkInput(info), "Predicate result"); } } @@ -133,11 +132,11 @@ public class SerialFilterFunctionTest { FilterInfo info = new SerialInfo(Object.class); // an info structure, unused ObjectInputFilter undecided = getFilter(UNDECIDED); - Assert.assertEquals(ObjectInputFilter.rejectUndecidedClass(undecided).checkInput(info), REJECTED, "undecided -> rejected"); + Assertions.assertEquals(REJECTED, ObjectInputFilter.rejectUndecidedClass(undecided).checkInput(info), "undecided -> rejected"); ObjectInputFilter allowed = getFilter(ALLOWED); - Assert.assertEquals(ObjectInputFilter.rejectUndecidedClass(allowed).checkInput(info), ALLOWED, "allowed -> rejected"); + Assertions.assertEquals(ALLOWED, ObjectInputFilter.rejectUndecidedClass(allowed).checkInput(info), "allowed -> rejected"); ObjectInputFilter rejected = getFilter(REJECTED); - Assert.assertEquals(ObjectInputFilter.rejectUndecidedClass(rejected).checkInput(info), REJECTED, "rejected -> rejected"); + Assertions.assertEquals(REJECTED, ObjectInputFilter.rejectUndecidedClass(rejected).checkInput(info), "rejected -> rejected"); // Specific cases of Classes the result in allowed, rejected, and undecided status ObjectInputFilter numberFilter = ObjectInputFilter.Config.createFilter("java.lang.Integer;!java.lang.Double"); @@ -164,9 +163,9 @@ public class SerialFilterFunctionTest { while (clazz.isArray()) clazz = clazz.getComponentType(); Status expected = (clazz.isPrimitive()) ? UNDECIDED : REJECTED; - Assert.assertEquals(st, expected, "Wrong status for class: " + obj.getClass()); + Assertions.assertEquals(expected, st, "Wrong status for class: " + obj.getClass()); } else { - Assert.assertEquals(rawSt, st, "raw filter and rejectUndecided filter disagree"); + Assertions.assertEquals(st, rawSt, "raw filter and rejectUndecided filter disagree"); } } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java b/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java index fb02cd02268..3b2ecc3d1b1 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.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 @@ -46,20 +46,20 @@ import java.util.concurrent.atomic.LongAdder; import javax.net.ssl.SSLEngineResult; -import org.testng.Assert; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @bug 8234836 * @build SerialFilterTest - * @run testng/othervm -Djava.util.logging.config.file=${test.src}/logging.properties + * @run junit/othervm -Djava.util.logging.config.file=${test.src}/logging.properties * SerialFilterTest - * @run testng/othervm -Djdk.serialSetFilterAfterRead=true SerialFilterTest + * @run junit/othervm -Djdk.serialSetFilterAfterRead=true SerialFilterTest * * @summary Test ObjectInputFilters using Builtin Filter Factory */ -@Test public class SerialFilterTest implements Serializable { @Serial @@ -89,9 +89,8 @@ public class SerialFilterTest implements Serializable { * Expand the patterns into cases for each of the Std and Compatibility APIs. * @return an array of arrays of the parameters including factories */ - @DataProvider(name="Patterns") static Object[][] patterns() { - Object[][] patterns = new Object[][]{ + return new Object[][]{ {"java.util.Hashtable"}, {"java.util.Hash*"}, {"javax.net.ssl.*"}, @@ -105,10 +104,8 @@ public class SerialFilterTest implements Serializable { {"maxbytes=+1024"}, {"java.base/java.util.Hashtable"}, }; - return patterns; } - @DataProvider(name="InvalidPatterns") static Object[][] invalidPatterns() { return new Object [][] { {".*"}, @@ -120,7 +117,6 @@ public class SerialFilterTest implements Serializable { }; } - @DataProvider(name="Limits") static Object[][] limits() { // The numbers are arbitrary > 1 return new Object[][] { @@ -133,7 +129,6 @@ public class SerialFilterTest implements Serializable { }; } - @DataProvider(name="InvalidLimits") static Object[][] invalidLimits() { return new Object[][] { {"maxrefs=-1"}, @@ -157,7 +152,6 @@ public class SerialFilterTest implements Serializable { * available to the filter. * @return Arrays of parameters with objects */ - @DataProvider(name="Objects") static Object[][] objects() { byte[] byteArray = new byte[0]; Object[] objArray = new Object[7]; @@ -168,7 +162,7 @@ public class SerialFilterTest implements Serializable { try { serClass = Class.forName(className); } catch (Exception e) { - Assert.fail("missing class: " + className, e); + Assertions.fail("missing class: " + className, e); } Class[] interfaces = {Runnable.class}; @@ -213,7 +207,6 @@ public class SerialFilterTest implements Serializable { return objects; } - @DataProvider(name="Arrays") static Object[][] arrays() { return new Object[][]{ {new Object[16], 16}, @@ -244,21 +237,22 @@ public class SerialFilterTest implements Serializable { * @param classes the expected (unique) classes * @throws IOException */ - @Test(dataProvider="Objects") + @ParameterizedTest + @MethodSource("objects") void t1(Object object, long count, long maxArray, long maxRefs, long maxDepth, long maxBytes, List> classes) throws IOException { byte[] bytes = writeObjects(object); Validator validator = new Validator(); validate(bytes, validator); - System.out.printf("v: %s%n", validator); + System.err.printf("v: %s%n", validator); - Assert.assertEquals(validator.count, count, "callback count wrong"); - Assert.assertEquals(validator.classes, classes, "classes mismatch"); - Assert.assertEquals(validator.maxArray, maxArray, "maxArray mismatch"); - Assert.assertEquals(validator.maxRefs, maxRefs, "maxRefs wrong"); - Assert.assertEquals(validator.maxDepth, maxDepth, "depth wrong"); - Assert.assertEquals(validator.maxBytes, maxBytes, "maxBytes wrong"); + Assertions.assertEquals(count, validator.count, "callback count wrong"); + Assertions.assertEquals(classes, validator.classes, "classes mismatch"); + Assertions.assertEquals(maxArray, validator.maxArray, "maxArray mismatch"); + Assertions.assertEquals(maxRefs, validator.maxRefs, "maxRefs wrong"); + Assertions.assertEquals(maxDepth, validator.maxDepth, "depth wrong"); + Assertions.assertEquals(maxBytes, validator.maxBytes, "maxBytes wrong"); } /** @@ -269,7 +263,8 @@ public class SerialFilterTest implements Serializable { * * @param pattern a pattern */ - @Test(dataProvider="Patterns") + @ParameterizedTest + @MethodSource("patterns") void testPatterns(String pattern) { evalPattern(pattern, (p, o, neg) -> testPatterns(p, o, neg)); } @@ -297,23 +292,23 @@ public class SerialFilterTest implements Serializable { // Check the initial filter is the global filter; may be null ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter(); ObjectInputFilter initial = ois.getObjectInputFilter(); - Assert.assertEquals(global, initial, "initial filter should be the global filter"); + Assertions.assertEquals(initial, global, "initial filter should be the global filter"); ois.setObjectInputFilter(validator); Object o = ois.readObject(); try { ois.setObjectInputFilter(validator2); - Assert.fail("Should not be able to set filter twice"); + Assertions.fail("Should not be able to set filter twice"); } catch (IllegalStateException ise) { // success, the exception was expected } } catch (EOFException eof) { - Assert.fail("Should not reach end-of-file", eof); + Assertions.fail("Should not reach end-of-file", eof); } catch (ClassNotFoundException cnf) { - Assert.fail("Deserializing", cnf); + Assertions.fail("Deserializing", cnf); } } catch (IOException ex) { - Assert.fail("Unexpected IOException", ex); + Assertions.fail("Unexpected IOException", ex); } } } @@ -336,7 +331,7 @@ public class SerialFilterTest implements Serializable { try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bais)) { Object actual1 = toggle ? ois.readObject() : ois.readUnshared(); - Assert.assertEquals(actual1, expected1, "unexpected string"); + Assertions.assertEquals(expected1, actual1, "unexpected string"); // Attempt to set filter ois.setObjectInputFilter(new ObjectInputFilter() { @Override @@ -345,14 +340,14 @@ public class SerialFilterTest implements Serializable { } }); if (!SET_FILTER_AFTER_READ) - Assert.fail("Should not be able to set filter after readObject has been called"); + Assertions.fail("Should not be able to set filter after readObject has been called"); } catch (IllegalStateException ise) { // success, the exception was expected if (SET_FILTER_AFTER_READ) - Assert.fail("With jdk.serialSetFilterAfterRead property set = true; " + + Assertions.fail("With jdk.serialSetFilterAfterRead property set = true; " + "should be able to set the filter after a read"); } catch (EOFException eof) { - Assert.fail("Should not reach end-of-file", eof); + Assertions.fail("Should not reach end-of-file", eof); } } } @@ -362,12 +357,13 @@ public class SerialFilterTest implements Serializable { * that the callback to the filter includes the proper array length. * @throws IOException if an error occurs */ - @Test(dataProvider="Arrays") + @ParameterizedTest + @MethodSource("arrays") void testReadResolveToArray(Object array, int length) throws IOException { ReadResolveToArray object = new ReadResolveToArray(array, length); byte[] bytes = writeObjects(object); Object o = validate(bytes, object); // the object is its own filter - Assert.assertEquals(o.getClass(), array.getClass(), "Filter not called with the array"); + Assertions.assertEquals(array.getClass(), o.getClass(), "Filter not called with the array"); } @@ -379,18 +375,17 @@ public class SerialFilterTest implements Serializable { * @param name the name of the limit to test * @param value a test value */ - @Test(dataProvider="Limits") + @ParameterizedTest + @MethodSource("limits") void testLimits(String name, long value) { Class arrayClass = new int[0].getClass(); String pattern = String.format("%s=%d;%s=%d", name, value, name, value - 1); ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); - Assert.assertEquals( + Assertions.assertEquals(ObjectInputFilter.Status.REJECTED, filter.checkInput(new FilterValues(arrayClass, value, value, value, value)), - ObjectInputFilter.Status.REJECTED, "last limit value not used: " + filter); - Assert.assertEquals( + Assertions.assertEquals(ObjectInputFilter.Status.UNDECIDED, filter.checkInput(new FilterValues(arrayClass, value-1, value-1, value-1, value-1)), - ObjectInputFilter.Status.UNDECIDED, "last limit value not used: " + filter); } @@ -399,46 +394,36 @@ public class SerialFilterTest implements Serializable { * Construct a filter with the limit, it should throw IllegalArgumentException. * @param pattern a pattern to test */ - @Test(dataProvider="InvalidLimits", expectedExceptions=java.lang.IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("invalidLimits") void testInvalidLimits(String pattern) { - try { - ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); - } catch (IllegalArgumentException iae) { - System.out.printf(" success exception: %s%n", iae); - throw iae; - } + var iae = Assertions.assertThrows(IllegalArgumentException.class, + () -> ObjectInputFilter.Config.createFilter(pattern)); + System.err.printf(" success exception: %s%n", iae); } /** * Test that returning null from a filter causes deserialization to fail. */ - @Test(expectedExceptions=InvalidClassException.class) + @Test void testNullStatus() throws IOException { - byte[] bytes = writeObjects(0); // an Integer - try { - Object o = validate(bytes, new ObjectInputFilter() { - public ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo f) { - return null; - } - }); - } catch (InvalidClassException ice) { - System.out.printf(" success exception: %s%n", ice); - throw ice; - } + var ice = Assertions.assertThrows(InvalidClassException.class, () -> { + byte[] bytes = writeObjects(0); // an Integer + validate(bytes, f -> null); + }); + System.err.printf(" success exception: %s%n", ice); } /** * Verify that malformed patterns throw IAE. * @param pattern pattern from the data source */ - @Test(dataProvider="InvalidPatterns", expectedExceptions=IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("invalidPatterns") void testInvalidPatterns(String pattern) { - try { - ObjectInputFilter.Config.createFilter(pattern); - } catch (IllegalArgumentException iae) { - System.out.printf(" success exception: %s%n", iae); - throw iae; - } + var iae = Assertions.assertThrows(IllegalArgumentException.class, + () -> ObjectInputFilter.Config.createFilter(pattern)); + System.err.printf(" success exception: %s%n", iae); } /** @@ -447,10 +432,10 @@ public class SerialFilterTest implements Serializable { @Test() void testEmptyPattern() { ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(""); - Assert.assertNull(filter, "empty pattern did not return null"); + Assertions.assertNull(filter, "empty pattern did not return null"); filter = ObjectInputFilter.Config.createFilter(";;;;"); - Assert.assertNull(filter, "pattern with only delimiters did not return null"); + Assertions.assertNull(filter, "pattern with only delimiters did not return null"); } /** @@ -472,7 +457,7 @@ public class SerialFilterTest implements Serializable { } catch (EOFException eof) { // normal completion } catch (ClassNotFoundException cnf) { - Assert.fail("Deserializing", cnf); + Assertions.fail("Deserializing", cnf); } return null; } @@ -514,7 +499,7 @@ public class SerialFilterTest implements Serializable { @Override public ObjectInputFilter.Status checkInput(FilterInfo filter) { Class serialClass = filter.serialClass(); - System.out.printf(" checkInput: class: %s, arrayLen: %d, refs: %d, depth: %d, bytes; %d%n", + System.err.printf(" checkInput: class: %s, arrayLen: %d, refs: %d, depth: %d, bytes; %d%n", serialClass, filter.arrayLength(), filter.references(), filter.depth(), filter.streamBytes()); count++; @@ -561,13 +546,13 @@ public class SerialFilterTest implements Serializable { byte[] bytes = SerialFilterTest.writeObjects(object); ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); validate(bytes, filter); - Assert.assertTrue(allowed, "filter should have thrown an exception"); + Assertions.assertTrue(allowed, "filter should have thrown an exception"); } catch (IllegalArgumentException iae) { - Assert.fail("bad format pattern", iae); + Assertions.fail("bad format pattern", iae); } catch (InvalidClassException ice) { - Assert.assertFalse(allowed, "filter should not have thrown an exception: " + ice); + Assertions.assertFalse(allowed, "filter should not have thrown an exception: " + ice); } catch (IOException ioe) { - Assert.fail("Unexpected IOException", ioe); + Assertions.fail("Unexpected IOException", ioe); } } @@ -578,12 +563,12 @@ public class SerialFilterTest implements Serializable { */ static void evalPattern(String pattern, TriConsumer action) { Object o = genTestObject(pattern, true); - Assert.assertNotNull(o, "success generation failed"); + Assertions.assertNotNull(o, "success generation failed"); action.accept(pattern, o, true); // Test the negative pattern o = genTestObject(pattern, false); - Assert.assertNotNull(o, "fail generation failed"); + Assertions.assertNotNull(o, "fail generation failed"); String negPattern = pattern.contains("=") ? pattern : "!" + pattern; action.accept(negPattern, o, false); } @@ -616,12 +601,12 @@ public class SerialFilterTest implements Serializable { Constructor cons = clazz.getConstructor(); return cons.newInstance(); } catch (ClassNotFoundException ex) { - Assert.fail("no such class available: " + pattern); + Assertions.fail("no such class available: " + pattern); } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException ex1) { - Assert.fail("newInstance: " + ex1); + Assertions.fail("newInstance: " + ex1); } } return null; @@ -661,7 +646,7 @@ public class SerialFilterTest implements Serializable { return new Hashtable(); } } - Assert.fail("Object could not be generated for pattern: " + Assertions.fail("Object could not be generated for pattern: " + pattern + ", allowed: " + allowed); return null; @@ -677,7 +662,7 @@ public class SerialFilterTest implements Serializable { */ static Object genTestLimit(String pattern, boolean allowed) { int ndx = pattern.indexOf('='); - Assert.assertNotEquals(ndx, -1, "missing value in limit"); + Assertions.assertNotEquals(-1, ndx, "missing value in limit"); long value = Long.parseUnsignedLong(pattern.substring(ndx+1)); if (pattern.startsWith("maxdepth=")) { // Return an object with the requested depth (or 1 greater) @@ -703,7 +688,7 @@ public class SerialFilterTest implements Serializable { } else if (pattern.startsWith("maxarray=")) { return allowed ? new int[(int)value] : new int[(int)value+1]; } - Assert.fail("Object could not be generated for pattern: " + Assertions.fail("Object could not be generated for pattern: " + pattern + ", allowed: " + allowed); return null; @@ -736,7 +721,7 @@ public class SerialFilterTest implements Serializable { os.flush(); actualSize = baos.size(); } catch (IOException ie) { - Assert.fail("exception generating stream", ie); + Assertions.fail("exception generating stream", ie); } } while (actualSize != desiredSize); return holder; From 6ad9f4ef6826bb031db7840ba3f689b0bde47775 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 14 Jan 2026 21:27:34 +0000 Subject: [PATCH 008/126] 8374493: Add missing @Override annotations in "com.sun.java.swing.plaf.motif" package Reviewed-by: tr, prr, aivanov --- .../java/swing/plaf/motif/MotifBorders.java | 21 ++++++++- .../swing/plaf/motif/MotifButtonListener.java | 3 +- .../java/swing/plaf/motif/MotifButtonUI.java | 9 +++- .../plaf/motif/MotifCheckBoxMenuItemUI.java | 13 +++++- .../swing/plaf/motif/MotifCheckBoxUI.java | 5 ++- .../swing/plaf/motif/MotifComboBoxUI.java | 20 ++++++++- .../swing/plaf/motif/MotifDesktopIconUI.java | 32 ++++++++++++- .../swing/plaf/motif/MotifDesktopPaneUI.java | 12 ++++- .../swing/plaf/motif/MotifEditorPaneUI.java | 3 +- .../swing/plaf/motif/MotifFileChooserUI.java | 45 ++++++++++++++++++- .../swing/plaf/motif/MotifIconFactory.java | 17 ++++++- .../motif/MotifInternalFrameTitlePane.java | 38 +++++++++++++++- .../plaf/motif/MotifInternalFrameUI.java | 19 +++++++- .../swing/plaf/motif/MotifLookAndFeel.java | 22 ++++++++- .../swing/plaf/motif/MotifMenuItemUI.java | 13 +++++- .../plaf/motif/MotifMenuMouseListener.java | 6 ++- .../motif/MotifMenuMouseMotionListener.java | 4 +- .../java/swing/plaf/motif/MotifMenuUI.java | 12 ++++- .../swing/plaf/motif/MotifOptionPaneUI.java | 8 +++- .../plaf/motif/MotifPasswordFieldUI.java | 3 +- .../plaf/motif/MotifPopupMenuSeparatorUI.java | 4 +- .../swing/plaf/motif/MotifPopupMenuUI.java | 5 ++- .../motif/MotifRadioButtonMenuItemUI.java | 13 +++++- .../swing/plaf/motif/MotifRadioButtonUI.java | 5 ++- .../plaf/motif/MotifScrollBarButton.java | 7 ++- .../swing/plaf/motif/MotifScrollBarUI.java | 7 ++- .../java/swing/plaf/motif/MotifSliderUI.java | 10 ++++- .../plaf/motif/MotifSplitPaneDivider.java | 8 +++- .../swing/plaf/motif/MotifSplitPaneUI.java | 3 +- .../swing/plaf/motif/MotifTabbedPaneUI.java | 12 ++++- .../swing/plaf/motif/MotifTextAreaUI.java | 3 +- .../swing/plaf/motif/MotifTextFieldUI.java | 3 +- .../swing/plaf/motif/MotifTextPaneUI.java | 3 +- .../java/swing/plaf/motif/MotifTextUI.java | 6 ++- .../swing/plaf/motif/MotifToggleButtonUI.java | 5 ++- .../plaf/motif/MotifTreeCellRenderer.java | 5 ++- .../java/swing/plaf/motif/MotifTreeUI.java | 10 ++++- 37 files changed, 377 insertions(+), 37 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java index c61b1258894..6a97dd22d75 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -67,6 +67,7 @@ public class MotifBorders { this.lightShadow = lightShadow; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { g.setColor((isRaised) ? lightShadow : darkShadow); g.drawLine(x, y, x+w-1, y); // top @@ -77,6 +78,7 @@ public class MotifBorders { g.drawLine(x+w-1, y+h-1, x+w-1, y+1); // right } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(1, 1, 1, 1); return insets; @@ -99,6 +101,7 @@ public class MotifBorders { this.focus = focus; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { if (c.hasFocus()) { g.setColor(focus); @@ -109,6 +112,7 @@ public class MotifBorders { } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(1, 1, 1, 1); return insets; @@ -130,6 +134,7 @@ public class MotifBorders { this.focus = focus; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { boolean isPressed = false; boolean hasFocus = false; @@ -187,6 +192,7 @@ public class MotifBorders { g.drawLine(bx1+1, by2, bx2, by2); } + @Override public Insets getBorderInsets(Component c, Insets insets) { int thickness = (c instanceof JButton && ((JButton)c).isDefaultCapable())? 8 : 2; insets.set(thickness, thickness, thickness, thickness); @@ -202,6 +208,7 @@ public class MotifBorders { super(shadow, highlight, darkShadow, focus); } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (c instanceof AbstractButton) { @@ -223,6 +230,7 @@ public class MotifBorders { } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(2, 2, 3, 3); return insets; @@ -236,6 +244,7 @@ public class MotifBorders { super(shadow, highlight, darkShadow, focus); } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (!(c instanceof JMenuBar)) { return; @@ -249,6 +258,7 @@ public class MotifBorders { } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(6, 6, 6, 6); return insets; @@ -297,6 +307,7 @@ public class MotifBorders { return frameShadow; } + @Override public Insets getBorderInsets(Component c, Insets newInsets) { newInsets.set(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE); return newInsets; @@ -424,6 +435,7 @@ public class MotifBorders { * drawTitleBar, drawLeftBorder, drawRightBorder and * drawBottomBorder. */ + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (isActiveFrame()) { @@ -487,6 +499,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's top border. */ + @Override protected boolean drawTopBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawTopBorder(c, g, x, y, width, height) && @@ -506,6 +519,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's left border. */ + @Override protected boolean drawLeftBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawLeftBorder(c, g, x, y, width, height) && @@ -525,6 +539,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's right border. */ + @Override protected boolean drawRightBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawRightBorder(c, g, x, y, width, height) && @@ -545,6 +560,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's bottom border. */ + @Override protected boolean drawBottomBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawBottomBorder(c, g, x, y, width, height) && @@ -567,6 +583,7 @@ public class MotifBorders { } // Returns true if the associated internal frame has focus. + @Override protected boolean isActiveFrame() { return frame.isSelected(); } @@ -667,6 +684,7 @@ public class MotifBorders { * @param width the width of the painted border * @param height the height of the painted border */ + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (!(c instanceof JPopupMenu)) { return; @@ -713,6 +731,7 @@ public class MotifBorders { * @param c the component for which this border insets value applies * @param insets the object to be reinitialized */ + @Override public Insets getBorderInsets(Component c, Insets insets) { if (!(c instanceof JPopupMenu)) { return insets; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java index a1a74f1230f..65754efed0d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2001, 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 @@ -42,6 +42,7 @@ public class MotifButtonListener extends BasicButtonListener { super(b); } + @Override protected void checkOpacity(AbstractButton b) { b.setOpaque( false ); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java index 08017a0b2c8..0e4c2e526ac 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -74,6 +74,7 @@ public class MotifButtonUI extends BasicButtonUI { // ******************************** // Create Listeners // ******************************** + @Override protected BasicButtonListener createButtonListener(AbstractButton b){ return new MotifButtonListener(b); } @@ -81,6 +82,7 @@ public class MotifButtonUI extends BasicButtonUI { // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -90,6 +92,7 @@ public class MotifButtonUI extends BasicButtonUI { LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -106,12 +109,14 @@ public class MotifButtonUI extends BasicButtonUI { // ******************************** // Paint Methods // ******************************** + @Override public void paint(Graphics g, JComponent c) { fillContentArea( g, (AbstractButton)c , c.getBackground() ); super.paint(g,c); } // Overridden to ensure we don't paint icon over button borders. + @Override protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) { Shape oldClip = g.getClip(); Rectangle newClip = @@ -127,10 +132,12 @@ public class MotifButtonUI extends BasicButtonUI { g.setClip(oldClip); } + @Override protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, Rectangle textRect, Rectangle iconRect){ // focus painting is handled by the border } + @Override protected void paintButtonPressed(Graphics g, AbstractButton b) { fillContentArea( g, b , selectColor ); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java index bcaf72cbe26..17d66a74bad 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, 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 @@ -50,12 +50,14 @@ public class MotifCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI return new MotifCheckBoxMenuItemUI(); } + @Override protected void installListeners() { super.installListeners(); changeListener = createChangeListener(menuItem); menuItem.addChangeListener(changeListener); } + @Override protected void uninstallListeners() { super.uninstallListeners(); menuItem.removeChangeListener(changeListener); @@ -66,23 +68,28 @@ public class MotifCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI } protected class ChangeHandler implements ChangeListener { + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); LookAndFeel.installProperty(c, "borderPainted", c.isArmed()); } } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.setSelectedPath(getPath()); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -99,11 +106,15 @@ public class MotifCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java index fa06aa6bd02..9e9f0008b07 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +60,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { return motifCheckBoxUI; } + @Override public String getPropertyPrefix() { return propertyPrefix; } @@ -67,6 +68,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { // ******************************** // Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -75,6 +77,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java index d0cea2ec35b..8a6cb918ae4 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -65,6 +65,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return new MotifComboBoxUI(); } + @Override public void installUI(JComponent c) { super.installUI(c); arrowIcon = new MotifComboBoxArrowIcon(UIManager.getColor("controlHighlight"), @@ -72,6 +73,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { UIManager.getColor("control")); } + @Override public Dimension getMinimumSize( JComponent c ) { if ( !isMinimumSizeDirty ) { return new Dimension( cachedMinimumSize ); @@ -89,6 +91,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return size; } + @Override protected ComboPopup createPopup() { return new MotifComboPopup( comboBox ); } @@ -106,10 +109,12 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { /** * Motif combo popup should not track the mouse in the list. */ + @Override public MouseMotionListener createListMouseMotionListener() { return new MouseMotionAdapter() {}; } + @Override public KeyListener createKeyListener() { return super.createKeyListener(); } @@ -121,6 +126,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { } } + @Override protected void installComponents() { if ( comboBox.isEditable() ) { addEditor(); @@ -129,11 +135,13 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { comboBox.add( currentValuePane ); } + @Override protected void uninstallComponents() { removeEditor(); comboBox.removeAll(); } + @Override public void paint(Graphics g, JComponent c) { boolean hasFocus = comboBox.hasFocus(); Rectangle r; @@ -180,6 +188,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { currentValuePane.removeAll(); } + @Override public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) { ListCellRenderer renderer = comboBox.getRenderer(); Component c; @@ -226,6 +235,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return b; } + @Override protected Rectangle rectangleForCurrentValue() { int width = comboBox.getWidth(); int height = comboBox.getHeight(); @@ -251,11 +261,13 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return arrowIcon.getIconWidth() + (3 * HORIZ_MARGIN) + 2; } + @Override public void configureEditor() { super.configureEditor(); editor.setBackground( UIManager.getColor( "text" ) ); } + @Override protected LayoutManager createLayoutManager() { return new ComboBoxLayoutManager(); } @@ -273,6 +285,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { public ComboBoxLayoutManager() { MotifComboBoxUI.this.super(); } + @Override public void layoutContainer(Container parent) { if ( motifGetEditor() != null ) { Rectangle cvb = rectangleForCurrentValue(); @@ -298,6 +311,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { } + @Override public void paintIcon(Component c, Graphics g, int xo, int yo) { int w = getIconWidth(); int h = getIconHeight(); @@ -322,10 +336,12 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { } + @Override public int getIconWidth() { return 11; } + @Override public int getIconHeight() { return 11; } @@ -336,6 +352,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { * * @since 1.6 */ + @Override protected PropertyChangeListener createPropertyChangeListener() { return new MotifPropertyChangeListener(); } @@ -345,6 +362,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { */ private class MotifPropertyChangeListener extends BasicComboBoxUI.PropertyChangeHandler { + @Override public void propertyChange(PropertyChangeEvent e) { super.propertyChange(e); String propertyName = e.getPropertyName(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java index b71363d1c0e..872d4992d91 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, 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 @@ -74,6 +74,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI public MotifDesktopIconUI() { } + @Override protected void installDefaults(){ super.installDefaults(); setDefaultIcon(UIManager.getIcon("DesktopIcon.icon")); @@ -94,12 +95,15 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI JLayeredPane.putLayer(desktopIcon, JLayeredPane.getLayer(frame)); } + @Override protected void installComponents(){ } + @Override protected void uninstallComponents(){ } + @Override protected void installListeners(){ super.installListeners(); desktopIconActionListener = createDesktopIconActionListener(); @@ -149,6 +153,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI return new DesktopIconMouseListener(); } + @Override protected void uninstallDefaults(){ super.uninstallDefaults(); desktopIcon.setLayout(null); @@ -156,6 +161,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI desktopIcon.remove(iconLabel); } + @Override protected void uninstallListeners(){ super.uninstallListeners(); iconButton.removeActionListener(desktopIconActionListener); @@ -163,6 +169,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI sysMenuTitlePane.uninstallListeners(); } + @Override public Dimension getMinimumSize(JComponent c) { JInternalFrame iframe = desktopIcon.getInternalFrame(); @@ -180,10 +187,12 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI return new Dimension(w, h); } + @Override public Dimension getPreferredSize(JComponent c) { return getMinimumSize(c); } + @Override public Dimension getMaximumSize(JComponent c){ return getMinimumSize(c); } @@ -213,26 +222,33 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI // Forward mouse events to titlebar for moves. addMouseMotionListener(new MouseMotionListener() { + @Override public void mouseDragged(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseMoved(MouseEvent e) { forwardEventToParent(e); } }); addMouseListener(new MouseListener() { + @Override public void mouseClicked(MouseEvent e) { forwardEventToParent(e); } + @Override public void mousePressed(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseReleased(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseEntered(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseExited(MouseEvent e) { forwardEventToParent(e); } @@ -251,16 +267,19 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI getParent().dispatchEvent(newEvent); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; } + @Override public Dimension getMinimumSize() { return new Dimension(defaultIcon.getIconWidth() + 1, LABEL_HEIGHT + LABEL_DIVIDER); } + @Override public Dimension getPreferredSize() { String title = frame.getTitle(); FontMetrics fm = frame.getFontMetrics(defaultTitleFont); @@ -271,6 +290,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI return new Dimension(w, LABEL_HEIGHT + LABEL_DIVIDER); } + @Override public void paint(Graphics g) { super.paint(g); @@ -308,28 +328,35 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI this.icon = icon; // Forward mouse events to titlebar for moves. addMouseMotionListener(new MouseMotionListener() { + @Override public void mouseDragged(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseMoved(MouseEvent e) { forwardEventToParent(e); } }); addMouseListener(new MouseListener() { + @Override public void mouseClicked(MouseEvent e) { forwardEventToParent(e); } + @Override public void mousePressed(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseReleased(MouseEvent e) { if (!systemMenu.isShowing()) { forwardEventToParent(e); } } + @Override public void mouseEntered(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseExited(MouseEvent e) { forwardEventToParent(e); } @@ -347,6 +374,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI getParent().dispatchEvent(newEvent); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; @@ -355,6 +383,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI protected class DesktopIconActionListener implements ActionListener { + @Override public void actionPerformed(ActionEvent e){ systemMenu.show(iconButton, 0, getDesktopIcon().getHeight()); } @@ -362,6 +391,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI protected class DesktopIconMouseListener extends MouseAdapter { // if we drag or move we should deengage the popup + @Override public void mousePressed(MouseEvent e){ if (e.getClickCount() > 1) { try { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java index 50563a971da..66f253be8c1 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java @@ -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 @@ -59,6 +59,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU public MotifDesktopPaneUI() { } + @Override protected void installDesktopManager() { desktopManager = desktop.getDesktopManager(); if(desktopManager == null) { @@ -75,6 +76,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU //////////////////////////////////////////////////////////////////////////////////// @SuppressWarnings("serial") // Superclass is not serializable across versions private static class DragPane extends JComponent { + @Override public void paint(Graphics g) { g.setColor(Color.darkGray); g.drawRect(0, 0, getWidth()-1, getHeight()-1); @@ -92,6 +94,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU int iconWidth, iconHeight; // PENDING(klobad) this should be optimized + @Override public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) { if(!usingDragPane) { @@ -112,6 +115,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void beginDraggingFrame(JComponent f) { usingDragPane = false; if(f.getParent() instanceof JLayeredPane) { @@ -125,10 +129,12 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void dragFrame(JComponent f, int newX, int newY) { setBoundsForFrame(f, newX, newY, f.getWidth(), f.getHeight()); } + @Override public void endDraggingFrame(JComponent f) { if(usingDragPane) { layeredPaneForDragPane.remove(dragPane); @@ -143,6 +149,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void beginResizingFrame(JComponent f, int direction) { usingDragPane = false; if(f.getParent() instanceof JLayeredPane) { @@ -157,11 +164,13 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) { setBoundsForFrame(f, newX, newY, newWidth, newHeight); } + @Override public void endResizingFrame(JComponent f) { if(usingDragPane) { JLayeredPane p = (JLayeredPane)f.getParent(); @@ -172,6 +181,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void iconifyFrame(JInternalFrame f) { JInternalFrame.JDesktopIcon icon = f.getDesktopIcon(); Point p = icon.getLocation(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java index 3367b36f0fd..e0be55c9be7 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -55,6 +55,7 @@ public class MotifEditorPaneUI extends BasicEditorPaneUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java index 7d820930415..10a193b1271 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.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 @@ -141,6 +141,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { super(filechooser); } + @Override public String getFileName() { if(filenameTextField != null) { return filenameTextField.getText(); @@ -149,30 +150,37 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public void setFileName(String filename) { if(filenameTextField != null) { filenameTextField.setText(filename); } } + @Override public String getDirectoryName() { return pathField.getText(); } + @Override public void setDirectoryName(String dirname) { pathField.setText(dirname); } + @Override public void ensureFileIsVisible(JFileChooser fc, File f) { // PENDING(jeff) } + @Override public void rescanCurrentDirectory(JFileChooser fc) { getModel().validateFileCache(); } + @Override public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { return new PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if(prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { @@ -266,10 +274,12 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return new MotifFileChooserUI((JFileChooser)c); } + @Override public void installUI(JComponent c) { super.installUI(c); } + @Override public void uninstallUI(JComponent c) { c.removePropertyChangeListener(filterComboBoxModel); approveButton.removeActionListener(getApproveSelectionAction()); @@ -277,11 +287,13 @@ public class MotifFileChooserUI extends BasicFileChooserUI { super.uninstallUI(c); } + @Override public void installComponents(JFileChooser fc) { fc.setLayout(new BorderLayout(10, 10)); fc.setAlignmentX(JComponent.CENTER_ALIGNMENT); JPanel interior = new JPanel() { + @Override public Insets getInsets() { return insets; } @@ -305,6 +317,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } JTextField tmp1 = new JTextField(curDirName, 35) { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -340,6 +353,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { leftPanel.add(l); JComboBox tmp2 = new JComboBox() { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -417,6 +431,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { interior.add(fileNameLabel); JTextField tmp3 = new JTextField(35) { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -441,6 +456,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton tmp4 = new JButton(getApproveButtonText(fc)) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -456,6 +472,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton updateButton = new JButton(updateButtonText) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -470,6 +487,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton cancelButton = new JButton(cancelButtonText) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -484,6 +502,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton helpButton = new JButton(helpButtonText) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -520,6 +539,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public void uninstallComponents(JFileChooser fc) { fc.removeAll(); bottomPanel = null; @@ -528,6 +548,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override protected void installStrings(JFileChooser fc) { super.installStrings(fc); @@ -555,11 +576,13 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return SwingUtilities2.getUIDefaultsInt(key, l); } + @Override protected void installIcons(JFileChooser fc) { // Since motif doesn't have button icons, leave this empty // which overrides the supertype icon loading } + @Override protected void uninstallIcons(JFileChooser fc) { // Since motif doesn't have button icons, leave this empty // which overrides the supertype icon loading @@ -580,6 +603,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { fileList.addListSelectionListener(createListSelectionListener(getFileChooser())); fileList.addMouseListener(createDoubleClickListener(getFileChooser(), fileList)); fileList.addMouseListener(new MouseAdapter() { + @Override public void mouseClicked(MouseEvent e) { JFileChooser chooser = getFileChooser(); if (SwingUtilities.isLeftMouseButton(e) && !chooser.isMultiSelectionEnabled()) { @@ -650,6 +674,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { @SuppressWarnings("serial") // Superclass is not serializable across versions protected class FileCellRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -665,6 +690,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { @SuppressWarnings("serial") // Superclass is not serializable across versions protected class DirectoryCellRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -684,18 +710,22 @@ public class MotifFileChooserUI extends BasicFileChooserUI { getModel().addListDataListener(this); } + @Override public int getSize() { return getModel().getDirectories().size(); } + @Override public File getElementAt(int index) { return getModel().getDirectories().elementAt(index); } + @Override public void intervalAdded(ListDataEvent e) { fireIntervalAdded(this, e.getIndex0(), e.getIndex1()); } + @Override public void intervalRemoved(ListDataEvent e) { fireIntervalRemoved(this, e.getIndex0(), e.getIndex1()); } @@ -709,6 +739,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { // PENDING(jeff) - fire the correct interval changed - currently sending // out that everything has changed + @Override public void contentsChanged(ListDataEvent e) { fireContentsChanged(); } @@ -721,6 +752,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { getModel().addListDataListener(this); } + @Override public int getSize() { return getModel().getFiles().size(); } @@ -733,14 +765,17 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return getModel().getFiles().indexOf(o); } + @Override public File getElementAt(int index) { return getModel().getFiles().elementAt(index); } + @Override public void intervalAdded(ListDataEvent e) { fireIntervalAdded(this, e.getIndex0(), e.getIndex1()); } + @Override public void intervalRemoved(ListDataEvent e) { fireIntervalRemoved(this, e.getIndex0(), e.getIndex1()); } @@ -753,6 +788,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } // PENDING(jeff) - fire the interval changed + @Override public void contentsChanged(ListDataEvent e) { fireContentsChanged(); } @@ -779,6 +815,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { */ @SuppressWarnings("serial") // Superclass is not serializable across versions public class FilterComboBoxRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -805,6 +842,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { filters = getFileChooser().getChoosableFileFilters(); } + @Override public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if(prop.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) { @@ -815,6 +853,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public void setSelectedItem(Object filter) { if(filter != null) { getFileChooser().setFileFilter((FileFilter) filter); @@ -822,6 +861,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public Object getSelectedItem() { // Ensure that the current filter is in the list. // NOTE: we shouldn't have to do this, since JFileChooser adds @@ -843,6 +883,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return getFileChooser().getFileFilter(); } + @Override public int getSize() { if(filters != null) { return filters.length; @@ -851,6 +892,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public FileFilter getElementAt(int index) { if(index > getSize() - 1) { // This shouldn't happen. Try to recover gracefully. @@ -864,6 +906,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override protected JButton getApproveButton(JFileChooser fc) { return approveButton; } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java index dac6514f0c4..748c4dc6155 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -93,6 +93,7 @@ public class MotifIconFactory implements Serializable private Color highlight = UIManager.getColor("controlHighlight"); private Color lightShadow = UIManager.getColor("controlLightShadow"); + @Override public void paintIcon(Component c, Graphics g, int x, int y) { AbstractButton b = (AbstractButton) c; ButtonModel model = b.getModel(); @@ -158,10 +159,12 @@ public class MotifIconFactory implements Serializable } } + @Override public int getIconWidth() { return csize; } + @Override public int getIconHeight() { return csize; } @@ -258,6 +261,7 @@ public class MotifIconFactory implements Serializable private Color highlight = UIManager.getColor("controlHighlight"); private Color shadow = UIManager.getColor("controlShadow"); + @Override public void paintIcon(Component c, Graphics g, int x, int y) { // fill interior AbstractButton b = (AbstractButton) c; @@ -303,10 +307,12 @@ public class MotifIconFactory implements Serializable } } + @Override public int getIconWidth() { return 14; } + @Override public int getIconHeight() { return 14; } @@ -315,10 +321,13 @@ public class MotifIconFactory implements Serializable @SuppressWarnings("serial") // Same-version serialization only private static class MenuItemCheckIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c,Graphics g, int x, int y) { } + @Override public int getIconWidth() { return 0; } + @Override public int getIconHeight() { return 0; } } // end class MenuItemCheckIcon @@ -326,10 +335,13 @@ public class MotifIconFactory implements Serializable @SuppressWarnings("serial") // Same-version serialization only private static class MenuItemArrowIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c,Graphics g, int x, int y) { } + @Override public int getIconWidth() { return 0; } + @Override public int getIconHeight() { return 0; } } // end class MenuItemArrowIcon @@ -340,6 +352,7 @@ public class MotifIconFactory implements Serializable private Color shadow = UIManager.getColor("controlShadow"); private Color highlight = UIManager.getColor("controlHighlight"); + @Override public void paintIcon(Component c, Graphics g, int x, int y) { AbstractButton b = (AbstractButton) c; ButtonModel model = b.getModel(); @@ -414,7 +427,9 @@ public class MotifIconFactory implements Serializable } } + @Override public int getIconWidth() { return 10; } + @Override public int getIconHeight() { return 10; } } // End class MenuArrowIcon } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java index b68200c1924..474bd707ce4 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, 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 @@ -66,20 +66,24 @@ public class MotifInternalFrameTitlePane super(frame); } + @Override protected void installDefaults() { setFont(UIManager.getFont("InternalFrame.titleFont")); setPreferredSize(new Dimension(100, BUTTON_SIZE)); } + @Override protected void uninstallListeners() { // Get around protected method in superclass super.uninstallListeners(); } + @Override protected PropertyChangeListener createPropertyChangeListener() { return this; } + @Override protected LayoutManager createLayout() { return this; } @@ -88,6 +92,7 @@ public class MotifInternalFrameTitlePane return systemMenu; } + @Override protected void assembleSystemMenu() { systemMenu = new JPopupMenu(); JMenuItem mi = systemMenu.add(restoreAction); @@ -106,12 +111,14 @@ public class MotifInternalFrameTitlePane systemButton = new SystemButton(); systemButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { systemMenu.show(systemButton, 0, BUTTON_SIZE); } }); systemButton.addMouseListener(new MouseAdapter() { + @Override public void mousePressed(MouseEvent evt) { try { frame.setSelected(true); @@ -137,6 +144,7 @@ public class MotifInternalFrameTitlePane } } + @Override protected void createButtons() { minimizeButton = new MinimizeButton(); minimizeButton.addActionListener(iconifyAction); @@ -146,6 +154,7 @@ public class MotifInternalFrameTitlePane } + @Override protected void addSubComponents() { title = new Title(frame.getTitle()); title.setFont(getFont()); @@ -156,6 +165,7 @@ public class MotifInternalFrameTitlePane add(maximizeButton); } + @Override public void paintComponent(Graphics g) { } @@ -165,9 +175,11 @@ public class MotifInternalFrameTitlePane shadow = s; } + @Override public void actionPerformed(ActionEvent e) { } + @Override public void propertyChange(PropertyChangeEvent evt) { String prop = evt.getPropertyName(); JInternalFrame f = (JInternalFrame)evt.getSource(); @@ -194,16 +206,21 @@ public class MotifInternalFrameTitlePane enableActions(); } + @Override public void addLayoutComponent(String name, Component c) {} + @Override public void removeLayoutComponent(Component c) {} + @Override public Dimension preferredLayoutSize(Container c) { return minimumLayoutSize(c); } + @Override public Dimension minimumLayoutSize(Container c) { return new Dimension(100, BUTTON_SIZE); } + @Override public void layoutContainer(Container c) { int w = getWidth(); systemButton.setBounds(0, 0, BUTTON_SIZE, BUTTON_SIZE); @@ -226,6 +243,7 @@ public class MotifInternalFrameTitlePane title.setBounds(BUTTON_SIZE, 0, x, BUTTON_SIZE); } + @Override protected void showSystemMenu(){ systemMenu.show(systemButton, 0, BUTTON_SIZE); } @@ -245,23 +263,28 @@ public class MotifInternalFrameTitlePane setBorderPainted(false); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; } + @Override public void requestFocus() { // ignore request. } + @Override public Dimension getMinimumSize() { return buttonDimension; } + @Override public Dimension getPreferredSize() { return buttonDimension; } + @Override public void paintComponent(Graphics g) { Dimension d = getSize(); int maxX = d.width - 1; @@ -284,6 +307,7 @@ public class MotifInternalFrameTitlePane @SuppressWarnings("serial") // Superclass is not serializable across versions private class MinimizeButton extends FrameButton { + @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(highlight); @@ -297,6 +321,7 @@ public class MotifInternalFrameTitlePane @SuppressWarnings("serial") // Superclass is not serializable across versions private class MaximizeButton extends FrameButton { + @Override public void paintComponent(Graphics g) { super.paintComponent(g); int max = BUTTON_SIZE - 5; @@ -312,9 +337,12 @@ public class MotifInternalFrameTitlePane @SuppressWarnings("serial") // Superclass is not serializable across versions private class SystemButton extends FrameButton { + @Override public boolean isFocusTraversable() { return false; } + @Override public void requestFocus() {} + @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(highlight); @@ -339,26 +367,33 @@ public class MotifInternalFrameTitlePane // Forward mouse events to titlebar for moves. addMouseMotionListener(new MouseMotionListener() { + @Override public void mouseDragged(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseMoved(MouseEvent e) { forwardEventToParent(e); } }); addMouseListener(new MouseListener() { + @Override public void mouseClicked(MouseEvent e) { forwardEventToParent(e); } + @Override public void mousePressed(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseReleased(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseEntered(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseExited(MouseEvent e) { forwardEventToParent(e); } @@ -377,6 +412,7 @@ public class MotifInternalFrameTitlePane getParent().dispatchEvent(newEvent); } + @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (frame.isSelected()) { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java index 1179df181ab..678686a9b17 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.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 @@ -81,11 +81,13 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { super(w); } + @Override public void installUI(JComponent c) { super.installUI(c); setColors((JInternalFrame)c); } + @Override protected void installDefaults() { Border frameBorder = frame.getBorder(); frame.setLayout(internalFrameLayout = createLayoutManager()); @@ -95,6 +97,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } + @Override protected void installKeyboardActions(){ super.installKeyboardActions(); // We replace the @@ -103,6 +106,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } + @Override protected void uninstallDefaults() { LookAndFeel.uninstallBorder(frame); frame.setLayout(null); @@ -113,15 +117,18 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { return frame; } + @Override public JComponent createNorthPane(JInternalFrame w) { titlePane = new MotifInternalFrameTitlePane(w); return titlePane; } + @Override public Dimension getMaximumSize(JComponent x) { return Toolkit.getDefaultToolkit().getScreenSize(); } + @Override protected void uninstallKeyboardActions(){ super.uninstallKeyboardActions(); if (isKeyBindingRegistered()){ @@ -132,6 +139,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } } + @Override protected void setupMenuOpenKey(){ super.setupMenuOpenKey(); ActionMap map = SwingUtilities.getUIActionMap(frame); @@ -141,9 +149,11 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { // titlePane ivar in BasicInternalFrameUI, making supers action throw // an NPE for us. map.put("showSystemMenu", new AbstractAction(){ + @Override public void actionPerformed(ActionEvent e){ titlePane.showSystemMenu(); } + @Override public boolean isEnabled(){ return isKeyBindingActive(); } @@ -151,13 +161,16 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } } + @Override protected void setupMenuCloseKey(){ ActionMap map = SwingUtilities.getUIActionMap(frame); if (map != null) { map.put("hideSystemMenu", new AbstractAction(){ + @Override public void actionPerformed(ActionEvent e){ titlePane.hideSystemMenu(); } + @Override public boolean isEnabled(){ return isKeyBindingActive(); } @@ -184,6 +197,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { if (diActionMap == null) { diActionMap = new ActionMapUIResource(); diActionMap.put("hideSystemMenu", new AbstractAction(){ + @Override public void actionPerformed(ActionEvent e){ JInternalFrame.JDesktopIcon icon = getFrame(). getDesktopIcon(); @@ -191,6 +205,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { getUI(); micon.hideSystemMenu(); } + @Override public boolean isEnabled(){ return isKeyBindingActive(); } @@ -201,12 +216,14 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { /** This method is called when the frame becomes selected. */ + @Override protected void activateFrame(JInternalFrame f) { super.activateFrame(f); setColors(f); } /** This method is called when the frame is no longer selected. */ + @Override protected void deactivateFrame(JInternalFrame f) { setColors(f); super.deactivateFrame(f); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java index aaeb0243ce7..d254443b8d1 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.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 @@ -56,24 +56,29 @@ import sun.swing.SwingUtilities2; @Deprecated(since="13", forRemoval=true) public class MotifLookAndFeel extends BasicLookAndFeel { + @Override public String getName() { return "CDE/Motif"; } + @Override public String getID() { return "Motif"; } + @Override public String getDescription() { return "The CDE/Motif Look and Feel"; } + @Override public boolean isNativeLookAndFeel() { return false; } + @Override public boolean isSupportedLookAndFeel() { return true; } @@ -87,6 +92,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel * values, otherwise we create color objects whose values match * the default CDE/Motif colors. */ + @Override protected void initSystemColorDefaults(UIDefaults table) { String[] defaultSystemColors = { @@ -123,6 +129,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel } + @Override protected void initClassDefaults(UIDefaults table) { super.initClassDefaults(table); @@ -178,6 +185,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel } + @Override protected void initComponentDefaults(UIDefaults table) { super.initComponentDefaults(table); @@ -255,36 +263,42 @@ public class MotifLookAndFeel extends BasicLookAndFeel )); Object menuItemCheckIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getMenuItemCheckIcon(); } }; Object menuItemArrowIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getMenuItemArrowIcon(); } }; Object menuArrowIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getMenuArrowIcon(); } }; Object checkBoxIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getCheckBoxIcon(); } }; Object radioButtonIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getRadioButtonIcon(); } }; Object unselectedTabBackground = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("control"); return new ColorUIResource(Math.max((int)(c.getRed()*.85),0), @@ -294,6 +308,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel }; Object unselectedTabForeground = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("controlText"); return new ColorUIResource(Math.max((int)(c.getRed()*.85),0), @@ -303,6 +318,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel }; Object unselectedTabShadow = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("control"); Color base = new Color(Math.max((int)(c.getRed()*.85),0), @@ -313,6 +329,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel }; Object unselectedTabHighlight = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("control"); Color base = new Color(Math.max((int)(c.getRed()*.85),0), @@ -462,18 +479,21 @@ public class MotifLookAndFeel extends BasicLookAndFeel "icons/TreeClosed.gif"); Object treeLeafIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifTreeCellRenderer.loadLeafIcon(); } }; Object treeExpandedIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifTreeUI.MotifExpandedIcon.createExpandedIcon(); } }; Object treeCollapsedIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifTreeUI.MotifCollapsedIcon.createCollapsedIcon(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java index 52116aaf588..50ea704c73a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2004, 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 @@ -50,12 +50,14 @@ public class MotifMenuItemUI extends BasicMenuItemUI return new MotifMenuItemUI(); } + @Override protected void installListeners() { super.installListeners(); changeListener = createChangeListener(menuItem); menuItem.addChangeListener(changeListener); } + @Override protected void uninstallListeners() { super.uninstallListeners(); menuItem.removeChangeListener(changeListener); @@ -65,12 +67,14 @@ public class MotifMenuItemUI extends BasicMenuItemUI return new ChangeHandler(); } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } protected class ChangeHandler implements ChangeListener { + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); LookAndFeel.installProperty(c, "borderPainted", @@ -79,11 +83,14 @@ public class MotifMenuItemUI extends BasicMenuItemUI } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.setSelectedPath(getPath()); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -97,11 +104,15 @@ public class MotifMenuItemUI extends BasicMenuItemUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java index 93f4d2c731c..c3f8e7c9ece 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 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 @@ -33,15 +33,19 @@ import javax.swing.MenuSelectionManager; * @author Arnaud Weber */ class MotifMenuMouseListener extends MouseAdapter { + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseEntered(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseExited(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java index 22fcd908a11..8171ad8934d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 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 @@ -33,10 +33,12 @@ import javax.swing.MenuSelectionManager; * @author Arnaud Weber */ class MotifMenuMouseMotionListener implements MouseMotionListener { + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.java index bcee2e666fa..5bf2b1e0503 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, 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 @@ -61,6 +61,7 @@ public class MotifMenuUI extends BasicMenuUI // menuItem.removeChangeListener(changeListener); // } + @Override protected ChangeListener createChangeListener(JComponent c) { return new MotifChangeHandler((JMenu)c, this); } @@ -76,6 +77,7 @@ public class MotifMenuUI extends BasicMenuUI return false; } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } @@ -86,6 +88,7 @@ public class MotifMenuUI extends BasicMenuUI } + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); if (c.isArmed() || c.isSelected()) { @@ -100,7 +103,9 @@ public class MotifMenuUI extends BasicMenuUI } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); JMenu menu = (JMenu)e.getComponent(); @@ -129,6 +134,7 @@ public class MotifMenuUI extends BasicMenuUI } } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -139,11 +145,15 @@ public class MotifMenuUI extends BasicMenuUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java index 21e8c0f6747..641d5ec1b99 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.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 @@ -57,6 +57,7 @@ public class MotifOptionPaneUI extends BasicOptionPaneUI * Creates and returns a Container containing the buttons. The buttons * are created by calling getButtons. */ + @Override protected Container createButtonArea() { Container b = super.createButtonArea(); @@ -69,17 +70,21 @@ public class MotifOptionPaneUI extends BasicOptionPaneUI /** * Returns null, CDE/Motif does not impose a minimum size. */ + @Override public Dimension getMinimumOptionPaneSize() { return null; } + @Override protected Container createSeparator() { return new JPanel() { + @Override public Dimension getPreferredSize() { return new Dimension(10, 2); } + @Override public void paint(Graphics g) { int width = getWidth(); g.setColor(Color.darkGray); @@ -95,6 +100,7 @@ public class MotifOptionPaneUI extends BasicOptionPaneUI * getIcon to top. This is messaged from * createMessageArea */ + @Override protected void addIcon(Container top) { /* Create the icon. */ Icon sideIcon = getIcon(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java index 8e1369d7e96..b906329d353 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -55,6 +55,7 @@ public class MotifPasswordFieldUI extends BasicPasswordFieldUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.java index 164b3227034..5556695429e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.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 @@ -47,6 +47,7 @@ public class MotifPopupMenuSeparatorUI extends MotifSeparatorUI return new MotifPopupMenuSeparatorUI(); } + @Override public void paint( Graphics g, JComponent c ) { Dimension s = c.getSize(); @@ -58,6 +59,7 @@ public class MotifPopupMenuSeparatorUI extends MotifSeparatorUI g.drawLine( 0, 1, s.width, 1 ); } + @Override public Dimension getPreferredSize( JComponent c ) { return new Dimension( 0, 2 ); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java index 0986f81f0ff..6012152905a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -61,6 +61,7 @@ public class MotifPopupMenuUI extends BasicPopupMenuUI { /* This has to deal with the fact that the title may be wider than the widest child component. */ + @Override public Dimension getPreferredSize(JComponent c) { LayoutManager layout = c.getLayout(); Dimension d = layout.preferredLayoutSize(c); @@ -94,10 +95,12 @@ public class MotifPopupMenuUI extends BasicPopupMenuUI { protected ChangeListener createChangeListener(JPopupMenu m) { return new ChangeListener() { + @Override public void stateChanged(ChangeEvent e) {} }; } + @Override @SuppressWarnings("deprecation") public boolean isPopupTrigger(MouseEvent e) { return ((e.getID()==MouseEvent.MOUSE_PRESSED) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java index 1a9d7fa1e91..914c305fa08 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -55,12 +55,14 @@ public class MotifRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI return new MotifRadioButtonMenuItemUI(); } + @Override protected void installListeners() { super.installListeners(); changeListener = createChangeListener(menuItem); menuItem.addChangeListener(changeListener); } + @Override protected void uninstallListeners() { super.uninstallListeners(); menuItem.removeChangeListener(changeListener); @@ -72,23 +74,28 @@ public class MotifRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI @SuppressWarnings("serial") // Same-version serialization only protected class ChangeHandler implements ChangeListener, Serializable { + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); LookAndFeel.installProperty(c, "borderPainted", c.isArmed()); } } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.setSelectedPath(getPath()); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -105,11 +112,15 @@ public class MotifRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java index 58619280493..f9a1dd7bb50 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -68,6 +68,7 @@ public class MotifRadioButtonUI extends BasicRadioButtonUI { // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -76,6 +77,7 @@ public class MotifRadioButtonUI extends BasicRadioButtonUI { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -92,6 +94,7 @@ public class MotifRadioButtonUI extends BasicRadioButtonUI { // ******************************** // Paint Methods // ******************************** + @Override protected void paintFocus(Graphics g, Rectangle t, Dimension d){ g.setColor(getFocusColor()); g.drawRect(0,0,d.width-1,d.height-1); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java index 5b812ca4c8c..2f5eb9708b3 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -64,6 +64,7 @@ public class MotifScrollBarButton extends BasicArrowButton } + @Override public Dimension getPreferredSize() { switch (direction) { case NORTH: @@ -76,18 +77,22 @@ public class MotifScrollBarButton extends BasicArrowButton } } + @Override public Dimension getMinimumSize() { return getPreferredSize(); } + @Override public Dimension getMaximumSize() { return getPreferredSize(); } + @Override public boolean isFocusTraversable() { return false; } + @Override public void paint(Graphics g) { int w = getWidth(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java index 08a283f5e52..48cee86636b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -52,6 +52,7 @@ public class MotifScrollBarUI extends BasicScrollBarUI return new MotifScrollBarUI(); } + @Override public Dimension getPreferredSize(JComponent c) { Insets insets = c.getInsets(); int dx = insets.left + insets.right; @@ -61,19 +62,23 @@ public class MotifScrollBarUI extends BasicScrollBarUI : new Dimension(dx + 33, dy + 11); } + @Override protected JButton createDecreaseButton(int orientation) { return new MotifScrollBarButton(orientation); } + @Override protected JButton createIncreaseButton(int orientation) { return new MotifScrollBarButton(orientation); } + @Override public void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { g.setColor(trackColor); g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height); } + @Override public void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) { return; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java index c8820de17c4..c6afd5ce953 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -64,22 +64,27 @@ public class MotifSliderUI extends BasicSliderUI { return new MotifSliderUI((JSlider)b); } + @Override public Dimension getPreferredHorizontalSize() { return PREFERRED_HORIZONTAL_SIZE; } + @Override public Dimension getPreferredVerticalSize() { return PREFERRED_VERTICAL_SIZE; } + @Override public Dimension getMinimumHorizontalSize() { return MINIMUM_HORIZONTAL_SIZE; } + @Override public Dimension getMinimumVerticalSize() { return MINIMUM_VERTICAL_SIZE; } + @Override protected Dimension getThumbSize() { if ( slider.getOrientation() == JSlider.HORIZONTAL ) { return new Dimension( 30, 15 ); @@ -89,12 +94,15 @@ public class MotifSliderUI extends BasicSliderUI { } } + @Override public void paintFocus(Graphics g) { } + @Override public void paintTrack(Graphics g) { } + @Override public void paintThumb(Graphics g) { Rectangle knobBounds = thumbRect; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java index ba8f0efde4d..21cc1f7c38b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -84,6 +84,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider * overrides to hardcode the size of the divider * PENDING(jeff) - rewrite JSplitPane so that this isn't needed */ + @Override public void setDividerSize(int newSize) { Insets insets = getInsets(); int borderSize = 0; @@ -109,6 +110,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider */ // PENDING(jeff) - the thumb's location and size is currently hard coded. // It should be dynamic. + @Override public void paint(Graphics g) { Color bgColor = getBackground(); Dimension size = getSize(); @@ -179,6 +181,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider /** * The minimums size is the same as the preferredSize */ + @Override public Dimension getMinimumSize() { return getPreferredSize(); } @@ -187,6 +190,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider * Sets the SplitPaneUI that is using the receiver. This is completely * overridden from super to create a different MouseHandler. */ + @Override public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) { if (splitPane != null) { splitPane.removePropertyChangeListener(this); @@ -268,6 +272,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider * in. */ private class MotifMouseHandler extends MouseHandler { + @Override public void mousePressed(MouseEvent e) { // Constrain the mouse pressed to the thumb. if (e.getSource() == MotifSplitPaneDivider.this && @@ -277,6 +282,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider } } + @Override public void mouseMoved(MouseEvent e) { if (getDragger() != null) { return; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java index c61a1713c9b..90ce672f5c9 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -51,6 +51,7 @@ public class MotifSplitPaneUI extends BasicSplitPaneUI /** * Creates the default divider. */ + @Override public BasicSplitPaneDivider createDefaultDivider() { return new MotifSplitPaneDivider(this); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java index 1c53fc4a179..2b1e9482f9d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -61,6 +61,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI // UI Installation/De-installation + @Override protected void installDefaults() { super.installDefaults(); @@ -70,6 +71,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI unselectedTabHighlight = UIManager.getColor("TabbedPane.unselectedTabHighlight"); } + @Override protected void uninstallDefaults() { super.uninstallDefaults(); @@ -81,6 +83,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI // UI Rendering + @Override protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -104,6 +107,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -126,6 +130,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -148,6 +153,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, @@ -174,6 +180,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } + @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, @@ -225,6 +232,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } + @Override protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, @@ -263,10 +271,12 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected int getTabRunIndent(int tabPlacement, int run) { return run*3; } + @Override protected int getTabRunOverlay(int tabPlacement) { tabRunOverlay = (tabPlacement == LEFT || tabPlacement == RIGHT)? (int)Math.round((float)maxTabWidth * .10) : diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java index baddbf3a75d..f71dfe3c30d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -57,6 +57,7 @@ public class MotifTextAreaUI extends BasicTextAreaUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java index 08437687ebc..8e64aa02a63 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -55,6 +55,7 @@ public class MotifTextFieldUI extends BasicTextFieldUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java index e47ad4c60bf..fabbbb204d0 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -55,6 +55,7 @@ public class MotifTextPaneUI extends BasicTextPaneUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java index b79ef19df8e..6e2e1059176 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -75,6 +75,7 @@ public class MotifTextUI { * @param e the focus event * @see FocusListener#focusGained */ + @Override public void focusGained(FocusEvent e) { super.focusGained(e); getComponent().repaint(); @@ -88,6 +89,7 @@ public class MotifTextUI { * @param e the focus event * @see FocusListener#focusLost */ + @Override public void focusLost(FocusEvent e) { super.focusLost(e); getComponent().repaint(); @@ -101,6 +103,7 @@ public class MotifTextUI { * @param r the current location of the caret, does nothing if null * @see #paint */ + @Override protected void damage(Rectangle r) { if (r != null) { x = r.x - IBeamOverhang - 1; @@ -121,6 +124,7 @@ public class MotifTextUI { * @param g the graphics context * @see #damage */ + @Override @SuppressWarnings("deprecation") public void paint(Graphics g) { if(isVisible()) { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java index eef60ade9c9..7997145e648 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -71,6 +71,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -80,6 +81,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -96,6 +98,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI // ******************************** // Paint Methods // ******************************** + @Override protected void paintButtonPressed(Graphics g, AbstractButton b) { if (b.isContentAreaFilled()) { Color oldColor = g.getColor(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java index 78ec251f05d..b060c62f24b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -70,6 +70,7 @@ public class MotifTreeCellRenderer extends DefaultTreeCellRenderer highlight = UIManager.getColor("Tree.iconHighlight"); } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(bg); @@ -90,10 +91,12 @@ public class MotifTreeCellRenderer extends DefaultTreeCellRenderer g.drawLine(x + 9, y + 8, x + 7, y + 6); } + @Override public int getIconWidth() { return LEAF_SIZE; } + @Override public int getIconHeight() { return LEAF_SIZE; } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java index 468ef4eb565..11313b9ad12 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -54,12 +54,14 @@ public class MotifTreeUI extends BasicTreeUI super(); } + @Override public void installUI(JComponent c) { super.installUI(c); } // BasicTreeUI overrides + @Override protected void paintVerticalLine( Graphics g, JComponent c, int x, int top, int bottom ) { if (tree.getComponentOrientation().isLeftToRight()) { @@ -69,6 +71,7 @@ public class MotifTreeUI extends BasicTreeUI } } + @Override protected void paintHorizontalLine( Graphics g, JComponent c, int y, int left, int right ) { g.fillRect( left, y, right - left + 1, 2 ); @@ -96,6 +99,7 @@ public class MotifTreeUI extends BasicTreeUI return new MotifExpandedIcon(); } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(highlight); g.drawLine(x, y, x+SIZE-1, y); @@ -113,7 +117,9 @@ public class MotifTreeUI extends BasicTreeUI g.drawLine(x+3, y+HALF_SIZE, x+SIZE-4, y+HALF_SIZE); } + @Override public int getIconWidth() { return SIZE; } + @Override public int getIconHeight() { return SIZE; } } @@ -126,6 +132,7 @@ public class MotifTreeUI extends BasicTreeUI return new MotifCollapsedIcon(); } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { super.paintIcon(c, g, x, y); g.drawLine(x + HALF_SIZE-1, y + 3, x + HALF_SIZE-1, y + (SIZE - 4)); @@ -141,6 +148,7 @@ public class MotifTreeUI extends BasicTreeUI * Returns the default cell renderer that is used to do the * stamping of each node. */ + @Override public TreeCellRenderer createDefaultCellRenderer() { return new MotifTreeCellRenderer(); } From fb526c8f45de6ca9a57608f728ac223cbca118be Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 14 Jan 2026 21:37:44 +0000 Subject: [PATCH 009/126] 8373001: LauncherFromOptions.create() not properly handling FileAssociationNoExtensionsException Reviewed-by: almatvee --- .../classes/jdk/jpackage/internal/LauncherFromOptions.java | 6 +++--- .../jpackage/internal/resources/MainResources.properties | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java index ed030a4a726..9e81d144a1e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.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 @@ -51,6 +51,7 @@ import jdk.jpackage.internal.FileAssociationGroup.FileAssociationNoMimesExceptio import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardFaOption; import jdk.jpackage.internal.model.CustomLauncherIcon; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; @@ -130,8 +131,7 @@ final class LauncherFromOptions { .advice("error.no-content-types-for-file-association.advice", faID) .create(); } catch (FileAssociationNoExtensionsException ex) { - // TODO: Must do something about this condition! - throw new AssertionError(); + throw new JPackageException(I18N.format("error.no-extensions-for-file-association", faID)); } catch (FileAssociationException ex) { // Should never happen throw new UnsupportedOperationException(ex); 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 07460a18fe8..e1f41e3ff48 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 @@ -93,6 +93,7 @@ error.properties-parameter-not-path=The value "{0}" provided for property "{1}" error.properties-parameter-not-file=The value "{0}" provided for property "{1}" in "{2}" file is not a file error.properties-parameter-not-launcher-shortcut-dir=The value "{0}" provided for property "{1}" in "{2}" file is not a valid shortcut startup directory +error.no-extensions-for-file-association=No extensions were specified for File Association number {0} error.no-content-types-for-file-association=No MIME types were specified for File Association number {0} error.no-content-types-for-file-association.advice=Specify MIME type for File Association number {0} error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0} From d8f45faf5849e66b8f0e35e1d18ed0331a0cb1c2 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 15 Jan 2026 02:40:36 +0000 Subject: [PATCH 010/126] 8374432: TimeoutResponseBodyTest.java#retriesEnabledForResponseFailure fails run with -Xcomp Reviewed-by: vyazici, dfuchs --- .../java/net/httpclient/TimeoutResponseTestSupport.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.java b/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.java index 67b993eef0b..2d8ff05dbd2 100644 --- a/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.java +++ b/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.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 @@ -75,7 +75,8 @@ public class TimeoutResponseTestSupport { private static final SSLContext SSL_CONTEXT = SimpleSSLContext.findSSLContext(); protected static final Duration REQUEST_TIMEOUT = - Duration.ofMillis(Long.parseLong(System.getProperty("test.requestTimeoutMillis"))); + Duration.ofMillis(jdk.test.lib.Utils.adjustTimeout( + Long.parseLong(System.getProperty("test.requestTimeoutMillis")))); static { assertTrue( @@ -87,7 +88,8 @@ public class TimeoutResponseTestSupport { Integer.parseInt(System.getProperty("jdk.httpclient.redirects.retrylimit", "0")); private static final long RESPONSE_FAILURE_WAIT_DURATION_MILLIS = - Long.parseLong(System.getProperty("test.responseFailureWaitDurationMillis", "0")); + jdk.test.lib.Utils.adjustTimeout( + Long.parseLong(System.getProperty("test.responseFailureWaitDurationMillis", "0"))); static { if (RETRY_LIMIT > 0) { From ce5e0d8a48296b51c9c2eff4867e2a9a70194091 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 15 Jan 2026 02:44:16 +0000 Subject: [PATCH 011/126] 8373945: Use WB.fullGC() in ClassUnloader.unloadClass to force GC for vmTestbase tests Reviewed-by: cjplummer, lmesnik --- .../LargeObjects/large001/large001.java | 26 ++++------ .../large002/TestDescription.java | 10 ++-- .../large003/TestDescription.java | 10 ++-- .../large004/TestDescription.java | 10 ++-- .../large005/TestDescription.java | 10 ++-- .../reflectype002/TestDescription.java | 9 ++-- .../classname001/TestDescription.java | 10 ++-- .../signature001/TestDescription.java | 10 ++-- .../exclfilter001/TestDescription.java | 9 ++-- .../filter001/TestDescription.java | 9 ++-- .../allfields003/TestDescription.java | 9 ++-- .../allmethods003/TestDescription.java | 9 ++-- .../classobj002/TestDescription.java | 9 ++-- .../equals/equals002/TestDescription.java | 9 ++-- .../failedtoinit002/TestDescription.java | 9 ++-- .../fieldbyname003/TestDescription.java | 9 ++-- .../fields/fields003/TestDescription.java | 9 ++-- .../hashCode/hashcode002/TestDescription.java | 9 ++-- .../isabstract002/TestDescription.java | 9 ++-- .../isinit002/TestDescription.java | 9 ++-- .../isprepared002/TestDescription.java | 9 ++-- .../isverified002/TestDescription.java | 9 ++-- .../methods/methods003/TestDescription.java | 10 ++-- .../methbyname_s003/TestDescription.java | 9 ++-- .../methbyname_ss003/TestDescription.java | 9 ++-- .../name/name002/TestDescription.java | 9 ++-- .../sourcename002/TestDescription.java | 9 ++-- .../visibfield003/TestDescription.java | 9 ++-- .../visibmethod003/TestDescription.java | 9 ++-- .../instancecounts003/instancecounts003.java | 8 ++- .../compmethunload001.java | 16 +++--- .../compmethunload001/TestDescription.java | 6 ++- .../objfree001/TestDescription.java | 5 +- .../events/EM02/em02t003/TestDescription.java | 6 ++- .../events/EM02/em02t005/TestDescription.java | 6 ++- .../events/EM07/em07t002/TestDescription.java | 6 ++- .../EX03/ex03t001/TestDescription.java | 6 ++- .../classload/load001/TestDescription.java | 6 ++- .../classload/load002/TestDescription.java | 6 ++- .../classload/load003/TestDescription.java | 6 ++- .../classload/load004/TestDescription.java | 6 ++- .../classload/load005/TestDescription.java | 6 ++- .../classload/load006/TestDescription.java | 6 ++- .../classload/load007/TestDescription.java | 6 ++- .../classload/load008/TestDescription.java | 6 ++- .../classload/load009/TestDescription.java | 6 ++- .../classload/load010/TestDescription.java | 6 ++- .../classload/load011/TestDescription.java | 6 ++- .../classload/load012/TestDescription.java | 6 ++- .../classload/unload001/TestDescription.java | 6 ++- .../classload/unload002/TestDescription.java | 6 ++- .../classload/unload003/TestDescription.java | 6 ++- .../classload/unload004/TestDescription.java | 6 ++- .../classload/unload005/TestDescription.java | 6 ++- .../classload/unload006/TestDescription.java | 6 ++- .../classload/unload007/TestDescription.java | 6 ++- .../classload/unload008/TestDescription.java | 6 ++- .../classload/unload009/TestDescription.java | 6 ++- .../classload/unload010/TestDescription.java | 6 ++- .../classload/unload011/TestDescription.java | 6 ++- .../classload/unload012/TestDescription.java | 6 ++- .../vmTestbase/nsk/share/ClassUnloader.java | 51 ++++--------------- 62 files changed, 324 insertions(+), 215 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.java index 8f8cfdcd541..1fd550d3571 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2025, 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 @@ -46,9 +46,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -57,10 +56,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xlog:gc* * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes @@ -79,6 +81,7 @@ import nsk.share.TestFailure; import nsk.share.gc.*; import nsk.share.*; +import jdk.test.whitebox.WhiteBox; public class large001 extends ThreadedGCTest { @@ -146,8 +149,8 @@ public class large001 extends ThreadedGCTest { List refs = new ArrayList(depth); addObjRef(loadedClassInstance, loadedClass, depth, refs); - // Drop all references to the class and try to unload it - Algorithms.eatMemory(getExecutionController()); + // Keep all references to the class and try to unload it + WhiteBox.getWhiteBox().fullGC(); log.debug(id + ": Testing non-null after GC force for: " + name); if (loadedClass == null || loadedClassInstance == null) { throw new Exception("Null class"); @@ -158,21 +161,14 @@ public class large001 extends ThreadedGCTest { throw new Exception("Unexpected null reference"); } } + // Drop all references to the class and try to unload it refs = null; loadedClass = null; loadedClassInstance = null; log.debug(id + ": Unloading class: " + name); - boolean result = unloader.unloadClass(getExecutionController()); - log.debug(id + ": Result of uloading " - + "class " + name + ": " + result); - } - } catch (OutOfMemoryError oome) { - // just skip if we eat memory in several threads... - // rethrow in the case of one thread - if (runParams.getNumberOfThreads() == 1) { - throw oome; + WhiteBox.getWhiteBox().fullGC(); } } catch (Throwable t) { throw new TestFailure("Unexpected exception: ", t); diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.java index c207b80c059..834a5d71822 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.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,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, and the number of fields is more than * the JVM limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -58,10 +57,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields true diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.java index fd7e1e5e8dd..d094776cb5d 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.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,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -62,10 +61,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields false diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.java index 2301daba8d3..021170d7d7a 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.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,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -62,10 +61,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields true diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.java index abf91f48dea..6ad629aa84e 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.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,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -62,10 +61,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields true diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.java index 20584002f6e..efec917716a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ClassObjectReference.reflectedType.reflectype002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.java index c967382ccfd..8f906d4d6b2 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.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 @@ -42,7 +42,7 @@ * debugger and debugee communicates with special commands. * The debugger forces debuggee to load checked class, creates and * enables ClassUnloadRequest. Next, debugger forces debuggee to - * unload class, using memory stressing techique, and waits for + * unload class, using whitebox full GC techique, and waits for * ClassUnloadEvent. * If expected ClassUnloadEvent occurs, debugger tests method * ClassUnloadEvent.className() and verifies that this event @@ -81,6 +81,8 @@ * nsk.jdi.ClassUnloadEvent.className.classname001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -92,6 +94,6 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.java index c83d6cdf0f3..6974e7077df 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.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 @@ -44,7 +44,7 @@ * debugger and debugee communicates with special commands. * The debugger forces debugge to load checked classes, creates and * enables ClassUnloadRequest. Next, debugger forces debuggee to - * unload classes, using memory stressing techique, and waits for + * unload classes, using whitebox full GC techique, and waits for * ClassUnloadEvent. * If each expected ClassUnloadEvent occurs, debugger tests method * ClassUnloadEvent.classSignature() and verifies that this event @@ -87,6 +87,8 @@ * nsk.jdi.ClassUnloadEvent.classSignature.signature001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -98,6 +100,6 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.java index ad14def18dc..d449ed04f91 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.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 @@ -67,6 +67,8 @@ * nsk.jdi.ClassUnloadRequest.addClassExclusionFilter.exclfilter001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -77,6 +79,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.java index 6a388fbaea9..1783e44fa43 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.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 @@ -65,6 +65,8 @@ * nsk.jdi.ClassUnloadRequest.addClassFilter.filter001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -75,6 +77,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.java index 25196c3ae9b..053a6ff34c5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.allFields.allfields003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.java index 149de042248..a0e8bd2b7f1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.allMethods.allmethods003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.java index a639fe76ad8..ab751a1f29c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.classObject.classobj002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.java index 1ccd1358c1c..e1c25e9dbdd 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.equals.equals002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.java index af68db9d869..703bb6c54aa 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.failedToInitialize.failedtoinit002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.java index bbd8998d3a1..fa5104cad55 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.fieldByName.fieldbyname003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.java index d6379dfbdbf..ab6af3a98e1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.fields.fields003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.java index f7a960746d9..693171c9dfe 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.hashCode.hashcode002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.java index 6f9c0d5b18e..b61c14d3d4b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.isAbstract.isabstract002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.java index c484851c178..c2b1eac7c1e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.isInitialized.isinit002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.java index 867b7c45e81..9030c57a727 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.isPrepared.isprepared002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.java index 5f638386da8..a766e7476f6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.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 @@ -57,6 +57,8 @@ * nsk.jdi.ReferenceType.isVerified.isverified002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -67,6 +69,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.java index df70528ffd9..5a37d7e66a4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.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 @@ -57,7 +57,8 @@ * @comment compile loadclassXX to bin/loadclassXX * @run driver nsk.share.ExtraClassesBuilder * loadclass - * + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver * nsk.jdi.ReferenceType.methods.methods003 * -verbose @@ -65,6 +66,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.java index fae4c67e986..6f70f225a14 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.methodsByName_s.methbyname_s003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.java index 5d21a5c868f..baf8931a636 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.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 @@ -57,6 +57,8 @@ * @comment compile loadclassXX to bin/loadclassXX * @run driver nsk.share.ExtraClassesBuilder * loadclass + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run driver * nsk.jdi.ReferenceType.methodsByName_ss.methbyname_ss003 @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.java index 91c37d75710..927e07929b4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.name.name002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.java index 74427e9a8bc..543272a52f0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.sourceName.sourcename002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.java index db4ba02e457..f349deaad85 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.visibleFields.visibfield003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.java index 11c4c3566bb..c6de37e2939 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.visibleMethods.visibmethod003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java index f2150775916..4f4e83188b4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, 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 @@ -48,6 +48,8 @@ * @build nsk.jdi.VirtualMachine.instanceCounts.instancecounts003.instancecounts003 * nsk.share.jdi.TestClass1 * nsk.share.jdi.TestInterfaceImplementer1 + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver * nsk.jdi.VirtualMachine.instanceCounts.instancecounts003.instancecounts003 * -verbose @@ -55,7 +57,9 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="-Xmx256M ${test.vm.opts} ${test.java.opts}" + * -debugee.vmkeys="-Xmx256M ${test.vm.opts} ${test.java.opts} + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI" * -testClassPath ${test.class.path} */ diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java index e03c4ff4be2..09fdb1ae3f7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java @@ -47,7 +47,7 @@ public class compmethunload001 { private final static String CLS_TO_BE_UNLOADED = "nsk.jvmti.CompiledMethodUnload.compmethunload001u"; - private final static int MAX_ITERATIONS = 5; + private final static int MAX_ITERATIONS = 10; static { try { @@ -95,7 +95,6 @@ public class compmethunload001 { boolean clsUnloaded = clsUnLoader.unloadClass(); clsUnLoader = null; - System.gc(); } private int runThis(String argv[], PrintStream out) throws Exception { @@ -110,12 +109,13 @@ public class compmethunload001 { int num = unloaded(); int iter = 0; while (num == 0) { - System.gc(); - num = unloaded(); - iter++; - if (iter > MAX_ITERATIONS) { - throw new Failure("PRODUCT BUG: class was not unloaded in " + MAX_ITERATIONS); - } + // The unload is delayed because it happens async + Thread.sleep(1000); + num = unloaded(); + iter++; + if (iter > MAX_ITERATIONS) { + throw new Failure("PRODUCT BUG: class was not unloaded in " + MAX_ITERATIONS); + } } System.out.println("Number of unloaded events " + num + " number of iterations " + iter); return check(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java index 975f0fdee28..b3e43bf7d03 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.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 @@ -46,12 +46,14 @@ * @build nsk.jvmti.CompiledMethodUnload.compmethunload001 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:compmethunload001=-waittime=5 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.CompiledMethodUnload.compmethunload001 * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.java index 8da18b646c7..c7f5376e53c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.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 @@ -51,12 +51,15 @@ * @build nsk.jvmti.ObjectFree.objfree001 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:objfree001=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.ObjectFree.objfree001 * ./bin 5 ./bin */ diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.java index d3179b3c2c0..c32abdb635b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.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 @@ -71,13 +71,15 @@ * @build nsk.jvmti.scenarios.events.EM02.em02t003 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:em02t003=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.events.EM02.em02t003 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.java index 5e8b186fe9d..35fc4ceacdb 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.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 @@ -64,13 +64,15 @@ * @build nsk.jvmti.scenarios.events.EM02.em02t005 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:em02t005=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.events.EM02.em02t005 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.java index aacc48a1e94..dea19222973 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.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 @@ -57,13 +57,15 @@ * @build nsk.jvmti.scenarios.events.EM07.em07t002 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:em07t002=attempts=2,-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.events.EM07.em07t002 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java index 0e63411b94b..76798c229f3 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.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 @@ -55,13 +55,15 @@ * @build nsk.jvmti.scenarios.extension.EX03.ex03t001 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:ex03t001=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.extension.EX03.ex03t001 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.java index e93d0052423..979f51007a2 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.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 @@ -43,12 +43,14 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.java index 563eb7500a0..e1dcaaf6946 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.java index d972a74492c..41ffed813d1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.java index 7789d6b70ce..b0470cca572 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.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 @@ -47,13 +47,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -singleClassloaderClass * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.java index b6eba9ca652..d5be1a61e8a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.java index af8f67362bf..eff574f05a2 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -55,4 +58,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.java index f8674cdbd21..ecd7e495fb9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.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 @@ -46,12 +46,14 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=360 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.java index f306fda9e02..9ebc7be7a72 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.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 @@ -44,13 +44,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=420 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.java index 50f8116f7c9..5355064e57a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=420 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.java index 4f477035cd2..fe0ec8c9b62 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=300 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -singleClassloaderClass * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.java index 110f270daff..a5a1a5ae2a8 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=360 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.java index 52c9d3e8ae7..ac3f8217a0d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=300 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -55,4 +58,3 @@ * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.java index cab8b333848..9690240caa5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.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 @@ -46,11 +46,13 @@ * /test/lib * @comment generate and compile LoadableClassXXX classes * @run driver nsk.monitoring.stress.classload.GenClassesBuilder + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.java index cf611e5eb05..52395f92b0c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.java index 6c8d9f61e36..7e24f92dafd 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.java index 8f8e46e40be..8dedf5c1f61 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.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 @@ -47,13 +47,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -singleClassloaderClass * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.java index 73aa31b3ff3..e4236d40e58 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.java index 1b3d309a7fd..ef6e3d996d9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -55,4 +58,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.java index 62eaaa6c40a..cbe155bc77e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.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 @@ -48,12 +48,14 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.java index f87f913b769..41894adaed7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.java index 27c19980411..5999a8c7f7c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.java index afc3cbb026c..683bca7ab93 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.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 @@ -48,13 +48,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -singleClassloaderClass * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.java index 037d4c2affe..26227644431 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.java index 256b0f91d1f..446db1d6e28 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -57,4 +60,3 @@ * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java index e3b6693657c..94b086f5757 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java @@ -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 @@ -33,13 +33,14 @@ import java.util.*; import nsk.share.gc.gp.*; import nsk.share.test.ExecutionController; import nsk.share.test.Stresser; +import jdk.test.whitebox.WhiteBox; /** * The ClassUnloader class allows to force VM to unload class(es) - * using memory stressing technique. + * using WhiteBox.fullGC technique. * - *

The method unloadClass() is provided which eats memory - * to enforce GC to cleanup the heap. So, if all references to a class + *

The method unloadClass() is provided which calls + * WhiteBox.fullGC to cleanup the heap. So, if all references to a class * and its loader are canceled, this may result in unloading the class. * *

ClassUnloader mainly intends to unload a class which was loaded @@ -228,24 +229,24 @@ public class ClassUnloader { /** * Forces GC to unload previously loaded classes by cleaning all references - * to class loader with its loaded classes and eating memory. + * to class loader with its loaded classes. * * @return true if classes unloading has been detected or false otherwise * * @throws Failure if exception other than OutOfMemoryError - * is thrown while eating memory + * is thrown while triggering full GC * - * @see #eatMemory() + * @see WhiteBox.getWhiteBox().fullGC() */ - public boolean unloadClass(ExecutionController stresser) { + public boolean unloadClass() { // free references to class and class loader to be able for collecting by GC classObjects.removeAllElements(); customClassLoader = null; - // force class unloading by eating memory pool - eatMemory(stresser); + // force class unloading by triggering full GC + WhiteBox.getWhiteBox().fullGC(); // force GC to unload marked class loader and its classes if (isClassLoaderReclaimed()) { @@ -256,34 +257,4 @@ public class ClassUnloader { // class loader has not been reclaimed return false; } - - public boolean unloadClass() { - Stresser stresser = new Stresser() { - - @Override - public boolean continueExecution() { - return true; - } - - }; - return unloadClass(stresser); - } - - // Stresses memory by allocating arrays of bytes. - public static void eatMemory(ExecutionController stresser) { - GarbageUtils.eatMemory(stresser, 50, 1024, 2); - } - - // Stresses memory by allocating arrays of bytes. - public static void eatMemory() { - Stresser stresser = new Stresser() { - - @Override - public boolean continueExecution() { - return true; - } - - }; - eatMemory(stresser); - } } From 2b1e11c2541f799142bd71e9526cbd04743c6f4e Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 15 Jan 2026 02:46:20 +0000 Subject: [PATCH 012/126] 8374879: NMethodRelocationTest fails with -Xcomp after 8369150 Reviewed-by: lmesnik, chagedorn --- .../jvmti/NMethodRelocation/NMethodRelocationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index 10888dce1b4..5b4a1c7e663 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -29,7 +29,8 @@ * vm.gc != "Epsilon" & * vm.flavor == "server" & * !vm.emulatedClient & - * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) & + * vm.compMode == "Xmixed" * @library /test/lib /test/hotspot/jtreg * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox From 499b58820225eb96c728816af9ea2ade47d1fc6b Mon Sep 17 00:00:00 2001 From: Alexander Matveev Date: Thu, 15 Jan 2026 03:53:53 +0000 Subject: [PATCH 013/126] 8374215: [macos] Clean and fix "lic_template.plist" to correctly work with multiple languages Reviewed-by: asemenyuk --- .../jdk/jpackage/internal/MacDmgLicense.java | 103 +++++++++ .../jdk/jpackage/internal/MacDmgPackager.java | 31 +-- .../resources/MacResources.properties | 5 + .../resources/MacResources_de.properties | 7 +- .../resources/MacResources_ja.properties | 7 +- .../resources/MacResources_zh_CN.properties | 7 +- .../internal/resources/lic_template.plist | 210 ++++-------------- .../helpers/jdk/jpackage/test/Executor.java | 4 +- .../jdk/tools/jpackage/share/LicenseTest.java | 33 ++- 9 files changed, 210 insertions(+), 197 deletions(-) create mode 100644 src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.java diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.java new file mode 100644 index 00000000000..eea09b80929 --- /dev/null +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.java @@ -0,0 +1,103 @@ +/* + * 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 jdk.jpackage.internal; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Base64; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import jdk.jpackage.internal.resources.ResourceLocator; + +final class MacDmgLicense { + + public static void prepareLicensePListFile(Path licenseFile, Path licensePListFile) + throws IOException { + byte[] licenseContentOriginal = + Files.readAllBytes(licenseFile); + String licenseInBase64 = + Base64.getEncoder().encodeToString(licenseContentOriginal); + + Map data = new HashMap<>(); + data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); + data.put("STR_DATA_ENGLISH", + getSTRData("English", Locale.ENGLISH, "MacRoman")); + data.put("STR_DATA_GERMAN", + getSTRData("German", Locale.GERMAN, "MacRoman")); + data.put("STR_DATA_JAPANESE", + getSTRData("Japanese", Locale.JAPANESE, "Shift_JIS")); + data.put("STR_DATA_SIMPLIFIED_CHINESE", + getSTRData("Simplified Chinese", Locale.SIMPLIFIED_CHINESE, "GB2312")); + + new OverridableResource(DEFAULT_LICENSE_PLIST, ResourceLocator.class) + .setCategory(I18N.getString("resource.license-setup")) + .setSubstitutionData(data) + .saveToFile(licensePListFile); + } + + private static void writeSTRDataString(ByteArrayOutputStream bos, + String str, String charset) { + byte [] bytes = str.getBytes(Charset.forName(charset)); + byte [] bytesLength = {(byte)bytes.length}; + bos.writeBytes(bytesLength); + bos.writeBytes(bytes); + } + + // Returns base64 decoded STR section data. + // Strings should be in following order: + // Language, message.dmg.license.button.agree, + // message.dmg.license.button.disagree, message.dmg.license.button.print + // message.dmg.license.button.save, message.dmg.license.message + // STR section data encoded: + // Number of strings in the list (unsigned 16-bit integer, big endian): 6 + // A sequence of strings prefixed with string length (unsigned 8-bit integer) + // Note: Language should not be translated. + private static String getSTRData(String language, Locale locale, String charset) { + ResourceBundle bundle = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.MacResources", locale); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + byte [] numberOfStrings = {0x00, 0x06}; // Always 6 + bos.writeBytes(numberOfStrings); + + writeSTRDataString(bos, language, charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.agree"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.disagree"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.print"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.save"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.message"), charset); + + return Base64.getEncoder().encodeToString(bos.toByteArray()); + } + + private static final String DEFAULT_LICENSE_PLIST = "lic_template.plist"; +} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index 82bb9fc4dad..cf4db226d37 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java @@ -34,7 +34,6 @@ import java.nio.file.LinkOption; import java.nio.file.Path; import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -105,7 +104,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, return env.configDir().resolve(pkg.app().name() + "-volume.icns"); } - Path licenseFile() { + Path licensePListFile() { return env.configDir().resolve(pkg.app().name() + "-license.plist"); } @@ -175,26 +174,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, .saveToFile(dmgSetup); } - private void prepareLicense() throws IOException { - final var licFile = pkg.licenseFile(); - if (licFile.isEmpty()) { - return; - } - - byte[] licenseContentOriginal = - Files.readAllBytes(licFile.orElseThrow()); - String licenseInBase64 = - Base64.getEncoder().encodeToString(licenseContentOriginal); - - Map data = new HashMap<>(); - data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); - - env.createResource(DEFAULT_LICENSE_PLIST) - .setCategory(I18N.getString("resource.license-setup")) - .setSubstitutionData(data) - .saveToFile(licenseFile()); - } - private void prepareConfigFiles() throws IOException { env.createResource(DEFAULT_BACKGROUND_IMAGE) @@ -206,7 +185,9 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, .setExternal(pkg.icon().orElse(null)) .saveToFile(volumeIcon()); - prepareLicense(); + if (pkg.licenseFile().isPresent()) { + MacDmgLicense.prepareLicensePListFile(pkg.licenseFile().get(), licensePListFile()); + } prepareDMGSetupScript(); } @@ -359,7 +340,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, "udifrez", normalizedAbsolutePathString(finalDMG), "-xml", - normalizedAbsolutePathString(licenseFile()) + normalizedAbsolutePathString(licensePListFile()) ).retry() .setMaxAttemptsCount(10) .setAttemptTimeout(3, TimeUnit.SECONDS) @@ -441,6 +422,4 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, private static final String DEFAULT_BACKGROUND_IMAGE = "background_dmg.tiff"; private static final String DEFAULT_DMG_SETUP_SCRIPT = "DMGsetup.scpt"; private static final String TEMPLATE_BUNDLE_ICON = "JavaApp.icns"; - - private static final String DEFAULT_LICENSE_PLIST="lic_template.plist"; } 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 ceeab587f66..0237d49f399 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 @@ -64,6 +64,11 @@ message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trus 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. +message.dmg.license.button.agree=Agree +message.dmg.license.button.disagree=Disagree +message.dmg.license.button.print=Print +message.dmg.license.button.save=Save... +message.dmg.license.message=If you agree with the terms of this license, press "Agree" to install the software. If you do not agree, press "Disagree". warning.unsigned.app.image=Warning: Using unsigned app-image to build signed {0}. warning.per.user.app.image.signed=Warning: Support for per-user configuration of the installed application will not be supported due to missing "{0}" in predefined signed application image. warning.non.standard.contents.sub.dir=Warning: The file name of the directory "{0}" specified for the --app-content option is not a standard subdirectory name in the "Contents" directory of the application bundle. The result application bundle may fail code signing and/or notarization. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties index 3cc56bb6cdf..367f3216f85 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties @@ -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 @@ -71,6 +71,11 @@ message.signing.pkg=Warnung: Zum Signieren von PKG müssen Sie möglicherweise m message.setfile.dmg=Das Festlegen des benutzerdefinierten Symbols für die DMG-Datei wurde übersprungen, weil das Utility "SetFile" nicht gefunden wurde. Durch Installieren von Xcode mit Befehlszeilentools sollte dieses Problem behoben werden. message.codesign.failed.reason.app.content="codesign" war nicht erfolgreich, und zusätzlicher Anwendungsinhalt wurde über den Parameter "--app-content" angegeben. Wahrscheinlich hat der zusätzliche Inhalt die Integrität des Anwendungs-Bundles beeinträchtigt und den Fehler verursacht. Stellen Sie sicher, das der über den Parameter "--app-content" angegebene Inhalt nicht die Integrität des Anwendungs-Bundles beeinträchtigt, oder fügen Sie ihn im Nachverarbeitungsschritt hinzu. message.codesign.failed.reason.xcode.tools=Möglicher Grund für "codesign"-Fehler ist fehlender Xcode mit Befehlszeilen-Entwicklertools. Installieren Sie Xcode mit Befehlszeilen-Entwicklertools, und prüfen Sie, ob das Problem dadurch beseitigt wird. +message.dmg.license.button.agree=Akzeptieren +message.dmg.license.button.disagree=Ablehnen +message.dmg.license.button.print=Drucken +message.dmg.license.button.save=Sichern... +message.dmg.license.message=Klicken Sie in “Akzeptieren”, wenn Sie mit den Bestimmungen des Software-Lizenzvertrags einverstanden sind. Falls nicht, bitte “Ablehnen” anklicken. Sie können die Software nur installieren, wenn Sie “Akzeptieren” angeklickt haben. warning.unsigned.app.image=Warnung: Nicht signiertes app-image wird zum Erstellen von signiertem {0} verwendet. warning.per.user.app.image.signed=Warnung: Konfiguration der installierten Anwendung pro Benutzer wird nicht unterstützt, da "{0}" im vordefinierten signierten Anwendungsimage fehlt. warning.non.standard.contents.sub.dir=Warnung: Der Dateiname des Verzeichnisses "{0}", das für die Option --app-content angegeben wurde, ist kein Standardunterverzeichnisname im Verzeichnis "Contents" des Anwendungs-Bundles. Möglicherweise verläuft die Codesignierung und/oder Notarisierung im Ergebnisanwendungs-Bundle nicht erfolgreich. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties index d3150a34a86..ba2497d30dd 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties @@ -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 @@ -71,6 +71,11 @@ message.signing.pkg=警告: PKGへの署名の場合、「キーチェーン・ message.setfile.dmg='SetFile'ユーティリティが見つからないため、DMGファイルでのカスタム・アイコンの設定がスキップされました。Xcodeとコマンド・ライン・ツールをインストールすると、この問題は解決されます。 message.codesign.failed.reason.app.content="codesign"が失敗したため、追加のアプリケーション・コンテンツが、"--app-content"パラメータを介して提供されました。追加のコンテンツにより、アプリケーション・バンドルの整合性が損われ、失敗の原因になった可能性があります。"--app-content"パラメータを介して提供されたコンテンツによって、アプリケーション・バンドルの整合性が損われていないことを確認するか、処理後のステップで追加してください。 message.codesign.failed.reason.xcode.tools="codesign"失敗の考えられる理由は、Xcodeとコマンドライン・デベロッパ・ツールの欠落です。Xcodeとコマンドライン・デベロッパ・ツールをインストールして、問題が解決されるかを確認してください。 +message.dmg.license.button.agree=同意します +message.dmg.license.button.disagree=同意しません +message.dmg.license.button.print=印刷する +message.dmg.license.button.save=保存... +message.dmg.license.message=本ソフトウエア使用許諾契約の条件に同意される場合には、ソフトウエアをインストールするために「同意します」を押してください。 同意されない場合には、「同意しません」を押してください。 warning.unsigned.app.image=警告: 署名されていないapp-imageを使用して署名された{0}を作成します。 warning.per.user.app.image.signed=警告: 事前定義済の署名付きアプリケーション・イメージに"{0}"がないため、インストール済アプリケーションのユーザーごとの構成はサポートされません。 warning.non.standard.contents.sub.dir=警告: --app-contentオプションに指定されたディレクトリ"{0}"のファイル名が、アプリケーション・バンドルの"Contents"ディレクトリ内の標準サブディレクトリ名ではありません。結果アプリケーション・バンドルは、コード署名および/または公証に失敗することがあります。 diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties index 8ca2219b72f..f855a31caae 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties @@ -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 @@ -71,6 +71,11 @@ message.signing.pkg=警告:要对 PKG 进行签名,可能需要使用“密 message.setfile.dmg=由于未找到 'SetFile' 实用程序,跳过了针对 DMG 文件设置定制图标的操作。安装带命令行工具的 Xcode 应能解决此问题。 message.codesign.failed.reason.app.content="codesign" 失败,并通过 "--app-content" 参数提供了附加应用程序内容。可能是附加内容破坏了应用程序包的完整性,导致了故障。请确保通过 "--app-content" 参数提供的内容不会破坏应用程序包的完整性,或者在后处理步骤中添加该内容。 message.codesign.failed.reason.xcode.tools="codesign" 失败可能是因为缺少带命令行开发人员工具的 Xcode。请安装带命令行开发人员工具的 Xcode,看看是否可以解决问题。 +message.dmg.license.button.agree=同意 +message.dmg.license.button.disagree=不同意 +message.dmg.license.button.print=打印 +message.dmg.license.button.save=存储... +message.dmg.license.message=如果您同意本许可协议的条款,请按“同意”来安装此软件。如果您不同意,请按“不同意”。 warning.unsigned.app.image=警告:使用未签名的 app-image 生成已签名的 {0}。 warning.per.user.app.image.signed=警告:由于预定义的已签名应用程序映像中缺少 "{0}",不支持对已安装应用程序的每用户配置提供支持。 warning.non.standard.contents.sub.dir=警告:为 --app-content 选项指定的目录 "{0}" 的文件名不是应用程序包的 "Contents" 目录中的标准子目录名称。结果应用程序包可能会使代码签名和/或公证失败。 diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist index 072e7168fb7..ac1b32325e8 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist @@ -8,7 +8,9 @@ Attributes 0x0000 Data - AAAAAgAAAAAAAAAAAAQAAA== + + AAAABAAAAAAAAAADAAEAAAAOAAIAAQA0AAMAAQ== + ID 5000 Name @@ -21,17 +23,22 @@ Attributes 0x0000 Data - AAYPRW5nbGlzaCBkZWZhdWx0BUFncmVlCERpc2FncmVlBVByaW50B1NhdmUuLi56SWYgeW91IGFncmVlIHdpdGggdGhlIHRlcm1zIG9mIHRoaXMgbGljZW5zZSwgY2xpY2sgIkFncmVlIiB0byBhY2Nlc3MgdGhlIHNvZnR3YXJlLiAgSWYgeW91IGRvIG5vdCBhZ3JlZSwgcHJlc3MgIkRpc2FncmVlLiI= + + STR_DATA_ENGLISH + ID 5000 + Name - English buttons + English (United States) Attributes 0x0000 Data - AAYHRGV1dHNjaAtBa3plcHRpZXJlbghBYmxlaG5lbgdEcnVja2VuClNpY2hlcm4uLi7nS2xpY2tlbiBTaWUgaW4g0kFremVwdGllcmVu0ywgd2VubiBTaWUgbWl0IGRlbiBCZXN0aW1tdW5nZW4gZGVzIFNvZnR3YXJlLUxpemVuenZlcnRyYWdzIGVpbnZlcnN0YW5kZW4gc2luZC4gRmFsbHMgbmljaHQsIGJpdHRlINJBYmxlaG5lbtMgYW5rbGlja2VuLiBTaWUga5pubmVuIGRpZSBTb2Z0d2FyZSBudXIgaW5zdGFsbGllcmVuLCB3ZW5uIFNpZSDSQWt6ZXB0aWVyZW7TIGFuZ2VrbGlja3QgaGFiZW4u + + STR_DATA_GERMAN + ID 5001 Name @@ -41,151 +48,25 @@ Attributes 0x0000 Data - AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4ue0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxpY2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29mdHdhcmUuICBJZiB5b3UgZG8gbm90IGFncmVlLCBwcmVzcyAiRGlzYWdyZWUiLg== + + STR_DATA_JAPANESE + ID 5002 Name - English - - - Attributes - 0x0000 - Data - AAYHRXNwYZZvbAdBY2VwdGFyCk5vIGFjZXB0YXIISW1wcmltaXIKR3VhcmRhci4uLsBTaSBlc3SHIGRlIGFjdWVyZG8gY29uIGxvcyB0jnJtaW5vcyBkZSBlc3RhIGxpY2VuY2lhLCBwdWxzZSAiQWNlcHRhciIgcGFyYSBpbnN0YWxhciBlbCBzb2Z0d2FyZS4gRW4gZWwgc3VwdWVzdG8gZGUgcXVlIG5vIGVzdI4gZGUgYWN1ZXJkbyBjb24gbG9zIHSOcm1pbm9zIGRlIGVzdGEgbGljZW5jaWEsIHB1bHNlICJObyBhY2VwdGFyLiI= - ID - 5003 - Name - Spanish - - - Attributes - 0x0000 - Data - AAYIRnJhbo1haXMIQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= - ID - 5004 - Name - French - - - Attributes - 0x0000 - Data - AAYISXRhbGlhbm8HQWNjZXR0bwdSaWZpdXRvBlN0YW1wYQtSZWdpc3RyYS4uLn9TZSBhY2NldHRpIGxlIGNvbmRpemlvbmkgZGkgcXVlc3RhIGxpY2VuemEsIGZhaSBjbGljIHN1ICJBY2NldHRvIiBwZXIgaW5zdGFsbGFyZSBpbCBzb2Z0d2FyZS4gQWx0cmltZW50aSBmYWkgY2xpYyBzdSAiUmlmaXV0byIu - ID - 5005 - Name - Italian - - - Attributes - 0x0000 - Data - AAYISmFwYW5lc2UKk6+I04K1gtyCtwyTr4jTgrWC3IK5gvEIiPON/IK3gukHlduRti4uLrSWe4Ncg3SDZ4NFg0eDQY5nl3CLlpH4jF+W8YLMj/CMj4LJk6+I04KzguqC6Y/qjYeCyYLNgUGDXIN0g2eDRYNHg0GC8INDg5ODWINngVuDi4K3gumCvYLfgsmBdZOviNOCtYLcgreBdoLwiZ+CtYLEgq2CvoKzgqKBQoFAk6+I04KzguqCyIKij+qNh4LJgs2BQYF1k6+I04K1gtyCuYLxgXaC8ImfgrWCxIKtgr6Cs4KigUI= - ID - 5006 - Name Japanese Attributes 0x0000 Data - AAYKTmVkZXJsYW5kcwJKYQNOZWUFUHJpbnQJQmV3YWFyLi4upEluZGllbiB1IGFra29vcmQgZ2FhdCBtZXQgZGUgdm9vcndhYXJkZW4gdmFuIGRlemUgbGljZW50aWUsIGt1bnQgdSBvcCAnSmEnIGtsaWtrZW4gb20gZGUgcHJvZ3JhbW1hdHV1ciB0ZSBpbnN0YWxsZXJlbi4gSW5kaWVuIHUgbmlldCBha2tvb3JkIGdhYXQsIGtsaWt0IHUgb3AgJ05lZScu + + STR_DATA_SIMPLIFIED_CHINESE + ID - 5007 + 5003 Name - Dutch - - - Attributes - 0x0000 - Data - AAYGU3ZlbnNrCEdvZGuKbm5zBkF2YppqcwhTa3JpdiB1dAhTcGFyYS4uLpNPbSBEdSBnb2Rrim5uZXIgbGljZW5zdmlsbGtvcmVuIGtsaWNrYSBwjCAiR29ka4pubnMiIGaaciBhdHQgaW5zdGFsbGVyYSBwcm9ncmFtcHJvZHVrdGVuLiBPbSBEdSBpbnRlIGdvZGuKbm5lciBsaWNlbnN2aWxsa29yZW4sIGtsaWNrYSBwjCAiQXZimmpzIi4= - ID - 5008 - Name - Swedish - - - Attributes - 0x0000 - Data - AAYRUG9ydHVndZBzLCBCcmFzaWwJQ29uY29yZGFyCURpc2NvcmRhcghJbXByaW1pcglTYWx2YXIuLi6MU2UgZXN0hyBkZSBhY29yZG8gY29tIG9zIHRlcm1vcyBkZXN0YSBsaWNlbo1hLCBwcmVzc2lvbmUgIkNvbmNvcmRhciIgcGFyYSBpbnN0YWxhciBvIHNvZnR3YXJlLiBTZSBui28gZXN0hyBkZSBhY29yZG8sIHByZXNzaW9uZSAiRGlzY29yZGFyIi4= - ID - 5009 - Name - Brazilian Portuguese - - - Attributes - 0x0000 - Data - AAYSU2ltcGxpZmllZCBDaGluZXNlBM2s0uIGsrvNrNLiBLTy06EGtOa0oqGtVMjnufvE+s2s0uKxvtDtv8nQrdLptcTM9b/uo6zH67C0obDNrNLiobHAtLCy17C0y8jtvP6ho8jnufvE+rK7zazS4qOsx+uwtKGwsrvNrNLiobGhow== - ID - 5010 - Name - Simplified Chinese - - - Attributes - 0x0000 - Data - AAYTVHJhZGl0aW9uYWwgQ2hpbmVzZQSmULdOBqSjplC3TgSmQ6ZMBsB4pnOhS1CmcKpHsXqmULdOpbuzXKVpw9K4zKq6sfi02qFBvdCr9qGnplC3TqGopUimd7jLs27F6aFDpnCqR6SjplC3TqFBvdCr9qGnpKOmULdOoaihQw== - ID - 5011 - Name - Traditional Chinese - - - Attributes - 0x0000 - Data - AAYFRGFuc2sERW5pZwVVZW5pZwdVZHNrcml2CkFya2l2ZXIuLi6YSHZpcyBkdSBhY2NlcHRlcmVyIGJldGluZ2Vsc2VybmUgaSBsaWNlbnNhZnRhbGVuLCBza2FsIGR1IGtsaWtrZSBwjCDSRW5pZ9MgZm9yIGF0IGluc3RhbGxlcmUgc29mdHdhcmVuLiBLbGlrIHCMINJVZW5pZ9MgZm9yIGF0IGFubnVsbGVyZSBpbnN0YWxsZXJpbmdlbi4= - ID - 5012 - Name - Danish - - - Attributes - 0x0000 - Data - AAYFU3VvbWkISHl2imtzeW4KRW4gaHl2imtzeQdUdWxvc3RhCVRhbGxlbm5hyW9IeXaKa3N5IGxpc2Vuc3Npc29waW11a3NlbiBlaGRvdCBvc29pdHRhbWFsbGEg1Uh5doprc3nVLiBKb3MgZXQgaHl2imtzeSBzb3BpbXVrc2VuIGVodG9qYSwgb3NvaXRhINVFbiBoeXaKa3N51S4= - ID - 5013 - Name - Finnish - - - Attributes - 0x0000 - Data - AAYRRnJhbo1haXMgY2FuYWRpZW4IQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= - ID - 5014 - Name - French Canadian - - - Attributes - 0x0000 - Data - AAYGS29yZWFuBLW/wMcJtb/AxyC+yMfUBsfBuLDGrgfA+sDlLi4ufrvnv+sgsOi+4LytwMcgs7u/67+hILW/wMfHz7jpLCAitb/AxyIgtNzD37imILStt68gvNLHwcauv/6+7rimILyzxKHHz73KvcO/wC4gtb/Ax8fPwfYgvsq0wrTZuOksICK1v8DHIL7Ix9QiILTcw9+4piC0qbijvcq9w7/ALg== - ID - 5015 - Name - Korean - - - Attributes - 0x0000 - Data - AAYFTm9yc2sERW5pZwlJa2tlIGVuaWcIU2tyaXYgdXQKQXJraXZlci4uLqNIdmlzIERlIGVyIGVuaWcgaSBiZXN0ZW1tZWxzZW5lIGkgZGVubmUgbGlzZW5zYXZ0YWxlbiwga2xpa2tlciBEZSBwjCAiRW5pZyIta25hcHBlbiBmb3IgjCBpbnN0YWxsZXJlIHByb2dyYW12YXJlbi4gSHZpcyBEZSBpa2tlIGVyIGVuaWcsIGtsaWtrZXIgRGUgcIwgIklra2UgZW5pZyIu - ID - 5016 - Name - Norwegian + Chinese (Simplified) TEXT @@ -194,50 +75,49 @@ Attributes 0x0000 Data - APPLICATION_LICENSE_TEXT + + APPLICATION_LICENSE_TEXT + ID 5000 Name - English SLA + English (United States) SLA + + + Attributes + 0x0000 + Data + + APPLICATION_LICENSE_TEXT + + ID + 5001 + Name + German SLA - - TMPL - Attributes 0x0000 Data - E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ== + + APPLICATION_LICENSE_TEXT + ID - 128 + 5002 Name - LPic + Japanese SLA - - plst - - - Attributes - 0x0050 - Data - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - ID - 0 - Name - - - - styl - Attributes 0x0000 Data - AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAAAAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA= + + APPLICATION_LICENSE_TEXT + ID - 5000 + 5003 Name - English SLA + Chinese (Simplified) SLA diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index b508d4f5ffe..1ae678cd944 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -183,12 +183,12 @@ public final class Executor extends CommandArguments { return commandOutputControl.charset(); } - Executor storeOutputInFiles(boolean v) { + public Executor storeOutputInFiles(boolean v) { commandOutputControl.storeOutputInFiles(v); return this; } - Executor storeOutputInFiles() { + public Executor storeOutputInFiles() { return storeOutputInFiles(true); } diff --git a/test/jdk/tools/jpackage/share/LicenseTest.java b/test/jdk/tools/jpackage/share/LicenseTest.java index 1c6bfd51b62..9c2d1077584 100644 --- a/test/jdk/tools/jpackage/share/LicenseTest.java +++ b/test/jdk/tools/jpackage/share/LicenseTest.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 @@ -56,6 +56,9 @@ import jdk.jpackage.test.TKit; * * Mac: * + * For DMG license should be displayed on command line when "hdiutil attach" + * is called. + * * Windows * * Installer should display license text matching contents of the license file @@ -96,6 +99,7 @@ public class LicenseTest { LICENSE_FILE)); }); + initMacDmgLicenseVerifier(test.forTypes(PackageType.MAC_DMG)); initLinuxLicenseVerifier(test.forTypes(PackageType.LINUX)); test.run(); @@ -131,6 +135,33 @@ public class LicenseTest { new CustomDebianCopyrightTest().withSubstitution(true).run(); } + private static PackageTest initMacDmgLicenseVerifier(PackageTest test) { + return test + .addBundleVerifier(cmd -> { + verifyLicenseFileInDMGPackage(cmd); + }); + } + + private static void verifyLicenseFileInDMGPackage(JPackageCommand cmd) + throws IOException { + // DMG should have license, so attach with "no", since we only need license. + // With "no" attach will be canceled. + final var attachExec = Executor.of("sh", "-c", String.join(" ", + "no", + "|", + "/usr/bin/hdiutil", + "attach", + JPackageCommand.escapeAndJoin(cmd.outputBundle().toString()) + )).saveOutput().storeOutputInFiles(); + + // Expected exit code is 1, since we canceling license. + final var attachResult = attachExec.executeAndRepeatUntilExitCode(1, 10, 6); + TKit.assertStringListEquals(Files.readAllLines(LICENSE_FILE), + attachResult.stdout(), String.format( + "Check output of \"hdiutil attach\" has the same license as contents of source license file [%s]", + LICENSE_FILE)); + } + private static PackageTest initLinuxLicenseVerifier(PackageTest test) { return test .addBundleVerifier(cmd -> { From b6b337926d5f13ee2bca12ea94530ea59911ff2f Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Thu, 15 Jan 2026 05:58:18 +0000 Subject: [PATCH 014/126] 8371762: Incorrect use of checked_cast in Arguments::process_settings_file Reviewed-by: dholmes, kbarrett --- src/hotspot/share/runtime/arguments.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index cf0a1ab9757..546ae610769 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.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 @@ -64,7 +64,6 @@ #include "runtime/vm_version.hpp" #include "services/management.hpp" #include "utilities/align.hpp" -#include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" #include "utilities/defaultStream.hpp" #include "utilities/macros.hpp" @@ -1207,16 +1206,22 @@ bool Arguments::process_settings_file(const char* file_name, bool should_exist, } char token[1024]; - int pos = 0; + size_t pos = 0; bool in_white_space = true; bool in_comment = false; bool in_quote = false; - int quote_c = 0; + char quote_c = 0; bool result = true; - int c = getc(stream); - while(c != EOF && pos < (int)(sizeof(token)-1)) { + int c_or_eof = getc(stream); + while (c_or_eof != EOF && pos < (sizeof(token) - 1)) { + // We have checked the c_or_eof for EOF. getc should only ever return the + // EOF or an unsigned char converted to an int. We cast down to a char to + // avoid the char to int promotions we would otherwise do in the comparisons + // below (which would be incorrect if we ever compared to a non-ascii char), + // and the int to char conversions we would otherwise do in the assignments. + const char c = static_cast(c_or_eof); if (in_white_space) { if (in_comment) { if (c == '\n') in_comment = false; @@ -1224,7 +1229,7 @@ bool Arguments::process_settings_file(const char* file_name, bool should_exist, if (c == '#') in_comment = true; else if (!isspace((unsigned char) c)) { in_white_space = false; - token[pos++] = checked_cast(c); + token[pos++] = c; } } } else { @@ -1244,10 +1249,10 @@ bool Arguments::process_settings_file(const char* file_name, bool should_exist, } else if (in_quote && (c == quote_c)) { in_quote = false; } else { - token[pos++] = checked_cast(c); + token[pos++] = c; } } - c = getc(stream); + c_or_eof = getc(stream); } if (pos > 0) { token[pos] = '\0'; From d16a9b2ec507251a44f034f1ccf8039f02023d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarre=C3=B1o?= Date: Thu, 15 Jan 2026 07:22:54 +0000 Subject: [PATCH 015/126] 8373134: C2: Min/Max users of Min/Max uses should be enqueued for GVN Reviewed-by: epeter, bmaillard, dlong --- src/hotspot/share/opto/addnode.cpp | 30 +-- src/hotspot/share/opto/addnode.hpp | 46 ++--- src/hotspot/share/opto/loopnode.cpp | 18 +- src/hotspot/share/opto/macro.cpp | 4 +- src/hotspot/share/opto/movenode.cpp | 4 +- src/hotspot/share/opto/node.hpp | 3 + src/hotspot/share/opto/phaseX.cpp | 9 + src/hotspot/share/opto/vectorization.cpp | 2 +- .../compiler/igvn/TestMinMaxIdentity.java | 186 ++++++++++++++++++ 9 files changed, 251 insertions(+), 51 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 40cd6337c17..e04da430ef0 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1195,7 +1195,7 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { return AddNode::Value(phase); } -Node* MaxNode::build_min_max_int(Node* a, Node* b, bool is_max) { +Node* MinMaxNode::build_min_max_int(Node* a, Node* b, bool is_max) { if (is_max) { return new MaxINode(a, b); } else { @@ -1203,7 +1203,7 @@ Node* MaxNode::build_min_max_int(Node* a, Node* b, bool is_max) { } } -Node* MaxNode::build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max) { +Node* MinMaxNode::build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max) { if (is_max) { return new MaxLNode(phase->C, a, b); } else { @@ -1211,7 +1211,7 @@ Node* MaxNode::build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max } } -Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn) { +Node* MinMaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn) { bool is_int = gvn.type(a)->isa_int(); assert(is_int || gvn.type(a)->isa_long(), "int or long inputs"); assert(is_int == (gvn.type(b)->isa_int() != nullptr), "inconsistent inputs"); @@ -1243,7 +1243,7 @@ Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, co return res; } -Node* MaxNode::build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn) { +Node* MinMaxNode::build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn) { bool is_int = gvn.type(a)->isa_int(); assert(is_int || gvn.type(a)->isa_long(), "int or long inputs"); assert(is_int == (gvn.type(b)->isa_int() != nullptr), "inconsistent inputs"); @@ -1290,7 +1290,7 @@ static bool can_overflow(const TypeLong* t, jlong c) { // Let = x_operands and = y_operands. // If x == y and neither add(x, x_off) nor add(y, y_off) overflow, return // add(x, op(x_off, y_off)). Otherwise, return nullptr. -Node* MaxNode::extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands) { +Node* MinMaxNode::extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands) { Node* x = x_operands.first; Node* y = y_operands.first; int opcode = Opcode(); @@ -1327,7 +1327,7 @@ static ConstAddOperands as_add_with_constant(Node* n) { return ConstAddOperands(x, c_type->is_int()->get_con()); } -Node* MaxNode::IdealI(PhaseGVN* phase, bool can_reshape) { +Node* MinMaxNode::IdealI(PhaseGVN* phase, bool can_reshape) { Node* n = AddNode::Ideal(phase, can_reshape); if (n != nullptr) { return n; @@ -1401,7 +1401,7 @@ Node* MaxINode::Identity(PhaseGVN* phase) { return in(2); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } //============================================================================= @@ -1434,7 +1434,7 @@ Node* MinINode::Identity(PhaseGVN* phase) { return in(1); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } //------------------------------add_ring--------------------------------------- @@ -1564,7 +1564,7 @@ Node* MaxLNode::Identity(PhaseGVN* phase) { return in(2); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } Node* MaxLNode::Ideal(PhaseGVN* phase, bool can_reshape) { @@ -1596,7 +1596,7 @@ Node* MinLNode::Identity(PhaseGVN* phase) { return in(1); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { @@ -1610,7 +1610,7 @@ Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } -int MaxNode::opposite_opcode() const { +int MinMaxNode::opposite_opcode() const { if (Opcode() == max_opcode()) { return min_opcode(); } else { @@ -1621,7 +1621,7 @@ int MaxNode::opposite_opcode() const { // Given a redundant structure such as Max/Min(A, Max/Min(B, C)) where A == B or A == C, return the useful part of the structure. // 'operation' is the node expected to be the inner 'Max/Min(B, C)', and 'operand' is the node expected to be the 'A' operand of the outer node. -Node* MaxNode::find_identity_operation(Node* operation, Node* operand) { +Node* MinMaxNode::find_identity_operation(Node* operation, Node* operand) { if (operation->Opcode() == Opcode() || operation->Opcode() == opposite_opcode()) { Node* n1 = operation->in(1); Node* n2 = operation->in(2); @@ -1645,17 +1645,17 @@ Node* MaxNode::find_identity_operation(Node* operation, Node* operand) { return nullptr; } -Node* MaxNode::Identity(PhaseGVN* phase) { +Node* MinMaxNode::Identity(PhaseGVN* phase) { if (in(1) == in(2)) { return in(1); } - Node* identity_1 = MaxNode::find_identity_operation(in(2), in(1)); + Node* identity_1 = MinMaxNode::find_identity_operation(in(2), in(1)); if (identity_1 != nullptr) { return identity_1; } - Node* identity_2 = MaxNode::find_identity_operation(in(1), in(2)); + Node* identity_2 = MinMaxNode::find_identity_operation(in(1), in(2)); if (identity_2 != nullptr) { return identity_2; } diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 1bbdae92e48..4151ab5d065 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -324,14 +324,16 @@ public: //------------------------------MaxNode---------------------------------------- // Max (or min) of 2 values. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. -class MaxNode : public AddNode { +class MinMaxNode : public AddNode { private: static Node* build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn); static Node* build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn); Node* extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands); public: - MaxNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + MinMaxNode(Node* in1, Node* in2) : AddNode(in1, in2) { + init_class_id(Class_MinMax); + } virtual int Opcode() const = 0; virtual int max_opcode() const = 0; virtual int min_opcode() const = 0; @@ -373,9 +375,9 @@ public: //------------------------------MaxINode--------------------------------------- // Maximum of 2 integers. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. -class MaxINode : public MaxNode { +class MaxINode : public MinMaxNode { public: - MaxINode( Node *in1, Node *in2 ) : MaxNode(in1,in2) {} + MaxINode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeInt::make(min_jint); } @@ -390,9 +392,9 @@ public: //------------------------------MinINode--------------------------------------- // MINimum of 2 integers. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. -class MinINode : public MaxNode { +class MinINode : public MinMaxNode { public: - MinINode( Node *in1, Node *in2 ) : MaxNode(in1,in2) {} + MinINode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeInt::make(max_jint); } @@ -406,9 +408,9 @@ public: //------------------------------MaxLNode--------------------------------------- // MAXimum of 2 longs. -class MaxLNode : public MaxNode { +class MaxLNode : public MinMaxNode { public: - MaxLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + MaxLNode(Compile* C, Node* in1, Node* in2) : MinMaxNode(in1, in2) { init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -425,9 +427,9 @@ public: //------------------------------MinLNode--------------------------------------- // MINimum of 2 longs. -class MinLNode : public MaxNode { +class MinLNode : public MinMaxNode { public: - MinLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + MinLNode(Compile* C, Node* in1, Node* in2) : MinMaxNode(in1, in2) { init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -444,9 +446,9 @@ public: //------------------------------MaxFNode--------------------------------------- // Maximum of 2 floats. -class MaxFNode : public MaxNode { +class MaxFNode : public MinMaxNode { public: - MaxFNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MaxFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeF::NEG_INF; } @@ -458,9 +460,9 @@ public: //------------------------------MinFNode--------------------------------------- // Minimum of 2 floats. -class MinFNode : public MaxNode { +class MinFNode : public MinMaxNode { public: - MinFNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MinFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeF::POS_INF; } @@ -472,9 +474,9 @@ public: //------------------------------MaxHFNode-------------------------------------- // Maximum of 2 half floats. -class MaxHFNode : public MaxNode { +class MaxHFNode : public MinMaxNode { public: - MaxHFNode(Node* in1, Node* in2) : MaxNode(in1, in2) {} + MaxHFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type* add_ring(const Type*, const Type*) const; virtual const Type* add_id() const { return TypeH::NEG_INF; } @@ -486,9 +488,9 @@ public: //------------------------------MinHFNode--------------------------------------- // Minimum of 2 half floats. -class MinHFNode : public MaxNode { +class MinHFNode : public MinMaxNode { public: - MinHFNode(Node* in1, Node* in2) : MaxNode(in1, in2) {} + MinHFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type* add_ring(const Type*, const Type*) const; virtual const Type* add_id() const { return TypeH::POS_INF; } @@ -500,9 +502,9 @@ public: //------------------------------MaxDNode--------------------------------------- // Maximum of 2 doubles. -class MaxDNode : public MaxNode { +class MaxDNode : public MinMaxNode { public: - MaxDNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MaxDNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeD::NEG_INF; } @@ -514,9 +516,9 @@ public: //------------------------------MinDNode--------------------------------------- // Minimum of 2 doubles. -class MinDNode : public MaxNode { +class MinDNode : public MinMaxNode { public: - MinDNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MinDNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeD::POS_INF; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index dacc1a1a734..8dc34af9c19 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -979,9 +979,9 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { Node* inner_iters_max = nullptr; if (stride_con > 0) { - inner_iters_max = MaxNode::max_diff_with_zero(limit, outer_phi, TypeInteger::bottom(bt), _igvn); + inner_iters_max = MinMaxNode::max_diff_with_zero(limit, outer_phi, TypeInteger::bottom(bt), _igvn); } else { - inner_iters_max = MaxNode::max_diff_with_zero(outer_phi, limit, TypeInteger::bottom(bt), _igvn); + inner_iters_max = MinMaxNode::max_diff_with_zero(outer_phi, limit, TypeInteger::bottom(bt), _igvn); } Node* inner_iters_limit = _igvn.integercon(iters_limit, bt); @@ -989,7 +989,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Long.MIN_VALUE to Long.MAX_VALUE for instance). Use an unsigned // min. const TypeInteger* inner_iters_actual_range = TypeInteger::make(0, iters_limit, Type::WidenMin, bt); - Node* inner_iters_actual = MaxNode::unsigned_min(inner_iters_max, inner_iters_limit, inner_iters_actual_range, _igvn); + Node* inner_iters_actual = MinMaxNode::unsigned_min(inner_iters_max, inner_iters_limit, inner_iters_actual_range, _igvn); Node* inner_iters_actual_int; if (bt == T_LONG) { @@ -1618,7 +1618,7 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List Node* max_jint_plus_one_long = longcon((jlong)max_jint + 1); Node* max_range = new AddLNode(max_jint_plus_one_long, L); register_new_node(max_range, entry_control); - R = MaxNode::unsigned_min(R, max_range, TypeLong::POS, _igvn); + R = MinMaxNode::unsigned_min(R, max_range, TypeLong::POS, _igvn); set_subtree_ctrl(R, true); } @@ -1717,9 +1717,9 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List } Node* PhaseIdealLoop::clamp(Node* R, Node* L, Node* H) { - Node* min = MaxNode::signed_min(R, H, TypeLong::LONG, _igvn); + Node* min = MinMaxNode::signed_min(R, H, TypeLong::LONG, _igvn); set_subtree_ctrl(min, true); - Node* max = MaxNode::signed_max(L, min, TypeLong::LONG, _igvn); + Node* max = MinMaxNode::signed_max(L, min, TypeLong::LONG, _igvn); set_subtree_ctrl(max, true); return max; } @@ -3485,14 +3485,14 @@ void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) { // the loop body to be run for LoopStripMiningIter. Node* max = nullptr; if (stride > 0) { - max = MaxNode::max_diff_with_zero(limit, iv_phi, TypeInt::INT, *igvn); + max = MinMaxNode::max_diff_with_zero(limit, iv_phi, TypeInt::INT, *igvn); } else { - max = MaxNode::max_diff_with_zero(iv_phi, limit, TypeInt::INT, *igvn); + max = MinMaxNode::max_diff_with_zero(iv_phi, limit, TypeInt::INT, *igvn); } // sub is positive and can be larger than the max signed int // value. Use an unsigned min. Node* const_iters = igvn->intcon(scaled_iters); - Node* min = MaxNode::unsigned_min(max, const_iters, TypeInt::make(0, scaled_iters, Type::WidenMin), *igvn); + Node* min = MinMaxNode::unsigned_min(max, const_iters, TypeInt::make(0, scaled_iters, Type::WidenMin), *igvn); // min is the number of iterations for the next inner loop execution: // unsigned_min(max(limit - iv_phi, 0), scaled_iters) if stride > 0 // unsigned_min(max(iv_phi - limit, 0), scaled_iters) if stride < 0 diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 80818a4ddc7..4df03714376 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2577,11 +2577,11 @@ void PhaseMacroExpand::eliminate_opaque_looplimit_macro_nodes() { // a CMoveL construct now. At least until here, the type could be computed // precisely. CMoveL is not so smart, but we can give it at least the best // type we know abouot n now. - Node* repl = MaxNode::signed_max(n->in(1), n->in(2), _igvn.type(n), _igvn); + Node* repl = MinMaxNode::signed_max(n->in(1), n->in(2), _igvn.type(n), _igvn); _igvn.replace_node(n, repl); success = true; } else if (n->Opcode() == Op_MinL) { - Node* repl = MaxNode::signed_min(n->in(1), n->in(2), _igvn.type(n), _igvn); + Node* repl = MinMaxNode::signed_min(n->in(1), n->in(2), _igvn.type(n), _igvn); _igvn.replace_node(n, repl); success = true; } diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index 66db1df339b..6b6becb434f 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -271,9 +271,9 @@ Node* CMoveNode::Ideal_minmax(PhaseGVN* phase, CMoveNode* cmove) { // Create the Min/Max node based on the type and kind if (cmp_op == Op_CmpL) { - return MaxNode::build_min_max_long(phase, cmp_l, cmp_r, is_max); + return MinMaxNode::build_min_max_long(phase, cmp_l, cmp_r, is_max); } else { - return MaxNode::build_min_max_int(cmp_l, cmp_r, is_max); + return MinMaxNode::build_min_max_int(cmp_l, cmp_r, is_max); } } diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 0adb2072100..f1d9785a746 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -130,6 +130,7 @@ class MemBarNode; class MemBarStoreStoreNode; class MemNode; class MergeMemNode; +class MinMaxNode; class MoveNode; class MulNode; class MultiNode; @@ -809,6 +810,7 @@ public: DEFINE_CLASS_ID(AddP, Node, 9) DEFINE_CLASS_ID(BoxLock, Node, 10) DEFINE_CLASS_ID(Add, Node, 11) + DEFINE_CLASS_ID(MinMax, Add, 0) DEFINE_CLASS_ID(Mul, Node, 12) DEFINE_CLASS_ID(ClearArray, Node, 14) DEFINE_CLASS_ID(Halt, Node, 15) @@ -986,6 +988,7 @@ public: DEFINE_CLASS_QUERY(MemBar) DEFINE_CLASS_QUERY(MemBarStoreStore) DEFINE_CLASS_QUERY(MergeMem) + DEFINE_CLASS_QUERY(MinMax) DEFINE_CLASS_QUERY(Move) DEFINE_CLASS_QUERY(Mul) DEFINE_CLASS_QUERY(Multi) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c4bdc5e8903..52badca8050 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2633,6 +2633,15 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } } + // 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->Opcode() == use->Opcode()) { + worklist.push(u); + } + } + } auto enqueue_init_mem_projs = [&](ProjNode* proj) { add_users_to_worklist0(proj, worklist); }; diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index 1755b0453eb..8e0ca927a16 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -1122,7 +1122,7 @@ Node* make_last(Node* initL, jint stride, Node* limitL, PhaseIdealLoop* phase) { Node* last = new AddLNode(initL, k_mul_stride); // Make sure that the last does not lie "before" init. - Node* last_clamped = MaxNode::build_min_max_long(&igvn, initL, last, stride > 0); + Node* last_clamped = MinMaxNode::build_min_max_long(&igvn, initL, last, stride > 0); phase->register_new_node_with_ctrl_of(diffL, initL); phase->register_new_node_with_ctrl_of(diffL_m1, initL); diff --git a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java new file mode 100644 index 00000000000..d358359ff14 --- /dev/null +++ b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2025 IBM Corporation. All rights reserved. + * 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 8373134 + * @summary Verify that min/max add identity optimizations get applied correctly + * @modules java.base/jdk.internal.misc + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.igvn; + +import compiler.lib.compile_framework.CompileFramework; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.TestFrameworkClass; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.scope; + +public class TestMinMaxIdentity { + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("compiler.igvn.templated.MinMaxIdentity", generate(comp)); + + // Compile the source file. + comp.compile("--add-modules=jdk.incubator.vector"); + + comp.invoke("compiler.igvn.templated.MinMaxIdentity", "main", new Object[] {new String[] { + "--add-modules=jdk.incubator.vector", + "--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED" + }}); + } + + private static String generate(CompileFramework comp) { + // Create a list to collect all tests. + List testTemplateTokens = new ArrayList<>(); + + Stream.of(MinMaxOp.values()) + .flatMap(MinMaxOp::generate) + .forEach(testTemplateTokens::add); + + Stream.of(Fp16MinMaxOp.values()) + .flatMap(Fp16MinMaxOp::generate) + .forEach(testTemplateTokens::add); + + // Create the test class, which runs all testTemplateTokens. + return TestFrameworkClass.render( + // package and class name. + "compiler.igvn.templated", "MinMaxIdentity", + // List of imports. + Set.of("jdk.incubator.vector.Float16"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + testTemplateTokens); + } + + enum MinMaxOp { + MIN_D("min", CodeGenerationDataNameType.doubles()), + MAX_D("max", CodeGenerationDataNameType.doubles()), + MIN_F("min", CodeGenerationDataNameType.floats()), + MAX_F("max", CodeGenerationDataNameType.floats()), + MIN_I("min", CodeGenerationDataNameType.ints()), + MAX_I("max", CodeGenerationDataNameType.ints()), + MIN_L("min", CodeGenerationDataNameType.longs()), + MAX_L("max", CodeGenerationDataNameType.longs()); + + final String functionName; + final PrimitiveType type; + + MinMaxOp(String functionName, PrimitiveType type) { + this.functionName = functionName; + this.type = type; + } + + Stream generate() { + return Stream.of(template("a", "b"), template("b", "a")). + map(Template.ZeroArgs::asToken); + } + + private Template.ZeroArgs template(String arg1, String arg2) { + return Template.make(() -> scope( + let("boxedTypeName", type.boxedTypeName()), + let("op", name()), + let("type", type.name()), + let("functionName", functionName), + let("arg1", arg1), + let("arg2", arg2), + """ + @Test + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + @Arguments(values = {Argument.NUMBER_42, Argument.NUMBER_42}) + public #type $test(#type #arg1, #type #arg2) { + int i; + for (i = -10; i < 1; i++) { + } + #type c = a * i; + return #boxedTypeName.#functionName(a, #boxedTypeName.#functionName(b, c)); + } + """ + )); + } + } + + enum Fp16MinMaxOp { + MAX_HF("max"), + MIN_HF("min"); + + final String functionName; + + Fp16MinMaxOp(String functionName) { + this.functionName = functionName; + } + + Stream generate() { + return Stream.of(template("a", "b"), template("b", "a")). + map(Template.ZeroArgs::asToken); + } + + private Template.ZeroArgs template(String arg1, String arg2) { + return Template.make(() -> scope( + let("op", name()), + let("functionName", functionName), + let("arg1", arg1), + let("arg2", arg2), + """ + @Setup + private static Object[] $setup() { + return new Object[] {Float16.valueOf(42), Float16.valueOf(42)}; + } + + @Test + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zfh", "true"}) + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + @Arguments(setup = "$setup") + public Float16 $test(Float16 #arg1, Float16 #arg2) { + int i; + for (i = -10; i < 1; i++) { + } + Float16 c = Float16.multiply(a, Float16.valueOf(i)); + return Float16.#functionName(a, Float16.#functionName(b, c)); + } + """ + )); + } + } +} From f6d26c6b32a3ea394cc9b7f6046cd9d7d635c568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 15 Jan 2026 07:50:52 +0000 Subject: [PATCH 016/126] 8354853: Clean up x86 registers after 32-bit x86 removal Reviewed-by: aph, shade, mchevalier --- src/hotspot/cpu/x86/register_x86.cpp | 8 +--- src/hotspot/cpu/x86/register_x86.hpp | 56 +++++++----------------- src/hotspot/cpu/x86/vmreg_x86.cpp | 4 +- src/hotspot/cpu/x86/vmreg_x86.hpp | 13 +----- src/hotspot/cpu/x86/vmreg_x86.inline.hpp | 4 +- 5 files changed, 23 insertions(+), 62 deletions(-) diff --git a/src/hotspot/cpu/x86/register_x86.cpp b/src/hotspot/cpu/x86/register_x86.cpp index e6083429344..188af9264a8 100644 --- a/src/hotspot/cpu/x86/register_x86.cpp +++ b/src/hotspot/cpu/x86/register_x86.cpp @@ -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 @@ -32,14 +32,10 @@ const KRegister::KRegisterImpl all_KRegisterImpls [KRegister::number_ const char * Register::RegisterImpl::name() const { static const char *const names[number_of_registers] = { -#ifdef _LP64 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" -#else - "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" -#endif // _LP64 }; return is_valid() ? names[encoding()] : "noreg"; } @@ -54,11 +50,9 @@ const char* FloatRegister::FloatRegisterImpl::name() const { const char* XMMRegister::XMMRegisterImpl::name() const { static const char *const names[number_of_registers] = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" -#ifdef _LP64 ,"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" ,"xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23" ,"xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31" -#endif // _LP64 }; return is_valid() ? names[encoding()] : "xnoreg"; } diff --git a/src/hotspot/cpu/x86/register_x86.hpp b/src/hotspot/cpu/x86/register_x86.hpp index 0a8ecb7be26..5e0326b9aad 100644 --- a/src/hotspot/cpu/x86/register_x86.hpp +++ b/src/hotspot/cpu/x86/register_x86.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 @@ -34,7 +34,7 @@ class VMRegImpl; typedef VMRegImpl* VMReg; -// The implementation of integer registers for the x86/x64 architectures. +// The implementation of integer registers for the x64 architectures. class Register { private: int _encoding; @@ -44,11 +44,9 @@ private: public: inline friend constexpr Register as_Register(int encoding); - enum { - number_of_registers = LP64_ONLY( 32 ) NOT_LP64( 8 ), - number_of_byte_registers = LP64_ONLY( 32 ) NOT_LP64( 4 ), - max_slots_per_register = LP64_ONLY( 2 ) NOT_LP64( 1 ) - }; + static const int number_of_registers = 32; + static const int number_of_byte_registers = 32; + static const int max_slots_per_register = 2; class RegisterImpl: public AbstractRegisterImpl { friend class Register; @@ -79,11 +77,9 @@ public: // Actually available GP registers for use, depending on actual CPU capabilities and flags. static int available_gp_registers() { -#ifdef _LP64 if (!UseAPX) { return number_of_registers / 2; } -#endif // _LP64 return number_of_registers; } }; @@ -116,9 +112,8 @@ constexpr Register rsp = as_Register(4); constexpr Register rbp = as_Register(5); constexpr Register rsi = as_Register(6); constexpr Register rdi = as_Register(7); -#ifdef _LP64 -constexpr Register r8 = as_Register( 8); -constexpr Register r9 = as_Register( 9); +constexpr Register r8 = as_Register(8); +constexpr Register r9 = as_Register(9); constexpr Register r10 = as_Register(10); constexpr Register r11 = as_Register(11); constexpr Register r12 = as_Register(12); @@ -141,7 +136,6 @@ constexpr Register r28 = as_Register(28); constexpr Register r29 = as_Register(29); constexpr Register r30 = as_Register(30); constexpr Register r31 = as_Register(31); -#endif // _LP64 // The implementation of x87 floating point registers for the ia32 architecture. @@ -154,10 +148,8 @@ private: public: inline friend constexpr FloatRegister as_FloatRegister(int encoding); - enum { - number_of_registers = 8, - max_slots_per_register = 2 - }; + static const int number_of_registers = 8; + static const int max_slots_per_register = 2; class FloatRegisterImpl: public AbstractRegisterImpl { friend class FloatRegister; @@ -217,10 +209,8 @@ private: public: inline friend constexpr XMMRegister as_XMMRegister(int encoding); - enum { - number_of_registers = LP64_ONLY( 32 ) NOT_LP64( 8 ), - max_slots_per_register = LP64_ONLY( 16 ) NOT_LP64( 16 ) // 512-bit - }; + static const int number_of_registers = 32; + static const int max_slots_per_register = 16; // 512-bit class XMMRegisterImpl: public AbstractRegisterImpl { friend class XMMRegister; @@ -250,11 +240,9 @@ public: // Actually available XMM registers for use, depending on actual CPU capabilities and flags. static int available_xmm_registers() { -#ifdef _LP64 if (UseAVX < 3) { return number_of_registers / 2; } -#endif // _LP64 return number_of_registers; } }; @@ -287,7 +275,6 @@ constexpr XMMRegister xmm4 = as_XMMRegister( 4); constexpr XMMRegister xmm5 = as_XMMRegister( 5); constexpr XMMRegister xmm6 = as_XMMRegister( 6); constexpr XMMRegister xmm7 = as_XMMRegister( 7); -#ifdef _LP64 constexpr XMMRegister xmm8 = as_XMMRegister( 8); constexpr XMMRegister xmm9 = as_XMMRegister( 9); constexpr XMMRegister xmm10 = as_XMMRegister(10); @@ -312,7 +299,6 @@ constexpr XMMRegister xmm28 = as_XMMRegister(28); constexpr XMMRegister xmm29 = as_XMMRegister(29); constexpr XMMRegister xmm30 = as_XMMRegister(30); constexpr XMMRegister xmm31 = as_XMMRegister(31); -#endif // _LP64 // The implementation of AVX-512 opmask registers. @@ -394,25 +380,17 @@ constexpr KRegister k7 = as_KRegister(7); // Define a class that exports it. class ConcreteRegisterImpl : public AbstractRegisterImpl { public: - enum { - max_gpr = Register::number_of_registers * Register::max_slots_per_register, - max_fpr = max_gpr + FloatRegister::number_of_registers * FloatRegister::max_slots_per_register, - max_xmm = max_fpr + XMMRegister::number_of_registers * XMMRegister::max_slots_per_register, - max_kpr = max_xmm + KRegister::number_of_registers * KRegister::max_slots_per_register, + static const int max_gpr = Register::number_of_registers * Register::max_slots_per_register; + static const int max_fpr = max_gpr + FloatRegister::number_of_registers * FloatRegister::max_slots_per_register; + static const int max_xmm = max_fpr + XMMRegister::number_of_registers * XMMRegister::max_slots_per_register; + static const int max_kpr = max_xmm + KRegister::number_of_registers * KRegister::max_slots_per_register; // A big enough number for C2: all the registers plus flags // This number must be large enough to cover REG_COUNT (defined by c2) registers. // There is no requirement that any ordering here matches any ordering c2 gives // it's optoregs. - - // x86_32.ad defines additional dummy FILL0-FILL7 registers, in order to tally - // REG_COUNT (computed by ADLC based on the number of reg_defs seen in .ad files) - // with ConcreteRegisterImpl::number_of_registers additional count of 8 is being - // added for 32 bit jvm. - number_of_registers = max_kpr + // gpr/fpr/xmm/kpr - NOT_LP64( 8 + ) // FILL0-FILL7 in x86_32.ad - 1 // eflags - }; + static const int number_of_registers = max_kpr + // gpr/fpr/xmm/kpr + 1; // eflags }; template <> diff --git a/src/hotspot/cpu/x86/vmreg_x86.cpp b/src/hotspot/cpu/x86/vmreg_x86.cpp index 44aee56ef15..45269e69fcb 100644 --- a/src/hotspot/cpu/x86/vmreg_x86.cpp +++ b/src/hotspot/cpu/x86/vmreg_x86.cpp @@ -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 @@ -32,9 +32,7 @@ void VMRegImpl::set_regName() { int i; for (i = 0; i < ConcreteRegisterImpl::max_gpr ; ) { regName[i++] = reg->name(); -#ifdef AMD64 regName[i++] = reg->name(); -#endif // AMD64 reg = reg->successor(); } diff --git a/src/hotspot/cpu/x86/vmreg_x86.hpp b/src/hotspot/cpu/x86/vmreg_x86.hpp index 6f7c7fafb32..032ce654f2f 100644 --- a/src/hotspot/cpu/x86/vmreg_x86.hpp +++ b/src/hotspot/cpu/x86/vmreg_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, 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 @@ -52,14 +52,8 @@ inline bool is_KRegister() { } inline Register as_Register() { - - assert( is_Register(), "must be"); - // Yuk -#ifdef AMD64 + assert(is_Register(), "must be"); return ::as_Register(value() >> 1); -#else - return ::as_Register(value()); -#endif // AMD64 } inline FloatRegister as_FloatRegister() { @@ -82,9 +76,6 @@ inline KRegister as_KRegister() { inline bool is_concrete() { assert(is_reg(), "must be"); -#ifndef AMD64 - if (is_Register()) return true; -#endif // AMD64 // Do not use is_XMMRegister() here as it depends on the UseAVX setting. if (value() >= ConcreteRegisterImpl::max_fpr && value() < ConcreteRegisterImpl::max_xmm) { int base = value() - ConcreteRegisterImpl::max_fpr; diff --git a/src/hotspot/cpu/x86/vmreg_x86.inline.hpp b/src/hotspot/cpu/x86/vmreg_x86.inline.hpp index 1aeedc094fd..76d70900fe4 100644 --- a/src/hotspot/cpu/x86/vmreg_x86.inline.hpp +++ b/src/hotspot/cpu/x86/vmreg_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021, 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 @@ -26,7 +26,7 @@ #define CPU_X86_VMREG_X86_INLINE_HPP inline VMReg Register::RegisterImpl::as_VMReg() const { - return VMRegImpl::as_VMReg(encoding() LP64_ONLY( << 1 )); + return VMRegImpl::as_VMReg(encoding() << 1); } inline VMReg FloatRegister::FloatRegisterImpl::as_VMReg() const { From bf0da3dd5c20410aceab8e6f7a7a31432d17b96d Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Thu, 15 Jan 2026 09:22:42 +0000 Subject: [PATCH 017/126] 8375040: Clearer names for non-metadata oop iterators in ObjArrayKlass Reviewed-by: tschatzl, kbarrett, aboldtch --- .../share/gc/g1/g1FullGCMarker.inline.hpp | 2 +- .../share/gc/g1/g1ParScanThreadState.cpp | 8 ++--- src/hotspot/share/gc/serial/serialFullGC.cpp | 2 +- .../gc/shenandoah/shenandoahMark.inline.hpp | 6 ++-- src/hotspot/share/gc/z/zHeapIterator.cpp | 2 +- src/hotspot/share/gc/z/zIterator.hpp | 2 +- src/hotspot/share/gc/z/zIterator.inline.hpp | 4 +-- src/hotspot/share/oops/objArrayKlass.hpp | 13 ++++--- .../share/oops/objArrayKlass.inline.hpp | 34 ++++++------------- src/hotspot/share/oops/objArrayOop.hpp | 4 +-- src/hotspot/share/oops/objArrayOop.inline.hpp | 10 ++++++ 11 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp index 6cbfe2674e8..398ef046bf5 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp @@ -108,7 +108,7 @@ void G1FullGCMarker::follow_array_chunk(objArrayOop array, int index) { push_objarray(array, end_index); } - array->oop_iterate_range(mark_closure(), beg_index, end_index); + array->oop_iterate_elements_range(mark_closure(), beg_index, end_index); } inline void G1FullGCMarker::follow_object(oop obj) { diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 80e5fd44fcd..e7b02ed68e7 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -238,9 +238,9 @@ void G1ParScanThreadState::do_partial_array(PartialArrayState* state, bool stole G1HeapRegionAttr dest_attr = _g1h->region_attr(to_array); G1SkipCardMarkSetter x(&_scanner, dest_attr.is_new_survivor()); // Process claimed task. - to_array->oop_iterate_range(&_scanner, - checked_cast(claim._start), - checked_cast(claim._end)); + to_array->oop_iterate_elements_range(&_scanner, + checked_cast(claim._start), + checked_cast(claim._end)); } MAYBE_INLINE_EVACUATION @@ -260,7 +260,7 @@ void G1ParScanThreadState::start_partial_objarray(oop from_obj, // Process the initial chunk. No need to process the type in the // klass, as it will already be handled by processing the built-in // module. - to_array->oop_iterate_range(&_scanner, 0, checked_cast(initial_chunk_size)); + to_array->oop_iterate_elements_range(&_scanner, 0, checked_cast(initial_chunk_size)); } MAYBE_INLINE_EVACUATION diff --git a/src/hotspot/share/gc/serial/serialFullGC.cpp b/src/hotspot/share/gc/serial/serialFullGC.cpp index 546084e38dd..0c8ca51fc99 100644 --- a/src/hotspot/share/gc/serial/serialFullGC.cpp +++ b/src/hotspot/share/gc/serial/serialFullGC.cpp @@ -412,7 +412,7 @@ void SerialFullGC::follow_array_chunk(objArrayOop array, int index) { const int stride = MIN2(len - beg_index, (int) ObjArrayMarkingStride); const int end_index = beg_index + stride; - array->oop_iterate_range(&mark_and_push_closure, beg_index, end_index); + array->oop_iterate_elements_range(&mark_and_push_closure, beg_index, end_index); if (end_index < len) { SerialFullGC::push_objarray(array, end_index); // Push the continuation. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 0a95ee9f149..849459157b5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -167,7 +167,7 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, if (len <= (int) ObjArrayMarkingStride*2) { // A few slices only, process directly - array->oop_iterate_range(cl, 0, len); + array->oop_iterate_elements_range(cl, 0, len); } else { int bits = log2i_graceful(len); // Compensate for non-power-of-two arrays, cover the array in excess: @@ -216,7 +216,7 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, // Process the irregular tail, if present int from = last_idx; if (from < len) { - array->oop_iterate_range(cl, from, len); + array->oop_iterate_elements_range(cl, from, len); } } } @@ -248,7 +248,7 @@ inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, assert (0 < to && to <= len, "to is sane: %d/%d", to, len); #endif - array->oop_iterate_range(cl, from, to); + array->oop_iterate_elements_range(cl, from, to); } template diff --git a/src/hotspot/share/gc/z/zHeapIterator.cpp b/src/hotspot/share/gc/z/zHeapIterator.cpp index d6289178ea8..73ced211058 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.cpp +++ b/src/hotspot/share/gc/z/zHeapIterator.cpp @@ -456,7 +456,7 @@ void ZHeapIterator::follow_array_chunk(const ZHeapIteratorContext& context, cons // Follow array chunk ZHeapIteratorOopClosure cl(this, context, obj); - ZIterator::oop_iterate_range(obj, &cl, start, end); + ZIterator::oop_iterate_elements_range(obj, &cl, start, end); } template diff --git a/src/hotspot/share/gc/z/zIterator.hpp b/src/hotspot/share/gc/z/zIterator.hpp index 3a1de049dd0..e048002e52e 100644 --- a/src/hotspot/share/gc/z/zIterator.hpp +++ b/src/hotspot/share/gc/z/zIterator.hpp @@ -41,7 +41,7 @@ public: static void oop_iterate(oop obj, OopClosureT* cl); template - static void oop_iterate_range(objArrayOop obj, OopClosureT* cl, int start, int end); + static void oop_iterate_elements_range(objArrayOop obj, OopClosureT* cl, int start, int end); // This function skips invisible roots template diff --git a/src/hotspot/share/gc/z/zIterator.inline.hpp b/src/hotspot/share/gc/z/zIterator.inline.hpp index fb20a424288..cbfe1a79aaf 100644 --- a/src/hotspot/share/gc/z/zIterator.inline.hpp +++ b/src/hotspot/share/gc/z/zIterator.inline.hpp @@ -66,9 +66,9 @@ void ZIterator::oop_iterate(oop obj, OopClosureT* cl) { } template -void ZIterator::oop_iterate_range(objArrayOop obj, OopClosureT* cl, int start, int end) { +void ZIterator::oop_iterate_elements_range(objArrayOop obj, OopClosureT* cl, int start, int end) { assert(!is_invisible_object_array(obj), "not safe"); - obj->oop_iterate_range(cl, start, end); + obj->oop_iterate_elements_range(cl, start, end); } template diff --git a/src/hotspot/share/oops/objArrayKlass.hpp b/src/hotspot/share/oops/objArrayKlass.hpp index 12db56351ed..cdf28b883e6 100644 --- a/src/hotspot/share/oops/objArrayKlass.hpp +++ b/src/hotspot/share/oops/objArrayKlass.hpp @@ -135,17 +135,16 @@ class ObjArrayKlass : public ArrayKlass { template inline void oop_oop_iterate_bounded(oop obj, OopClosureType* closure, MemRegion mr); - // Iterate over oop elements within [start, end), and metadata. - template - inline void oop_oop_iterate_range(objArrayOop a, OopClosureType* closure, int start, int end); - - public: - // Iterate over all oop elements. + // Iterate over all oop elements, and no metadata. template inline void oop_oop_iterate_elements(objArrayOop a, OopClosureType* closure); + // Iterate over oop elements within index range [start, end), and no metadata. + template + inline void oop_oop_iterate_elements_range(objArrayOop a, OopClosureType* closure, int start, int end); + private: - // Iterate over all oop elements with indices within mr. + // Iterate over all oop elements bounded by addresses [low, high), and no metadata. template inline void oop_oop_iterate_elements_bounded(objArrayOop a, OopClosureType* closure, void* low, void* high); diff --git a/src/hotspot/share/oops/objArrayKlass.inline.hpp b/src/hotspot/share/oops/objArrayKlass.inline.hpp index a92c42d21a8..ee559548529 100644 --- a/src/hotspot/share/oops/objArrayKlass.inline.hpp +++ b/src/hotspot/share/oops/objArrayKlass.inline.hpp @@ -38,10 +38,18 @@ template void ObjArrayKlass::oop_oop_iterate_elements(objArrayOop a, OopClosureType* closure) { - T* p = (T*)a->base(); - T* const end = p + a->length(); + oop_oop_iterate_elements_range(a, closure, 0, a->length()); +} - for (;p < end; p++) { +// Like oop_oop_iterate but only iterates over a specified range and only used +// for objArrayOops. +template +void ObjArrayKlass::oop_oop_iterate_elements_range(objArrayOop a, OopClosureType* closure, int start, int end) { + T* base = (T*)a->base(); + T* p = base + start; + T* const end_p = base + end; + + for (;p < end_p; ++p) { Devirtualizer::do_oop(closure, p); } } @@ -98,24 +106,4 @@ void ObjArrayKlass::oop_oop_iterate_bounded(oop obj, OopClosureType* closure, Me oop_oop_iterate_elements_bounded(a, closure, mr.start(), mr.end()); } -// Like oop_oop_iterate but only iterates over a specified range and only used -// for objArrayOops. -template -void ObjArrayKlass::oop_oop_iterate_range(objArrayOop a, OopClosureType* closure, int start, int end) { - T* low = (T*)a->base() + start; - T* high = (T*)a->base() + end; - - oop_oop_iterate_elements_bounded(a, closure, low, high); -} - -// Placed here to resolve include cycle between objArrayKlass.inline.hpp and objArrayOop.inline.hpp -template -void objArrayOopDesc::oop_iterate_range(OopClosureType* blk, int start, int end) { - if (UseCompressedOops) { - ((ObjArrayKlass*)klass())->oop_oop_iterate_range(this, blk, start, end); - } else { - ((ObjArrayKlass*)klass())->oop_oop_iterate_range(this, blk, start, end); - } -} - #endif // SHARE_OOPS_OBJARRAYKLASS_INLINE_HPP diff --git a/src/hotspot/share/oops/objArrayOop.hpp b/src/hotspot/share/oops/objArrayOop.hpp index 82bb41b5fd1..0af059efccf 100644 --- a/src/hotspot/share/oops/objArrayOop.hpp +++ b/src/hotspot/share/oops/objArrayOop.hpp @@ -83,9 +83,9 @@ class objArrayOopDesc : public arrayOopDesc { Klass* element_klass(); public: - // special iterators for index ranges, returns size of object + // Special iterators for an element index range. template - void oop_iterate_range(OopClosureType* blk, int start, int end); + void oop_iterate_elements_range(OopClosureType* blk, int start, int end); }; // See similar requirement for oopDesc. diff --git a/src/hotspot/share/oops/objArrayOop.inline.hpp b/src/hotspot/share/oops/objArrayOop.inline.hpp index 21f95b756de..63295a1459d 100644 --- a/src/hotspot/share/oops/objArrayOop.inline.hpp +++ b/src/hotspot/share/oops/objArrayOop.inline.hpp @@ -29,6 +29,7 @@ #include "oops/access.hpp" #include "oops/arrayOop.hpp" +#include "oops/objArrayKlass.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/globals.hpp" @@ -51,4 +52,13 @@ inline void objArrayOopDesc::obj_at_put(int index, oop value) { HeapAccess::oop_store_at(as_oop(), offset, value); } +template +void objArrayOopDesc::oop_iterate_elements_range(OopClosureType* blk, int start, int end) { + if (UseCompressedOops) { + ((ObjArrayKlass*)klass())->oop_oop_iterate_elements_range(this, blk, start, end); + } else { + ((ObjArrayKlass*)klass())->oop_oop_iterate_elements_range(this, blk, start, end); + } +} + #endif // SHARE_OOPS_OBJARRAYOOP_INLINE_HPP From f6e5c885e7ca90da2f9fd9ec1c00b4a955ccdf29 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 15 Jan 2026 11:16:00 +0000 Subject: [PATCH 018/126] 8375282: G1: Fix wrong indendation introduced by JDK-8374743 Reviewed-by: kbarrett --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 2ad5a26d5e6..3a0c4a04441 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -687,8 +687,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { // before the allocation is that we avoid having to keep track of the newly // allocated memory while we do a GC. // Only try that if we can actually perform a GC. - if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation", - word_size)) { + if (is_init_completed() && + policy()->need_to_start_conc_mark("concurrent humongous allocation", word_size)) { try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this)); } From 8ad8920aae5c27de947532ba3cd2b57213208d1e Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 15 Jan 2026 12:37:50 +0000 Subject: [PATCH 019/126] 8374984: Convert workerUtils to use Atomic Reviewed-by: shade, stefank --- src/hotspot/share/gc/shared/workerUtils.cpp | 25 +++++++++++---------- src/hotspot/share/gc/shared/workerUtils.hpp | 12 +++++----- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/gc/shared/workerUtils.cpp b/src/hotspot/share/gc/shared/workerUtils.cpp index 422d513a5cd..1826b9d7df8 100644 --- a/src/hotspot/share/gc/shared/workerUtils.cpp +++ b/src/hotspot/share/gc/shared/workerUtils.cpp @@ -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 @@ -22,8 +22,9 @@ * */ +#include "cppstdlib/new.hpp" #include "gc/shared/workerUtils.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" // *** WorkerThreadsBarrierSync @@ -80,21 +81,21 @@ void WorkerThreadsBarrierSync::abort() { SubTasksDone::SubTasksDone(uint n) : _tasks(nullptr), _n_tasks(n) { - _tasks = NEW_C_HEAP_ARRAY(bool, n, mtInternal); + _tasks = NEW_C_HEAP_ARRAY(Atomic, n, mtInternal); for (uint i = 0; i < _n_tasks; i++) { - _tasks[i] = false; + ::new (&_tasks[i]) Atomic(false); } } #ifdef ASSERT void SubTasksDone::all_tasks_claimed_impl(uint skipped[], size_t skipped_size) { - if (AtomicAccess::cmpxchg(&_verification_done, false, true)) { + if (!_verification_done.compare_set(false, true)) { // another thread has done the verification return; } // all non-skipped tasks are claimed for (uint i = 0; i < _n_tasks; ++i) { - if (!_tasks[i]) { + if (!_tasks[i].load_relaxed()) { auto is_skipped = false; for (size_t j = 0; j < skipped_size; ++j) { if (i == skipped[j]) { @@ -109,27 +110,27 @@ void SubTasksDone::all_tasks_claimed_impl(uint skipped[], size_t skipped_size) { for (size_t i = 0; i < skipped_size; ++i) { auto task_index = skipped[i]; assert(task_index < _n_tasks, "Array in range."); - assert(!_tasks[task_index], "%d is both claimed and skipped.", task_index); + assert(!_tasks[task_index].load_relaxed(), "%d is both claimed and skipped.", task_index); } } #endif bool SubTasksDone::try_claim_task(uint t) { assert(t < _n_tasks, "bad task id."); - return !_tasks[t] && !AtomicAccess::cmpxchg(&_tasks[t], false, true); + return !_tasks[t].load_relaxed() && _tasks[t].compare_set(false, true); } SubTasksDone::~SubTasksDone() { - assert(_verification_done, "all_tasks_claimed must have been called."); - FREE_C_HEAP_ARRAY(bool, _tasks); + assert(_verification_done.load_relaxed(), "all_tasks_claimed must have been called."); + FREE_C_HEAP_ARRAY(Atomic, _tasks); } // *** SequentialSubTasksDone bool SequentialSubTasksDone::try_claim_task(uint& t) { - t = _num_claimed; + t = _num_claimed.load_relaxed(); if (t < _num_tasks) { - t = AtomicAccess::add(&_num_claimed, 1u) - 1; + t = _num_claimed.fetch_then_add(1u); } return t < _num_tasks; } diff --git a/src/hotspot/share/gc/shared/workerUtils.hpp b/src/hotspot/share/gc/shared/workerUtils.hpp index 5f771a57837..7bf9d7d7656 100644 --- a/src/hotspot/share/gc/shared/workerUtils.hpp +++ b/src/hotspot/share/gc/shared/workerUtils.hpp @@ -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 @@ -28,6 +28,7 @@ #include "cppstdlib/type_traits.hpp" #include "memory/allocation.hpp" #include "metaprogramming/enableIf.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -79,11 +80,11 @@ public: // enumeration type. class SubTasksDone: public CHeapObj { - volatile bool* _tasks; + Atomic* _tasks; uint _n_tasks; // make sure verification logic is run exactly once to avoid duplicate assertion failures - DEBUG_ONLY(volatile bool _verification_done = false;) + DEBUG_ONLY(Atomic _verification_done;) void all_tasks_claimed_impl(uint skipped[], size_t skipped_size) NOT_DEBUG_RETURN; NONCOPYABLE(SubTasksDone); @@ -127,7 +128,7 @@ public: class SequentialSubTasksDone : public CHeapObj { uint _num_tasks; // Total number of tasks available. - volatile uint _num_claimed; // Number of tasks claimed. + Atomic _num_claimed; // Number of tasks claimed. NONCOPYABLE(SequentialSubTasksDone); @@ -135,7 +136,8 @@ public: SequentialSubTasksDone(uint num_tasks) : _num_tasks(num_tasks), _num_claimed(0) { } ~SequentialSubTasksDone() { // Claiming may try to claim more tasks than there are. - assert(_num_claimed >= _num_tasks, "Claimed %u tasks of %u", _num_claimed, _num_tasks); + assert(_num_claimed.load_relaxed() >= _num_tasks, + "Claimed %u tasks of %u", _num_claimed.load_relaxed(), _num_tasks); } // Attempt to claim the next unclaimed task in the sequence, From 78a106ffbba0e056e7421ca9d77af02f9b8379d3 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Thu, 15 Jan 2026 13:18:20 +0000 Subject: [PATCH 020/126] 8375183: Remove unused SSLConfiguration.maximumProtocolVersion variable Reviewed-by: djelinski, myankelevich, hchao --- .../sun/security/ssl/SSLConfiguration.java | 19 +------------------ .../sun/security/ssl/TransportContext.java | 3 +-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index ace60e41af9..3c68c669d05 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.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 @@ -64,9 +64,6 @@ final class SSLConfiguration implements Cloneable { // the configured named groups for the "supported_groups" extensions String[] namedGroups; - // the maximum protocol version of enabled protocols - ProtocolVersion maximumProtocolVersion; - // Configurations per SSLSocket or SSLEngine instance. boolean isClientMode; boolean enableSessionCreation; @@ -249,13 +246,6 @@ final class SSLConfiguration implements Cloneable { CustomizedServerSignatureSchemes.signatureSchemes : SupportedSigSchemes.DEFAULT; this.namedGroups = NamedGroup.SupportedGroups.namedGroups; - this.maximumProtocolVersion = ProtocolVersion.NONE; - for (ProtocolVersion pv : enabledProtocols) { - if (pv.compareTo(maximumProtocolVersion) > 0) { - this.maximumProtocolVersion = pv; - } - } - // Configurations per SSLSocket or SSLEngine instance. this.isClientMode = isClientMode; this.enableSessionCreation = true; @@ -323,13 +313,6 @@ final class SSLConfiguration implements Cloneable { sa = params.getProtocols(); if (sa != null) { this.enabledProtocols = ProtocolVersion.namesOf(sa); - - this.maximumProtocolVersion = ProtocolVersion.NONE; - for (ProtocolVersion pv : enabledProtocols) { - if (pv.compareTo(maximumProtocolVersion) > 0) { - this.maximumProtocolVersion = pv; - } - } } // otherwise, use the default values if (params.getNeedClientAuth()) { diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index 980d9c4a6ce..35bdd2fff36 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.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 @@ -144,7 +144,6 @@ final class TransportContext implements ConnectionContext { // initial security parameters this.conSession = new SSLSessionImpl(); - this.protocolVersion = this.sslConfig.maximumProtocolVersion; this.clientVerifyData = emptyByteArray; this.serverVerifyData = emptyByteArray; From 203eb70110dd546784e03243bf98ff3ddb407030 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Thu, 15 Jan 2026 15:54:11 +0000 Subject: [PATCH 021/126] 8291986: ProcessBuilder.redirectErrorStream(true) leaves error stream available Reviewed-by: jpai --- .../unix/native/libjava/ProcessImpl_md.c | 13 +- .../lang/ProcessBuilder/PipelineLeaksFD.java | 263 +++++++++++++++--- .../java/lang/ProcessBuilder/TEST.properties | 1 + 3 files changed, 230 insertions(+), 47 deletions(-) create mode 100644 test/jdk/java/lang/ProcessBuilder/TEST.properties diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index f7531ad5abe..12597fbb650 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -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 @@ -729,12 +729,13 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, if ((fds[0] == -1 && pipe(in) < 0) || (fds[1] == -1 && pipe(out) < 0) || - (fds[2] == -1 && pipe(err) < 0) || + (fds[2] == -1 && !redirectErrorStream && pipe(err) < 0) || // if not redirecting create the pipe (pipe(childenv) < 0) || (pipe(fail) < 0)) { throwInternalIOException(env, errno, "Bad file descriptor", mode); goto Catch; } + c->fds[0] = fds[0]; c->fds[1] = fds[1]; c->fds[2] = fds[2]; @@ -764,17 +765,19 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, assert(resultPid != 0); if (resultPid < 0) { + char * failMessage = "unknown"; switch (c->mode) { case MODE_VFORK: - throwInternalIOException(env, errno, "vfork failed", c->mode); + failMessage = "vfork failed"; break; case MODE_FORK: - throwInternalIOException(env, errno, "fork failed", c->mode); + failMessage = "fork failed"; break; case MODE_POSIX_SPAWN: - throwInternalIOException(env, errno, "posix_spawn failed", c->mode); + failMessage = "posix_spawn failed"; break; } + throwInternalIOException(env, errno, failMessage, c->mode); goto Catch; } close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index d3c44bc9279..2b4cdfa9bdd 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 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 @@ -21,32 +21,42 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.Writer; +import java.lang.ProcessHandle; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /* * @test - * @bug 8289643 8291760 - * @requires (os.family == "linux" & !vm.musl) + * @bug 8289643 8291760 8291986 + * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) * @summary File descriptor leak detection with ProcessBuilder.startPipeline - * @run testng/othervm PipelineLeaksFD + * @run junit/othervm PipelineLeaksFD */ -@Test public class PipelineLeaksFD { - @DataProvider - public Object[][] builders() { + + private static final String OS_NAME = System.getProperty("os.name", "Unknown"); + + private static final long MY_PID = ProcessHandle.current().pid(); + + private static final boolean LSOF_AVAILABLE = checkForLSOF(); + + // Test cases for pipelines with a number of pipeline sequences + public static Object[][] builders() { return new Object[][]{ {List.of(new ProcessBuilder("cat"))}, {List.of(new ProcessBuilder("cat"), @@ -59,20 +69,43 @@ public class PipelineLeaksFD { }; } - @Test(dataProvider = "builders") + // True if lsof is runnable and collect pipe usage. + private static boolean lsofAvailable() { + return LSOF_AVAILABLE; + } + + // Check if lsof is available + private static boolean checkForLSOF() { + try { + lsofForAll(); + return true; + } catch (IOException ioe) { + System.err.println("Skipping: " + ioe); + return false; + } + } + + @EnabledIf("lsofAvailable") + @ParameterizedTest + @MethodSource("builders") void checkForLeaks(List builders) throws IOException { - Set pipesBefore = myPipes(); + List lsofLines = lsofForAll(); + Set pipesBefore = pipesFromLSOF(lsofLines, MY_PID); if (pipesBefore.size() < 3) { - System.out.println(pipesBefore); - Assert.fail("There should be at least 3 pipes before, (0, 1, 2)"); + // Dump all lsof output to aid debugging + System.out.println("Output from lsof"); + lsofLines.forEach(System.err::println); + System.err.println(pipesBefore); + fail("There should be at least 3 pipes before, (0, 1, 2)"); } + printPipes(pipesBefore, "Before start"); List processes = ProcessBuilder.startPipeline(builders); // Write something through the pipeline final String text = "xyz"; - try (Writer out = processes.get(0).outputWriter()) { + try (Writer out = processes.getFirst().outputWriter()) { out.write(text); } @@ -84,16 +117,17 @@ public class PipelineLeaksFD { try (BufferedReader inputStream = p.inputReader(); BufferedReader errorStream = p.errorReader()) { String outActual = inputStream.readLine(); - Assert.assertEquals(outActual, expectedOut, "stdout, process[ " + i + "]: " + p); + assertEquals(expectedOut, outActual, "stdout, process[ " + i + "]: " + p); String errActual = errorStream.readLine(); - Assert.assertEquals(errActual, expectedErr, "stderr, process[ " + i + "]: " + p); + assertEquals(expectedErr, errActual, "stderr, process[ " + i + "]: " + p); } } - processes.forEach(p -> waitForQuiet(p)); + processes.forEach(PipelineLeaksFD::waitForQuiet); - Set pipesAfter = myPipes(); + lsofLines = lsofForAll(); + Set pipesAfter = pipesFromLSOF(lsofLines, MY_PID); if (!pipesBefore.equals(pipesAfter)) { Set missing = new HashSet<>(pipesBefore); missing.removeAll(pipesAfter); @@ -101,46 +135,191 @@ public class PipelineLeaksFD { Set extra = new HashSet<>(pipesAfter); extra.removeAll(pipesBefore); printPipes(extra, "Extra pipes in pipesAfter"); - Assert.fail("More or fewer pipes than expected"); + // Dump all lsof output to aid debugging + System.out.println("\nOutput from lsof"); + lsofLines.forEach(System.err::println); + fail("More or fewer pipes than expected"); + } + } + + // Test redirectErrorStream, both true and false + public static Object[][] redirectCases() { + return new Object[][] { + {true}, + {false}, + }; + } + + // Test redirectErrorStream (true/false) has the right number of pipes in use + @EnabledIf("lsofAvailable") + @ParameterizedTest() + @MethodSource("redirectCases") + void checkRedirectErrorStream(boolean redirectError) throws IOException { + try (Process p = new ProcessBuilder("cat") + .redirectErrorStream(redirectError) + .start()) { + System.err.printf("Parent PID; %d, Child Pid: %d\n", MY_PID, p.pid()); + List lsofLines = lsofForAll(); + final Set pipes = pipesFromLSOF(lsofLines, p.pid()); + printPipes(pipes, "Parent and waiting child pipes"); + int uniquePipes = redirectError ? 8 : 9; + if (uniquePipes != pipes.size()) { + // Dump all lsof output to aid debugging + System.out.println("\nOutput from lsof"); + lsofLines.forEach(System.err::println); + } + assertEquals(uniquePipes, pipes.size(), + "wrong number of pipes for redirect: " + redirectError); + String expectedTypeName = redirectError + ? "java.lang.ProcessBuilder$NullInputStream" + : "java.lang.ProcessImpl$ProcessPipeInputStream"; + assertEquals(expectedTypeName, p.getErrorStream().getClass().getName(), + "errorStream type is incorrect"); + } catch (IOException ioe) { + fail("Process start", ioe); } } static void printPipes(Set pipes, String label) { - System.out.printf("%s: [%d]%n", label, pipes.size()); - pipes.forEach(r -> System.out.printf("%-20s: %s%n", r.fd(), r.link())); + System.err.printf("%s: [%d]%n", label, pipes.size()); + pipes.forEach(System.err::println); } static void waitForQuiet(Process p) { try { int st = p.waitFor(); if (st != 0) { - System.out.println("non-zero exit status: " + p); + System.err.println("non-zero exit status: " + p); } } catch (InterruptedException ie) { } } /** - * Collect a Set of pairs of /proc fd paths and the symbol links that are pipes. + * Collect a Set of file descriptors and identifying information. + * To identify the pipes in use the `lsof` command is invoked and output scrapped for + * fd's, pids, unique identities of the pipes (to match with parent). * @return A set of PipeRecords, possibly empty */ - static Set myPipes() { - Path path = Path.of("/proc/" + ProcessHandle.current().pid() + "/fd"); - Set pipes = new HashSet<>(); - File[] files = path.toFile().listFiles(f -> Files.isSymbolicLink(f.toPath())); - if (files != null) { - for (File file : files) { - try { - Path link = Files.readSymbolicLink(file.toPath()); - if (link.toString().startsWith("pipe:")) { - pipes.add(new PipeRecord(file.toPath(), link)); - } - } catch (IOException ioe) { - } - } - } - return pipes; + private static Set pipesForPid(long pid) throws IOException { + var lines = lsofForAll(); + return pipesFromLSOF(lines, pid); } - record PipeRecord(Path fd, Path link) { }; + /** + * Extract the PipeRecords from the `lsof` output for this process and a designated child process. + * + * @param lines lines of lsof output + * @param pid pid of child process of interest + * @return a Set of PipeRecords for parent and child + */ + private static LinkedHashSet pipesFromLSOF(List lines, long pid) { + return lines.stream() + .map(PipelineLeaksFD::pipeFromLSOF) + .filter(pr -> pr != null && + (pr.pid() == pid || pr.pid() == MY_PID)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** + * Collect the output of `lsof` for all files. + * Files are used for `lsof` input and output to avoid creating pipes. + * @return a List of lines output from `lsof`. + */ + private static List lsofForAll() throws IOException { + Path tmpDir = Path.of("."); + String tmpPrefix = "lsof-"; + Path lsofEmptyInput = Files.createTempFile(tmpDir, tmpPrefix, ".empty"); + Path lsofOutput = Files.createTempFile(tmpDir, tmpPrefix, ".tmp"); + try (Process p = new ProcessBuilder("lsof") + .redirectOutput(lsofOutput.toFile()) + .redirectInput(lsofEmptyInput.toFile()) // empty input + .redirectError(ProcessBuilder.Redirect.DISCARD) // ignored output + .start()) { + int status = p.waitFor(); + assertEquals(0, status, "Process 'lsof' failed"); + + return Files.readAllLines(lsofOutput); + } catch (InterruptedException ie) { + throw new IOException("Waiting for lsof exit interrupted", ie); + } + } + + // Return pipe records by parsing the appropriate platform specific `lsof` output. + static PipeRecord pipeFromLSOF(String s) { + return switch (OS_NAME) { + case "Linux" -> pipeFromLinuxLSOF(s); + case "Mac OS X" -> pipeFromMacLSOF(s); + default -> throw new RuntimeException("lsof not supported on platform: " + OS_NAME); + }; + } + + // Return Pipe from lsof output put, or null (on Mac OS X) + // lsof 55221 rriggs 0 PIPE 0xc76402237956a5cb 16384 ->0xfcb0c07ae447908c + // lsof 55221 rriggs 1 PIPE 0xb486e02f86da463e 16384 ->0xf94eacc85896b4e6 + static PipeRecord pipeFromMacLSOF(String s) { + String[] fields = s.split("\\s+"); + if ("PIPE".equals(fields[4])) { + final int pid = Integer.parseInt(fields[1]); + final String myKey = (fields.length > 5) ? fields[5] : ""; + final String otherKey = (fields.length > 7) ? fields[7].substring(2) : ""; + return PipeRecord.lookup(Integer.parseInt(fields[3]), myKey, otherKey, pid); + } + return null; + } + + // Return Pipe from lsof output put, or null (on Linux) + // java 7612 rriggs 14w FIFO 0,12 0t0 117662267 pipe + // java 7612 rriggs 15r FIFO 0,12 0t0 117662268 pipe + static PipeRecord pipeFromLinuxLSOF(String s) { + String[] fields = s.split("\\s+"); + if ("FIFO".equals(fields[4])) { + final int pid = Integer.parseInt(fields[1]); + final String key = (fields.length > 7) ? fields[7] : ""; + final int fdNum = Integer.parseInt(fields[3].substring(0, fields[3].length() - 1)); + return PipeRecord.lookup(fdNum, key, null, pid); + } + return null; + } + + // Identify a pipe by pid, fd, and a key (unique across processes) + // Mac OS X has separate keys for read and write sides, both are matched to the same "name" + record PipeRecord(long pid, int fd, KeyedString myKey) { + static PipeRecord lookup(int fd, String myKey, String otherKey, int pid) { + return new PipeRecord(pid, fd, KeyedString.getKey(myKey, otherKey)); + } + } + + // A unique name for a string with a count of uses + // Used to associate pipe between parent and child. + static class KeyedString { + private static final HashMap map = new HashMap<>(); + private static int nextInt = 1; + private final String key; + private final String name; + private int count; + KeyedString(String key, String name) { + this.key = key; + this.name = name; + this.count = 0; + } + + KeyedString(String s) { + String k = "p" + nextInt++; + this(s, k); + } + + static KeyedString getKey(String key, String otherKey) { + var k = map.computeIfAbsent(key, KeyedString::new); + k.count++; + if (otherKey != null) { + map.putIfAbsent(otherKey, k); + } + return k; + } + + public String toString() { + return name + "(" + count + ")"; + } + } } diff --git a/test/jdk/java/lang/ProcessBuilder/TEST.properties b/test/jdk/java/lang/ProcessBuilder/TEST.properties new file mode 100644 index 00000000000..c7e8a6850ca --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/TEST.properties @@ -0,0 +1 @@ +maxOutputSize=6000000 From ee0387be4c562c7f7ad5240f412d4d5363358855 Mon Sep 17 00:00:00 2001 From: Roger Calnan Date: Thu, 15 Jan 2026 17:08:49 +0000 Subject: [PATCH 022/126] 8375342: jdk/javadoc/doccheck/checks/jdkCheckHtml.java failed with duplicate anchors Reviewed-by: alanb, iris --- src/java.base/share/man/java.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 4343df663e6..956a6aa144b 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -3283,7 +3283,7 @@ Flags to Xlog]. The following provides quick reference to the `-Xlog` command and syntax for options: -[`-Xlog`]{#-Xlog} +`-Xlog` : Enables JVM logging on an `info` level. `-Xlog:help` From 34705a77f9a90da5ab2a440c11d79aef7bb3ba54 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 15 Jan 2026 17:38:46 +0000 Subject: [PATCH 023/126] 8375231: Refactor util/ServiceLoader tests to use JUnit 8375232: Refactor util/StringJoiner tests to use JUnit 8375233: Refactor util/Vector tests to use JUnit Reviewed-by: naoto, alanb --- .../util/ServiceLoader/BadProvidersTest.java | 79 +++----- .../java/util/ServiceLoader/CachingTest.java | 18 +- .../java/util/ServiceLoader/ModulesTest.java | 42 ++-- .../ServiceLoader/NoInterferenceTest.java | 14 +- .../java/util/ServiceLoader/ReloadTest.java | 31 +-- .../java/util/ServiceLoader/TwoIterators.java | 19 +- .../basic/ServiceLoaderBasicTest.java | 40 ++-- .../jdk/java/util/StringJoiner/MergeTest.java | 55 +++--- .../StringJoinerOomUtf16Test.java | 19 +- .../util/StringJoiner/StringJoinerTest.java | 182 ++++++++++-------- .../jdk/java/util/Vector/ArrayManagement.java | 81 ++++---- 11 files changed, 304 insertions(+), 276 deletions(-) diff --git a/test/jdk/java/util/ServiceLoader/BadProvidersTest.java b/test/jdk/java/util/ServiceLoader/BadProvidersTest.java index 8560126eb00..0a734a2b214 100644 --- a/test/jdk/java/util/ServiceLoader/BadProvidersTest.java +++ b/test/jdk/java/util/ServiceLoader/BadProvidersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, 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,7 +26,7 @@ * @library /test/lib * @modules jdk.compiler * @build jdk.test.lib.compiler.CompilerUtils - * @run testng/othervm BadProvidersTest + * @run junit/othervm BadProvidersTest * @summary Basic test of ServiceLoader with bad provider and bad provider * factories deployed on the module path */ @@ -50,8 +50,6 @@ import java.util.stream.Collectors; import jdk.test.lib.compiler.CompilerUtils; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; import static java.lang.classfile.ClassFile.ACC_PUBLIC; import static java.lang.classfile.ClassFile.ACC_STATIC; @@ -59,13 +57,16 @@ import static java.lang.constant.ConstantDescs.CD_Object; import static java.lang.constant.ConstantDescs.INIT_NAME; import static java.lang.constant.ConstantDescs.MTD_void; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; /** * Basic test of `provides S with PF` and `provides S with P` where the provider * factory or provider */ - public class BadProvidersTest { private static final String TEST_SRC = System.getProperty("test.src"); @@ -137,22 +138,10 @@ public class BadProvidersTest { assertTrue(p != null); } - - @DataProvider(name = "badfactories") - public Object[][] createBadFactories() { - return new Object[][] { - { "classnotpublic", null }, - { "methodnotpublic", null }, - { "badreturntype", null }, - { "returnsnull", null }, - { "throwsexception", null }, - }; - } - - - @Test(dataProvider = "badfactories", - expectedExceptions = ServiceConfigurationError.class) - public void testBadFactory(String testName, String ignore) throws Exception { + @ParameterizedTest + @ValueSource(strings = { "classnotpublic", "methodnotpublic", "badreturntype", + "returnsnull", "throwsexception" }) + public void testBadFactory(String testName) throws Exception { Path mods = compileTest(TEST1_MODULE); // compile the bad factory @@ -164,28 +153,18 @@ public class BadProvidersTest { // copy the compiled class into the module Path classFile = Paths.get("p", "ProviderFactory.class"); Files.copy(output.resolve(classFile), - mods.resolve(TEST1_MODULE).resolve(classFile), - StandardCopyOption.REPLACE_EXISTING); + mods.resolve(TEST1_MODULE).resolve(classFile), + StandardCopyOption.REPLACE_EXISTING); - // load providers and instantiate each one - loadProviders(mods, TEST1_MODULE).forEach(Provider::get); + Assertions.assertThrows(ServiceConfigurationError.class, + // load providers and instantiate each one + () -> loadProviders(mods, TEST1_MODULE).forEach(Provider::get) + ); } - - @DataProvider(name = "badproviders") - public Object[][] createBadProviders() { - return new Object[][] { - { "notpublic", null }, - { "ctornotpublic", null }, - { "notasubtype", null }, - { "throwsexception", null } - }; - } - - - @Test(dataProvider = "badproviders", - expectedExceptions = ServiceConfigurationError.class) - public void testBadProvider(String testName, String ignore) throws Exception { + @ParameterizedTest + @ValueSource(strings = { "notpublic", "ctornotpublic", "notasubtype", "throwsexception" }) + public void testBadProvider(String testName) throws Exception { Path mods = compileTest(TEST2_MODULE); // compile the bad provider @@ -197,11 +176,13 @@ public class BadProvidersTest { // copy the compiled class into the module Path classFile = Paths.get("p", "Provider.class"); Files.copy(output.resolve(classFile), - mods.resolve(TEST2_MODULE).resolve(classFile), - StandardCopyOption.REPLACE_EXISTING); + mods.resolve(TEST2_MODULE).resolve(classFile), + StandardCopyOption.REPLACE_EXISTING); - // load providers and instantiate each one - loadProviders(mods, TEST2_MODULE).forEach(Provider::get); + Assertions.assertThrows(ServiceConfigurationError.class, + // load providers and instantiate each one + () -> loadProviders(mods, TEST2_MODULE).forEach(Provider::get) + ); } @@ -209,7 +190,7 @@ public class BadProvidersTest { * Test a service provider that defines more than one no-args * public static "provider" method. */ - @Test(expectedExceptions = ServiceConfigurationError.class) + @Test public void testWithTwoFactoryMethods() throws Exception { Path mods = compileTest(TEST1_MODULE); @@ -244,8 +225,10 @@ public class BadProvidersTest { .resolve("ProviderFactory.class"); Files.write(classFile, bytes); - // load providers and instantiate each one - loadProviders(mods, TEST1_MODULE).forEach(Provider::get); + Assertions.assertThrows(ServiceConfigurationError.class, + // load providers and instantiate each one + () -> loadProviders(mods, TEST1_MODULE).forEach(Provider::get) + ); } } diff --git a/test/jdk/java/util/ServiceLoader/CachingTest.java b/test/jdk/java/util/ServiceLoader/CachingTest.java index 264c646b5af..138b6b6862f 100644 --- a/test/jdk/java/util/ServiceLoader/CachingTest.java +++ b/test/jdk/java/util/ServiceLoader/CachingTest.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,7 +24,7 @@ /** * @test * @summary Test ServiceLoader caches - * @run testng CachingTest + * @run junit CachingTest */ import java.nio.file.Files; @@ -38,9 +38,9 @@ import java.util.ServiceLoader.Provider; import java.util.Spliterator; import java.util.stream.Collectors; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class CachingTest { @@ -52,17 +52,17 @@ public class CachingTest { public static class S2 implements S { } - private ClassLoader testClassLoader; + private static ClassLoader testClassLoader; // creates the services configuration file and sets the ClassLoader - @BeforeClass - void setup() throws Exception { + @BeforeAll + static void setup() throws Exception { String classes = System.getProperty("test.classes"); Path dir = Paths.get(classes, "META-INF", "services"); Files.createDirectories(dir); Path config = dir.resolve(S.class.getName()); Files.write(config, List.of(S1.class.getName(), S2.class.getName())); - this.testClassLoader = CachingTest.class.getClassLoader(); + testClassLoader = CachingTest.class.getClassLoader(); } private void checkLists(List list1, List list2) { diff --git a/test/jdk/java/util/ServiceLoader/ModulesTest.java b/test/jdk/java/util/ServiceLoader/ModulesTest.java index 002b8458865..d265ce18f60 100644 --- a/test/jdk/java/util/ServiceLoader/ModulesTest.java +++ b/test/jdk/java/util/ServiceLoader/ModulesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, 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 @@ -29,7 +29,7 @@ * @build jdk.test.lib.util.JarUtils * @compile classpath/pearscript/org/pear/PearScriptEngineFactory.java * classpath/pearscript/org/pear/PearScript.java - * @run testng/othervm ModulesTest + * @run junit/othervm ModulesTest * @summary Basic test for ServiceLoader with a provider deployed as a module. */ @@ -55,9 +55,10 @@ import javax.script.ScriptEngineFactory; import jdk.test.lib.util.JarUtils; -import org.testng.annotations.Test; -import org.testng.annotations.BeforeTest; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** * Basic test for ServiceLoader. The test make use of two service providers: @@ -67,12 +68,11 @@ import static org.testng.Assert.*; * 2. PearScriptEngine - a ScriptEngineFactory deployed on the class path * with a service configuration file. */ - public class ModulesTest { // Copy the services configuration file for "pearscript" into place. - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { Path src = Paths.get(System.getProperty("test.src")); Path classes = Paths.get(System.getProperty("test.classes")); String st = ScriptEngineFactory.class.getName(); @@ -428,30 +428,36 @@ public class ModulesTest { // -- nulls -- - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull1() { - ServiceLoader.load(null); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.load(null)); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull2() { - ServiceLoader.load((Class) null, ClassLoader.getSystemClassLoader()); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.load((Class) null, ClassLoader.getSystemClassLoader())); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull3() { class S { } - ServiceLoader.load((ModuleLayer) null, S.class); + Assertions.assertThrows(NullPointerException.class, () -> { + ServiceLoader.load((ModuleLayer) null, S.class); + }); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull4() { - ServiceLoader.load(ModuleLayer.empty(), null); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.load(ModuleLayer.empty(), null)); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull5() { - ServiceLoader.loadInstalled(null); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.loadInstalled(null)); } /** diff --git a/test/jdk/java/util/ServiceLoader/NoInterferenceTest.java b/test/jdk/java/util/ServiceLoader/NoInterferenceTest.java index ff83b836e20..d91bc9c759f 100644 --- a/test/jdk/java/util/ServiceLoader/NoInterferenceTest.java +++ b/test/jdk/java/util/ServiceLoader/NoInterferenceTest.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 @@ -26,7 +26,7 @@ * @library /test/lib * @modules jdk.compiler * @build jdk.test.lib.compiler.CompilerUtils - * @run testng NoInterferenceTest + * @run junit NoInterferenceTest * @summary Basic test of ServiceLoader that ensures there is no interference * when there are two service interfaces of the same name in a layer * or overridden in a child layer. @@ -46,9 +46,9 @@ import java.util.Set; import jdk.test.lib.compiler.CompilerUtils; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class NoInterferenceTest { @@ -57,8 +57,8 @@ public class NoInterferenceTest { private static final Path MODS_DIR = Paths.get("mods"); private static final List MODULES = Arrays.asList("s1", "p1", "s2", "p2"); - @BeforeTest - void compile() throws Exception { + @BeforeAll + static void compile() throws Exception { Files.createDirectory(MODS_DIR); for (String name : MODULES) { Path src = SRC_DIR.resolve(name); diff --git a/test/jdk/java/util/ServiceLoader/ReloadTest.java b/test/jdk/java/util/ServiceLoader/ReloadTest.java index d48b5c29f4d..be2c0d68773 100644 --- a/test/jdk/java/util/ServiceLoader/ReloadTest.java +++ b/test/jdk/java/util/ServiceLoader/ReloadTest.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 @@ -26,7 +26,7 @@ * @modules java.scripting * @library modules classpath/pearscript * @build ReloadTest org.pear.PearScript org.pear.PearScriptEngineFactory bananascript/* - * @run testng/othervm ReloadTest + * @run junit/othervm ReloadTest * @summary Basic test of ServiceLoader.reload */ @@ -40,12 +40,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.ServiceLoader.*; import javax.script.ScriptEngineFactory; -import org.testng.annotations.Test; -import static org.testng.Assert.*; -@Test +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + public class ReloadTest { + @Test public void testReload() { ServiceLoader sl = load(ScriptEngineFactory.class); List names1 = sl.stream() @@ -58,41 +60,42 @@ public class ReloadTest { .map(Provider::get) .map(ScriptEngineFactory::getEngineName) .collect(Collectors.toList()); - assertEquals(names1, names2); + assertEquals(names2, names1); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testIteratorHasNext() { ServiceLoader sl = load(ScriptEngineFactory.class); Iterator iterator = sl.iterator(); sl.reload(); - iterator.hasNext(); + Assertions.assertThrows(ConcurrentModificationException.class, iterator::hasNext); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testIteratorNext() { ServiceLoader sl = load(ScriptEngineFactory.class); Iterator iterator = sl.iterator(); assertTrue(iterator.hasNext()); sl.reload(); - iterator.next(); + Assertions.assertThrows(ConcurrentModificationException.class, iterator::next); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testStreamFindAny() { ServiceLoader sl = load(ScriptEngineFactory.class); Stream> stream = sl.stream(); sl.reload(); - stream.findAny(); + Assertions.assertThrows(ConcurrentModificationException.class, stream::findAny); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testSpliteratorTryAdvance() { ServiceLoader sl = load(ScriptEngineFactory.class); Stream> stream = sl.stream(); Spliterator> spliterator = stream.spliterator(); sl.reload(); - spliterator.tryAdvance(System.out::println); + Assertions.assertThrows(ConcurrentModificationException.class, + () -> spliterator.tryAdvance(System.out::println)); } } diff --git a/test/jdk/java/util/ServiceLoader/TwoIterators.java b/test/jdk/java/util/ServiceLoader/TwoIterators.java index ddb9cc09508..b86cc45f17f 100644 --- a/test/jdk/java/util/ServiceLoader/TwoIterators.java +++ b/test/jdk/java/util/ServiceLoader/TwoIterators.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -25,7 +25,7 @@ * @test * @summary Test ServiceLoader with two iterators, interleaving their use * to test that they don't interfere with each other - * @run testng TwoIterators + * @run junit TwoIterators */ import java.nio.file.Files; @@ -35,9 +35,9 @@ import java.util.Arrays; import java.util.Iterator; import java.util.ServiceLoader; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class TwoIterators { @@ -48,18 +48,17 @@ public class TwoIterators { public static class S1 implements S { } public static class S2 implements S { } - private ClassLoader testClassLoader; + private static ClassLoader testClassLoader; // creates the services configuration file and sets the ClassLoader - @BeforeClass - void setup() throws Exception { + @BeforeAll + static void setup() throws Exception { String classes = System.getProperty("test.classes"); Path dir = Paths.get(classes, "META-INF", "services"); Files.createDirectories(dir); Path config = dir.resolve(S.class.getName()); Files.write(config, Arrays.asList(S1.class.getName(), S2.class.getName())); - - this.testClassLoader = TwoIterators.class.getClassLoader(); + testClassLoader = TwoIterators.class.getClassLoader(); } @Test diff --git a/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.java b/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.java index 91aba564cd1..4a9bc808851 100644 --- a/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.java +++ b/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.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,7 +29,7 @@ * @build jdk.test.lib.process.* * jdk.test.lib.util.JarUtils * Basic Load FooService FooProvider1 FooProvider2 FooProvider3 BarProvider - * @run testng ServiceLoaderBasicTest + * @run junit ServiceLoaderBasicTest */ @@ -44,14 +44,16 @@ import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.Arrays.asList; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + public class ServiceLoaderBasicTest { private static final String METAINFO = "META-INF/services/FooService"; @@ -79,8 +81,8 @@ public class ServiceLoaderBasicTest { private static final String XTESTXMETA_CP = XTEST_CP + XMETA; private static final String XTESTXMETAP2_CP = XTESTXMETA_CP + P2; - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { createProviderConfig(XTEST_CONFIG, "FooProvider1"); createProviderConfig(XMETA_CONFIG, "FooProvider42"); createJar(P2JAR, "FooProvider2", List.of("FooProvider2")); @@ -88,8 +90,7 @@ public class ServiceLoaderBasicTest { Files.copy(P2JAR, P2DUPJAR, REPLACE_EXISTING); } - @DataProvider - public Object[][] testCases() { + private static Object[][] testCases() { return new Object[][]{ // CLI options, Test, Runtime arguments // Success cases @@ -110,23 +111,14 @@ public class ServiceLoaderBasicTest { }; } - @DataProvider - public Object[][] negativeTestCases() { - return new Object[][]{ - {"blah blah"}, - {"9234"}, - {"X!"}, - {"BarProvider"}, - {"FooProvider42"} - }; - } - - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testProvider(List args) throws Throwable { runJava(args); } - @Test(dataProvider = "negativeTestCases") + @ParameterizedTest // negative test cases + @ValueSource(strings = { "blah blah", "9234", "X!", "BarProvider", "FooProvider42" }) public void testBadProvider(String providerName) throws Throwable { Files.write(XMETA_CONFIG, providerName.getBytes()); runJava(List.of("-cp", XMETA_CP, "Load", "fail")); @@ -144,12 +136,12 @@ public class ServiceLoaderBasicTest { .shouldHaveExitValue(0); } - private void createProviderConfig(Path config, String providerName) throws Exception { + private static void createProviderConfig(Path config, String providerName) throws Exception { Files.createDirectories(config.getParent()); Files.write(config, providerName.getBytes(), CREATE); } - private void createJar(Path jar, String provider, List files) throws Exception { + private static void createJar(Path jar, String provider, List files) throws Exception { Path xdir = Path.of(provider); createProviderConfig(xdir.resolve(METAINFO), provider); diff --git a/test/jdk/java/util/StringJoiner/MergeTest.java b/test/jdk/java/util/StringJoiner/MergeTest.java index f47ed3ee094..574124b9bdd 100644 --- a/test/jdk/java/util/StringJoiner/MergeTest.java +++ b/test/jdk/java/util/StringJoiner/MergeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, 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,17 +27,18 @@ * @summary test StringJoiner::merge * @modules java.base/jdk.internal.util * @requires vm.bits == "64" & os.maxMemory > 4G - * @run testng/othervm -Xmx4g -XX:+CompactStrings MergeTest + * @run junit/othervm -Xmx4g -XX:+CompactStrings MergeTest */ import java.util.StringJoiner; import java.util.stream.Stream; -import org.testng.annotations.Test; import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; -@Test +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; + public class MergeTest { private static final String[] PREFIXES = {"", "{", "@#$%"}; private static final String[] SUFFIXES = {"", "}", "*&%$"}; @@ -69,12 +70,13 @@ public class MergeTest { return builder.build(); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void testNull() { StringJoiner sj = new StringJoiner(",", "{", "}"); - sj.merge(null); + Assertions.assertThrows(NullPointerException.class, () -> sj.merge(null)); } + @Test public void testSimple() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -83,10 +85,11 @@ public class MergeTest { Stream.of("d", "e", "f").forEachOrdered(other::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c,d,e,f" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c,d,e,f" + fixes.suf0, sj.toString()); }); } + @Test public void testEmptyOther() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -94,14 +97,15 @@ public class MergeTest { Stream.of("a", "b", "c").forEachOrdered(sj::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c" + fixes.suf0, sj.toString()); other.setEmptyValue("EMPTY"); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c" + fixes.suf0, sj.toString()); }); } + @Test public void testEmptyThis() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -109,34 +113,36 @@ public class MergeTest { Stream.of("d", "e", "f").forEachOrdered(other::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "d:e:f" + fixes.suf0); + assertEquals(fixes.pre0 + "d:e:f" + fixes.suf0, sj.toString()); sj = new StringJoiner(",", fixes.pre0, fixes.suf0).setEmptyValue("EMPTY"); - assertEquals(sj.toString(), "EMPTY"); + assertEquals("EMPTY", sj.toString()); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "d:e:f" + fixes.suf0); + assertEquals(fixes.pre0 + "d:e:f" + fixes.suf0, sj.toString()); }); } + @Test public void testEmptyBoth() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); StringJoiner other = new StringJoiner(":", fixes.pre1, fixes.suf1); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + fixes.suf0); + assertEquals(fixes.pre0 + fixes.suf0, sj.toString()); other.setEmptyValue("NOTHING"); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + fixes.suf0); + assertEquals(fixes.pre0 + fixes.suf0, sj.toString()); sj = new StringJoiner(",", fixes.pre0, fixes.suf0).setEmptyValue("EMPTY"); - assertEquals(sj.toString(), "EMPTY"); + assertEquals("EMPTY", sj.toString()); sj.merge(other); - assertEquals(sj.toString(), "EMPTY"); + assertEquals("EMPTY", sj.toString()); }); } + @Test public void testCascadeEmpty() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -144,13 +150,14 @@ public class MergeTest { StringJoiner o2 = new StringJoiner(",", "<", ">").setEmptyValue("Empty2"); o1.merge(o2); - assertEquals(o1.toString(), "Empty1"); + assertEquals("Empty1", o1.toString()); sj.merge(o1); - assertEquals(sj.toString(), fixes.pre0 + fixes.suf0); + assertEquals(fixes.pre0 + fixes.suf0, sj.toString()); }); } + @Test public void testDelimiter() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -159,18 +166,20 @@ public class MergeTest { Stream.of("d", "e", "f").forEachOrdered(other::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c,d:e:f" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c,d:e:f" + fixes.suf0, sj.toString()); }); } + @Test public void testMergeSelf() { fixesStream().forEach(fixes -> { final StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0).add("a").add("b"); - assertEquals(sj.merge(sj).toString(), fixes.pre0 + "a,b,a,b" + fixes.suf0); - assertEquals(sj.merge(sj).toString(), fixes.pre0 + "a,b,a,b,a,b,a,b" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,a,b" + fixes.suf0, sj.merge(sj).toString()); + assertEquals(fixes.pre0 + "a,b,a,b,a,b,a,b" + fixes.suf0, sj.merge(sj).toString()); }); } + @Test public void OOM() { String maxString = "*".repeat(SOFT_MAX_ARRAY_LENGTH); diff --git a/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.java b/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.java index b45e66b4d8b..7fde8b36ffa 100644 --- a/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.java +++ b/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.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 @@ -26,18 +26,23 @@ * @summary tests StringJoiner OOME when joining sub-max-length Strings * @modules java.base/jdk.internal.util * @requires vm.bits == "64" & os.maxMemory > 4G - * @run testng/othervm -Xmx4g -XX:+CompactStrings StringJoinerOomUtf16Test + * @run junit/othervm -Xmx4g -XX:+CompactStrings StringJoinerOomUtf16Test */ -import org.testng.annotations.Test; import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH; -import static org.testng.Assert.fail; + +import static org.junit.jupiter.api.Assertions.fail; import java.util.StringJoiner; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Test(groups = {"unit","string","util","libs"}) +@Tag("unit") +@Tag("string") +@Tag("util") +@Tag("libs") public class StringJoinerOomUtf16Test { // the sum of lengths of the following two strings is way less than @@ -48,6 +53,7 @@ public class StringJoinerOomUtf16Test { private static final String OVERFLOW_UTF16_STRING = "\u017D".repeat(((Integer.MAX_VALUE - SOFT_MAX_ARRAY_LENGTH) >> 1) + 1); + @Test public void OOM1() { try { new StringJoiner("") @@ -60,6 +66,7 @@ public class StringJoinerOomUtf16Test { } } + @Test public void OOM2() { try { new StringJoiner(HALF_MAX_LATIN1_STRING) @@ -72,6 +79,7 @@ public class StringJoinerOomUtf16Test { } } + @Test public void OOM3() { try { new StringJoiner(OVERFLOW_UTF16_STRING) @@ -84,6 +92,7 @@ public class StringJoinerOomUtf16Test { } } + @Test public void OOM4() { try { new StringJoiner("", HALF_MAX_LATIN1_STRING, OVERFLOW_UTF16_STRING) diff --git a/test/jdk/java/util/StringJoiner/StringJoinerTest.java b/test/jdk/java/util/StringJoiner/StringJoinerTest.java index 25948fb8e55..aa25623dbc3 100644 --- a/test/jdk/java/util/StringJoiner/StringJoinerTest.java +++ b/test/jdk/java/util/StringJoiner/StringJoinerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, 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 @@ -26,18 +26,25 @@ * @summary tests StringJoinerTest * @modules java.base/jdk.internal.util * @requires vm.bits == "64" & os.maxMemory > 4G - * @run testng/othervm -Xmx4g -XX:+CompactStrings StringJoinerTest + * @run junit/othervm -Xmx4g -XX:+CompactStrings StringJoinerTest * @author Jim Gish */ + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + import java.util.ArrayList; import java.util.StringJoiner; -import org.testng.annotations.Test; + import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; - -@Test(groups = {"unit","string","util","libs"}) +@Tag("unit") +@Tag("string") +@Tag("util") +@Tag("libs") public class StringJoinerTest { private static final String EMPTY = "EMPTY"; @@ -51,6 +58,7 @@ public class StringJoinerTest { private static final String DASH = "-"; private static final String MAX_STRING = "*".repeat(SOFT_MAX_ARRAY_LENGTH); + @Test public void addAddAll() { StringJoiner sj = new StringJoiner(DASH, "{", "}"); sj.add(ONE); @@ -61,7 +69,7 @@ public class StringJoinerTest { nextOne.stream().forEachOrdered(sj::add); String expected = "{"+ONE+DASH+TWO+DASH+THREE+"}"; - assertEquals(sj.toString(), expected); + assertEquals(expected, sj.toString()); } void addAlladd() { @@ -75,10 +83,11 @@ public class StringJoinerTest { sj.add(THREE); String expected = "{"+ONE+DASH+TWO+DASH+THREE+"}"; - assertEquals(sj.toString(), expected); + assertEquals(expected, sj.toString()); } // The following tests do two successive adds of different types + @Test public void addAlladdAll() { StringJoiner sj = new StringJoiner(DASH, "{", "}"); ArrayList firstOne = new ArrayList<>(); @@ -93,9 +102,10 @@ public class StringJoinerTest { nextOne.stream().forEachOrdered(sj::add); String expected = "{"+ONE+DASH+TWO+DASH+THREE+DASH+FOUR+DASH+FIVE+"}"; - assertEquals(sj.toString(), expected); + assertEquals(expected, sj.toString()); } + @Test public void addCharSequence() { StringJoiner sj = new StringJoiner(","); CharSequence cs_one = ONE; @@ -104,13 +114,13 @@ public class StringJoinerTest { sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(DASH, "{", "}"); sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); StringBuilder builder = new StringBuilder(ONE); StringBuffer buffer = new StringBuffer(THREE); @@ -118,13 +128,14 @@ public class StringJoinerTest { sj.add(builder).add(buffer); builder.append(TWO); buffer.append(FOUR); - assertEquals(sj.toString(), "{ " + ONE + ", " + THREE + " }", + assertEquals("{ " + ONE + ", " + THREE + " }", sj.toString(), "CharSequence is copied when add"); sj.add(builder); - assertEquals(sj.toString(), "{ " + ONE + ", " + THREE + ", " + ONE + - TWO + " }"); + assertEquals("{ " + ONE + ", " + THREE + ", " + ONE + + TWO + " }", sj.toString()); } + @Test public void addCharSequenceWithEmptyValue() { StringJoiner sj = new StringJoiner(",").setEmptyValue(EMPTY); CharSequence cs_one = ONE; @@ -133,189 +144,200 @@ public class StringJoinerTest { sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(DASH, "{", "}"); sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); sj = new StringJoiner(DASH, "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); sj = new StringJoiner("=", "{", "}").setEmptyValue(""); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(DASH, "{", "}").setEmptyValue(EMPTY); - assertEquals(sj.toString(), EMPTY); + assertEquals(EMPTY, sj.toString()); sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); } + @Test public void addString() { StringJoiner sj = new StringJoiner(DASH); sj.add(ONE); - assertEquals(sj.toString(), ONE); + assertEquals(ONE, sj.toString()); sj = new StringJoiner(DASH, "{", "}"); sj.add(ONE); - assertEquals(sj.toString(), "{" + ONE + "}"); + assertEquals("{" + ONE + "}", sj.toString()); sj.add(TWO); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); } + @Test public void lengthWithCustomEmptyValue() { StringJoiner sj = new StringJoiner(DASH, "<", ">").setEmptyValue(EMPTY); - assertEquals(sj.length(), EMPTY.length()); + assertEquals(EMPTY.length(), sj.length()); sj.add(""); - assertEquals(sj.length(), "<>".length()); + assertEquals("<>".length(), sj.length()); sj.add(""); - assertEquals(sj.length(), "<->".length()); + assertEquals("<->".length(), sj.length()); sj.add(ONE); - assertEquals(sj.length(), 4 + ONE_LEN); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(4 + ONE_LEN, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj.add(TWO); - assertEquals(sj.length(), 5 + ONE_LEN + TWO_LEN); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(5 + ONE_LEN + TWO_LEN, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj = new StringJoiner("||", "<", "-->"); - assertEquals(sj.length(), 4); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(4, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj.add("abcdef"); - assertEquals(sj.length(), 10); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(10, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj.add("xyz"); - assertEquals(sj.length(), 15); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(15, sj.length()); + assertEquals(sj.length(), sj.toString().length()); } + @Test public void noAddAndEmptyValue() { StringJoiner sj = new StringJoiner(DASH, "", "").setEmptyValue(EMPTY); - assertEquals(sj.toString(), EMPTY); + assertEquals(EMPTY, sj.toString()); sj = new StringJoiner(DASH, "<..", ""); - assertEquals(sj.toString(), "<.."); + assertEquals("<..", sj.toString()); sj = new StringJoiner(DASH, "<..", ""); - assertEquals(sj.toString(), "<.."); + assertEquals("<..", sj.toString()); sj = new StringJoiner(DASH, "", "==>"); - assertEquals(sj.toString(), "==>"); + assertEquals("==>", sj.toString()); sj = new StringJoiner(DASH, "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setEmptyValueNull() { - new StringJoiner(DASH, "{", "}").setEmptyValue(null); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(DASH, "{", "}").setEmptyValue(null)); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setDelimiterNull() { - new StringJoiner(null); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(null)); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setPrefixNull() { - new StringJoiner(DASH, null, "}"); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(DASH, null, "}")); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setSuffixNull() { - new StringJoiner(DASH, "{", null); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(DASH, "{", null)); } + @Test public void stringFromtoString() { StringJoiner sj = new StringJoiner(", "); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(",", "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); sj = new StringJoiner(","); sj.add(ONE); - assertEquals(sj.toString(), ONE); + assertEquals(ONE, sj.toString()); sj.add(TWO); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(",", "{--", "--}"); sj.add(ONE); sj.add(TWO); - assertEquals(sj.toString(), "{--" + ONE + "," + TWO + "--}"); + assertEquals("{--" + ONE + "," + TWO + "--}", sj.toString()); } + @Test public void stringFromtoStringWithEmptyValue() { StringJoiner sj = new StringJoiner(" ", "", ""); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(", "); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(",", "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); sj = new StringJoiner(",", "{", "}").setEmptyValue(""); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(","); sj.add(ONE); - assertEquals(sj.toString(), ONE); + assertEquals(ONE, sj.toString()); sj.add(TWO); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(",", "{--", "--}"); sj.add(ONE); - assertEquals(sj.toString(), "{--" + ONE + "--}" ); + assertEquals("{--" + ONE + "--}", sj.toString() ); sj.add(TWO); - assertEquals(sj.toString(), "{--" + ONE + "," + TWO + "--}"); + assertEquals("{--" + ONE + "," + TWO + "--}", sj.toString()); } + @Test public void toStringWithCustomEmptyValue() { StringJoiner sj = new StringJoiner(DASH, "<", ">").setEmptyValue(EMPTY); - assertEquals(sj.toString(), EMPTY); + assertEquals(EMPTY, sj.toString()); sj.add(""); - assertEquals(sj.toString(), "<>"); + assertEquals("<>", sj.toString()); sj.add(""); - assertEquals(sj.toString(), "<->"); + assertEquals("<->", sj.toString()); } private void testCombos(String infix, String prefix, String suffix) { StringJoiner sj = new StringJoiner(infix, prefix, suffix); - assertEquals(sj.toString(), prefix + suffix); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(prefix + suffix, sj.toString()); + assertEquals(sj.length(), sj.toString().length()); // EmptyValue sj = new StringJoiner(infix, prefix, suffix).setEmptyValue(""); - assertEquals(sj.toString(), ""); - assertEquals(sj.toString().length(), sj.length()); + assertEquals("", sj.toString()); + assertEquals(sj.length(), sj.toString().length()); // empty in front sj.add(""); - assertEquals(sj.toString(), prefix + suffix); + assertEquals(prefix + suffix, sj.toString()); // empty in middle sj.add(""); - assertEquals(sj.toString(), prefix + infix + suffix); + assertEquals(prefix + infix + suffix, sj.toString()); sj.add("1"); - assertEquals(sj.toString(), prefix + infix + infix + "1" + suffix); + assertEquals(prefix + infix + infix + "1" + suffix, sj.toString()); // empty at end sj.add(""); - assertEquals(sj.toString(), prefix + infix + infix + "1" + infix + suffix); + assertEquals(prefix + infix + infix + "1" + infix + suffix, sj.toString()); sj = new StringJoiner(infix, prefix, suffix).setEmptyValue(""); sj.add("1"); - assertEquals(sj.toString(), prefix + "1" + suffix); + assertEquals(prefix + "1" + suffix, sj.toString()); sj.add("2"); - assertEquals(sj.toString(), prefix + "1" + infix + "2" + suffix); + assertEquals(prefix + "1" + infix + "2" + suffix, sj.toString()); sj.add(""); - assertEquals(sj.toString(), prefix + "1" + infix + "2" + infix + suffix); + assertEquals(prefix + "1" + infix + "2" + infix + suffix, sj.toString()); sj.add("3"); - assertEquals(sj.toString(), prefix + "1" + infix + "2" + infix + infix + "3" + suffix); + assertEquals(prefix + "1" + infix + "2" + infix + infix + "3" + suffix, sj.toString()); } + @Test public void testDelimiterCombinations() { testCombos("", "", ""); testCombos("", "<", ""); @@ -327,6 +349,7 @@ public class StringJoinerTest { testCombos(",", "<", ">"); } + @Test public void OOM1() { try { new StringJoiner(MAX_STRING, MAX_STRING, MAX_STRING).toString(); @@ -336,6 +359,7 @@ public class StringJoinerTest { } } + @Test public void OOM2() { try { new StringJoiner(MAX_STRING, MAX_STRING, "").toString(); @@ -345,6 +369,7 @@ public class StringJoinerTest { } } + @Test public void OOM3() { try { new StringJoiner(MAX_STRING, "", MAX_STRING).toString(); @@ -354,6 +379,7 @@ public class StringJoinerTest { } } + @Test public void OOM4() { try { new StringJoiner("", MAX_STRING, MAX_STRING).toString(); diff --git a/test/jdk/java/util/Vector/ArrayManagement.java b/test/jdk/java/util/Vector/ArrayManagement.java index ce4e695487c..c1fa6d1c9bc 100644 --- a/test/jdk/java/util/Vector/ArrayManagement.java +++ b/test/jdk/java/util/Vector/ArrayManagement.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2016 Google, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,7 +26,7 @@ * @test * @bug 8148174 * @summary brittle white box test of internal array management - * @run testng ArrayManagement + * @run junit ArrayManagement */ import java.lang.reflect.Field; @@ -35,8 +36,8 @@ import java.util.Collections; import java.util.List; import java.util.SplittableRandom; -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 ArrayManagement { @@ -61,9 +62,9 @@ public class ArrayManagement { super.ensureCapacity(minCapacity); assertTrue(capacity() >= minCapacity); if (minCapacity <= oldCapacity) - assertEquals(capacity(), oldCapacity); + assertEquals(oldCapacity, capacity()); if (minCapacity > 0) - assertEquals(modCount(), oldModCount + 1); + assertEquals(oldModCount + 1, modCount()); } } @@ -89,117 +90,117 @@ public class ArrayManagement { case 3: assertTrue(list.addAll(size, singletonList())); break; default: throw new AssertionError(); } - assertEquals(list.modCount(), modCount + 1); - assertEquals(list.size(), size + 1); + assertEquals(modCount + 1, list.modCount()); + assertEquals(size + 1, list.size()); } @Test public void defaultCapacity() { PublicVector list = new PublicVector<>(); - assertEquals(new PublicVector().capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, new PublicVector().capacity()); for (int i = 0; i < DEFAULT_CAPACITY; i++) { addOneElement(list); - assertEquals(list.capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, list.capacity()); } addOneElement(list); - assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + assertEquals(newCapacity(DEFAULT_CAPACITY), list.capacity()); } @Test public void defaultCapacityEnsureCapacity() { PublicVector list = new PublicVector<>(); for (int i = 0; i <= DEFAULT_CAPACITY; i++) { list.ensureCapacity(i); // no-op! - assertEquals(list.capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, list.capacity()); } for (int i = 0; i < DEFAULT_CAPACITY; i++) { addOneElement(list); - assertEquals(list.capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, list.capacity()); } addOneElement(list); - assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + assertEquals(newCapacity(DEFAULT_CAPACITY), list.capacity()); { int capacity = list.capacity(); list.ensureCapacity(capacity + 1); - assertEquals(list.capacity(), newCapacity(capacity)); + assertEquals(newCapacity(capacity), list.capacity()); } { int capacity = list.capacity(); list.ensureCapacity(3 * capacity); - assertEquals(list.capacity(), 3 * capacity); + assertEquals(3 * capacity, list.capacity()); } } @Test public void ensureCapacityBeyondDefaultCapacity() { PublicVector list = new PublicVector<>(); list.ensureCapacity(DEFAULT_CAPACITY + 1); - assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + assertEquals(newCapacity(DEFAULT_CAPACITY), list.capacity()); } @Test public void explicitZeroCapacity() { PublicVector list = new PublicVector<>(0); - assertEquals(list.capacity(), 0); + assertEquals(0, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 1); + assertEquals(1, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 2); + assertEquals(2, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); list.clear(); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); } @Test public void explicitZeroCapacityWithCapacityIncrement() { PublicVector list = new PublicVector<>(0, 2); - assertEquals(list.capacity(), 0); + assertEquals(0, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 2); + assertEquals(2, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 2); + assertEquals(2, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 6); + assertEquals(6, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 6); + assertEquals(6, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); list.clear(); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); } @Test public void explicitLargeCapacity() { int n = DEFAULT_CAPACITY * 3; PublicVector list = new PublicVector<>(n); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); list.ensureCapacity(0); list.ensureCapacity(n); for (int i = 0; i < n; i++) addOneElement(list); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), newCapacity(n)); + assertEquals(newCapacity(n), list.capacity()); } @Test public void explicitLargeCapacityWithCapacityIncrement() { int n = DEFAULT_CAPACITY * 3; PublicVector list = new PublicVector<>(n, 2); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); list.ensureCapacity(0); list.ensureCapacity(n); for (int i = 0; i < n; i++) addOneElement(list); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), n + 2); + assertEquals(n + 2, list.capacity()); } @Test public void emptyArraysAreNotShared() { From 3f01e8b9b8f68560545540f9a70391a7ff7726d0 Mon Sep 17 00:00:00 2001 From: Kirill Shirokov Date: Thu, 15 Jan 2026 18:52:44 +0000 Subject: [PATCH 024/126] 8366522: CodeSource.getCodeSigners() throws NPE within empty certs Reviewed-by: mullan --- .../classes/java/security/CodeSource.java | 9 +- .../CodeSource/CodeSourceNoInputs.java | 110 ++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/security/CodeSource/CodeSourceNoInputs.java diff --git a/src/java.base/share/classes/java/security/CodeSource.java b/src/java.base/share/classes/java/security/CodeSource.java index 7476b8a1d61..9e69b2f0849 100644 --- a/src/java.base/share/classes/java/security/CodeSource.java +++ b/src/java.base/share/classes/java/security/CodeSource.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 @@ -238,7 +238,12 @@ public class CodeSource implements java.io.Serializable { } else if (certs != null) { // Convert the certs to code signers signers = convertCertArrayToSignerArray(certs); - return signers.clone(); + if (signers != null) { + return signers.clone(); + + } else { + return new CodeSigner[0]; + } } else { return null; diff --git a/test/jdk/java/security/CodeSource/CodeSourceNoInputs.java b/test/jdk/java/security/CodeSource/CodeSourceNoInputs.java new file mode 100644 index 00000000000..e1d2497667b --- /dev/null +++ b/test/jdk/java/security/CodeSource/CodeSourceNoInputs.java @@ -0,0 +1,110 @@ +/* + * 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.security.CodeSigner; +import java.security.CodeSource; +import java.security.PublicKey; +import java.security.cert.Certificate; + +/** + * @test + * @bug 8366522 + * @summary Verify that getCertificates() and getCodeSigners() return correct + * results when CodeSource is created with empty or null Certificate[] + * or CodeSigner[] arguments, or there are no X509 certificates in + * certs. Make sure that NPE is not thrown from + * CodeSource.getCodeSigners() + */ +public class CodeSourceNoInputs { + private static final Certificate NON_X509_CERT = new Certificate("") { + @Override + public byte[] getEncoded() { + return new byte[0]; + } + + @Override + public void verify(PublicKey key) { + } + + @Override + public void verify(PublicKey key, String sigProvider) { + } + + @Override + public String toString() { + return ""; + } + + @Override + public PublicKey getPublicKey() { + return null; + } + }; + + public static void main(String[] args) throws Exception { + CodeSource cs; + + cs = new CodeSource(null, (Certificate[]) null); + if (cs.getCodeSigners() != null || cs.getCertificates() != null) { + + throw new SecurityException("Both CodeSource.getCodeSigners() " + + "and CodeSource.getCertificates() should return null for " + + "new CodeSource(null, (Certificate[]) null)"); + } + + cs = new CodeSource(null, (CodeSigner[]) null); + if (cs.getCodeSigners() != null || cs.getCertificates() != null) { + + throw new SecurityException("Both CodeSource.getCodeSigners() " + + "and CodeSource.getCertificates() should return null for " + + "new CodeSource(null, (CodeSigners[]) null)"); + } + + cs = new CodeSource(null, new Certificate[0]); + if (cs.getCodeSigners().length != 0 + || cs.getCertificates().length != 0) { + + throw new SecurityException("Both CodeSource.getCodeSigners()" + + "and CodeSource.getCertificates() should return empty arrays " + + "for new CodeSource(null, new Certificate[0])"); + + } + + cs = new CodeSource(null, new CodeSigner[0]); + if (cs.getCodeSigners().length != 0 + || cs.getCertificates().length != 0) { + + throw new SecurityException("Both CodeSource.getCodeSigners() and" + + " CodeSource.getCertificates() should return empty arrays for" + + " new CodeSource(null, new CodeSigners[0])"); + } + + cs = new CodeSource(null, new Certificate[]{NON_X509_CERT}); + if (cs.getCodeSigners().length != 0 || cs.getCertificates() == null) { + + throw new SecurityException("Both CodeSource.getCodeSigners() and" + + " CodeSource.getCertificates() should return arrays for new" + + " CodeSource(null, new CodeSigners[1]{NON-X509-CERTIFICATE})"); + } + } +} From e97fb0e2072a16c59014599719b64e8ea52a4976 Mon Sep 17 00:00:00 2001 From: Koushik Thirupattur Date: Thu, 15 Jan 2026 19:01:24 +0000 Subject: [PATCH 025/126] 8367024: JNI exception pending in Java_sun_security_pkcs11_wrapper_PKCS11_C_1DeriveKey of p11_keymgmt.c:950 Reviewed-by: valeriep, hchao, djelinski --- .../share/native/libj2pkcs11/p11_keymgmt.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c index f2e1a46565d..e540f3b5ed9 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c @@ -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. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -929,6 +929,11 @@ JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1DeriveKey rv = (*ckpFunctions->C_DeriveKey)(ckSessionHandle, ckpMechanism, ckBaseKeyHandle, ckpAttributes, ckAttributesLength, phKey); + /* If derivation failed, do not attempt copy-back */ + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { + goto cleanup; + } + jKeyHandle = ckLongToJLong(ckKeyHandle); switch (ckpMechanism->mechanism) { @@ -956,8 +961,9 @@ JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1DeriveKey // empty break; } - if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { - jKeyHandle =0L; + /* Do not continue if any copy-back operation raised an exception */ + if ((*env)->ExceptionCheck(env)) { + goto cleanup; } cleanup: From 25c834a897ac0cac94942a019c9e377a53851f2c Mon Sep 17 00:00:00 2001 From: Koushik Thirupattur Date: Thu, 15 Jan 2026 19:05:19 +0000 Subject: [PATCH 026/126] 8366807: JNI exception pending in Java_sun_security_pkcs11_wrapper_PKCS11_initializeLibrary of p11_general.c:106 Reviewed-by: valeriep --- .../share/native/libj2pkcs11/p11_general.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c index e9bcf597a03..f61b8a5fb49 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -100,6 +100,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_initializeLibrary #ifndef NO_CALLBACKS if (notifyListLock == NULL) { notifyListLock = createLockObject(env); + + /* Return immediately if lock creation failed or an exception is pending. */ + if (notifyListLock == NULL || (*env)->ExceptionCheck(env)) { + TRACE0("DEBUG: createLockObject failed, aborting initialization\n"); + return; + } } #endif From a8b845e08ce2f1fbe7d807cd963cb6b5e4df5ce6 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 15 Jan 2026 19:14:46 +0000 Subject: [PATCH 027/126] 8374445: Fix -Wzero-as-null-pointer-constant warnings in JfrSet Reviewed-by: mgronlun --- src/hotspot/share/jfr/utilities/jfrSet.hpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index 1432d40e4b2..3d394d10d8b 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.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 @@ -25,6 +25,7 @@ #ifndef SHARE_JFR_UTILITIES_JFRSET_HPP #define SHARE_JFR_UTILITIES_JFRSET_HPP +#include "cppstdlib/new.hpp" #include "jfr/utilities/jfrTypes.hpp" #include "memory/allocation.hpp" @@ -67,7 +68,9 @@ class JfrSetStorage : public AnyObj { } else { table = NEW_RESOURCE_ARRAY(K, table_size); } - memset(table, 0, table_size * sizeof(K)); + for (unsigned i = 0; i < table_size; ++i) { + ::new (&table[i]) K{}; + } return table; } @@ -88,7 +91,7 @@ class JfrSetStorage : public AnyObj { assert(is_nonempty(), "invariant"); for (unsigned i = 0; i < _table_size; ++i) { K k = _table[i]; - if (k != 0) { + if (k != K{}) { functor(k); } } @@ -136,11 +139,11 @@ class JfrSet : public JfrSetStorage { _resize_threshold = old_table_size; for (unsigned i = 0; i < old_table_size; ++i) { const K k = old_table[i]; - if (k != 0) { + if (k != K{}) { uint32_t idx = slot_idx(CONFIG::hash(k)); do { K v = this->_table[idx]; - if (v == 0) { + if (v == K{}) { this->_table[idx] = k; break; } @@ -161,7 +164,7 @@ class JfrSet : public JfrSetStorage { K* result = nullptr; while (true) { K v = this->_table[idx]; - if (v == 0) { + if (v == K{}) { result = &this->_table[idx]; break; } @@ -196,7 +199,7 @@ class JfrSet : public JfrSetStorage { // Already exists. return false; } - assert(*slot == 0, "invariant"); + assert(*slot == K{}, "invariant"); *slot = k; if (++this->_elements == _resize_threshold) { resize(); From 30cda00010888b6e9a2bf8cdeaedbb3eb4b6a222 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 15 Jan 2026 19:31:11 +0000 Subject: [PATCH 028/126] 8375294: (fs) Files.copy can fail with EOPNOTSUPP when copy_file_range not supported Reviewed-by: alanb, jpai --- src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c | 5 +++-- src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c index efbd0ca5684..54d640b03a4 100644 --- a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c +++ b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c @@ -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 @@ -63,7 +63,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferFrom0(JNIEnv *env, jobject this, if (n < 0) { if (errno == EAGAIN) return IOS_UNAVAILABLE; - if (errno == ENOSYS) + if (errno == ENOSYS || errno == EOPNOTSUPP) return IOS_UNSUPPORTED_CASE; if ((errno == EBADF || errno == EINVAL || errno == EXDEV) && ((ssize_t)count >= 0)) @@ -103,6 +103,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this, case EINVAL: case ENOSYS: case EXDEV: + case EOPNOTSUPP: // ignore and try sendfile() break; default: diff --git a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c index c90e99dda07..4677411b0ba 100644 --- a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c +++ b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, 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 @@ -193,6 +193,7 @@ Java_sun_nio_fs_LinuxNativeDispatcher_directCopy0 case EINVAL: case ENOSYS: case EXDEV: + case EOPNOTSUPP: // ignore and try sendfile() break; default: From a1b039aa989ca91b6e70962363f720f581c5bfaf Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Thu, 15 Jan 2026 22:33:34 +0000 Subject: [PATCH 029/126] 8286032: keytool -list -alias should not assume it is always a certificate Reviewed-by: weijun --- .../sun/security/tools/keytool/Main.java | 9 +-- .../sun/security/tools/keytool/ListAlias.java | 71 +++++++++++++++++++ .../sun/security/tools/keytool/WeakAlg.java | 14 ++-- 3 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 test/jdk/sun/security/tools/keytool/ListAlias.java diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index 9fb830da338..7f415da5270 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.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 @@ -1294,7 +1294,7 @@ public final class Main { } if (alias != null) { - doPrintEntry(rb.getString("the.certificate"), alias, out); + doPrintEntry(alias, out); } else { doPrintEntries(out); } @@ -2177,9 +2177,10 @@ public final class Main { /** * Prints a single keystore entry. */ - private void doPrintEntry(String label, String alias, PrintStream out) + private void doPrintEntry(String alias, PrintStream out) throws Exception { + String label = "<" + alias + ">"; CertPathConstraintsParameters cpcp; if (!keyStore.containsAlias(alias)) { MessageFormat form = new MessageFormat @@ -2631,7 +2632,7 @@ public final class Main { List aliases = Collections.list(keyStore.aliases()); aliases.sort(String::compareTo); for (String alias : aliases) { - doPrintEntry("<" + alias + ">", alias, out); + doPrintEntry(alias, out); if (verbose || rfc) { out.println(rb.getString("NEWLINE")); out.println(rb.getString diff --git a/test/jdk/sun/security/tools/keytool/ListAlias.java b/test/jdk/sun/security/tools/keytool/ListAlias.java new file mode 100644 index 00000000000..91994c04a9c --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/ListAlias.java @@ -0,0 +1,71 @@ +/* + * 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 8286032 + * @summary Validate the warnings of the keytool -list -alias command + * @library /test/lib + */ + +import jdk.test.lib.SecurityTools; + +public class ListAlias { + + public static void main(String[] args) throws Exception { + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-genseckey -keyalg DES -alias deskey") + .shouldContain("Warning") + .shouldMatch("The generated secret key uses the DES algorithm.*considered a security risk") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-list -alias deskey -v") + .shouldContain("Warning") + .shouldMatch(" uses the DES algorithm.*considered a security risk") + .shouldNotContain("The certificate") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-genkeypair -keyalg RSA -alias ca -dname CN=CA -ext bc:c " + + "-sigalg SHA1withRSA") + .shouldContain("Warning") + .shouldMatch("The generated certificate uses the SHA1withRSA.*considered a security risk") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-list -alias ca -v") + .shouldContain("Warning") + .shouldMatch(" uses the SHA1withRSA.*considered a security risk") + .shouldNotContain("The certificate") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-list -v") + .shouldContain("Warning") + .shouldMatch(" uses the DES algorithm.*considered a security risk") + .shouldMatch(" uses the SHA1withRSA.*considered a security risk") + .shouldNotContain("The certificate") + .shouldHaveExitValue(0); + } +} \ No newline at end of file diff --git a/test/jdk/sun/security/tools/keytool/WeakAlg.java b/test/jdk/sun/security/tools/keytool/WeakAlg.java index de2435a4f4b..546a4182e97 100644 --- a/test/jdk/sun/security/tools/keytool/WeakAlg.java +++ b/test/jdk/sun/security/tools/keytool/WeakAlg.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 @@ -750,7 +750,7 @@ public class WeakAlg { oa.shouldNotContain("Warning"); } else { oa.shouldContain("Warning") - .shouldMatch("The certificate.*" + bad + ".*is disabled"); + .shouldMatch("uses.*" + bad + ".*is disabled"); } // With cert content @@ -770,7 +770,7 @@ public class WeakAlg { } else { oa.shouldContain("Warning") .shouldContain(bad + " (disabled)") - .shouldMatch("The certificate.*" + bad + ".*is disabled"); + .shouldMatch("uses.*" + bad + ".*is disabled"); } } @@ -844,11 +844,11 @@ public class WeakAlg { break; case "SHA1withRSA": oa.shouldContain("Warning") - .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); + .shouldMatch("uses.*" + bad + ".*considered a security risk"); break; case "1024-bit RSA key": oa.shouldContain("Warning") - .shouldMatch("The certificate.*" + bad + ".*will be disabled"); + .shouldMatch("uses.*" + bad + ".*will be disabled"); break; } @@ -879,12 +879,12 @@ public class WeakAlg { case "SHA1withRSA": oa.shouldContain("Warning") .shouldContain(bad + " (weak)") - .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); + .shouldMatch("uses.*" + bad + ".*considered a security risk"); break; case "1024-bit RSA key": oa.shouldContain("Warning") .shouldContain(bad + " (weak)") - .shouldMatch("The certificate.*" + bad + ".*will be disabled"); + .shouldMatch("uses.*" + bad + ".*will be disabled"); break; } } From 87cbcadacfa20b24e9ba0bf8374ecbcd331d2b35 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Thu, 15 Jan 2026 22:35:49 +0000 Subject: [PATCH 030/126] 8351892: GenShen: Remove vestigial young generation sizing options Reviewed-by: kdnilsen, ysr --- .../gc/shenandoah/shenandoahGenerationalHeap.cpp | 11 ----------- .../share/gc/shenandoah/shenandoah_globals.hpp | 12 ------------ 2 files changed, 23 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index f887cc9064e..fa78e02e6af 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -104,17 +104,6 @@ void ShenandoahGenerationalHeap::initialize_heuristics() { // Initialize global generation and heuristics even in generational mode. ShenandoahHeap::initialize_heuristics(); - // Max capacity is the maximum _allowed_ capacity. That is, the maximum allowed capacity - // for old would be total heap - minimum capacity of young. This means the sum of the maximum - // allowed for old and young could exceed the total heap size. It remains the case that the - // _actual_ capacity of young + old = total. - size_t region_count = num_regions(); - size_t max_young_regions = MAX2((region_count * ShenandoahMaxYoungPercentage) / 100, (size_t) 1U); - size_t initial_capacity_young = max_young_regions * ShenandoahHeapRegion::region_size_bytes(); - size_t max_capacity_young = initial_capacity_young; - size_t initial_capacity_old = max_capacity() - max_capacity_young; - size_t max_capacity_old = max_capacity() - initial_capacity_young; - _young_generation = new ShenandoahYoungGeneration(max_workers()); _old_generation = new ShenandoahOldGeneration(max_workers()); _young_generation->initialize_heuristics(mode()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 15520c47bbc..254483d1923 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -430,18 +430,6 @@ "by thread type (worker or mutator) and evacuation type (young, " \ "old, or promotion.") \ \ - product(uintx, ShenandoahMinYoungPercentage, 20, EXPERIMENTAL, \ - "The minimum percentage of the heap to use for the young " \ - "generation. Heuristics will not adjust the young generation " \ - "to be less than this.") \ - range(0, 100) \ - \ - product(uintx, ShenandoahMaxYoungPercentage, 100, EXPERIMENTAL, \ - "The maximum percentage of the heap to use for the young " \ - "generation. Heuristics will not adjust the young generation " \ - "to be more than this.") \ - range(0, 100) \ - \ product(uintx, ShenandoahCriticalFreeThreshold, 1, EXPERIMENTAL, \ "How much of the heap needs to be free after recovery cycles, " \ "either Degenerated or Full GC to be claimed successful. If this "\ From 1d889b92bde5dfcb1fbe6cddb389a77f92eb1ce7 Mon Sep 17 00:00:00 2001 From: Volodymyr Paprotski Date: Thu, 15 Jan 2026 23:11:12 +0000 Subject: [PATCH 031/126] 8360271: String.indexOf intrinsics fail with +EnableX86ECoreOpts and -CompactStrings Reviewed-by: thartmann, jbhateja, sviswanathan --- .../x86/c2_stubGenerator_x86_64_string.cpp | 8 +++++--- test/jdk/java/lang/String/IndexOf.java | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp index 0951bda0d17..77a149addb5 100644 --- a/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp +++ b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Intel Corporation. All rights reserved. + * Copyright (c) 2024, 2026, Intel Corporation. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1330,10 +1330,12 @@ static void big_case_loop_helper(bool sizeKnown, int size, Label &noMatch, Label // Clarification: The BYTE_K compare above compares haystack[(n-32):(n-1)]. We need to // compare haystack[(k-1):(k-1+31)]. Subtracting either index gives shift value of // (k + 31 - n): x = (k-1+31)-(n-1) = k-1+31-n+1 = k+31-n. + // When isU is set, similarly, shift is from haystack[(n-32):(n-1)] to [(k-2):(k-2+31)] + if (sizeKnown) { - __ movl(temp2, 31 + size); + __ movl(temp2, (isU ? 30 : 31) + size); } else { - __ movl(temp2, 31); + __ movl(temp2, isU ? 30 : 31); __ addl(temp2, needleLen); } __ subl(temp2, hsLength); diff --git a/test/jdk/java/lang/String/IndexOf.java b/test/jdk/java/lang/String/IndexOf.java index b4fd17105a8..48f23de79b0 100644 --- a/test/jdk/java/lang/String/IndexOf.java +++ b/test/jdk/java/lang/String/IndexOf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Intel Corporation. All rights reserved. + * Copyright (c) 2024, 2026, Intel Corporation. All rights reserved. * 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,6 +37,15 @@ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:-TieredCompilation -XX:UseAVX=2 -XX:+UnlockDiagnosticVMOptions -XX:+EnableX86ECoreOpts IndexOf */ +/* + * @test + * @bug 8360271 + * @summary test String indexOf() intrinsic + * @requires vm.cpu.features ~= ".*avx2.*" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:-TieredCompilation -XX:UseAVX=2 -XX:+UnlockDiagnosticVMOptions -XX:+EnableX86ECoreOpts -XX:-CompactStrings IndexOf + */ + public class IndexOf { final int scope = 32*2+16+8; final char a, aa, b, c, d; @@ -56,11 +65,11 @@ d = 'd'; break; case UU: - a = '\u0061'; + a = '\u1061'; aa = a; - b = '\u0062'; + b = '\u1062'; c = '\u1063'; - d = '\u0064'; + d = '\u1064'; break; default: //case UL: a = 'a'; @@ -73,7 +82,7 @@ } // needle =~ /ab*d/ - // badNeedle =~ /ab*db*d/ + // badNeedle =~ /ab*cb*d/ interface Append {void append(int pos, char cc);} String newNeedle(int size, int badPosition) { if (size<2) {throw new RuntimeException("Fix testcase "+size);} From fddba3b7ecb11136e9699861b5d86aeb3d481be6 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 16 Jan 2026 00:47:24 +0000 Subject: [PATCH 032/126] 8375350: Remove usage of AppContext from javax.imageio implementation Reviewed-by: kizune, dnguyen --- .../share/classes/javax/imageio/ImageIO.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/java.desktop/share/classes/javax/imageio/ImageIO.java b/src/java.desktop/share/classes/javax/imageio/ImageIO.java index 8180145a8ea..5d3526df917 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageIO.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageIO.java @@ -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 @@ -47,7 +47,6 @@ import javax.imageio.spi.ImageTranscoderSpi; import javax.imageio.spi.ServiceRegistry; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; -import sun.awt.AppContext; /** * A class containing static convenience methods for locating @@ -108,9 +107,7 @@ public final class ImageIO { // ImageInputStreams /** - * A class to hold information about caching. Each - * {@code ThreadGroup} will have its own copy - * via the {@code AppContext} mechanism. + * A class to hold information about caching. */ static class CacheInfo { boolean useCache = true; @@ -144,17 +141,12 @@ public final class ImageIO { } } + private static final CacheInfo info = new CacheInfo(); + /** - * Returns the {@code CacheInfo} object associated with this - * {@code ThreadGroup}. + * Returns the {@code CacheInfo} object. */ private static synchronized CacheInfo getCacheInfo() { - AppContext context = AppContext.getAppContext(); - CacheInfo info = (CacheInfo)context.get(CacheInfo.class); - if (info == null) { - info = new CacheInfo(); - context.put(CacheInfo.class, info); - } return info; } From 9876875e37b5cd4ac5263007ff96611ab0707cd5 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 16 Jan 2026 02:51:40 +0000 Subject: [PATCH 033/126] 8375364: [macos] Some jpackage signing tests fail after JDK-8375240 Reviewed-by: almatvee --- .../jpackage/helpers/jdk/jpackage/test/JPackageCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9b8b05af93b..7874df3fd69 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -971,7 +971,7 @@ public class JPackageCommand extends CommandArguments { executePrerequisiteActions(); nullableOutputBundle().filter(_ -> { - return removeOldOutputBundle; + return !(TKit.isOSX() && MacHelper.signPredefinedAppImage(this)) && removeOldOutputBundle; }).ifPresent(path -> { ThrowingRunnable.toRunnable(() -> { if (Files.isDirectory(path)) { From e4474ad8ae250771e031b8c18809d3e461970365 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 16 Jan 2026 03:19:28 +0000 Subject: [PATCH 034/126] 8375367: vmTestbase tests reported variable uninitialized by clang23 Reviewed-by: sspitsyn, amenkov, lmesnik --- .../nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp | 4 ++-- .../nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp index 52459a7994f..8205729e4ef 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp @@ -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 @@ -113,7 +113,7 @@ static int prepare(JNIEnv* jni) { /** Agent algorithm. */ static void JNICALL agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) { - jint dummy; + jint dummy = 0; if (!nsk_jvmti_waitForSync(timeout)) return; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp index 7f716d009ce..f2cff01948b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp @@ -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 @@ -113,7 +113,7 @@ static int prepare(JNIEnv* jni) { /** Agent algorithm. */ static void JNICALL agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) { - jint dummy; + jint dummy = 0; if (!nsk_jvmti_waitForSync(timeout)) return; From fda8d0506a511c00e65c3f97aaaf6f018945b213 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 16 Jan 2026 07:48:26 +0000 Subject: [PATCH 035/126] 8375455: G1: Remove unused G1HeapRegionStats::coarsen_stats() Reviewed-by: kbarrett --- src/hotspot/share/gc/g1/g1CardSet.cpp | 6 +----- src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index 80cf5fea76a..3441e6bc608 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1024,10 +1024,6 @@ size_t G1CardSet::num_containers() { return cl._count; } -G1CardSetCoarsenStats G1CardSet::coarsen_stats() { - return _coarsen_stats; -} - void G1CardSet::print_coarsen_stats(outputStream* out) { _last_coarsen_stats.subtract_from(_coarsen_stats); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 0f78e0d6271..878d35397aa 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.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 @@ -123,9 +123,6 @@ public: static void initialize(MemRegion reserved); - // Coarsening statistics since VM start. - static G1CardSetCoarsenStats coarsen_stats() { return G1CardSet::coarsen_stats(); } - inline uintptr_t to_card(OopOrNarrowOopStar from) const; private: From 5664d9148401934cd26308dc4493f4a5656e89bd Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Fri, 16 Jan 2026 08:01:40 +0000 Subject: [PATCH 036/126] 8374769: PPC: MASM::pop_cont_fastpath() should reset _cont_fastpath if SP == _cont_fastpath Reviewed-by: mdoerr --- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 5649ead2ea8..809285afddb 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -4535,7 +4535,7 @@ void MacroAssembler::push_cont_fastpath() { Label done; ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); cmpld(CR0, R1_SP, R0); - ble(CR0, done); + ble(CR0, done); // if (SP <= _cont_fastpath) goto done; st_ptr(R1_SP, JavaThread::cont_fastpath_offset(), R16_thread); bind(done); } @@ -4546,7 +4546,7 @@ void MacroAssembler::pop_cont_fastpath() { Label done; ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); cmpld(CR0, R1_SP, R0); - ble(CR0, done); + blt(CR0, done); // if (SP < _cont_fastpath) goto done; li(R0, 0); st_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); bind(done); From b7346c307fc1aba01c10fc6dc745e5e520b1d7b9 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 16 Jan 2026 08:03:55 +0000 Subject: [PATCH 037/126] 8375311: Some builds are missing debug helpers Reviewed-by: mdoerr, aph --- src/hotspot/share/utilities/debug.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 9e167141259..0e1ca1efb98 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -70,8 +70,10 @@ #include #include -// These functions needs to be exported on Windows only -#define DEBUGEXPORT WINDOWS_ONLY(JNIEXPORT) +// These functions needs to be exported on Windows +// On Linux it is also beneficial to export them to avoid +// losing them e.g. with linktime gc +#define DEBUGEXPORT JNIEXPORT // Support for showing register content on asserts/guarantees. #ifdef CAN_SHOW_REGISTERS_ON_ASSERT @@ -653,13 +655,12 @@ extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr, void pp(intptr_t p) { pp((void*)p); } void pp(oop p) { pp((void*)p); } -void help() { +extern "C" DEBUGEXPORT void help() { Command c("help"); tty->print_cr("basic"); tty->print_cr(" pp(void* p) - try to make sense of p"); tty->print_cr(" ps() - print current thread stack"); tty->print_cr(" pss() - print all thread stacks"); - tty->print_cr(" pm(int pc) - print Method* given compiled PC"); tty->print_cr(" findnm(intptr_t pc) - find nmethod*"); tty->print_cr(" findm(intptr_t pc) - find Method*"); tty->print_cr(" find(intptr_t x) - find & print nmethod/stub/bytecode/oop based on pointer into it"); From e7432d574540109e2c4faca11cf49d9272a147e6 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 16 Jan 2026 20:03:00 +0000 Subject: [PATCH 038/126] 8375323: Improve handling of the "--app-content" and "--input" options in jpackage Reviewed-by: almatvee --- .../jdk/jpackage/internal/LinuxPackager.java | 4 +- .../jdk/jpackage/internal/AppImageSigner.java | 2 +- .../internal/MacApplicationBuilder.java | 22 +- .../internal/MacDmgPackageBuilder.java | 16 +- .../jdk/jpackage/internal/MacDmgPackager.java | 6 +- .../jdk/jpackage/internal/MacFromOptions.java | 11 +- .../jdk/jpackage/internal/MacPkgPackager.java | 1 - .../internal/model/MacDmgPackageMixin.java | 13 +- .../jpackage/internal/ApplicationBuilder.java | 24 +- .../internal/ApplicationImageUtils.java | 49 +- .../internal/DefaultBundlingEnvironment.java | 3 +- .../jdk/jpackage/internal/FromOptions.java | 15 +- .../internal/LauncherFromOptions.java | 5 +- .../jpackage/internal/PackagingPipeline.java | 10 +- .../cli/JOptSimpleOptionsBuilder.java | 7 +- .../cli/OptionArrayValueConverter.java | 4 +- .../jdk/jpackage/internal/cli/OptionSpec.java | 13 +- .../internal/cli/OptionSpecBuilder.java | 120 +++-- .../cli/OptionSpecMapperOptionScope.java | 103 +++- .../internal/cli/OptionValueConverter.java | 503 ++++++++++++++---- .../internal/cli/OptionsAnalyzer.java | 1 - .../internal/cli/OptionsProcessor.java | 7 +- .../cli/StandardAppImageFileOption.java | 2 +- .../jpackage/internal/cli/StandardOption.java | 100 +++- .../internal/cli/StandardValueConverter.java | 76 ++- .../jdk/jpackage/internal/cli/Validator.java | 10 +- .../jpackage/internal/cli/ValueConverter.java | 27 +- .../internal/cli/ValueConverterFunction.java | 40 ++ .../jpackage/internal/model/Application.java | 43 +- .../resources/MainResources.properties | 1 + .../jdk/jpackage/internal/util/FileUtils.java | 26 +- .../jpackage/internal/util/RootedPath.java | 185 +++++++ .../jdk/jpackage/internal/WinExePackager.java | 2 +- .../jdk/jpackage/internal/WinMsiPackager.java | 2 +- .../jpackage/internal/AppImageFileTest.java | 2 +- .../internal/PackagingPipelineTest.java | 2 +- .../cli/OptionSpecMutatorOptionScopeTest.java | 10 +- .../jpackage/internal/cli/OptionSpecTest.java | 10 +- .../cli/OptionValueConverterTest.java | 188 ++++++- .../internal/cli/StandardOptionTest.java | 256 ++++++++- .../cli/StandardValueConverterTest.java | 12 +- .../jpackage/internal/util/FileUtilsTest.java | 76 +-- .../tools/jdk/jpackage/test/JUnitUtils.java | 23 +- .../tools/jpackage/share/AppContentTest.java | 80 ++- .../tools/jpackage/share/InOutPathTest.java | 173 +++--- 45 files changed, 1706 insertions(+), 579 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java index af7f5288cc5..551e1ab1af6 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.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 @@ -61,7 +61,7 @@ abstract class LinuxPackager implements Consumer { + return rootedPath.branch().getNameCount() == 1; + }).map(RootedPath::fullPath).forEach(contentDir -> { if (!Files.isDirectory(contentDir)) { Log.info(I18N.format("warning.app.content.is.not.dir", contentDir)); - } else if (!CONTENTS_SUB_DIRS.contains(contentDir.getFileName().toString())) { + } else if (!CONTENTS_SUB_DIRS.contains(contentDir.getFileName())) { Log.info(I18N.format("warning.non.standard.contents.sub.dir", contentDir)); } - } + }); } private MacApplicationBuilder createCopyForExternalInfoPlistFile() { @@ -258,6 +263,11 @@ final class MacApplicationBuilder { private static final int MAX_BUNDLE_NAME_LENGTH = 16; // List of standard subdirectories of the "Contents" directory - private static final Set CONTENTS_SUB_DIRS = Set.of("MacOS", - "Resources", "Frameworks", "PlugIns", "SharedSupport"); + private static final Set CONTENTS_SUB_DIRS = Stream.of( + "MacOS", + "Resources", + "Frameworks", + "PlugIns", + "SharedSupport" + ).map(Path::of).collect(Collectors.toUnmodifiableSet()); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java index 10754b1f1b6..b9e407b4a47 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.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 @@ -25,11 +25,13 @@ package jdk.jpackage.internal; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import jdk.jpackage.internal.model.MacDmgPackage; import jdk.jpackage.internal.model.MacDmgPackageMixin; +import jdk.jpackage.internal.util.RootedPath; final class MacDmgPackageBuilder { @@ -37,8 +39,8 @@ final class MacDmgPackageBuilder { this.pkgBuilder = Objects.requireNonNull(pkgBuilder); } - MacDmgPackageBuilder dmgContent(List v) { - dmgContent = v; + MacDmgPackageBuilder dmgRootDirSources(Collection v) { + dmgRootDirSources = v; return this; } @@ -47,8 +49,8 @@ final class MacDmgPackageBuilder { return this; } - List validatedDmgContent() { - return Optional.ofNullable(dmgContent).orElseGet(List::of); + private Collection validatedDmgRootDirSources() { + return Optional.ofNullable(dmgRootDirSources).orElseGet(List::of); } MacDmgPackage create() { @@ -56,10 +58,10 @@ final class MacDmgPackageBuilder { return MacDmgPackage.create(pkg, new MacDmgPackageMixin.Stub( Optional.ofNullable(icon).or((pkg.app())::icon), - validatedDmgContent())); + validatedDmgRootDirSources())); } private Path icon; - private List dmgContent; + private Collection dmgRootDirSources; private final MacPackageBuilder pkgBuilder; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index cf4db226d37..24474c88162 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java @@ -46,6 +46,7 @@ import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.MacDmgPackage; import jdk.jpackage.internal.util.FileUtils; import jdk.jpackage.internal.util.PathGroup; +import jdk.jpackage.internal.util.RootedPath; record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, MacDmgSystemEnvironment sysEnv) implements Consumer { @@ -60,7 +61,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { pipelineBuilder - .excludeDirFromCopying(outputDir) .task(DmgPackageTaskID.COPY_DMG_CONTENT) .action(this::copyDmgContent) .addDependent(PackageTaskID.CREATE_PACKAGE_FILE) @@ -130,9 +130,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, private void copyDmgContent() throws IOException { final var srcFolder = env.appImageDir(); - for (Path path : pkg.content()) { - FileUtils.copyRecursive(path, srcFolder.resolve(path.getFileName())); - } + RootedPath.copy(pkg.dmgRootDirSources().stream(), srcFolder); } private Executor hdiutil(String... args) { 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 cdf33d6dcba..f3c1765210f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -52,6 +52,7 @@ import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; import static jdk.jpackage.internal.util.function.ExceptionBox.toUnchecked; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -75,6 +76,7 @@ import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.Result; +import jdk.jpackage.internal.util.RootedPath; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -92,7 +94,14 @@ final class MacFromOptions { final var pkgBuilder = new MacDmgPackageBuilder(superPkgBuilder); - MAC_DMG_CONTENT.ifPresentIn(options, pkgBuilder::dmgContent); + MAC_DMG_CONTENT.findIn(options).map((List> v) -> { + // Reverse the order of content sources. + // If there are multiple source files for the same + // destination file, only the first will be used. + // Reversing the order of content sources makes it use the last file + // from the original list of source files for the given destination file. + return v.reversed().stream().flatMap(Collection::stream).toList(); + }).ifPresent(pkgBuilder::dmgRootDirSources); return pkgBuilder.create(); } 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 126248e2330..fc5c85c699c 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java @@ -215,7 +215,6 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { pipelineBuilder - .excludeDirFromCopying(outputDir) .task(PkgPackageTaskID.PREPARE_MAIN_SCRIPTS) .action(this::prepareMainScripts) .addDependent(PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.java index 0b502e7ce7e..08797b11509 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.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 @@ -25,22 +25,23 @@ package jdk.jpackage.internal.model; import java.nio.file.Path; -import java.util.List; +import java.util.Collection; import java.util.Optional; +import jdk.jpackage.internal.util.RootedPath; public interface MacDmgPackageMixin { Optional icon(); /** - * Returns additional top=level content for DMG package. + * Returns the source paths that should be copied into the top-level directory of a DMG package. *

* Each item in the list can be a directory or a file. * - * @return the additional top=level content for DMG package + * @return the source paths of additional top-level content for DMG package */ - List content(); + Collection dmgRootDirSources(); - record Stub(Optional icon, List content) implements MacDmgPackageMixin { + record Stub(Optional icon, Collection dmgRootDirSources) implements MacDmgPackageMixin { } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java index 6896668ffdc..9d5407524a8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.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 @@ -27,6 +27,7 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.I18N.buildConfigException; import java.nio.file.Path; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; @@ -44,6 +45,7 @@ import jdk.jpackage.internal.model.LauncherIcon; import jdk.jpackage.internal.model.LauncherStartupInfo; import jdk.jpackage.internal.model.ResourceDirLauncherIcon; import jdk.jpackage.internal.model.RuntimeBuilder; +import jdk.jpackage.internal.util.RootedPath; final class ApplicationBuilder { @@ -65,8 +67,8 @@ final class ApplicationBuilder { Optional.ofNullable(version).orElseGet(DEFAULTS::version), Optional.ofNullable(vendor).orElseGet(DEFAULTS::vendor), Optional.ofNullable(copyright).orElseGet(DEFAULTS::copyright), - Optional.ofNullable(srcDir), - Optional.ofNullable(contentDirs).orElseGet(List::of), + Optional.ofNullable(appDirSources).orElseGet(List::of), + Optional.ofNullable(contentDirSources).orElseGet(List::of), appImageLayout, Optional.ofNullable(runtimeBuilder), launchersAsList, @@ -126,13 +128,13 @@ final class ApplicationBuilder { return this; } - ApplicationBuilder srcDir(Path v) { - srcDir = v; + ApplicationBuilder appDirSources(Collection v) { + appDirSources = v; return this; } - ApplicationBuilder contentDirs(List v) { - contentDirs = v; + ApplicationBuilder contentDirSources(Collection v) { + contentDirSources = v; return this; } @@ -246,8 +248,8 @@ final class ApplicationBuilder { app.version(), app.vendor(), app.copyright(), - app.srcDir(), - app.contentDirs(), + app.appDirSources(), + app.contentDirSources(), Objects.requireNonNull(appImageLayout), app.runtimeBuilder(), app.launchers(), @@ -294,9 +296,9 @@ final class ApplicationBuilder { private String version; private String vendor; private String copyright; - private Path srcDir; + private Collection appDirSources; private ExternalApplication externalApp; - private List contentDirs; + private Collection contentDirSources; private AppImageLayout appImageLayout; private RuntimeBuilder runtimeBuilder; private ApplicationLaunchers launchers; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java index 3d2ffbfdc7c..5edc4d69c81 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.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 @@ -25,19 +25,14 @@ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.io.IOException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; import jdk.jpackage.internal.PackagingPipeline.ApplicationImageTaskAction; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; @@ -45,7 +40,7 @@ import jdk.jpackage.internal.model.CustomLauncherIcon; import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.ResourceDirLauncherIcon; -import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.RootedPath; final class ApplicationImageUtils { @@ -86,21 +81,14 @@ final class ApplicationImageUtils { }; } - static ApplicationImageTaskAction createCopyContentAction(Supplier> excludeCopyDirs) { + static ApplicationImageTaskAction createCopyContentAction() { return env -> { - var excludeCandidates = Stream.concat( - excludeCopyDirs.get().stream(), - Stream.of(env.env().buildRoot(), env.env().appImageDir()) - ).map(Path::toAbsolutePath).toList(); - - env.app().srcDir().ifPresent(toConsumer(srcDir -> { - copyRecursive(srcDir, env.resolvedLayout().appDirectory(), excludeCandidates); - })); - - for (var srcDir : env.app().contentDirs()) { - copyRecursive(srcDir, - env.resolvedLayout().contentDirectory().resolve(srcDir.getFileName()), - excludeCandidates); + for (var e : List.of( + Map.entry(env.app().appDirSources(), env.resolvedLayout().appDirectory()), + Map.entry(env.app().contentDirSources(), env.resolvedLayout().contentDirectory()) + )) { + RootedPath.copy(e.getKey().stream(), e.getValue(), + StandardCopyOption.REPLACE_EXISTING, LinkOption.NOFOLLOW_LINKS); } }; } @@ -126,21 +114,4 @@ final class ApplicationImageUtils { } }; } - - private static void copyRecursive(Path srcDir, Path dstDir, List excludeDirs) throws IOException { - srcDir = srcDir.toAbsolutePath(); - - List excludes = new ArrayList<>(); - - for (var path : excludeDirs) { - if (Files.isDirectory(path)) { - if (path.startsWith(srcDir) && !Files.isSameFile(path, srcDir)) { - excludes.add(path); - } - } - } - - FileUtils.copyRecursive(srcDir, dstDir.toAbsolutePath(), excludes, - LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING); - } } 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 3a99dfb04da..331bde29d27 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java @@ -146,8 +146,7 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { throw new JPackageException(I18N.format("error.root-exists", outputDir)); } - pipelineBuilder.excludeDirFromCopying(outputDir.getParent()) - .create().execute(BuildEnv.withAppImageDir(env, outputDir), app); + pipelineBuilder.create().execute(BuildEnv.withAppImageDir(env, outputDir), app); Log.verbose(I18N.getString("message.app-image-created")); } 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 6b74cab4e65..cccd05792d6 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.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,6 +46,7 @@ import static jdk.jpackage.internal.cli.StandardOption.RESOURCE_DIR; import static jdk.jpackage.internal.cli.StandardOption.VENDOR; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -60,6 +61,7 @@ import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; +import jdk.jpackage.internal.util.RootedPath; final class FromOptions { @@ -168,8 +170,15 @@ final class FromOptions { APP_VERSION.ifPresentIn(options, appBuilder::version); VENDOR.ifPresentIn(options, appBuilder::vendor); COPYRIGHT.ifPresentIn(options, appBuilder::copyright); - INPUT.ifPresentIn(options, appBuilder::srcDir); - APP_CONTENT.ifPresentIn(options, appBuilder::contentDirs); + INPUT.ifPresentIn(options, appBuilder::appDirSources); + APP_CONTENT.findIn(options).map((List> v) -> { + // Reverse the order of content sources. + // If there are multiple source files for the same + // destination file, only the first will be used. + // Reversing the order of content sources makes it use the last file + // from the original list of source files for the given destination file. + return v.reversed().stream().flatMap(Collection::stream).toList(); + }).ifPresent(appBuilder::contentDirSources); if (isRuntimeInstaller) { appBuilder.appImageLayout(runtimeLayout); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java index 9e81d144a1e..1ba384dba2d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java @@ -56,6 +56,7 @@ import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherIcon; +import jdk.jpackage.internal.util.RootedPath; final class LauncherFromOptions { @@ -92,7 +93,9 @@ final class LauncherFromOptions { if (PREDEFINED_APP_IMAGE.findIn(options).isEmpty()) { final var startupInfoBuilder = new LauncherStartupInfoBuilder(); - INPUT.ifPresentIn(options, startupInfoBuilder::inputDir); + INPUT.findIn(options).flatMap(v -> { + return v.stream().findAny().map(RootedPath::root); + }).ifPresent(startupInfoBuilder::inputDir); ARGUMENTS.ifPresentIn(options, startupInfoBuilder::defaultParameters); JAVA_OPTIONS.ifPresentIn(options, startupInfoBuilder::javaOptions); MAIN_JAR.ifPresentIn(options, startupInfoBuilder::mainJar); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index f15768b2cbf..315e3bce0f1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java @@ -32,7 +32,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -428,12 +427,6 @@ final class PackagingPipeline { }); } - Builder excludeDirFromCopying(Path path) { - Objects.requireNonNull(path); - excludeCopyDirs.add(path); - return this; - } - Builder contextMapper(UnaryOperator v) { contextMapper = v; return this; @@ -456,7 +449,6 @@ final class PackagingPipeline { } private final FixedDAG.Builder taskGraphBuilder = FixedDAG.build(); - private final List excludeCopyDirs = new ArrayList<>(); private final Map taskConfig = new HashMap<>(); private UnaryOperator contextMapper; private FixedDAG taskGraphSnapshot; @@ -490,7 +482,7 @@ final class PackagingPipeline { builder.task(BuildApplicationTaskID.CONTENT) .addDependent(BuildApplicationTaskID.APP_IMAGE_FILE) - .applicationAction(ApplicationImageUtils.createCopyContentAction(() -> builder.excludeCopyDirs)).add(); + .applicationAction(ApplicationImageUtils.createCopyContentAction()).add(); return builder; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java index 57b92471e4a..bff35874645 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.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 @@ -27,6 +27,7 @@ package jdk.jpackage.internal.cli; import static java.util.stream.Collectors.toUnmodifiableMap; import static java.util.stream.Collectors.toUnmodifiableSet; +import static jdk.jpackage.internal.cli.OptionValueConverter.convertString; import java.lang.reflect.Array; import java.util.ArrayList; @@ -565,7 +566,7 @@ final class JOptSimpleOptionsBuilder { final var converter = optionSpec.converter().orElseThrow(); final Result conversionResult = optionValue.map(v -> { - return converter.convert(optionName(), StringToken.of(v)); + return convertString(converter, optionName(), StringToken.of(v)); }).orElseGet(() -> { return Result.ofValue(optionSpec.defaultOptionalValue().orElseThrow()); }); @@ -579,7 +580,7 @@ final class JOptSimpleOptionsBuilder { final String str = getOptionValue(List.of(tokens), optionSpec.mergePolicy()).getFirst(); final String[] token = arrConverter.tokenize(str); if (token.length == 1 && str.equals(token[0])) { - final var singleTokenConversionResult = converter.convert(optionName(), StringToken.of(str)); + final var singleTokenConversionResult = convertString(converter, optionName(), StringToken.of(str)); if (singleTokenConversionResult.hasValue()) { return singleTokenConversionResult; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.java index 401d5e15d28..9b757c29d36 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.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 @@ -29,7 +29,7 @@ package jdk.jpackage.internal.cli; * * @param option value element type */ -interface OptionArrayValueConverter extends OptionValueConverter { +interface OptionArrayValueConverter extends OptionValueConverter { /** * Splits the given string into tokens and returns the result. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.java index c6aa46a6940..60674e6bdfe 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.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 @@ -30,6 +30,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; +import jdk.jpackage.internal.util.Result; /** @@ -56,7 +57,7 @@ import java.util.stream.Stream; */ record OptionSpec( List names, - Optional> converter, + Optional> converter, Set scope, MergePolicy mergePolicy, Optional defaultOptionalValue, @@ -134,7 +135,7 @@ record OptionSpec( }); } - OptionSpec copyWithConverter(OptionValueConverter converter) { + OptionSpec copyWithConverter(OptionValueConverter converter) { if (!defaultOptionalValue.isEmpty()) { throw new UnsupportedOperationException("Can not convert an option spec with optional value"); } @@ -169,12 +170,16 @@ record OptionSpec( return valueType(converter).orElseThrow(); } + Result convert(OptionName optionName, StringToken optionValue) { + return OptionValueConverter.convertString(converter().orElseThrow(), optionName, optionValue); + } + @SuppressWarnings("unchecked") Optional> arrayValueConverter() { return converter.filter(OptionArrayValueConverter.class::isInstance).map(v -> (OptionArrayValueConverter)v); } - private static Optional> valueType(Optional> valueConverter) { + private static Optional> valueType(Optional> valueConverter) { return valueConverter.map(OptionValueConverter::valueType); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java index 6cd1c05b57e..5eecbc9b464 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java @@ -24,6 +24,8 @@ */ package jdk.jpackage.internal.cli; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; + import java.io.File; import java.lang.reflect.Array; import java.util.Arrays; @@ -61,23 +63,49 @@ final class OptionSpecBuilder { this.valueType = Objects.requireNonNull(valueType); } - OptionSpecBuilder(OptionSpecBuilder other) { + private OptionSpecBuilder(OptionSpecBuilder other) { valueType = other.valueType; - name = other.name; - nameAliases.addAll(other.nameAliases); - description = other.description; - mergePolicy = other.mergePolicy; - scope = Set.copyOf(other.scope); + initFrom(other); defaultValue = other.defaultValue; defaultOptionalValue = other.defaultOptionalValue; - valuePattern = other.valuePattern; converterBuilder = other.converterBuilder.copy(); validatorBuilder = other.validatorBuilder.copy(); validator = other.validator; if (other.arrayDefaultValue != null) { arrayDefaultValue = Arrays.copyOf(other.arrayDefaultValue, other.arrayDefaultValue.length); + } else { + arrayDefaultValue = null; } + } + + private OptionSpecBuilder(OptionSpecBuilder other, ValueConverter converter) { + Function converterFunction = toFunction(converter::convert); + + this.valueType = converter.valueType(); + initFrom(other); + converter(other, converter); + + other.defaultValue().map(converterFunction).ifPresent(this::defaultValue); + other.defaultOptionalValue().map(converterFunction).ifPresent(this::defaultOptionalValue); + + if (other.arrayDefaultValue != null) { + arrayDefaultValue = Stream.of(other.arrayDefaultValue).map(converterFunction).toArray(length -> { + @SuppressWarnings("unchecked") + var arr = (T[])Array.newInstance(valueType, length); + return arr; + }); + } + } + + private void initFrom(OptionSpecBuilder other) { + name = other.name; + nameAliases.clear(); + nameAliases.addAll(other.nameAliases); + description = other.description; + mergePolicy = other.mergePolicy; + scope = Set.copyOf(other.scope); + valuePattern = other.valuePattern; arrayValuePatternSeparator = other.arrayValuePatternSeparator; arrayTokenizer = other.arrayTokenizer; } @@ -86,6 +114,14 @@ final class OptionSpecBuilder { return new OptionSpecBuilder<>(this); } + OptionSpecBuilder map(ValueConverter converter) { + return new OptionSpecBuilder<>(this, converter); + } + + OptionSpecBuilder map(Function, OptionSpecBuilder> mapper) { + return mapper.apply(this); + } + Class valueType() { return valueType; } @@ -177,8 +213,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder validatorExceptionFormatString(UnaryOperator mutator) { - validatorBuilder.formatString(mutator.apply(validatorBuilder.formatString().orElse(null))); + OptionSpecBuilder validatorExceptionFormatString(UnaryOperator mapper) { + validatorBuilder.formatString(mapper.apply(validatorBuilder.formatString().orElse(null))); validator = null; return this; } @@ -188,8 +224,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder converterExceptionFormatString(UnaryOperator mutator) { - converterBuilder.formatString(mutator.apply(converterBuilder.formatString().orElse(null))); + OptionSpecBuilder converterExceptionFormatString(UnaryOperator mapper) { + converterBuilder.formatString(mapper.apply(converterBuilder.formatString().orElse(null))); return this; } @@ -199,8 +235,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder validatorExceptionFactory(UnaryOperator> mutator) { - return validatorExceptionFactory(mutator.apply(validatorBuilder.exceptionFactory().orElse(null))); + OptionSpecBuilder validatorExceptionFactory(UnaryOperator> mapper) { + return validatorExceptionFactory(mapper.apply(validatorBuilder.exceptionFactory().orElse(null))); } OptionSpecBuilder converterExceptionFactory(OptionValueExceptionFactory v) { @@ -208,32 +244,42 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder converterExceptionFactory(UnaryOperator> mutator) { - return converterExceptionFactory(mutator.apply(converterBuilder.exceptionFactory().orElse(null))); + OptionSpecBuilder converterExceptionFactory(UnaryOperator> mapper) { + return converterExceptionFactory(mapper.apply(converterBuilder.exceptionFactory().orElse(null))); } OptionSpecBuilder exceptionFormatString(String v) { return validatorExceptionFormatString(v).converterExceptionFormatString(v); } - OptionSpecBuilder exceptionFormatString(UnaryOperator mutator) { - return validatorExceptionFormatString(mutator).converterExceptionFormatString(mutator); + OptionSpecBuilder exceptionFormatString(UnaryOperator mapper) { + return validatorExceptionFormatString(mapper).converterExceptionFormatString(mapper); } OptionSpecBuilder exceptionFactory(OptionValueExceptionFactory v) { return validatorExceptionFactory(v).converterExceptionFactory(v); } - OptionSpecBuilder exceptionFactory(UnaryOperator> mutator) { - return validatorExceptionFactory(mutator).converterExceptionFactory(mutator); + OptionSpecBuilder exceptionFactory(UnaryOperator> mapper) { + return validatorExceptionFactory(mapper).converterExceptionFactory(mapper); } - OptionSpecBuilder converter(ValueConverter v) { + OptionSpecBuilder converter(ValueConverter v) { converterBuilder.converter(v); return this; } - OptionSpecBuilder converter(Function v) { + OptionSpecBuilder converter(OptionSpecBuilder other, ValueConverter v) { + converterBuilder = other.finalizeConverterBuilder().map(v); + return this; + } + + OptionSpecBuilder interimConverter(OptionSpecBuilder other) { + converterBuilder = converterBuilder.map(other.finalizeConverterBuilder()); + return this; + } + + OptionSpecBuilder converter(ValueConverterFunction v) { return converter(ValueConverter.create(v, valueType)); } @@ -251,8 +297,8 @@ final class OptionSpecBuilder { } @SuppressWarnings("overloads") - OptionSpecBuilder validator(UnaryOperator> mutator) { - validatorBuilder = mutator.apply(validatorBuilder); + OptionSpecBuilder validator(UnaryOperator> mapper) { + validatorBuilder = mapper.apply(validatorBuilder); validator = null; return this; } @@ -303,8 +349,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder scope(UnaryOperator> mutator) { - return scope(mutator.apply(scope().orElseGet(Set::of))); + OptionSpecBuilder scope(UnaryOperator> mapper) { + return scope(mapper.apply(scope().orElseGet(Set::of))); } OptionSpecBuilder inScope(OptionScope... v) { @@ -424,10 +470,12 @@ final class OptionSpecBuilder { } private Optional defaultValuePattern() { - return converterBuilder.converter().map(_ -> { + if (converterBuilder.hasConverter()) { final var tokens = name.split("-"); - return tokens[tokens.length - 1]; - }); + return Optional.of(tokens[tokens.length - 1]); + } else { + return Optional.empty(); + } } private List names() { @@ -437,17 +485,21 @@ final class OptionSpecBuilder { ).flatMap(Collection::stream).map(OptionName::new).distinct().toList(); } - private Optional> createConverter() { - if (converterBuilder.converter().isPresent()) { - final var newBuilder = converterBuilder.copy(); - createValidator().ifPresent(newBuilder::validator); - return Optional.of(newBuilder.create()); + private Optional> createConverter() { + if (converterBuilder.hasConverter()) { + return Optional.of(finalizeConverterBuilder().create()); } else { return Optional.empty(); } } - private OptionValueConverter createArrayConverter() { + private OptionValueConverter.Builder finalizeConverterBuilder() { + final var newBuilder = converterBuilder.copy(); + createValidator().ifPresent(newBuilder::validator); + return newBuilder; + } + + private OptionValueConverter createArrayConverter() { final var newBuilder = converterBuilder.copy(); newBuilder.tokenizer(Optional.ofNullable(arrayTokenizer).orElse(str -> { return new String[] { str }; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.java index 7b517f768bf..42ed704ec6e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.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 @@ -25,11 +25,17 @@ package jdk.jpackage.internal.cli; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.SequencedMap; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.IdentityWrapper; import jdk.jpackage.internal.util.SetBuilder; /** @@ -42,9 +48,9 @@ import jdk.jpackage.internal.util.SetBuilder; * varies depending on the current OS. * * @param the type of option value - * @param the type of context + * @param the type of context */ -interface OptionSpecMapperOptionScope extends OptionScope { +sealed interface OptionSpecMapperOptionScope extends OptionScope { OptionSpec createOptionSpec(U context, boolean createArray); @@ -52,7 +58,7 @@ interface OptionSpecMapperOptionScope extends OptionScope { @SuppressWarnings("unchecked") static OptionSpec mapOptionSpec(OptionSpec optionSpec, U context) { - return optionSpec.scope().stream() + var mappedOptionSpec = optionSpec.scope().stream() .filter(OptionSpecMapperOptionScope.class::isInstance) .map(OptionSpecMapperOptionScope.class::cast) .filter(scope -> { @@ -62,6 +68,25 @@ interface OptionSpecMapperOptionScope extends OptionScope { return ((OptionSpecMapperOptionScope)scope).createOptionSpec( context, optionSpec.arrayValueConverter().isPresent()); }).orElse(optionSpec); + + Optional valueType = optionSpec.converter().map(OptionValueConverter::valueType); + Optional mappedValueType = mappedOptionSpec.converter().map(OptionValueConverter::valueType); + + while (!mappedValueType.equals(valueType)) { + // Source and mapped option specs have different option value types. + if (Stream.of(valueType, mappedValueType).anyMatch(Optional::isEmpty) && + Stream.of(valueType, mappedValueType) + .filter(Optional::isPresent).map(Optional::get) + .anyMatch(Predicate.isEqual(Boolean.class))) { + // One option spec doesn't have a converter and another has a converter of type `Boolean`. + // They are compatible, let it pass. + break; + } + + throw new IllegalStateException(String.format("Bad option spec mapping from %s to %s", valueType, mappedValueType)); + } + + return mappedOptionSpec; } static Consumer> createOptionSpecBuilderMutator( @@ -94,23 +119,23 @@ interface OptionSpecMapperOptionScope extends OptionScope { var contextOptionScope = scope.stream() .filter(AccumulatingContextOptionScope.class::isInstance) - .map(AccumulatingContextOptionScope.class::cast) + .map(v -> { + @SuppressWarnings("unchecked") + var tv = (AccumulatingContextOptionScope)v; + return tv; + }) .filter(s -> { return s.contextType().equals(contextType); }) .findFirst(); + contextOptionScope.ifPresent(v -> { - @SuppressWarnings("unchecked") - var mutators = (AccumulatingContextOptionScope)v; - mutators.addMutator(optionSpecBuilderMutator); - if (optionSpecBuilder != mutators.optionSpecBuilder) { - throw new IllegalArgumentException(); - } + v.addMutator(optionSpecBuilder, optionSpecBuilderMutator); }); if (contextOptionScope.isEmpty()) { - var mutators = new AccumulatingContextOptionScope(optionSpecBuilder, contextType); - mutators.addMutator(optionSpecBuilderMutator); + var mutators = new AccumulatingContextOptionScope(contextType); + mutators.addMutator(optionSpecBuilder, optionSpecBuilderMutator); scope = SetBuilder.build().add(scope).add(mutators).create(); } @@ -119,23 +144,24 @@ interface OptionSpecMapperOptionScope extends OptionScope { private static final class AccumulatingContextOptionScope implements OptionSpecMapperOptionScope { - AccumulatingContextOptionScope(OptionSpecBuilder optionSpecBuilder, Class contextType) { - this.optionSpecBuilder = Objects.requireNonNull(optionSpecBuilder); + AccumulatingContextOptionScope(Class contextType) { this.contextType = Objects.requireNonNull(contextType); } @SuppressWarnings("unchecked") @Override public OptionSpec createOptionSpec(U context, boolean createArray) { - var copy = optionSpecBuilder.copy(); - for (var mutator : optionSpecBuilderMutators) { - mutator.accept(copy, context); + var it = builders.values().iterator(); + + var builder = it.next().initBuilder(context, Optional.empty()); + while (it.hasNext()) { + builder = it.next().initBuilder(context, Optional.of(builder)); } if (createArray) { - return (OptionSpec)copy.createArrayOptionSpec(); + return (OptionSpec)builder.createArrayOptionSpec(); } else { - return copy.createOptionSpec(); + return (OptionSpec)builder.createOptionSpec(); } } @@ -144,14 +170,43 @@ interface OptionSpecMapperOptionScope extends OptionScope { return contextType; } - void addMutator(BiConsumer, U> mutator) { - optionSpecBuilderMutators.add(mutator); + void addMutator(OptionSpecBuilder builder, BiConsumer, U> mutator) { + @SuppressWarnings("unchecked") + var builderWithMutators = ((OptionSpecBuilderWithMutators)builders.computeIfAbsent(new IdentityWrapper<>(builder), _ -> { + return new OptionSpecBuilderWithMutators(builder); + })); + + builderWithMutators.addMutator(mutator); } - private final OptionSpecBuilder optionSpecBuilder; private final Class contextType; - private final List, U>> optionSpecBuilderMutators = new ArrayList<>(); + private final SequencedMap>, OptionSpecBuilderWithMutators> builders = new LinkedHashMap<>(); } + private static final class OptionSpecBuilderWithMutators { + + OptionSpecBuilderWithMutators(OptionSpecBuilder builder) { + this.builder = Objects.requireNonNull(builder); + } + + OptionSpecBuilder initBuilder(U context, Optional> other) { + Objects.requireNonNull(context); + Objects.requireNonNull(other); + + var copy = builder.copy(); + other.ifPresent(copy::interimConverter); + for (var mutator : mutators) { + mutator.accept(copy, context); + } + return copy; + } + + void addMutator(BiConsumer, U> mutator) { + mutators.add(Objects.requireNonNull(mutator)); + } + + private final OptionSpecBuilder builder; + private final List, U>> mutators = new ArrayList<>(); + } } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.java index 8544853d584..7e249ca1508 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.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,28 +35,35 @@ import jdk.jpackage.internal.cli.Validator.ParsedValue; import jdk.jpackage.internal.util.Result; /** - * Defines creating an option value of type {@link T} from a string. + * Defines creating an option value of type {@link U} from value of type {@link T}. * - * @param option value type + * @param input option value type + * @param output option value type */ -interface OptionValueConverter { +interface OptionValueConverter { /** - * Converts the given string value corresponding to the given option name into a - * Java type. + * Converts the given value of type {@link T} corresponding to the given option name + * and option string value to an object of type {@link U}. * * @param optionName the option name - * @param optionValue the string value of the option to convert + * @param optionValue the string value of the option + * @param value the value of the option to convert * @return the conversion result + * @throws ConverterException if internal converter error occurs */ - Result convert(OptionName optionName, StringToken optionValue); + Result convert(OptionName optionName, StringToken optionValue, T value) throws ConverterException; /** * Gives the class of the type of values this converter converts to. * * @return the target class for conversion */ - Class valueType(); + Class valueType(); + + static Result convertString(OptionValueConverter converter, OptionName optionName, StringToken optionValue) { + return converter.convert(optionName, optionValue, optionValue.value()); + } /** * Thrown to indicate an error in the normal execution of the converter. @@ -74,54 +81,78 @@ interface OptionValueConverter { return new Builder<>(); } + static final class Builder { private Builder() { + this(new OneStepBackend<>(new StepBuilder<>(true))); + } + + private Builder(Backend backend) { + this.backend = Objects.requireNonNull(backend); } private Builder(Builder other) { - converter = other.converter; - validator = other.validator; + backend = other.backend.copy(); tokenizer = other.tokenizer; - formatString = other.formatString; - exceptionFactory = other.exceptionFactory; } Builder copy() { return new Builder<>(this); } - OptionValueConverter create() { - return new DefaultOptionValueConverter<>( - converter, - formatString().orElseGet(() -> { - if (exceptionFactory == null) { - return ""; - } else { - return null; - } - }), - exceptionFactory().orElseGet(() -> { - if (formatString == null) { - return OptionValueExceptionFactory.unreachable(); - } else { - return null; - } - }), - validator()); + Builder map(ValueConverter converter) { + Objects.requireNonNull(converter); + return new Builder<>(new TwoStepBackend<>( + backend, + new StepBuilder(false).converter(converter))).tokenizer(tokenizer); + } + + Builder map(Builder other) { + Objects.requireNonNull(other); + switch (backend) { + case OneStepBackend _ -> { + throw new UnsupportedOperationException(); + } + case TwoStepBackend b -> { + var fromInterimValueType = other.backend.valueType().orElseThrow(); + var toInterimValueType = b.interimValueType(); + if (fromInterimValueType.equals(toInterimValueType)) { + @SuppressWarnings("unchecked") + var twoStepBackend = (TwoStepBackend)b; + return new Builder<>(new TwoStepBackend<>( + other.backend, + twoStepBackend.otherConvBuilder())).tokenizer(tokenizer); + } else { + throw new IllegalArgumentException(String.format( + "Expected (%s); actual (%s)", toInterimValueType, fromInterimValueType)); + } + } + } + } + + OptionValueConverter create() { + return backend.create(); } OptionArrayValueConverter createArray() { return new DefaultOptionArrayValueConverter<>(create(), tokenizer); } - Builder converter(ValueConverter v) { - converter = v; + Builder converter(ValueConverter v) { + switch (backend) { + case OneStepBackend b -> { + b.stringConvBuilder().converter(v); + } + case TwoStepBackend _ -> { + throw new UnsupportedOperationException(); + } + } return this; } Builder validator(Validator v) { - validator = v; + backend.validator(v); return this; } @@ -131,12 +162,12 @@ interface OptionValueConverter { } Builder formatString(String v) { - formatString = v; + backend.formatString(v); return this; } Builder exceptionFactory(OptionValueExceptionFactory v) { - exceptionFactory = v; + backend.exceptionFactory(v); return this; } @@ -145,12 +176,30 @@ interface OptionValueConverter { return this; } - Optional> converter() { - return Optional.ofNullable(converter); + boolean hasConverter() { + switch (backend) { + case OneStepBackend b -> { + return b.stringConvBuilder().converter().isPresent(); + } + case TwoStepBackend _ -> { + return true; + } + } } Optional> validator() { - return Optional.ofNullable(validator); + return backend.validator(); + } + + Optional> converter() { + switch (backend) { + case OneStepBackend b -> { + return b.stringConvBuilder().converter(); + } + case TwoStepBackend _ -> { + throw new UnsupportedOperationException(); + } + } } Optional> tokenizer() { @@ -158,68 +207,15 @@ interface OptionValueConverter { } Optional formatString() { - return Optional.ofNullable(formatString); + return backend.formatString(); } Optional> exceptionFactory() { - return Optional.ofNullable(exceptionFactory); + return backend.exceptionFactory(); } - private record DefaultOptionValueConverter(ValueConverter converter, String formatString, - OptionValueExceptionFactory exceptionFactory, - Optional> validator) implements OptionValueConverter { - - DefaultOptionValueConverter { - Objects.requireNonNull(converter); - Objects.requireNonNull(formatString); - Objects.requireNonNull(exceptionFactory); - Objects.requireNonNull(validator); - } - - @Override - public Result convert(OptionName optionName, StringToken optionValue) { - Objects.requireNonNull(optionName); - - final T convertedValue; - try { - convertedValue = converter.convert(optionValue.value()); - } catch (Exception ex) { - return handleException(optionName, optionValue, ex); - } - - final List validationExceptions = validator.map(val -> { - try { - return val.validate(optionName, ParsedValue.create(convertedValue, optionValue)); - } catch (Validator.ValidatorException ex) { - // All unexpected exceptions that the converter yields should be tunneled via ConverterException. - throw new ConverterException(ex.getCause()); - } - }).orElseGet(List::of); - - if (validationExceptions.isEmpty()) { - return Result.ofValue(convertedValue); - } else { - return Result.ofErrors(validationExceptions); - } - } - - @Override - public Class valueType() { - return converter.valueType(); - } - - private Result handleException(OptionName optionName, StringToken optionValue, Exception ex) { - if (ex instanceof IllegalArgumentException) { - return Result.ofError(exceptionFactory.create(optionName, optionValue, formatString, Optional.of(ex))); - } else { - throw new ConverterException(ex); - } - } - } - - - private record DefaultOptionArrayValueConverter(OptionValueConverter elementConverter, + private record DefaultOptionArrayValueConverter(OptionValueConverter elementConverter, Function tokenizer) implements OptionArrayValueConverter { DefaultOptionArrayValueConverter { @@ -229,14 +225,18 @@ interface OptionValueConverter { @SuppressWarnings("unchecked") @Override - public Result convert(OptionName optionName, StringToken optionValue) { + public Result convert(OptionName optionName, StringToken optionValue, String value) { + + if (!value.equals(optionValue.value())) { + throw new IllegalArgumentException(); + } final List exceptions = new ArrayList<>(); final List convertedValues = new ArrayList<>(); final var tokens = tokenize(optionValue.value()); for (var token : tokens) { - final var result = elementConverter.convert(optionName, StringToken.of(optionValue.value(), token)); + final var result = elementConverter.convert(optionName, StringToken.of(optionValue.value(), token), token); exceptions.addAll(result.errors()); if (exceptions.isEmpty()) { result.value().ifPresent(convertedValues::add); @@ -264,10 +264,311 @@ interface OptionValueConverter { } } - private ValueConverter converter; - private Validator validator; + + private record TwoStepOptionValueConverter(OptionValueConverter stringConverter, + OptionValueConverter converter) implements OptionValueConverter { + + TwoStepOptionValueConverter { + Objects.requireNonNull(stringConverter); + Objects.requireNonNull(converter); + } + + @Override + public Result convert(OptionName optionName, StringToken optionValue, String value) { + final var interimResult = stringConverter.convert(optionName, optionValue, value); + return interimResult.flatMap(interimValue -> { + return converter.convert(optionName, optionValue, interimValue); + }); + } + + @Override + public Class valueType() { + return converter.valueType(); + } + } + + + private sealed interface Backend { + + OptionValueConverter create(); + + Backend copy(); + + void validator(Validator v); + + void formatString(String v); + + void exceptionFactory(OptionValueExceptionFactory v); + + Optional> valueType(); + + Optional> validator(); + + Optional formatString(); + + Optional> exceptionFactory(); + } + + + private record OneStepBackend(StepBuilder stringConvBuilder) implements Backend { + + OneStepBackend { + Objects.requireNonNull(stringConvBuilder); + } + + @Override + public Backend copy() { + return new OneStepBackend<>(stringConvBuilder.copy()); + } + + @Override + public OptionValueConverter create() { + return stringConvBuilder.create(); + } + + @Override + public void validator(Validator v) { + stringConvBuilder.validator(v); + } + + @Override + public void formatString(String v) { + stringConvBuilder.formatString(v); + } + + @Override + public void exceptionFactory(OptionValueExceptionFactory v) { + stringConvBuilder.exceptionFactory(v); + } + + @Override + public Optional> valueType() { + return stringConvBuilder.converter().map(ValueConverter::valueType); + } + + @Override + public Optional> validator() { + return stringConvBuilder.validator(); + } + + @Override + public Optional formatString() { + return stringConvBuilder.formatString(); + } + + @Override + public Optional> exceptionFactory() { + return stringConvBuilder.exceptionFactory(); + } + } + + + private record TwoStepBackend(Backend stringConvBuilder, StepBuilder otherConvBuilder) implements Backend { + + TwoStepBackend { + Objects.requireNonNull(stringConvBuilder); + Objects.requireNonNull(otherConvBuilder); + } + + Class interimValueType() { + return stringConvBuilder.valueType().orElseThrow(); + } + + @Override + public Backend copy() { + return new TwoStepBackend<>(stringConvBuilder.copy(), otherConvBuilder.copy()); + } + + @Override + public OptionValueConverter create() { + return new TwoStepOptionValueConverter<>(stringConvBuilder.create(), otherConvBuilder.create()); + } + + @Override + public void validator(Validator v) { + otherConvBuilder.validator(v); + } + + @Override + public void formatString(String v) { + otherConvBuilder.formatString(v); + } + + @Override + public void exceptionFactory(OptionValueExceptionFactory v) { + otherConvBuilder.exceptionFactory(v); + } + + @Override + public Optional> valueType() { + return otherConvBuilder.converter().map(ValueConverter::valueType); + } + + @Override + public Optional> validator() { + return otherConvBuilder.validator(); + } + + @Override + public Optional formatString() { + return otherConvBuilder.formatString(); + } + + @Override + public Optional> exceptionFactory() { + return otherConvBuilder.exceptionFactory(); + } + } + + + private static final class StepBuilder { + + private StepBuilder(boolean starter) { + this.starter = starter; + } + + private StepBuilder(StepBuilder other) { + starter = other.starter; + converter = other.converter; + validator = other.validator; + formatString = other.formatString; + exceptionFactory = other.exceptionFactory; + } + + StepBuilder copy() { + return new StepBuilder<>(this); + } + + OptionValueConverter create() { + return new DefaultOptionValueConverter<>( + converter, + formatString().orElseGet(() -> { + if (exceptionFactory == null) { + return ""; + } else { + return null; + } + }), + exceptionFactory().orElseGet(() -> { + if (formatString == null) { + return OptionValueExceptionFactory.unreachable(); + } else { + return null; + } + }), + validator(), + starter); + } + + StepBuilder converter(ValueConverter v) { + converter = v; + return this; + } + + StepBuilder validator(Validator v) { + validator = v; + return this; + } + + StepBuilder formatString(String v) { + formatString = v; + return this; + } + + StepBuilder exceptionFactory(OptionValueExceptionFactory v) { + exceptionFactory = v; + return this; + } + + Optional> converter() { + return Optional.ofNullable(converter); + } + + Optional> validator() { + return Optional.ofNullable(validator); + } + + Optional formatString() { + return Optional.ofNullable(formatString); + } + + Optional> exceptionFactory() { + return Optional.ofNullable(exceptionFactory); + } + + + private record DefaultOptionValueConverter( + ValueConverter converter, + String formatString, + OptionValueExceptionFactory exceptionFactory, + Optional> validator, + boolean starter) implements OptionValueConverter { + + DefaultOptionValueConverter { + Objects.requireNonNull(converter); + Objects.requireNonNull(formatString); + Objects.requireNonNull(exceptionFactory); + Objects.requireNonNull(validator); + } + + @Override + public Result convert(OptionName optionName, StringToken optionValue, T value) { + Objects.requireNonNull(optionName); + Objects.requireNonNull(optionValue); + Objects.requireNonNull(value); + + if (starter && !value.equals(optionValue.value())) { + throw new IllegalArgumentException(); + } + + final U convertedValue; + try { + convertedValue = converter.convert(value); + } catch (Exception ex) { + return handleException(optionName, optionValue, ex); + } + + final List validationExceptions = validator.map(val -> { + try { + return val.validate(optionName, ParsedValue.create(convertedValue, optionValue)); + } catch (Validator.ValidatorException ex) { + // All unexpected exceptions that the converter yields should be tunneled via ConverterException. + throw new ConverterException(ex.getCause()); + } + }).orElseGet(List::of); + + if (validationExceptions.isEmpty()) { + return Result.ofValue(convertedValue); + } else { + return Result.ofErrors(validationExceptions); + } + } + + @Override + public Class valueType() { + return converter.valueType(); + } + + private Result handleException(OptionName optionName, StringToken optionValue, Exception ex) { + if (ex instanceof IllegalArgumentException) { + return Result.ofError(exceptionFactory.create(optionName, optionValue, formatString, Optional.of(ex))); + } else { + throw new ConverterException(ex); + } + } + } + + + private final boolean starter; + private ValueConverter converter; + private Validator validator; + private String formatString; + private OptionValueExceptionFactory exceptionFactory; + } + + + private final Backend backend; private Function tokenizer; - private String formatString; - private OptionValueExceptionFactory exceptionFactory; } } + 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 363aa1b863e..6c32a3d1569 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 @@ -272,7 +272,6 @@ final class OptionsAnalyzer { } else { var spec = new StandardOptionContext(os).mapOptionSpec(typeOption.spec()); return spec - .converter().orElseThrow() .convert(spec.name(), StringToken.of(((String[])obj)[0])) .orElseThrow(); } 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 54003b714a2..3c0eab77f8a 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 @@ -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,6 +29,7 @@ import static java.util.stream.Collectors.counting; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toMap; import static jdk.jpackage.internal.cli.Option.fromOptionSpecPredicate; +import static jdk.jpackage.internal.cli.OptionValueConverter.convertString; import static jdk.jpackage.internal.cli.StandardOption.ADDITIONAL_LAUNCHERS; import static jdk.jpackage.internal.cli.StandardOption.platformOption; @@ -380,8 +381,8 @@ final class OptionsProcessor { } @Override - public Result convert(OptionName optionName, StringToken optionValue) { - return converter.convert(optionName, optionValue).flatMap(arr -> { + public Result convert(OptionName optionName, StringToken optionValue, String value) { + return convertString(converter, optionName, optionValue).flatMap(arr -> { return Stream.of(arr).map(mapper).reduce(Result.>ofValue(new ArrayList<>()), (result, o) -> { if (Result.allHaveValues(result, o)) { return result.map(v -> { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java index 42f90536753..2538bbf4fb4 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java @@ -230,7 +230,7 @@ public final class StandardAppImageFileOption { } return strValue.map(v -> { - return spec.converter().orElseThrow().convert(spec.name(), StringToken.of(v)).orElseThrow(); + return spec.convert(spec.name(), StringToken.of(v)).orElseThrow(); }).map(v -> { return Map.entry(option, v); }); 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 fadd9bacb1c..fedb55116a3 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 @@ -37,6 +37,7 @@ import static jdk.jpackage.internal.cli.StandardOptionValueExceptionFactory.ERRO import static jdk.jpackage.internal.cli.StandardOptionValueExceptionFactory.forMessageWithOptionValueAndName; import static jdk.jpackage.internal.cli.StandardValueConverter.addLauncherShortcutConv; import static jdk.jpackage.internal.cli.StandardValueConverter.booleanConv; +import static jdk.jpackage.internal.cli.StandardValueConverter.explodedPathConverter; import static jdk.jpackage.internal.cli.StandardValueConverter.identityConv; import static jdk.jpackage.internal.cli.StandardValueConverter.mainLauncherShortcutConv; import static jdk.jpackage.internal.cli.StandardValueConverter.pathConv; @@ -44,11 +45,13 @@ import static jdk.jpackage.internal.cli.StandardValueConverter.uuidConv; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; import java.util.regex.Matcher; @@ -61,7 +64,7 @@ import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; -import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.util.RootedPath; import jdk.jpackage.internal.model.SelfContainedException; import jdk.jpackage.internal.util.SetBuilder; @@ -120,9 +123,12 @@ public final class StandardOption { }); })).create(); - public static final OptionValue INPUT = directoryOption("input").addAliases("i") + public static final OptionValue> INPUT = directoryOption("input").addAliases("i") .outOfScope(NOT_BUILDING_APP_IMAGE) - .create(); + .map(explodedPathOptionMapper(explodedPathConverter().create())) + .create(optionValueBuilder -> { + return optionValueBuilder.to(List::of).create(); + }); public static final OptionValue DEST = directoryOption("dest").addAliases("d") .valuePattern("destination path") @@ -193,16 +199,17 @@ public final class StandardOption { .inScope(LauncherProperty.VALUE) .createArray(toList()); - public static final OptionValue> APP_CONTENT = pathOption("app-content") + public static final OptionValue>> APP_CONTENT = existingPathOption("app-content") .tokenizer(",") .valuePattern("additional content") .outOfScope(NOT_BUILDING_APP_IMAGE) + .map(explodedPathOptionMapper(explodedPathConverter().withPathFileName().create())) .mutate(createOptionSpecBuilderMutator((b, context) -> { if (context.os() == OperatingSystem.MACOS) { b.description("help.option.app-content" + resourceKeySuffix(context.os())); } })) - .createArray(toList()); + .createArray(toExplodedPathList()); static final OptionValue FILE_ASSOCIATIONS_INTERNAL = fileOption("file-associations") .tokenizer(pathSeparator()) @@ -324,10 +331,11 @@ public final class StandardOption { // MacOS-specific // - public static final OptionValue> MAC_DMG_CONTENT = pathOption("mac-dmg-content") + public static final OptionValue>> MAC_DMG_CONTENT = existingPathOption("mac-dmg-content") .valuePattern("additional content path") .tokenizer(",") - .createArray(toList()); + .map(explodedPathOptionMapper(explodedPathConverter().withPathFileName().create())) + .createArray(toExplodedPathList()); public static final OptionValue MAC_SIGN = booleanOption("mac-sign").scope(MAC_SIGNING).addAliases("s").create(); @@ -513,12 +521,57 @@ public final class StandardOption { static Consumer> directoryOptionMutator() { return builder -> { builder.mutate(pathOptionMutator()) + .mutate(createOptionSpecBuilderMutator((b, context) -> { + context.asFileSource().ifPresent(propertyFile -> { + b.validatorExceptionFactory(forMessageWithOptionValueAndName(propertyFile)); + b.validatorExceptionFormatString("error.properties-parameter-not-directory"); + }); + })) .validator(StandardValidator.IS_DIRECTORY) .validatorExceptionFactory(ERROR_WITH_VALUE_AND_OPTION_NAME) .validatorExceptionFormatString("error.parameter-not-directory"); }; } + static Consumer> existingPathOptionMutator() { + + return builder -> { + builder.mutate(pathOptionMutator()) + .validator(createExistingPathValidator(Validator.build().exceptionFactory(ERROR_WITH_VALUE_AND_OPTION_NAME), true)) + .mutate(createOptionSpecBuilderMutator((b, context) -> { + context.asFileSource().ifPresent(propertyFile -> { + var validatorBuilder = Validator.build(); + validatorBuilder.exceptionFactory(forMessageWithOptionValueAndName(propertyFile)); + b.validator(createExistingPathValidator(validatorBuilder, false)); + }); + })); + }; + } + + private static Validator createExistingPathValidator(Validator.Builder builder, boolean cmdline) { + + if (cmdline) { + builder.formatString("error.parameter-not-directory"); + } else { + builder.formatString("error.properties-parameter-not-directory"); + } + + var isDirectoryValidator = builder.predicate(StandardValidator.IS_DIRECTORY).create(); + + if (cmdline) { + builder.formatString("error.parameter-not-file"); + } else { + builder.formatString("error.properties-parameter-not-file"); + } + + var isFileValidator = builder.predicate(StandardValidator.IS_FILE_OR_SYMLINK).create(); + + @SuppressWarnings("unchecked") + var validator = (Validator)isDirectoryValidator.or(isFileValidator); + + return validator; + } + static Consumer> booleanOptionMutator() { return builder -> { builder.mutate(createOptionSpecBuilderMutator((b, context) -> { @@ -546,6 +599,30 @@ public final class StandardOption { }; } + static Function, OptionSpecBuilder> explodedPathOptionMapper(ValueConverter conv) { + Objects.requireNonNull(conv); + return builder -> { + return builder.map(conv) + .converterExceptionFactory(ERROR_WITH_VALUE_AND_OPTION_NAME) + .converterExceptionFormatString("error.path-parameter-ioexception") + // Add empty mutator to OptionSpecMapperOptionScope to make + // mapped option spec have `RootedPath[]` type. + // Otherwise, it will have `Path` type. + .mutate(createOptionSpecBuilderMutator((b, context) -> { + })); + }; + } + + private static Function, OptionValue>>> toExplodedPathList() { + return builder -> { + return builder.to((RootedPath[][] v) -> { + return Stream.of(v).map(arr -> { + return (Collection)List.of(arr); + }).toList(); + }).create(); + }; + } + private static OptionSpecBuilder option(String name, Class valueType) { return OptionSpecBuilder.create(valueType) .name(Objects.requireNonNull(name)) @@ -574,6 +651,10 @@ public final class StandardOption { return option(name, Path.class).mutate(pathOptionMutator()); } + private static OptionSpecBuilder existingPathOption(String name) { + return option(name, Path.class).mutate(existingPathOptionMutator()); + } + private static OptionSpecBuilder fileOption(String name) { return option(name, Path.class) .valuePattern("file path") @@ -624,8 +705,7 @@ public final class StandardOption { } private static OptionValue createAddLauncherOption(String name) { - OptionValueConverter propertyFileConverter = fileOption(name) - .create().getSpec().converter().orElseThrow(); + var propertyFileSpec = fileOption(name).create().getSpec(); return option(name, AdditionalLauncher.class) .valuePattern("=") @@ -657,7 +737,7 @@ public final class StandardOption { final Path propertyFile; try { - propertyFile = propertyFileConverter.convert(OptionName.of(name), + propertyFile = propertyFileSpec.convert(OptionName.of(name), StringToken.of(value, components[1])).orElseThrow(); } catch (JPackageException ex) { throw new AddLauncherInvalidPropertyFileException(I18N.format( diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.java index 3a310166fa8..b1e1189654d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.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 @@ -25,11 +25,15 @@ package jdk.jpackage.internal.cli; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.UUID; +import java.util.function.Function; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.ParseUtils; +import jdk.jpackage.internal.util.RootedPath; final class StandardValueConverter { @@ -37,33 +41,59 @@ final class StandardValueConverter { private StandardValueConverter() { } - static ValueConverter identityConv() { + static ValueConverter identityConv() { return IDENTITY_CONV; } - static ValueConverter pathConv() { + static ValueConverter pathConv() { return PATH_CONV; } - static ValueConverter uuidConv() { + static ValueConverter uuidConv() { return UUID_CONV; } - static ValueConverter booleanConv() { + static ValueConverter booleanConv() { return BOOLEAN_CONV; } - static ValueConverter mainLauncherShortcutConv() { + static ValueConverter mainLauncherShortcutConv() { return MAIN_LAUNCHER_SHORTCUT_CONV; } - static ValueConverter addLauncherShortcutConv() { + static ValueConverter addLauncherShortcutConv() { return ADD_LAUNCHER_SHORTCUT_CONV; } - private static final ValueConverter IDENTITY_CONV = ValueConverter.create(x -> x, String.class); + static ExplodedPathConverterBuilder explodedPathConverter() { + return new ExplodedPathConverterBuilder(); + } - private static final ValueConverter PATH_CONV = ValueConverter.create(str -> { + static final class ExplodedPathConverterBuilder { + private ExplodedPathConverterBuilder() { + } + + ValueConverter create() { + return ValueConverter.create(path -> { + return explodePath(path, withPathFileName); + }, RootedPath[].class); + } + + ExplodedPathConverterBuilder withPathFileName(boolean v) { + withPathFileName = v; + return this; + } + + ExplodedPathConverterBuilder withPathFileName() { + return withPathFileName(true); + } + + private boolean withPathFileName; + } + + private static final ValueConverter IDENTITY_CONV = ValueConverter.create(x -> x, String.class); + + private static final ValueConverter PATH_CONV = ValueConverter.create(str -> { try { return Path.of(str); } catch (InvalidPathException ex) { @@ -71,13 +101,33 @@ final class StandardValueConverter { } }, Path.class); - private static final ValueConverter UUID_CONV = ValueConverter.create(UUID::fromString, UUID.class); + private static final ValueConverter UUID_CONV = ValueConverter.create(UUID::fromString, UUID.class); - private static final ValueConverter BOOLEAN_CONV = ValueConverter.create(Boolean::valueOf, Boolean.class); + private static final ValueConverter BOOLEAN_CONV = ValueConverter.create(Boolean::valueOf, Boolean.class); - private static final ValueConverter MAIN_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( + private static final ValueConverter MAIN_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( ParseUtils::parseLauncherShortcutForMainLauncher, LauncherShortcut.class); - private static final ValueConverter ADD_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( + private static final ValueConverter ADD_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( ParseUtils::parseLauncherShortcutForAddLauncher, LauncherShortcut.class); + + private static RootedPath[] explodePath(Path path, boolean withPathFileName) throws Exception { + + Function mapper; + if (withPathFileName) { + mapper = RootedPath.toRootedPath(path.getParent()); + } else { + mapper = RootedPath.toRootedPath(path); + } + + RootedPath[] items; + try (var walk = Files.walk(path)) { + items = walk.map(mapper).toArray(RootedPath[]::new); + } catch (IOException ex) { + // IOException is not a converter error, it is a converting error, so map it into IAE. + throw new IllegalArgumentException(ex); + } + + return items; + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java index 91d9d03bd9f..0701071b00f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java @@ -34,7 +34,15 @@ import java.util.stream.Stream; @FunctionalInterface interface Validator { - List validate(OptionName optionName, ParsedValue optionValue); + /** + * Validates the given option value. + * + * @param optionName the name of an option to validate + * @param optionValue the value of an option to validate + * @return the list of validation errors + * @throws ValidatorException if internal validator error occurs + */ + List validate(OptionName optionName, ParsedValue optionValue) throws ValidatorException; default Validator and(Validator after) { Objects.requireNonNull(after); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.java index ba52ca35f50..122b212d295 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.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 @@ -25,41 +25,30 @@ package jdk.jpackage.internal.cli; import java.util.Objects; -import java.util.function.Function; -interface ValueConverter { - - /** - * Converts the given string value into a Java type. - * - * @param value the string to convert - * @return the converted value - * @throws IllegalArgumentException if the given string value can not be - * converted to an object of type {@link T} - */ - T convert(String value) throws IllegalArgumentException; +interface ValueConverter extends ValueConverterFunction { /** * Gives the class of the type of values this converter converts to. * * @return the target class for conversion */ - Class valueType(); + Class valueType(); - static ValueConverter create(Function mapper, Class type) { - Objects.requireNonNull(mapper); + static ValueConverter create(ValueConverterFunction conv, Class type) { + Objects.requireNonNull(conv); Objects.requireNonNull(type); return new ValueConverter<>() { @Override - public T convert(String value) { + public U convert(T value) throws Exception { Objects.requireNonNull(value); - return Objects.requireNonNull(mapper.apply(value)); + return Objects.requireNonNull(conv.convert(value)); } @Override - public Class valueType() { + public Class valueType() { return type; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java new file mode 100644 index 00000000000..28702afff75 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java @@ -0,0 +1,40 @@ +/* + * 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 + * 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.cli; + +@FunctionalInterface +interface ValueConverterFunction { + + /** + * Converts value of one type into another. + * + * @param value the value to convert + * @return the converted value + * @throws IllegalArgumentException if the given value can not be converted to + * an object of type {@link U} + * @throws Exception if internal converter error occurs + */ + U convert(T value) throws Exception, IllegalArgumentException; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java index 5b20a0690d3..7860a04faac 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.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 @@ -25,11 +25,13 @@ package jdk.jpackage.internal.model; import java.nio.file.Path; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; +import jdk.jpackage.internal.util.RootedPath; /** * A generic application for packaging. @@ -78,27 +80,25 @@ public non-sealed interface Application extends BundleSpec { String copyright(); /** - * Gets the source directory of this application if available or an empty - * {@link Optional} instance. + * Gets the source paths that should be copied into + * {@link ApplicationLayout#appDirectory()} directory of the image of this + * application. *

- * Source directory is a directory with the applications's classes and other + * Source paths are supposed to contain the applications's classes and other * resources. * - * @return the source directory of this application + * @return the source paths */ - Optional srcDir(); + Collection appDirSources(); /** - * Gets the input content directories of this application. - *

- * Contents of the content directories will be copied as-is into the dedicated - * location of the application image. + * Gets the source paths that should be copied into + * {@link ApplicationLayout#contentDirectory()} directory of the image of this + * application. * - * @see ApplicationLayout#contentDirectory - * - * @return the input content directories of this application + * @return the source paths */ - List contentDirs(); + Collection contentDirSources(); /** * Gets the unresolved app image layout of this application. @@ -244,8 +244,17 @@ public non-sealed interface Application extends BundleSpec { /** * Default implementation of {@link Application} interface. */ - record Stub(String name, String description, String version, String vendor, String copyright, Optional srcDir, - List contentDirs, AppImageLayout imageLayout, Optional runtimeBuilder, - List launchers, Map extraAppImageFileData) implements Application { + record Stub( + String name, + String description, + String version, + String vendor, + String copyright, + Collection appDirSources, + Collection contentDirSources, + AppImageLayout imageLayout, + Optional runtimeBuilder, + List launchers, + Map extraAppImageFileData) implements Application { } } 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 e1f41e3ff48..6e5de3d9729 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 @@ -91,6 +91,7 @@ error.parameter-add-launcher-malformed=The value "{0}" provided for parameter {1 error.parameter-add-launcher-not-file=The value of path to a property file "{0}" provided for additional launcher "{1}" is not a valid file path error.properties-parameter-not-path=The value "{0}" provided for property "{1}" in "{2}" file is not a valid path error.properties-parameter-not-file=The value "{0}" provided for property "{1}" in "{2}" file is not a file +error.properties-parameter-not-directory=The value "{0}" provided for property "{1}" in "{2}" file is not a directory error.properties-parameter-not-launcher-shortcut-dir=The value "{0}" provided for property "{1}" in "{2}" file is not a valid shortcut startup directory error.no-extensions-for-file-association=No extensions were specified for File Association number {0} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java index 2f7f2682a71..b143a54cb5c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.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 @@ -57,12 +57,6 @@ public final class FileUtils { public static void copyRecursive(Path src, Path dest, CopyOption... options) throws IOException { - copyRecursive(src, dest, List.of(), options); - } - - public static void copyRecursive(Path src, Path dest, - final List excludes, CopyOption... options) - throws IOException { List copyActions = new ArrayList<>(); @@ -71,24 +65,18 @@ public final class FileUtils { @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) { - if (isPathMatch(dir, excludes)) { - return FileVisitResult.SKIP_SUBTREE; - } else { - copyActions.add(new CopyAction(null, dest.resolve(src.relativize(dir)))); - return FileVisitResult.CONTINUE; - } + copyActions.add(new CopyAction(null, dest.resolve(src.relativize(dir)))); + return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) { - if (!isPathMatch(file, excludes)) { - copyActions.add(new CopyAction(file, dest.resolve(src.relativize(file)))); - } + copyActions.add(new CopyAction(file, dest.resolve(src.relativize(file)))); return FileVisitResult.CONTINUE; } }); - } else if (!isPathMatch(src, excludes)) { + } else { Optional.ofNullable(dest.getParent()).ifPresent(dstDir -> { copyActions.add(new CopyAction(null, dstDir)); }); @@ -113,10 +101,6 @@ public final class FileUtils { } } - private static boolean isPathMatch(Path what, List paths) { - return paths.stream().anyMatch(what::endsWith); - } - private static record CopyAction(Path src, Path dest) { void apply(CopyOption... options) throws IOException { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java new file mode 100644 index 00000000000..21d3b6b17b0 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java @@ -0,0 +1,185 @@ +/* + * 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 + * 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.util; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.CopyOption; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * A relative path (branch) rooted at the root. + */ +public sealed interface RootedPath { + + Path root(); + Path branch(); + + default Path fullPath() { + return root().resolve(branch()); + } + + public static Function toRootedPath(Path root) { + return path -> { + if (!path.startsWith(root)) { + throw new IllegalArgumentException(String.format("Expected path [%s] to start with [%s] root", path, root)); + } + return new Details.DefaultRootedPath(root, root.relativize(path), Files.isDirectory(path)); + }; + } + + public static void copy(Stream rootedPaths, Path dest, CopyOption...options) throws IOException { + Objects.requireNonNull(rootedPaths); + Objects.requireNonNull(dest); + + var marks = new HashMap(); + + // Preset marks for the preexisting paths. + Files.walkFileTree(dest, new FileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + marks.put(dir, new Details.PathMark(true, true)); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + marks.put(file, new Details.PathMark(false, true)); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + }); + + var replacePreexisting = Set.of(options).contains(StandardCopyOption.REPLACE_EXISTING); + + Predicate canReplace = v -> { + return v.isPreexisting() && replacePreexisting; + }; + + try { + rootedPaths.sequential().map(Details.DefaultRootedPath.class::cast).forEach(rootedPath -> { + + final var dstPath = dest.resolve(rootedPath.branch()); + + var dstPathMark = marks.get(dstPath); + + if (!Optional.ofNullable(dstPathMark).map(canReplace::test).orElse(true)) { + // Destination path can not be replaced, bail out. + return; + } + + // Check the ancestors of the destination path. + for (var ancestor : Details.ancestorPaths(rootedPath.branch())) { + var mark = Optional.ofNullable(marks.get(dest.resolve(ancestor))); + + if (!mark.map(Details.PathMark::isDirectory).orElse(true)) { + // `ancestor` is a file, don't overwrite it. + return; + } + } + + dstPathMark = rootedPath.createPathMark(); + marks.put(dstPath, dstPathMark); + + try { + if (replacePreexisting && (rootedPath.isDirectory() != dstPathMark.isDirectory())) { + FileUtils.deleteRecursive(dstPath); + } + + if (rootedPath.isDirectory()) { + Files.createDirectories(dstPath); + } else { + Files.createDirectories(dstPath.getParent()); + Files.copy(rootedPath.fullPath(), dstPath, options); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + static final class Details { + + private Details() { + } + + private record DefaultRootedPath(Path root, Path branch, boolean isDirectory) implements RootedPath { + + DefaultRootedPath { + Objects.requireNonNull(root); + Objects.requireNonNull(branch); + if (branch.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + + PathMark createPathMark() { + return new PathMark(isDirectory); + } + } + + private record PathMark(boolean isDirectory, boolean isPreexisting) { + PathMark(boolean isDirectory) { + this(isDirectory, false); + } + } + + private static List ancestorPaths(Path path) { + var ancestors = new ArrayList(); + while ((path = path.getParent()) != null) { + ancestors.add(path); + } + return ancestors; + } + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java index fd86331e2f1..21f941eedf5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java @@ -52,7 +52,7 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { - pipelineBuilder.excludeDirFromCopying(outputDir) + pipelineBuilder .task(ExePackageTaskID.RUN_POST_MSI_USER_SCRIPT) .action(this::runPostMsiScript) .addDependency(PackageTaskID.CREATE_PACKAGE_FILE) 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 c52be726fd2..c72b14a76d5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java @@ -162,7 +162,7 @@ final class WinMsiPackager implements Consumer { @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { - pipelineBuilder.excludeDirFromCopying(outputDir) + pipelineBuilder .task(PackagingPipeline.PackageTaskID.CREATE_CONFIG_FILES) .action(this::prepareConfigFiles) .add() 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 53f4b9b95aa..8fdcc96acff 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 @@ -222,7 +222,7 @@ public class AppImageFileTest { version, null, null, - Optional.empty(), + List.of(), List.of(), null, Optional.empty(), diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java index 86a6cb075d0..de0ba67fece 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java @@ -616,7 +616,7 @@ public class PackagingPipelineTest { "1.0", "Acme", "copyright", - Optional.empty(), + List.of(), List.of(), appImageLayout, runtimeBuilder, diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.java index 1849a3b5333..59c7c85ce85 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.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 @@ -75,11 +75,11 @@ public class OptionSpecMutatorOptionScopeTest { assertEquals(OptionName.of("foo"), mappedSpec.name()); } - assertEquals("731", mappedSpec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertEquals("731", mappedSpec.convert(spec.name(), StringToken.of("str")).orElseThrow()); assertEquals(OptionName.of("foo"), spec.name()); - assertEquals("123", spec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertEquals("123", spec.convert(spec.name(), StringToken.of("str")).orElseThrow()); } @Test @@ -99,11 +99,11 @@ public class OptionSpecMutatorOptionScopeTest { var mappedSpec = new DummyContext(731).mapOptionSpec(spec); - assertArrayEquals(new String[] {"731"}, mappedSpec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertArrayEquals(new String[] {"731"}, mappedSpec.convert(spec.name(), StringToken.of("str")).orElseThrow()); assertEquals(OptionName.of("foo"), spec.name()); - assertArrayEquals(new String[] {"123"}, spec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertArrayEquals(new String[] {"123"}, spec.convert(spec.name(), StringToken.of("str")).orElseThrow()); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.java index a92c677d9cf..66bd44a2402 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.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 @@ -293,11 +293,11 @@ public class OptionSpecTest { return toOptionNames(List.of(names)); } - private static OptionValueConverter converter(ValueConverter conv) { + private static OptionValueConverter converter(ValueConverter conv) { return buildConverter(conv).create(); } - private static OptionValueConverter.Builder buildConverter(ValueConverter conv) { + private static OptionValueConverter.Builder buildConverter(ValueConverter conv) { return OptionValueConverter.build().converter(conv); } @@ -329,7 +329,7 @@ public class OptionSpecTest { return this; } - OptionSpecBuilder converter(OptionValueConverter v) { + OptionSpecBuilder converter(OptionValueConverter v) { converter = v; return this; } @@ -355,7 +355,7 @@ public class OptionSpecTest { } private List names; - private OptionValueConverter converter; + private OptionValueConverter converter; private T defaultOptionalValue; private Set scope = Set.of(new OptionScope() {}); private MergePolicy mergePolicy = MergePolicy.USE_LAST; diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.java index c026ee0639f..3d8f26523a0 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.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 @@ -22,10 +22,12 @@ */ package jdk.jpackage.internal.cli; +import static jdk.jpackage.internal.cli.OptionValueConverter.convertString; import static jdk.jpackage.internal.cli.TestUtils.assertExceptionListEquals; import static jdk.jpackage.internal.cli.TestUtils.configureConverter; import static jdk.jpackage.internal.cli.TestUtils.configureValidator; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -33,6 +35,7 @@ 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.nio.file.Path; import java.util.List; import java.util.function.Function; import java.util.stream.Stream; @@ -55,10 +58,10 @@ public class OptionValueConverterTest { if (positive) { final var token = StringToken.of("758"); - assertEquals(758, converter.convert(OptionName.of("number"), token).orElseThrow()); + assertEquals(758, convertString(converter, OptionName.of("number"), token).orElseThrow()); } else { final var token = StringToken.of("foo"); - final var result = converter.convert(OptionName.of("number"), token); + final var result = convertString(converter, OptionName.of("number"), token); assertEquals(1, result.errors().size()); @@ -77,7 +80,7 @@ public class OptionValueConverterTest { .converter(ValueConverter.create(Integer::valueOf, Integer.class)).create(); Function> convertString = (v) -> { - return converter.convert(OptionName.of("foo"), StringToken.of(v)); + return convertString(converter, OptionName.of("foo"), StringToken.of(v)); }; assertEquals(10, convertString.apply("10").orElseThrow()); @@ -89,24 +92,32 @@ public class OptionValueConverterTest { public void test_Builder_invalid() { assertThrowsExactly(NullPointerException.class, OptionValueConverter.build() - .converter(ValueConverter.create(Integer::valueOf, Integer.class)) + .converter(phonyConverter(Integer.class)) .mutate(configureConverter()) .formatString(null)::create); assertThrowsExactly(NullPointerException.class, OptionValueConverter.build() - .converter(ValueConverter.create(Integer::valueOf, Integer.class)) + .converter(phonyConverter(Integer.class)) .mutate(configureConverter()) .exceptionFactory(null)::create); } - @Test - public void test_Builder_copy() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Builder_copy(boolean twoStep) { Function tokenizer = _ -> { throw new UnsupportedOperationException(); }; - var builder = OptionValueConverter.build() - .converter(ValueConverter.create(Integer::valueOf, Integer.class)) - .validator(Validator.build().predicate(_ -> true).create()) + final OptionValueConverter.Builder builder; + if (twoStep) { + builder = OptionValueConverter.build() + .converter(phonyConverter(Path.class)) + .map(phonyConverter(Integer.class)); + } else { + builder = OptionValueConverter.build().converter(phonyConverter(Integer.class)); + } + + builder.validator(Validator.build().predicate(_ -> true).create()) .mutate(configureConverter()) .tokenizer(tokenizer); @@ -114,7 +125,10 @@ public class OptionValueConverterTest { assertNotSame(copy, builder); - assertSame(builder.converter().orElse(null), copy.converter().orElse(null)); + if (!twoStep) { + assertSame(builder.converter().orElse(null), copy.converter().orElse(null)); + } + assertSame(builder.formatString().orElse(null), copy.formatString().orElse(null)); assertSame(builder.exceptionFactory().orElse(null), copy.exceptionFactory().orElse(null)); assertSame(builder.tokenizer().orElse(null), copy.tokenizer().orElse(null)); @@ -125,6 +139,23 @@ public class OptionValueConverterTest { assertNotEquals(builder.formatString().orElse(null), copy.formatString().orElse(null)); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Builder_hasConverter(boolean twoStep) { + + if (twoStep) { + final var builder = OptionValueConverter.build() + .converter(phonyConverter(Path.class)) + .map(phonyConverter(String.class)); + assertTrue(builder.hasConverter()); + } else { + final var builder = OptionValueConverter.build(); + assertFalse(builder.hasConverter()); + builder.converter(phonyConverter(String.class)); + assertTrue(builder.hasConverter()); + } + } + @Test public void test_Builder_createArray() { @@ -134,7 +165,7 @@ public class OptionValueConverterTest { .tokenizer(str -> str.split(":")) .createArray(); - assertNotEquals(List.of(100, 67, 145), List.of(converter.convert(OptionName.of("foo"), StringToken.of("110:67:145")).orElseThrow())); + assertNotEquals(List.of(100, 67, 145), List.of(convertString(converter, OptionName.of("foo"), StringToken.of("110:67:145")).orElseThrow())); assertEquals(Integer[].class, converter.valueType()); } @@ -170,7 +201,7 @@ public class OptionValueConverterTest { var converter = builder.createArray(); - var result = converter.convert(OptionName.of("foo"), StringToken.of("100:-10:-10:67:str:145:-7")); + var result = convertString(converter, OptionName.of("foo"), StringToken.of("100:-10:-10:67:str:145:-7")); assertExceptionListEquals(Stream.of( new IllegalArgumentException("-10"), @@ -192,7 +223,7 @@ public class OptionValueConverterTest { }, Integer.class)).mutate(configureConverter()).create(); final var token = StringToken.of("foo"); - final var ex = assertThrowsExactly(ConverterException.class, () -> converter.convert(OptionName.of("number"), token)); + final var ex = assertThrowsExactly(ConverterException.class, () -> convertString(converter, OptionName.of("number"), token)); assertSame(exception, ex.getCause()); } @@ -209,8 +240,133 @@ public class OptionValueConverterTest { }).mutate(configureValidator()).create()).create(); final var token = StringToken.of("100"); - final var ex = assertThrowsExactly(ConverterException.class, () -> converter.convert(OptionName.of("number"), token)); + final var ex = assertThrowsExactly(ConverterException.class, () -> convertString(converter, OptionName.of("number"), token)); assertSame(exception, ex.getCause()); } + + @Test + public void testTwoStep() { + + final var multiplyConverter = ValueConverter.create(v -> { + return (long)(v * -1); + }, Long.class); + + final var converter = OptionValueConverter.build() + .converter(ValueConverter.create(Integer::parseInt, Integer.class)) + .map(multiplyConverter) + .create(); + + assertEquals(Long.class, converter.valueType()); + + var result = convertString(converter, OptionName.of("foo"), StringToken.of("123")).orElseThrow(); + assertEquals(-123, result); + } + + @Test + public void testMultiStep() { + + final var converter = OptionValueConverter.build() + .converter(ValueConverter.create(Path::of, Path.class)) + .map(ValueConverter.create(v -> { + return v.normalize().toString(); + }, String.class)) + .map(ValueConverter.create(Integer::valueOf, Integer.class)) + .create(); + + assertEquals(Integer.class, converter.valueType()); + + var result = convertString(converter, OptionName.of("foo"), StringToken.of("./123")).orElseThrow(); + assertEquals(123, result); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2}) + public void testTwoStep_map(int type) { + + var tolower = ValueConverter.create(String::toLowerCase, String.class); + var toupper = ValueConverter.create(String::toUpperCase, String.class); + var reverse = ValueConverter.create(str -> { + return new StringBuilder(str).reverse().toString(); + }, String.class); + + final var baseBuilder = OptionValueConverter.build().converter(toupper); + + switch (type) { + case 0 -> { + assertThrowsExactly(UnsupportedOperationException.class, () -> { + baseBuilder.map(OptionValueConverter.build()); + }); + } + case 1 -> { + var builder = baseBuilder.map(reverse); + var ex = assertThrowsExactly(IllegalArgumentException.class, () -> { + builder.map(OptionValueConverter.build().converter(phonyConverter(Object.class))); + }); + assertEquals(String.format("Expected (%s); actual (%s)", String.class, Object.class), ex.getMessage()); + + assertEquals("CBA", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + var copy = builder.map(baseBuilder); + assertNotSame(copy, builder); + assertEquals("CBA", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("CBA", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + copy = builder.map(baseBuilder.copy().converter(tolower)); + assertNotSame(copy, builder); + assertEquals("cba", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("CBA", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + } + case 2 -> { + var builder = baseBuilder.map(reverse).map(reverse); + var ex = assertThrowsExactly(IllegalArgumentException.class, () -> { + builder.map(OptionValueConverter.build().converter(phonyConverter(Object.class))); + }); + assertEquals(String.format("Expected (%s); actual (%s)", String.class, Object.class), ex.getMessage()); + + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + var copy = builder.map(baseBuilder.map(reverse)); + assertNotSame(copy, builder); + assertEquals("ABC", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + copy = builder.map(baseBuilder.copy().converter(tolower).map(reverse)); + assertNotSame(copy, builder); + assertEquals("abc", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + copy = builder.map(baseBuilder.copy().converter(tolower)); + assertNotSame(copy, builder); + assertEquals("cba", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + } + } + } + + @Test + public void testTwoStep_converter() { + + final var builder = OptionValueConverter.build() + // Create the "String -> Path" converter + .converter(ValueConverter.create(Path::of, Path.class)) + // Map the "String -> Path" converter into the "Path -> String" converter + .map(phonyConverter(String.class)) + // Map the "Path -> String" converter into the "String -> String" converter + .map(phonyConverter(String.class)); + + assertThrowsExactly(UnsupportedOperationException.class, () -> { + builder.converter(phonyConverter(String.class)); + }); + + assertThrowsExactly(UnsupportedOperationException.class, () -> { + builder.converter(); + }); + } + + private static ValueConverter phonyConverter(Class valueType) { + return ValueConverter.create(v -> { + throw new AssertionError(); + }, valueType); + } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java index 0b70f4151cc..58a78edb627 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java @@ -24,6 +24,8 @@ package jdk.jpackage.internal.cli; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.cli.TestUtils.assertExceptionListEquals; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; @@ -58,9 +60,11 @@ import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; +import jdk.jpackage.internal.util.RootedPath; import jdk.jpackage.internal.util.StringBundle; import jdk.jpackage.test.Comm; import jdk.jpackage.test.JUnitAdapter; +import jdk.jpackage.test.JUnitUtils; import jdk.jpackage.test.TKit; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -111,7 +115,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.ICON.getSpec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(name)); + var result = spec.convert(spec.name(), StringToken.of(name)); assertEquals(Path.of(name), result.orElseThrow()); } @@ -121,7 +125,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.ICON.getSpec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(workDir.toString())); + var result = spec.convert(spec.name(), StringToken.of(workDir.toString())); var ex = assertThrows(JPackageException.class, result::orElseThrow); @@ -135,7 +139,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = new StandardOptionContext().forFile(propertyFile).mapOptionSpec(StandardOption.ICON.getSpec()); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(workDir.toString())); + var result = spec.convert(spec.name(), StringToken.of(workDir.toString())); var ex = assertThrows(JPackageException.class, result::orElseThrow); @@ -150,7 +154,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var tempRoot = workDir.resolve(dir); - var value = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(tempRoot.toString())).orElseThrow(); + var value = spec.convert(spec.name(), StringToken.of(tempRoot.toString())).orElseThrow(); assertEquals(tempRoot, value); } @@ -165,14 +169,14 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { Files.writeString(tempRoot, "foo"); var ex = assertThrowsExactly(JPackageException.class, - spec.converter().orElseThrow().convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); + spec.convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); assertEquals(I18N.format("error.parameter-not-empty-directory", tempRoot, "--temp"), ex.getMessage()); assertEquals(NotDirectoryException.class, ex.getCause().getClass()); tempRoot = workDir; ex = assertThrowsExactly(JPackageException.class, - spec.converter().orElseThrow().convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); + spec.convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); assertEquals(I18N.format("error.parameter-not-empty-directory", tempRoot, "--temp"), ex.getMessage()); assertEquals(DirectoryNotEmptyException.class, ex.getCause().getClass()); } @@ -200,13 +204,146 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.TYPE.getSpec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(name)); + var result = spec.convert(spec.name(), StringToken.of(name)); var ex = assertThrows(JPackageException.class, result::orElseThrow); assertEquals(I18N.format("ERR_InvalidInstallerType", name), ex.getMessage()); } + @Test + public void test_APP_CONTENT_valid(@TempDir Path workDir) throws IOException { + + var spec = StandardOption.APP_CONTENT.getSpec(); + + var contentDir = workDir.resolve("a"); + var emptyDir = contentDir.resolve("b/empty-dir"); + var file = contentDir.resolve("file.txt"); + + Files.createDirectories(emptyDir); + Files.createDirectories(file.getParent()); + Files.createFile(file); + + Object convertedValue = spec.convert( + spec.name(), + StringToken.of(Stream.of(contentDir, file).map(Path::toString).collect(joining(","))) + ).orElseThrow(); + + var paths = StandardOption.APP_CONTENT.getFrom(Options.of(Map.of(StandardOption.APP_CONTENT, convertedValue))); + var sortedPathList = paths.stream().flatMap(Collection::stream).map(RootedPath::branch).sorted().toList(); + + var expectedPathList = Stream.of( + "a", + "a/b", + "a/b/empty-dir", + "a/file.txt", + "file.txt" + ).map(Path::of).sorted().toList(); + + assertEquals(expectedPathList, sortedPathList); + } + + @Test + public void test_APP_CONTENT_invalid(@TempDir Path workDir) throws IOException { + var spec = StandardOption.APP_CONTENT.getSpec(); + + var token = StringToken.of(workDir.resolve("nonexistent").toString()); + var result = spec.convert(spec.name(), token); + + assertExceptionListEquals(Stream.of( + "error.parameter-not-directory", + "error.parameter-not-file" + ).map(key -> { + return new JPackageException(I18N.format(key, token.value(), spec.name().formatForCommandLine())); + }).toList(), result.errors()); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_pathOptionMutator(OptionMutatorTest.TestType type) { + new OptionMutatorTest<>(StandardOption.pathOptionMutator(), StandardValueConverter.pathConv()) + .validValue("file.txt") + .invalidValue("\0") + .cmdlineErrorFormatKeys("error.parameter-not-path") + .propertyFileErrorFormatKeys("error.properties-parameter-not-path") + .test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_existingPathOptionMutator(OptionMutatorTest.TestType type, @TempDir Path workDir) { + new OptionMutatorTest<>(StandardOption.existingPathOptionMutator(), StandardValueConverter.pathConv()) + .validValue(workDir.toString()) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-directory", "error.parameter-not-file") + .propertyFileErrorFormatKeys("error.properties-parameter-not-directory", "error.properties-parameter-not-file") + .test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_directoryOptionMutator(OptionMutatorTest.TestType type, @TempDir Path workDir) { + new OptionMutatorTest<>(StandardOption.directoryOptionMutator(), StandardValueConverter.pathConv()) + .validValue(workDir.toString()) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-directory") + .propertyFileErrorFormatKeys("error.properties-parameter-not-directory") + .test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_fileOptionMutator(OptionMutatorTest.TestType type, @TempDir Path workDir) throws IOException { + var test = new OptionMutatorTest<>(StandardOption.fileOptionMutator(), StandardValueConverter.pathConv()) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-file") + .propertyFileErrorFormatKeys("error.properties-parameter-not-file"); + + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + var file = workDir.resolve("file.txt"); + Files.createFile(file); + test.validValue(file.toString()); + } + default -> {} + } + + test.test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_explodedPathOptionMapper(OptionMutatorTest.TestType type, @TempDir Path workDir) throws IOException { + + var explodePath = StandardValueConverter.explodedPathConverter().withPathFileName().create(); + + ValueConverter conv = ValueConverter.create(str -> { + var path = StandardValueConverter.pathConv().convert(str); + return explodePath.convert(path); + }, RootedPath[].class); + + var test = new OptionMutatorTest<>(_ -> {}, conv) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-directory") + .propertyFileErrorFormatKeys("error.properties-parameter-not-directory"); + + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + var dir = workDir.resolve("dir"); + Files.createDirectories(dir); + for (var file : List.of("foo.txt", "bar.txt")) { + Files.createFile(workDir.resolve(file)); + } + test.validValue(dir.toString()); + } + default -> {} + } + + test.test(OptionSpecBuilder.create(Path.class) + .mutate(StandardOption.directoryOptionMutator()) + .map(StandardOption.explodedPathOptionMapper(explodePath)), type); + } + @Test public void test_booleanOptionMutator() { @@ -221,6 +358,13 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { assertFalse(option.containsIn(empty)); } + @Test + public void test_booleanOptionMutator_in_property_file() { + new OptionMutatorTest<>(StandardOption.booleanOptionMutator(), StandardValueConverter.booleanConv()) + .validValue(Boolean.TRUE.toString()) + .test(OptionSpecBuilder.create(Boolean.class), OptionMutatorTest.TestType.TEST_PROPERTY_FILE_VALID); + } + @ParameterizedTest @MethodSource public void testLauncherShortcutOptions(LauncherShortcutTestSpec testSpec) { @@ -303,7 +447,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.ARGUMENTS.getOption().spec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(value)); + var result = spec.convert(spec.name(), StringToken.of(value)); assertEquals(expectedTokens, List.of(result.map(String[].class::cast).orElseThrow())); } @@ -350,7 +494,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { || (bundlingOperation.os() == appImageOS); }).forEach(bundlingOperation -> { var bundleTypeStr = bundlingOperation.bundleTypeValue(); - var bundleType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(bundleTypeStr)).orElseThrow(); + var bundleType = spec.convert(spec.name(), StringToken.of(bundleTypeStr)).orElseThrow(); assertSame(bundlingOperation.bundleType(), bundleType); }); } @@ -391,6 +535,100 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { } + static final class OptionMutatorTest { + + enum TestType { + TEST_CMDLINE_VALID, + TEST_PROPERTY_FILE_VALID, + TEST_CMDLINE_INVALID, + TEST_PROPERTY_FILE_INVALID; + } + + OptionMutatorTest(Consumer> testee, ValueConverter conv) { + this.testee = Objects.requireNonNull(testee); + this.conv = Objects.requireNonNull(conv); + } + + void test(OptionSpecBuilder specBuilder, TestType type) { + var srcSpec = specBuilder.name("foo").mutate(testee).createOptionSpec(); + OptionSpec spec; + + var propertyFile = Path.of("foo.properties"); + + switch (type) { + case TEST_PROPERTY_FILE_VALID, TEST_PROPERTY_FILE_INVALID -> { + spec = new StandardOptionContext().forFile(propertyFile).mapOptionSpec(srcSpec); + } + default -> { + spec = srcSpec; + } + } + + StringToken token; + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + token = StringToken.of(Objects.requireNonNull(validValue)); + } + default -> { + token = StringToken.of(Objects.requireNonNull(invalidValue)); + } + } + + var result = spec.convert(spec.name(), token); + + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + var expected = toFunction(conv::convert).apply(token.value()); + var actual = result.orElseThrow(); + + if (spec.valueType().isArray()) { + JUnitUtils.assertArrayEquals(expected, actual); + } else { + assertEquals(expected, actual); + } + } + case TEST_CMDLINE_INVALID -> { + assertExceptionListEquals(cmdlineErrorFormatKeys.stream().map(key -> { + return new JPackageException(I18N.format(key, token.value(), spec.name().formatForCommandLine())); + }).map(JUnitUtils::removeExceptionCause).toList(), result.errors().stream().map(JUnitUtils::removeExceptionCause).toList()); + } + case TEST_PROPERTY_FILE_INVALID -> { + assertExceptionListEquals(propertyFileErrorFormatKeys.stream().map(key -> { + return new JPackageException(I18N.format(key, token.value(), spec.name().name(), propertyFile)); + }).map(JUnitUtils::removeExceptionCause).toList(), result.errors().stream().map(JUnitUtils::removeExceptionCause).toList()); + } + } + } + + OptionMutatorTest validValue(String v) { + validValue = v; + return this; + } + + OptionMutatorTest invalidValue(String v) { + invalidValue = v; + return this; + } + + OptionMutatorTest cmdlineErrorFormatKeys(String... v) { + cmdlineErrorFormatKeys = List.of(v); + return this; + } + + OptionMutatorTest propertyFileErrorFormatKeys(String... v) { + propertyFileErrorFormatKeys = List.of(v); + return this; + } + + private final Consumer> testee; + private final ValueConverter conv; + private List cmdlineErrorFormatKeys; + private List propertyFileErrorFormatKeys; + private String validValue; + private String invalidValue; + } + + record AddLauncherTestSpec(Optional expected, List expectedErrors, Optional optionValue) { AddLauncherTestSpec { if (expected.isEmpty() == expectedErrors.isEmpty()) { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.java index 6b16b1099c7..491dae60d5f 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.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 @@ -41,7 +41,7 @@ import org.junit.jupiter.params.provider.ValueSource; public class StandardValueConverterTest { @Test - public void test_identityConv() { + public void test_identityConv() throws Exception { final var testee = StandardValueConverter.identityConv(); @@ -58,7 +58,7 @@ public class StandardValueConverterTest { @ParameterizedTest @ValueSource(booleans = {true, false}) - public void test_pathConv(boolean positive) { + public void test_pathConv(boolean positive) throws Exception { final var testee = StandardValueConverter.pathConv(); @@ -72,14 +72,14 @@ public class StandardValueConverterTest { @ParameterizedTest @MethodSource - public void test_booleanConv(String value, Boolean expected) { + public void test_booleanConv(String value, Boolean expected) throws Exception { assertEquals(expected, StandardValueConverter.booleanConv().convert(value)); } @ParameterizedTest @MethodSource - public void test_mainLauncherShortcutConv(String value, LauncherShortcut expected) { + public void test_mainLauncherShortcutConv(String value, LauncherShortcut expected) throws Exception { assertEquals(expected, StandardValueConverter.mainLauncherShortcutConv().convert(value)); } @@ -93,7 +93,7 @@ public class StandardValueConverterTest { @ParameterizedTest @MethodSource - public void test_addLauncherShortcutConv(String value, LauncherShortcut expected) { + public void test_addLauncherShortcutConv(String value, LauncherShortcut expected) throws Exception { assertEquals(expected, StandardValueConverter.addLauncherShortcutConv().convert(value)); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.java index 2067f0fa628..b7639dbfad4 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.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,92 +24,36 @@ package jdk.jpackage.internal.util; 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 java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; +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.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; public class FileUtilsTest { - @ParameterizedTest - @EnumSource(ExcludeType.class) - public void test_copyRecursive_dir(ExcludeType exclude, @TempDir Path workdir) throws IOException { + @Test + public void test_copyRecursive_dir(@TempDir Path workdir) throws IOException { Files.createDirectories(workdir.resolve("from/foo/bar")); Files.createDirectories(workdir.resolve("from/foo/buz")); Files.writeString(workdir.resolve("from/foo/bar/file.txt"), "Hello"); - List excludes = new ArrayList<>(); - switch (exclude) { - case EXCLUDE_FILE -> { - excludes.add(Path.of("file.txt")); - } - case EXCLUDE_DIR -> { - excludes.add(Path.of("bar")); - } - case EXCLUDE_SUBDIR -> { - excludes.add(Path.of("foo")); - } - case EXCLUDE_NONE -> { - } - } - - FileUtils.copyRecursive(workdir.resolve("from"), workdir.resolve("to"), excludes); + FileUtils.copyRecursive(workdir.resolve("from"), workdir.resolve("to")); assertEquals("Hello", Files.readString(workdir.resolve("from/foo/bar/file.txt"))); - - switch (exclude) { - case EXCLUDE_FILE -> { - assertFalse(Files.exists(workdir.resolve("to/foo/bar/file.txt"))); - assertTrue(Files.isDirectory(workdir.resolve("to/foo/bar"))); - } - case EXCLUDE_DIR -> { - assertFalse(Files.exists(workdir.resolve("to/foo/bar"))); - assertTrue(Files.isDirectory(workdir.resolve("to/foo/buz"))); - } - case EXCLUDE_SUBDIR -> { - assertFalse(Files.exists(workdir.resolve("to/foo"))); - assertTrue(Files.isDirectory(workdir.resolve("to"))); - } - case EXCLUDE_NONE -> { - assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); - } - } + assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); } - @ParameterizedTest - @ValueSource(booleans = {true, false}) - public void test_copyRecursive_file(boolean exclude, @TempDir Path workdir) throws IOException { + @Test + public void test_copyRecursive_file(@TempDir Path workdir) throws IOException { Files.createDirectories(workdir.resolve("from/foo/bar")); Files.writeString(workdir.resolve("from/foo/bar/file.txt"), "Hello"); - List excludes = new ArrayList<>(); - if (exclude) { - excludes.add(Path.of("bar/file.txt")); - } - - FileUtils.copyRecursive(workdir.resolve("from/foo/bar/file.txt"), workdir.resolve("to/foo/bar/file.txt"), excludes); + FileUtils.copyRecursive(workdir.resolve("from/foo/bar/file.txt"), workdir.resolve("to/foo/bar/file.txt")); assertEquals("Hello", Files.readString(workdir.resolve("from/foo/bar/file.txt"))); - if (exclude) { - assertFalse(Files.exists(workdir.resolve("to"))); - } else { - assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); - } - } - - enum ExcludeType { - EXCLUDE_NONE, - EXCLUDE_FILE, - EXCLUDE_DIR, - EXCLUDE_SUBDIR, + assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); } } diff --git a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java index 0b63688ee20..530a0d5cb1f 100644 --- a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java +++ b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.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 @@ -64,6 +64,10 @@ public final class JUnitUtils { return EXCEPTION_OM.toMap(ex); } + public static Exception removeExceptionCause(Exception ex) { + return new ExceptionCauseRemover(ex); + } + public static final class ExceptionPattern { @@ -117,6 +121,23 @@ public final class JUnitUtils { } + private static final class ExceptionCauseRemover extends Exception { + + ExceptionCauseRemover(Exception ex) { + super(ex.getMessage()); + type = ex.getClass(); + } + + public Class getType() { + return type; + } + + private final Class type; + + private static final long serialVersionUID = 1L; + } + + @FunctionalInterface private interface ArrayEqualsAsserter { void accept(T expected, T actual); diff --git a/test/jdk/tools/jpackage/share/AppContentTest.java b/test/jdk/tools/jpackage/share/AppContentTest.java index b6066bb9cc4..e275fae262c 100644 --- a/test/jdk/tools/jpackage/share/AppContentTest.java +++ b/test/jdk/tools/jpackage/share/AppContentTest.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 @@ -41,6 +41,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeMap; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; @@ -85,6 +86,7 @@ public class AppContentTest { @Test @ParameterSupplier("test") @ParameterSupplier(value="testSymlink", ifNotOS = WINDOWS) + @ParameterSupplier public void testAppImage(TestSpec testSpec) throws Exception { testSpec.test(new ConfigurationTarget(JPackageCommand.helloAppImage())); } @@ -126,6 +128,14 @@ public class AppContentTest { }).toList(); } + public static Collection testAppImage() { + return Stream.of( + build().add(NonExistentPath.create("*output-app-image*", JPackageCommand::outputBundle)) + ).map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); + } + public static Collection testSymlink() { return Stream.of( build().add(TEST_JAVA) @@ -150,7 +160,7 @@ public class AppContentTest { void test(ConfigurationTarget target) { final int expectedJPackageExitCode; - if (contentFactories.stream().flatMap(List::stream).anyMatch(TEST_BAD::equals)) { + if (contentFactories.stream().flatMap(List::stream).anyMatch(NonExistentPath.class::isInstance)) { expectedJPackageExitCode = 1; } else { expectedJPackageExitCode = 0; @@ -159,9 +169,11 @@ public class AppContentTest { final List> allContent = new ArrayList<>(); target.addInitializer(JPackageCommand::setFakeRuntime) - .addRunOnceInitializer(_ -> { + .addInitializer(cmd -> { contentFactories.stream().map(group -> { - return group.stream().map(ContentFactory::create).toList(); + return group.stream().map(contentFactory -> { + return contentFactory.create(cmd); + }).toList(); }).forEach(allContent::add); }).addInitializer(cmd -> { allContent.stream().map(group -> { @@ -192,6 +204,10 @@ public class AppContentTest { }); target.addInstallVerifier(cmd -> { + if (expectedJPackageExitCode != 0) { + return; + } + var appContentRoot = getAppContentRoot(cmd); Set disabledVerifiers = new HashSet<>(); @@ -329,7 +345,7 @@ public class AppContentTest { @FunctionalInterface private interface ContentFactory { - Content create(); + Content create(JPackageCommand cmd); } private interface Content { @@ -337,12 +353,7 @@ public class AppContentTest { Iterable verifiers(Path appContentRoot); } - private sealed interface PathVerifier permits - RegularFileVerifier, - DirectoryVerifier, - SymlinkTargetVerifier, - NoPathVerifier { - + private sealed interface PathVerifier { Path path(); void verify(); } @@ -467,22 +478,45 @@ public class AppContentTest { /** * Non-existing content. */ - private static final class NonExistantPath implements ContentFactory { + private static final class NonExistentPath implements ContentFactory { + + private NonExistentPath(String label, Function makePath) { + this.label = Objects.requireNonNull(label); + this.makePath = Objects.requireNonNull(makePath); + } + @Override - public Content create() { - var nonExistant = TKit.createTempFile("non-existant"); - try { - TKit.deleteIfExists(nonExistant); - } catch (IOException ex) { - throw new UncheckedIOException(ex); + public Content create(JPackageCommand cmd) { + var nonexistent = makePath.apply(cmd); + if (Files.exists(nonexistent)) { + throw new IllegalStateException(); } - return new FileContent(nonExistant, 0); + return new FileContent(nonexistent, 0); } @Override public String toString() { - return "*non-existant*"; + return label; } + + static NonExistentPath create(String label, Function makePath) { + return new NonExistentPath(label, makePath); + } + + static NonExistentPath create(String path) { + return new NonExistentPath(String.format("*%s*", Objects.requireNonNull(path)), _ -> { + var nonexistent = TKit.createTempFile(path); + try { + TKit.deleteIfExists(nonexistent); + return nonexistent; + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + } + + private final String label; + private final Function makePath; } /** @@ -565,7 +599,7 @@ public class AppContentTest { } @Override - public Content create() { + public Content create(JPackageCommand cmd) { final var appContentRoot = createAppContentRoot(); final var symlinkPath = appContentRoot.resolve(symlinkPath()); @@ -639,7 +673,7 @@ public class AppContentTest { } @Override - public Content create() { + public Content create(JPackageCommand cmd) { Path srcPath = factory.get(); if (!srcPath.endsWith(pathInAppContentRoot)) { throw new IllegalArgumentException(); @@ -672,7 +706,7 @@ public class AppContentTest { private static final ContentFactory TEST_JAVA = createTextFileContent("apps/PrintEnv.java", "Not what someone would expect"); private static final ContentFactory TEST_DUKE = createTextFileContent("duke.txt", "Hi Duke!"); private static final ContentFactory TEST_DIR = createDirTreeContent("apps"); - private static final ContentFactory TEST_BAD = new NonExistantPath(); + private static final ContentFactory TEST_BAD = NonExistentPath.create("non-existent"); // On OSX `--app-content` paths will be copied into the "Contents" folder // of the output app image. diff --git a/test/jdk/tools/jpackage/share/InOutPathTest.java b/test/jdk/tools/jpackage/share/InOutPathTest.java index 6956f66df42..a9fa48dc1c6 100644 --- a/test/jdk/tools/jpackage/share/InOutPathTest.java +++ b/test/jdk/tools/jpackage/share/InOutPathTest.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 @@ -24,26 +24,28 @@ import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK; import java.io.IOException; +import java.io.UncheckedIOException; 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.Set; +import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; -import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.test.Annotations.Parameters; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.AppImageFile; import jdk.jpackage.test.ApplicationLayout; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.JPackageCommand.StandardAssert; import jdk.jpackage.test.PackageFile; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; +import jdk.jpackage.internal.util.function.ThrowingConsumer; /* * @test @@ -76,25 +78,6 @@ public final class InOutPathTest { return data; } - @Parameters(ifNotOS = OperatingSystem.MACOS) - public static Collection appContentInputOther() { - return List.of(new Object[][]{ - {PackageTypeAlias.IMAGE, wrap(cmd -> { - additionalContent(cmd, "--app-content", cmd.outputBundle()); - }, "--app-content same as output bundle")}, - }); - } - - @Parameters(ifOS = OperatingSystem.MACOS) - public static Collection appContentInputOSX() { - var contentsFolder = "Contents/MacOS"; - return List.of(new Object[][]{ - {PackageTypeAlias.IMAGE, wrap(cmd -> { - additionalContent(cmd, "--app-content", cmd.outputBundle().resolve(contentsFolder)); - }, String.format("--app-content same as the \"%s\" folder in the output bundle", contentsFolder))}, - }); - } - @Parameters(ifOS = OperatingSystem.MACOS) public static Collection inputOSX() { return List.of(additionalContentInput(PackageType.MAC_DMG, "--mac-dmg-content").toArray(Object[][]::new)); @@ -146,67 +129,54 @@ public final class InOutPathTest { } @Test - public void test() throws Exception { + public void test() { runTest(packageTypes, configure); } private static Envelope wrap(ThrowingConsumer v, String label) { - return new Envelope(v, label); - } - - private static boolean isAppImageValid(JPackageCommand cmd) { - return !cmd.hasArgument("--app-content") && !cmd.hasArgument("--mac-dmg-content"); + return new Envelope(ThrowingConsumer.toConsumer(v), label); } private static void runTest(Set packageTypes, - ThrowingConsumer configure) throws Exception { - ThrowingConsumer configureWrapper = cmd -> { + Consumer configure) { + + ConfigurationTarget cfg; + if (packageTypes.contains(PackageType.IMAGE)) { + cfg = new ConfigurationTarget( + JPackageCommand.helloAppImage(JAR_PATH.toString() + ":")); + } else { + cfg = new ConfigurationTarget(new PackageTest() + .forTypes(packageTypes) + .configureHelloApp(JAR_PATH.toString() + ":")); + } + + var verifier = new AppDirContentVerifier(); + + cfg.addInitializer(cmd -> { // Make sure the input directory is empty in every test run. // This is needed because jpackage output directories in this test // are subdirectories of the input directory. cmd.setInputToEmptyDirectory(); configure.accept(cmd); if (cmd.hasArgument("--temp") && cmd.isImagePackageType()) { - // Request to build app image wit user supplied temp directory, + // Request to build app image with user supplied temp directory, // ignore external runtime if any to make use of the temp directory // for runtime generation. cmd.ignoreDefaultRuntime(true); } else { cmd.setFakeRuntime(); } + }) + .addInitializer(JPackageCommand::executePrerequisiteActions) + .addInitializer(verifier::captureInputDir); - if (!isAppImageValid(cmd)) { - // Standard asserts for .jpackage.xml fail in messed up app image. Disable them. - // Other standard asserts for app image contents should pass. - cmd.excludeStandardAsserts(StandardAssert.APP_IMAGE_FILE); - } - }; + cfg.cmd().ifPresent(JPackageCommand::executeAndAssertHelloAppImageCreated); - if (packageTypes.contains(PackageType.IMAGE)) { - JPackageCommand cmd = JPackageCommand.helloAppImage(JAR_PATH.toString() + ":"); - configureWrapper.accept(cmd); - cmd.executeAndAssertHelloAppImageCreated(); - if (isAppImageValid(cmd)) { - verifyAppImage(cmd); - } + cfg.addInstallVerifier(verifier::verify); - if (cmd.hasArgument("--app-content")) { - // `--app-content` can be set to the app image directory which - // should not exist before jpackage is executed: - // jpackage --name Foo --dest output --app-content output/Foo - // Verify the directory exists after jpackage execution. - // At least this will catch the case when the value of - // `--app-content` option refers to a path unrelated to jpackage I/O. - TKit.assertDirectoryExists(Path.of(cmd.getArgumentValue("--app-content"))); - } - } else { - new PackageTest() - .forTypes(packageTypes) - .configureHelloApp(JAR_PATH.toString() + ":") - .addInitializer(configureWrapper) - .addInstallVerifier(InOutPathTest::verifyAppImage) - .run(CREATE_AND_UNPACK); - } + cfg.test().ifPresent(pkg -> { + pkg.run(CREATE_AND_UNPACK); + }); } private static void outputDirInInputDir(JPackageCommand cmd) throws @@ -217,8 +187,7 @@ public final class InOutPathTest { cmd.setArgumentValue("--dest", outputDir); } - private static void outputDirSameAsInputDir(JPackageCommand cmd) throws - IOException { + private static void outputDirSameAsInputDir(JPackageCommand cmd) { // Set output dir the same as the input dir cmd.setArgumentValue("--dest", cmd.inputDir()); } @@ -238,38 +207,7 @@ public final class InOutPathTest { cmd.addArguments(argName, appContentFile.getParent()); } - private static void verifyAppImage(JPackageCommand cmd) throws IOException { - if (!isAppImageValid(cmd)) { - // Don't verify the contents of app image as it is invalid. - // jpackage exited without getting stuck in infinite spiral. - // No more expectations from the tool for the give arguments. - return; - } - - final Path rootDir = cmd.isImagePackageType() ? cmd.outputBundle() : cmd.pathToUnpackedPackageFile( - cmd.appInstallationDirectory()); - final Path appDir = ApplicationLayout.platformAppImage().resolveAt( - rootDir).appDirectory(); - - final var knownFiles = Set.of( - JAR_PATH.getName(0).toString(), - PackageFile.getPathInAppImage(Path.of("")).getFileName().toString(), - AppImageFile.getPathInAppImage(Path.of("")).getFileName().toString(), - cmd.name() + ".cfg" - ); - - TKit.assertFileExists(appDir.resolve(JAR_PATH)); - - try (Stream actualFilesStream = Files.list(appDir)) { - var unexpectedFiles = actualFilesStream.map(path -> { - return path.getFileName().toString(); - }).filter(Predicate.not(knownFiles::contains)).toList(); - TKit.assertStringListEquals(List.of(), unexpectedFiles, - "Check there are no unexpected files in `app` folder"); - } - } - - private static final record Envelope(ThrowingConsumer value, String label) { + private record Envelope(Consumer value, String label) { @Override public String toString() { // Will produce the same test description for the same label every @@ -291,8 +229,49 @@ public final class InOutPathTest { private final Set packageTypes; } + private static final class AppDirContentVerifier { + + void captureInputDir(JPackageCommand cmd) { + var root = Path.of(cmd.getArgumentValue("--input")); + try (var walk = Files.walk(root)) { + inputDirFiles = walk.map(root::relativize).toList(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + void verify(JPackageCommand cmd) { + var expectedContent = new HashSet<>(inputDirFiles); + + expectedContent.add(Path.of(cmd.name() + ".cfg")); + if (cmd.isImagePackageType()) { + expectedContent.add(AppImageFile.getPathInAppImage(Path.of("")).getFileName()); + } else { + expectedContent.add(PackageFile.getPathInAppImage(Path.of("")).getFileName()); + } + + final var rootDir = cmd.isImagePackageType() ? cmd.outputBundle() : cmd.pathToUnpackedPackageFile( + cmd.appInstallationDirectory()); + final var appDir = ApplicationLayout.platformAppImage().resolveAt(rootDir).appDirectory(); + + try (var walk = Files.walk(appDir)) { + var unexpectedFiles = walk + .map(appDir::relativize) + .filter(Predicate.not(expectedContent::contains)) + .map(Path::toString) + .toList(); + TKit.assertStringListEquals(List.of(), unexpectedFiles, + "Check there are no unexpected files in `app` folder"); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private Collection inputDirFiles; + } + private final Set packageTypes; - private final ThrowingConsumer configure; + private final Consumer configure; // Placing jar file in the "Resources" subdir of the input directory would allow // to use the input directory with `--app-content` on OSX. From 9b47c23b4b809f7070c6c8279b7ffdf83234dcdb Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 16 Jan 2026 23:16:43 +0000 Subject: [PATCH 039/126] 8375242: [macos] Improve jpackage signing coverage Reviewed-by: almatvee --- .../jdk/jpackage/test/JPackageCommand.java | 107 +++-- .../helpers/jdk/jpackage/test/MacHelper.java | 419 ++++++++++++++++-- .../helpers/jdk/jpackage/test/MacSign.java | 13 + .../jdk/jpackage/test/MacSignVerify.java | 18 +- .../jpackage/macosx/EntitlementsTest.java | 10 +- .../tools/jpackage/macosx/MacSignTest.java | 122 ++--- .../jpackage/macosx/SigningAppImageTest.java | 102 ++--- .../macosx/SigningAppImageTwoStepsTest.java | 202 ++++++--- .../tools/jpackage/macosx/SigningBase.java | 178 ++++++++ .../jpackage/macosx/SigningPackageTest.java | 335 ++++++++------ .../macosx/SigningPackageTwoStepTest.java | 312 ++++++------- .../SigningRuntimeImagePackageTest.java | 268 ++++++----- .../jpackage/macosx/base/SigningBase.java | 316 ------------- 13 files changed, 1413 insertions(+), 989 deletions(-) create mode 100644 test/jdk/tools/jpackage/macosx/SigningBase.java delete mode 100644 test/jdk/tools/jpackage/macosx/base/SigningBase.java 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 7874df3fd69..f81c35cea0b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -294,34 +294,8 @@ public class JPackageCommand extends CommandArguments { public JPackageCommand setFakeRuntime() { verifyMutable(); - - ThrowingConsumer createBulkFile = path -> { - Files.createDirectories(path.getParent()); - try (FileOutputStream out = new FileOutputStream(path.toFile())) { - byte[] bytes = new byte[4 * 1024]; - new SecureRandom().nextBytes(bytes); - out.write(bytes); - } - }; - addPrerequisiteAction(cmd -> { - Path fakeRuntimeDir = TKit.createTempDirectory("fake_runtime"); - - TKit.trace(String.format("Init fake runtime in [%s] directory", - fakeRuntimeDir)); - - if (TKit.isOSX()) { - // Make MacAppImageBuilder happy - createBulkFile.accept(fakeRuntimeDir.resolve(Path.of( - "lib/jli/libjli.dylib"))); - } - - // Make sure fake runtime takes some disk space. - // Package bundles with 0KB size are unexpected and considered - // an error by PackageTest. - createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("lib", "bulk"))); - - cmd.setArgumentValue("--runtime-image", fakeRuntimeDir); + cmd.setArgumentValue("--runtime-image", createInputRuntimeImage(RuntimeImageType.RUNTIME_TYPE_FAKE)); }); return this; @@ -390,24 +364,77 @@ public class JPackageCommand extends CommandArguments { return cmd; } + public enum RuntimeImageType { + + /** + * Runtime suitable for running the default "Hello" test app. + */ + RUNTIME_TYPE_HELLO_APP, + + /** + * Fake runtime. + */ + RUNTIME_TYPE_FAKE, + + ; + } + public static Path createInputRuntimeImage() { + return createInputRuntimeImage(RuntimeImageType.RUNTIME_TYPE_HELLO_APP); + } + + public static Path createInputRuntimeImage(RuntimeImageType role) { + Objects.requireNonNull(role); final Path runtimeImageDir; + switch (role) { - if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { - runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; - } else { - runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); + case RUNTIME_TYPE_FAKE -> { + Consumer createBulkFile = ThrowingConsumer.toConsumer(path -> { + Files.createDirectories(path.getParent()); + try (FileOutputStream out = new FileOutputStream(path.toFile())) { + byte[] bytes = new byte[4 * 1024]; + new SecureRandom().nextBytes(bytes); + out.write(bytes); + } + }); - new Executor().setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments( - "--output", runtimeImageDir.toString(), - "--add-modules", "java.desktop", - "--strip-debug", - "--no-header-files", - "--no-man-pages") - .execute(); + runtimeImageDir = TKit.createTempDirectory("fake_runtime"); + + TKit.trace(String.format("Init fake runtime in [%s] directory", runtimeImageDir)); + + if (TKit.isOSX()) { + // Make MacAppImageBuilder happy + createBulkFile.accept(runtimeImageDir.resolve(Path.of("lib/jli/libjli.dylib"))); + } + + // Make sure fake runtime takes some disk space. + // Package bundles with 0KB size are unexpected and considered + // an error by PackageTest. + createBulkFile.accept(runtimeImageDir.resolve(Path.of("lib", "bulk"))); + } + + case RUNTIME_TYPE_HELLO_APP -> { + if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null && !isFakeRuntime(DEFAULT_RUNTIME_IMAGE)) { + runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; + } else { + runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); + + new Executor().setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", runtimeImageDir.toString(), + "--add-modules", "java.desktop", + "--strip-debug", + "--no-header-files", + "--no-man-pages") + .execute(); + } + } + + default -> { + throw ExceptionBox.reachedUnreachable(); + } } return runtimeImageDir; 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 1cb5532d46a..dc1a7b3512b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -46,6 +46,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; +import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -66,6 +67,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; +import jdk.jpackage.internal.util.Enquoter; import jdk.jpackage.internal.util.FileUtils; import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.PListReader; @@ -75,6 +77,8 @@ 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.CertificateRequest; +import jdk.jpackage.test.MacSign.CertificateType; +import jdk.jpackage.test.MacSign.ResolvedKeychain; import jdk.jpackage.test.PackageTest.PackageHandlers; import jdk.jpackage.test.RunnablePackageTest.Action; import org.xml.sax.SAXException; @@ -430,18 +434,50 @@ public final class MacHelper { } } + public static final class RuntimeBundleBuilder { + + public Path create() { + return createRuntimeBundle(type, Optional.ofNullable(mutator)); + } + + public RuntimeBundleBuilder type(JPackageCommand.RuntimeImageType v) { + type = Objects.requireNonNull(v); + return this; + } + + public RuntimeBundleBuilder mutator(Consumer v) { + mutator = v; + return this; + } + + public RuntimeBundleBuilder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + private RuntimeBundleBuilder() { + } + + private JPackageCommand.RuntimeImageType type = JPackageCommand.RuntimeImageType.RUNTIME_TYPE_HELLO_APP; + private Consumer mutator; + }; + + public static RuntimeBundleBuilder buildRuntimeBundle() { + return new RuntimeBundleBuilder(); + } + public static Path createRuntimeBundle(Consumer mutator) { - return createRuntimeBundle(Optional.of(mutator)); + return buildRuntimeBundle().mutator(Objects.requireNonNull(mutator)).create(); } public static Path createRuntimeBundle() { - return createRuntimeBundle(Optional.empty()); + return buildRuntimeBundle().create(); } - public static Path createRuntimeBundle(Optional> mutator) { + private static Path createRuntimeBundle(JPackageCommand.RuntimeImageType type, Optional> mutator) { Objects.requireNonNull(mutator); - final var runtimeImage = JPackageCommand.createInputRuntimeImage(); + final var runtimeImage = JPackageCommand.createInputRuntimeImage(type); final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); @@ -495,25 +531,244 @@ public final class MacHelper { return cmd; } - public record SignKeyOption(Type type, CertificateRequest certRequest) { + public static final class ResolvableCertificateRequest { + + public ResolvableCertificateRequest( + CertificateRequest certRequest, + Function certResolver, + String label) { + + Objects.requireNonNull(certRequest); + Objects.requireNonNull(certResolver); + Objects.requireNonNull(label); + if (label.isBlank()) { + throw new IllegalArgumentException(); + } + + this.certRequest = certRequest; + this.certResolver = certResolver; + this.label = label; + } + + public ResolvableCertificateRequest( + CertificateRequest certRequest, + ResolvedKeychain keychain, + String label) { + this(certRequest, keychain.asCertificateResolver(), label); + } + + @Override + public String toString() { + return label; + } + + public CertificateRequest certRequest() { + return certRequest; + } + + public X509Certificate cert() { + return certResolver.apply(certRequest); + } + + public CertificateType type() { + return certRequest.type(); + } + + public String name() { + return certRequest.name(); + } + + public String shortName() { + return certRequest.shortName(); + } + + public int days() { + return certRequest.days(); + } + + public boolean expired() { + return certRequest.expired(); + } + + public boolean trusted() { + return certRequest.trusted(); + } + + private final CertificateRequest certRequest; + private final Function certResolver; + private final String label; + } + + public interface NamedCertificateRequestSupplier { + + String name(); + + CertificateRequest certRequest(); + + default ResolvableCertificateRequest certRequest(ResolvedKeychain keychain) { + Objects.requireNonNull(keychain); + var certRequest = Objects.requireNonNull(certRequest()); + if (keychain.spec().certificateRequests().contains(certRequest)) { + return new ResolvableCertificateRequest(certRequest, keychain.asCertificateResolver(), name()); + } else { + throw new IllegalArgumentException(String.format( + "Certificate request %s not found in [%s] keychain", + name(), keychain.spec().keychain().name())); + } + } + } + + public record SignKeyOption(Type type, ResolvableCertificateRequest certRequest, Optional customOptionValue) { public SignKeyOption { Objects.requireNonNull(type); Objects.requireNonNull(certRequest); + Objects.requireNonNull(customOptionValue); + if (customOptionValue.isEmpty() == (type == Type.SIGN_KEY_USER_NAME)) { + throw new IllegalArgumentException(); + } + } + + public SignKeyOption(Type type, ResolvableCertificateRequest certRequest) { + this(type, certRequest, Optional.empty()); + } + + public SignKeyOption( + Type type, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(type, certRequestSupplier.certRequest(keychain)); + } + + public SignKeyOption(String optionValue, ResolvableCertificateRequest certRequest) { + this(Type.SIGN_KEY_USER_NAME, certRequest, Optional.of(optionValue)); + } + + public SignKeyOption( + String optionValue, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(optionValue, certRequestSupplier.certRequest(keychain)); + } + + public enum Name { + KEY_USER_NAME("--mac-signing-key-user-name"), + KEY_IDENTITY_APP_IMAGE("--mac-app-image-sign-identity"), + KEY_IDENTITY_INSTALLER("--mac-installer-sign-identity"), + ; + + Name(String optionName) { + this.optionName = Objects.requireNonNull(optionName); + } + + public String optionName() { + return optionName; + } + + public boolean passThrough() { + return this != KEY_USER_NAME; + } + + private final String optionName; } public enum Type { - SIGN_KEY_USER_NAME, - SIGN_KEY_IDENTITY, + /** + * "--mac-signing-key-user-name" option with custom value + */ + SIGN_KEY_USER_NAME(Name.KEY_USER_NAME), + + /** + * "--mac-signing-key-user-name" option with the short user name, e.g.: + * {@code --mac-signing-key-user-name foo} + */ + SIGN_KEY_USER_SHORT_NAME(Name.KEY_USER_NAME), + + /** + * "--mac-signing-key-user-name" option with the full user name (aka signing + * identity name), e.g.: + * {@code --mac-signing-key-user-name 'Developer ID Application: foo'} + */ + SIGN_KEY_USER_FULL_NAME(Name.KEY_USER_NAME), + + /** + * "--mac-installer-sign-identity" or "--mac-app-image-sign-identity" option + * with the signing identity name, e.g.: + * {@code --mac-app-image-sign-identity 'Developer ID Application: foo'} + */ + SIGN_KEY_IDENTITY(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). + */ + SIGN_KEY_IDENTITY_APP_IMAGE(Name.KEY_IDENTITY_APP_IMAGE), + + /** + * "--mac-installer-sign-identity" regardless of the type of signing identity + * (for signing app image or .pkg installer). + */ + SIGN_KEY_IDENTITY_INSTALLER(Name.KEY_IDENTITY_INSTALLER), + ; + + Type(Map optionNameMap) { + Objects.requireNonNull(optionNameMap); + this.optionNameMapper = certType -> { + return Optional.of(optionNameMap.get(certType)); + }; + } + + Type(Name optionName) { + Objects.requireNonNull(optionName); + this.optionNameMapper = _ -> Optional.of(optionName); + } + + Type() { + this.optionNameMapper = _ -> Optional.empty(); + } + + public Optional mapOptionName(MacSign.CertificateType certType) { + return optionNameMapper.apply(Objects.requireNonNull(certType)); + } + + public static Type[] defaultValues() { + return new Type[] { + SIGN_KEY_USER_SHORT_NAME, + SIGN_KEY_USER_FULL_NAME, + SIGN_KEY_IDENTITY + }; + } + + private final Function> optionNameMapper; } @Override public String toString() { var sb = new StringBuilder(); + sb.append('{'); applyTo((optionName, _) -> { - sb.append(String.format("{%s: %s}", optionName, certRequest)); + sb.append(optionName); + switch (type) { + case SIGN_KEY_USER_FULL_NAME -> { + sb.append("/full"); + } + case SIGN_KEY_USER_NAME -> { + customOptionValue.ifPresent(optionValue -> { + sb.append("=").append(ENQUOTER.applyTo(optionValue)); + }); + } + default -> { + // NOP + } + } + sb.append(": "); }); + sb.append(certRequest).append('}'); return sb.toString(); } @@ -527,35 +782,127 @@ public final class MacHelper { return sign(cmd); } - private void applyTo(BiConsumer sink) { - switch (certRequest.type()) { - case INSTALLER -> { - switch (type) { - case SIGN_KEY_IDENTITY -> { - sink.accept("--mac-installer-sign-identity", certRequest.name()); - return; - } - case SIGN_KEY_USER_NAME -> { - sink.accept("--mac-signing-key-user-name", certRequest.shortName()); - return; - } - } - } - case CODE_SIGN -> { - switch (type) { - case SIGN_KEY_IDENTITY -> { - sink.accept("--mac-app-image-sign-identity", certRequest.name()); - return; - } - case SIGN_KEY_USER_NAME -> { - sink.accept("--mac-signing-key-user-name", certRequest.shortName()); - return; - } - } - } - } + public List asCmdlineArgs() { + String[] args = new String[2]; + applyTo((optionName, optionValue) -> { + args[0] = optionName; + args[1] = optionValue; + }); + return List.of(args); + } - throw new AssertionError(); + private void applyTo(BiConsumer sink) { + type.mapOptionName(certRequest.type()).ifPresent(optionName -> { + sink.accept(optionName.optionName(), optionValue()); + }); + } + + private String optionValue() { + return customOptionValue.orElseGet(() -> { + switch (type) { + case SIGN_KEY_IDENTITY, + SIGN_KEY_USER_FULL_NAME, + SIGN_KEY_IDENTITY_APP_IMAGE, + SIGN_KEY_IDENTITY_INSTALLER -> { + return certRequest.name(); + } + case SIGN_KEY_USER_SHORT_NAME -> { + return certRequest.shortName(); + } + default -> { + throw new IllegalStateException(); + } + } + }); + } + + private static final Enquoter ENQUOTER = Enquoter.identity() + .setEnquotePredicate(Enquoter.QUOTE_IF_WHITESPACES).setQuoteChar('\''); + } + + public record SignKeyOptionWithKeychain(SignKeyOption signKeyOption, ResolvedKeychain keychain) { + + public SignKeyOptionWithKeychain { + Objects.requireNonNull(signKeyOption); + Objects.requireNonNull(keychain); + } + + public SignKeyOptionWithKeychain( + SignKeyOption.Type type, + ResolvableCertificateRequest certRequest, + ResolvedKeychain keychain) { + + this(new SignKeyOption(type, certRequest), keychain); + } + + public SignKeyOptionWithKeychain( + SignKeyOption.Type type, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(type, certRequestSupplier.certRequest(keychain), keychain); + } + + public SignKeyOptionWithKeychain( + String optionValue, + ResolvableCertificateRequest certRequest, + ResolvedKeychain keychain) { + + this(new SignKeyOption(optionValue, certRequest), keychain); + } + + public SignKeyOptionWithKeychain( + String optionValue, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(optionValue, certRequestSupplier.certRequest(keychain), keychain); + } + + public SignKeyOptionWithKeychain( + SignKeyOption.Type type, + CertificateRequest certRequest, + ResolvedKeychain keychain) { + + this(new SignKeyOption( + type, + new ResolvableCertificateRequest( + certRequest, + keychain.asCertificateResolver(), + certRequest.toString())), + keychain); + } + + @Override + public String toString() { + return String.format("%s@%s", signKeyOption, keychain.name()); + } + + public SignKeyOption.Type type() { + return signKeyOption.type(); + } + + public ResolvableCertificateRequest certRequest() { + return signKeyOption.certRequest(); + } + + public JPackageCommand addTo(JPackageCommand cmd) { + Optional.ofNullable(cmd.getArgumentValue("--mac-signing-keychain")).ifPresentOrElse(configuredKeychain -> { + if (!configuredKeychain.equals(keychain.name())) { + throw new IllegalStateException(String.format( + "Command line [%s] already has the '--mac-signing-keychain' option, not adding another one with [%s] value", + cmd, keychain.name())); + } + }, () -> { + useKeychain(cmd, keychain); + }); + return signKeyOption.addTo(cmd); + } + + public JPackageCommand setTo(JPackageCommand cmd) { + cmd.removeArgumentWithValue("--mac-signing-keychain"); + useKeychain(cmd, keychain); + return signKeyOption.setTo(cmd); } } 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 70de6ba92af..3bbbf436300 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java @@ -61,6 +61,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import javax.naming.ldap.LdapName; @@ -1132,6 +1133,18 @@ public final class MacSign { return certMap; } + public Function asCertificateResolver() { + return certRequest -> { + if (!spec.certificateRequests().contains(certRequest)) { + throw new IllegalArgumentException(String.format( + "Certificate request %s not found in [%s] keychain", + certRequest, name())); + } else { + return Objects.requireNonNull(mapCertificateRequests().get(certRequest)); + } + }; + } + private final KeychainWithCertsSpec spec; private volatile Map certMap; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java index 9c469c9362e..ddf899d603c 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java @@ -33,6 +33,7 @@ import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; import jdk.jpackage.internal.util.PListReader; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; import jdk.jpackage.test.MacSign.CertificateHash; import jdk.jpackage.test.MacSign.CertificateRequest; @@ -41,12 +42,10 @@ import jdk.jpackage.test.MacSign.CertificateRequest; */ public final class MacSignVerify { - public static void verifyAppImageSigned( - JPackageCommand cmd, CertificateRequest certRequest, MacSign.ResolvedKeychain keychain) { + public static void verifyAppImageSigned(JPackageCommand cmd, ResolvableCertificateRequest certRequest) { - cmd.verifyIsOfType(PackageType.MAC); + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); Objects.requireNonNull(certRequest); - Objects.requireNonNull(keychain); final Path bundleRoot; if (cmd.isImagePackageType()) { @@ -70,13 +69,12 @@ public final class MacSignVerify { String.format("Check [%s] has sign origin as expected", bundleRoot)); } - public static void verifyPkgSigned(JPackageCommand cmd, CertificateRequest certRequest, MacSign.ResolvedKeychain keychain) { + public static void verifyPkgSigned(JPackageCommand cmd, ResolvableCertificateRequest certRequest) { cmd.verifyIsOfType(PackageType.MAC_PKG); - assertPkgSigned(cmd.outputBundle(), certRequest, - Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest))); + assertPkgSigned(cmd.outputBundle(), certRequest); } - public static void assertSigned(Path path, CertificateRequest certRequest) { + public static void assertSigned(Path path, ResolvableCertificateRequest certRequest) { assertSigned(path); TKit.assertEquals(certRequest.name(), findCodesignSignOrigin(path).orElse(null), String.format("Check [%s] signed with certificate", path)); @@ -108,6 +106,10 @@ public final class MacSignVerify { String.format("Check [%s] unsigned", path)); } + public static void assertPkgSigned(Path path, ResolvableCertificateRequest certRequest) { + assertPkgSigned(path, certRequest.certRequest(), certRequest.cert()); + } + public static void assertPkgSigned(Path path, CertificateRequest certRequest, X509Certificate cert) { final var expectedCertChain = List.of(new SignIdentity(certRequest.name(), CertificateHash.of(cert, SHA256))); final var actualCertChain = getPkgCertificateChain(path); diff --git a/test/jdk/tools/jpackage/macosx/EntitlementsTest.java b/test/jdk/tools/jpackage/macosx/EntitlementsTest.java index 6e03c858db3..052e301a9ae 100644 --- a/test/jdk/tools/jpackage/macosx/EntitlementsTest.java +++ b/test/jdk/tools/jpackage/macosx/EntitlementsTest.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 @@ -56,10 +56,9 @@ import jdk.jpackage.test.TKit; * @test * @summary jpackage with --type app-image "--mac-entitlements" parameter * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build EntitlementsTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror EntitlementsTest.java * @requires (jpackage.test.MacSignTests == "run") * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=EntitlementsTest @@ -142,7 +141,8 @@ public class EntitlementsTest { cmd.mutate(MacHelper.useKeychain(keychain)).mutate(new SignKeyOption( SignKeyOption.Type.SIGN_KEY_IDENTITY, - SigningBase.StandardCertificateRequest.CODESIGN.spec() + SigningBase.StandardCertificateRequest.CODESIGN, + keychain )::addTo); cmd.mutate(new AdditionalLauncher("x")::applyTo); diff --git a/test/jdk/tools/jpackage/macosx/MacSignTest.java b/test/jdk/tools/jpackage/macosx/MacSignTest.java index af7cf448bdc..a824fdb0925 100644 --- a/test/jdk/tools/jpackage/macosx/MacSignTest.java +++ b/test/jdk/tools/jpackage/macosx/MacSignTest.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 @@ -21,12 +21,16 @@ * questions. */ +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY; +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_FULL_NAME; +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME; +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY_APP_IMAGE; + import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; @@ -37,8 +41,11 @@ import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.NamedCertificateRequestSupplier; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; -import jdk.jpackage.test.MacSign.CertificateRequest; import jdk.jpackage.test.MacSign.CertificateType; import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageType; @@ -48,10 +55,9 @@ import jdk.jpackage.test.TKit; * @test * @summary jpackage with --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build MacSignTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror MacSignTest.java * @requires (jpackage.test.MacSignTests == "run") * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=MacSignTest @@ -79,22 +85,28 @@ public class MacSignTest { } MacSign.withKeychain(keychain -> { + + var signingKeyOption = new SignKeyOptionWithKeychain( + SIGN_KEY_IDENTITY, + SigningBase.StandardCertificateRequest.CODESIGN, + keychain); + // --app-content and --type app-image // Expect `message.codesign.failed.reason.app.content` message in the log. // This is not a fatal error, just a warning. // To make jpackage fail, specify bad additional content. - final var cmd = JPackageCommand.helloAppImage() + JPackageCommand.helloAppImage() .ignoreDefaultVerbose(true) .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) .addArguments("--app-content", appContent) - .addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name()); + .mutate(signingKeyOption::addTo) + .mutate(cmd -> { + if (MacHelper.isXcodeDevToolsInstalled()) { + // Check there is no warning about missing xcode command line developer tools. + cmd.validateOutput(TKit.assertTextStream(xcodeWarning.getValue()).negate()); + } + }).execute(1); - if (MacHelper.isXcodeDevToolsInstalled()) { - // Check there is no warning about missing xcode command line developer tools. - cmd.validateOutput(TKit.assertTextStream(xcodeWarning.getValue()).negate()); - } - - MacHelper.useKeychain(cmd, keychain).execute(1); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } @@ -116,13 +128,19 @@ public class MacSignTest { expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign")); MacSign.withKeychain(keychain -> { - final var cmd = new JPackageCommand().setPackageType(PackageType.IMAGE) + + var signingKeyOption = new SignKeyOptionWithKeychain( + SIGN_KEY_IDENTITY, + SigningBase.StandardCertificateRequest.CODESIGN, + keychain); + + new JPackageCommand().setPackageType(PackageType.IMAGE) .ignoreDefaultVerbose(true) .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) .addArguments("--app-image", appImageCmd.outputBundle()) - .addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name()); + .mutate(signingKeyOption::addTo) + .execute(1); - MacHelper.useKeychain(cmd, keychain).execute(1); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } @@ -189,71 +207,78 @@ public class MacSignTest { @Test @ParameterSupplier @ParameterSupplier("testSelectSigningIdentity_JDK_8371094") - public static void testSelectSigningIdentity(String signingKeyUserName, CertificateRequest certRequest) { + public static void testSelectSigningIdentity(SignKeyOptionWithKeychain signKeyOption) { MacSign.withKeychain(keychain -> { - final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) - .setFakeRuntime() - .addArguments("--mac-signing-key-user-name", signingKeyUserName); + final var cmd = JPackageCommand.helloAppImage().setFakeRuntime().mutate(signKeyOption::addTo); - cmd.executeAndAssertHelloAppImageCreated(); + cmd.executeAndAssertImageCreated(); - MacSignVerify.assertSigned(cmd.outputBundle(), certRequest); + MacSignVerify.verifyAppImageSigned(cmd, signKeyOption.certRequest()); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } public static Collection testSelectSigningIdentity() { + var keychain = SigningBase.StandardKeychain.MAIN.keychain(); return Stream.of( SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.CODESIGN_UNICODE - ).map(SigningBase.StandardCertificateRequest::spec).mapMulti((certRequest, acc) -> { - acc.accept(new Object[] {certRequest.shortName(), certRequest}); - acc.accept(new Object[] {certRequest.name(), certRequest}); + ).map(certRequest -> { + return Stream.of( + SIGN_KEY_USER_FULL_NAME, + SIGN_KEY_USER_SHORT_NAME + ).map(type -> { + return new SignKeyOptionWithKeychain(type, certRequest, keychain); + }); + }).flatMap(x -> x).map(v -> { + return new Object[] {v}; }).toList(); } public static Collection testSelectSigningIdentity_JDK_8371094() { return List.of(new Object[] { - "ACME Technologies Limited", SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD.spec() + new SignKeyOptionWithKeychain( + "ACME Technologies Limited", + SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD, + SigningBase.StandardKeychain.MAIN.keychain()) }); } enum SignOption { - EXPIRED_SIGNING_KEY_USER_NAME("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), true, false), - EXPIRED_SIGNING_KEY_USER_NAME_PKG("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.PKG_EXPIRED.spec(), true, false), - EXPIRED_SIGN_IDENTITY("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), false, false), - EXPIRED_CODESIGN_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), false, true), - EXPIRED_PKG_SIGN_IDENTITY("--mac-installer-sign-identity", SigningBase.StandardCertificateRequest.PKG_EXPIRED.spec(), false, true), - GOOD_SIGNING_KEY_USER_NAME("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN.spec(), true, false), - GOOD_SIGNING_KEY_USER_NAME_PKG("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.PKG.spec(), true, false), - GOOD_CODESIGN_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec(), false, true), - GOOD_PKG_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.PKG.spec(), false, true); + EXPIRED_SIGNING_KEY_USER_NAME(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED), + EXPIRED_SIGNING_KEY_USER_NAME_PKG(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.PKG_EXPIRED), + EXPIRED_SIGN_IDENTITY(SIGN_KEY_USER_FULL_NAME, SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED), + EXPIRED_CODESIGN_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED), + EXPIRED_PKG_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.PKG_EXPIRED), + GOOD_SIGNING_KEY_USER_NAME(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.CODESIGN), + GOOD_SIGNING_KEY_USER_NAME_PKG(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.PKG), + GOOD_CODESIGN_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.CODESIGN), + GOOD_PKG_SIGN_IDENTITY(SIGN_KEY_IDENTITY_APP_IMAGE, SigningBase.StandardCertificateRequest.PKG); - SignOption(String option, MacSign.CertificateRequest cert, boolean shortName, boolean passThrough) { - this.option = Objects.requireNonNull(option); - this.cert = Objects.requireNonNull(cert); - this.shortName = shortName; - this.passThrough = passThrough; + SignOption(SignKeyOption.Type optionType, NamedCertificateRequestSupplier certRequestSupplier) { + this.option = new SignKeyOption(optionType, new ResolvableCertificateRequest(certRequestSupplier.certRequest(), _ -> { + throw new UnsupportedOperationException(); + }, certRequestSupplier.name())); } boolean passThrough() { - return passThrough; + return option.type().mapOptionName(option.certRequest().type()).orElseThrow().passThrough(); } boolean expired() { - return cert.expired(); + return option.certRequest().expired(); } String identityName() { - return cert.name(); + return option.certRequest().name(); } CertificateType identityType() { - return cert.type(); + return option.certRequest().type(); } List args() { - return List.of(option, shortName ? cert.shortName() : cert.name()); + return option.asCmdlineArgs(); } static JPackageCommand configureOutputValidation(JPackageCommand cmd, List options, @@ -274,9 +299,6 @@ public class MacSignTest { return cmd; } - private final String option; - private final MacSign.CertificateRequest cert; - private final boolean shortName; - private final boolean passThrough; + private final SignKeyOption option; } } diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index 37ba8a6c299..0f299cb5a24 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.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 @@ -21,90 +21,76 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import jdk.jpackage.test.AdditionalLauncher; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; /** - * Tests generation of app image with --mac-sign and related arguments. Test will - * generate app image and verify signature of main launcher and app bundle itself. - * This test requires that machine is configured with test certificate for - * "Developer ID Application: jpackage.openjdk.java.net" or alternately - * "Developer ID Application: " + name specified by system property: - * "jpackage.mac.signing.key.user.name" - * in the jpackagerTest keychain (or alternately the keychain specified with - * the system property "jpackage.mac.signing.keychain". - * If this certificate is self-signed, it must have be set to - * always allowed access to this keychain" for user which runs test. - * (If cert is real (not self signed), the do not set trust to allow.) + * Tests signing of an app image. + * + *

+ * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type app-image --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningAppImageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningAppImageTest.java * @requires (jpackage.test.MacSignTests == "run") - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningAppImageTest * --jpt-before-run=SigningBase.verifySignTestEnvReady */ public class SigningAppImageTest { @Test - // ({"sign or not", "signing-key or sign-identity", "certificate index"}) - // Sign, signing-key and ASCII certificate - @Parameter({"true", "true", "ASCII_INDEX"}) - // Sign, signing-key and UNICODE certificate - @Parameter({"true", "true", "UNICODE_INDEX"}) - // Sign, signing-indentity and UNICODE certificate - @Parameter({"true", "false", "UNICODE_INDEX"}) - // Unsigned - @Parameter({"false", "true", "INVALID_INDEX"}) - public void test(boolean doSign, boolean signingKey, SigningBase.CertIndex certEnum) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, doSign, signingKey, certEnum); - }), SigningBase.StandardKeychain.MAIN.keychain()); - } + @ParameterSupplier + public static void test(SignKeyOptionWithKeychain sign) { - private void test(MacSign.ResolvedKeychain keychain, boolean doSign, boolean signingKey, SigningBase.CertIndex certEnum) throws Exception { - final var certIndex = certEnum.value(); + var cmd = JPackageCommand.helloAppImage(); - JPackageCommand cmd = JPackageCommand.helloAppImage(); - if (doSign) { - cmd.addArguments("--mac-sign", - "--mac-signing-keychain", - keychain.name()); - if (signingKey) { - cmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(certIndex)); - } else { - cmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(certIndex)); - } - } - AdditionalLauncher testAL = new AdditionalLauncher("testAL"); + var testAL = new AdditionalLauncher("testAL"); testAL.applyTo(cmd); cmd.executeAndAssertHelloAppImageCreated(); - Path launcherPath = cmd.appLauncherPath(); - SigningBase.verifyCodesign(launcherPath, doSign, certIndex); + MacSign.withKeychain(keychain -> { + sign.addTo(cmd); + cmd.executeAndAssertHelloAppImageCreated(); + MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + }, sign.keychain()); + } - Path testALPath = launcherPath.getParent().resolve("testAL"); - SigningBase.verifyCodesign(testALPath, doSign, certIndex); + public static Collection test() { - Path appImage = cmd.outputBundle(); - SigningBase.verifyCodesign(appImage, doSign, certIndex); - if (doSign) { - SigningBase.verifySpctl(appImage, "exec", certIndex); + List data = new ArrayList<>(); + + for (var certRequest : List.of( + SigningBase.StandardCertificateRequest.CODESIGN, + SigningBase.StandardCertificateRequest.CODESIGN_UNICODE + )) { + for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + data.add(new SignKeyOptionWithKeychain( + signIdentityType, + certRequest, + SigningBase.StandardKeychain.MAIN.keychain())); + } } + + return data.stream().map(v -> { + return new Object[] {v}; + }).toList(); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java index 906734e6a9c..2c7ae205e69 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.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 @@ -21,38 +21,40 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.jpackage.test.AdditionalLauncher; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; /** - * Tests generation of app image and then signs generated app image with --mac-sign - * and related arguments. Test will generate app image and verify signature of main - * launcher and app bundle itself. This test requires that machine is configured with - * test certificate for "Developer ID Application: jpackage.openjdk.java.net" or - * alternately "Developer ID Application: " + name specified by system property: - * "jpackage.mac.signing.key.user.name" in the jpackagerTest keychain - * (or alternately the keychain specified with the system property - * "jpackage.mac.signing.keychain". If this certificate is self-signed, it must - * have be set to always allowed access to this keychain" for user which runs test. - * (If cert is real (not self signed), the do not set trust to allow.) + * Tests signing of a signed/unsigned predefined app image. + * + *

+ * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type app-image --app-image "appImage" --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningAppImageTwoStepsTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningAppImageTwoStepsTest.java * @requires (jpackage.test.MacSignTests == "run") * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningAppImageTwoStepsTest @@ -61,67 +63,123 @@ import jdk.jpackage.test.TKit; public class SigningAppImageTwoStepsTest { @Test - // ({"sign or not", "signing-key or sign-identity"}) - // Sign and signing-key - @Parameter({"true", "true"}) - // Sign and sign-identity - @Parameter({"true", "false"}) - // Unsigned - @Parameter({"false", "true"}) - public void test(boolean signAppImage, boolean signingKey) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, signAppImage, signingKey); - }), SigningBase.StandardKeychain.MAIN.keychain()); + @ParameterSupplier + public static void test(TestSpec spec) { + spec.test(); } - private static void test(MacSign.ResolvedKeychain keychain, boolean signAppImage, boolean signingKey) throws Exception { + public record TestSpec(Optional signAppImage, SignKeyOptionWithKeychain sign) { - Path appimageOutput = TKit.createTempDirectory("appimage"); + public TestSpec { + Objects.requireNonNull(signAppImage); + Objects.requireNonNull(sign); + } - // Generate app image. Signed or unsigned based on test - // parameter. We should able to sign predfined app images - // which are signed or unsigned. - JPackageCommand appImageCmd = JPackageCommand.helloAppImage() - .setArgumentValue("--dest", appimageOutput); - if (signAppImage) { - appImageCmd.addArguments("--mac-sign", - "--mac-signing-keychain", - keychain.name()); - if (signingKey) { - appImageCmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); - } else { - appImageCmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(SigningBase.DEFAULT_INDEX)); + @Override + public String toString() { + return Stream.of( + String.format("app-image=%s", signAppImage.map(Objects::toString).orElse("unsigned")), + sign.toString() + ).collect(Collectors.joining("; ")); + } + + static Builder build() { + return new Builder(); + } + + static class Builder { + + TestSpec create() { + return new TestSpec(Optional.ofNullable(signAppImage), sign); + } + + Builder certRequest(SigningBase.StandardCertificateRequest v) { + certRequest = Objects.requireNonNull(v); + return this; + } + + Builder signIdentityType(SignKeyOption.Type v) { + signIdentityType = Objects.requireNonNull(v); + return this; + } + + Builder sign() { + sign = createSignKeyOption(); + return this; + } + + Builder signAppImage() { + signAppImage = createSignKeyOption(); + return this; + } + + private SignKeyOptionWithKeychain createSignKeyOption() { + return new SignKeyOptionWithKeychain( + signIdentityType, + certRequest, + SigningBase.StandardKeychain.MAIN.keychain()); + } + + private SigningBase.StandardCertificateRequest certRequest = SigningBase.StandardCertificateRequest.CODESIGN; + private SignKeyOption.Type signIdentityType = SignKeyOption.Type.SIGN_KEY_IDENTITY; + + private SignKeyOptionWithKeychain signAppImage; + private SignKeyOptionWithKeychain sign; + } + + void test() { + var appImageCmd = JPackageCommand.helloAppImage() + .setFakeRuntime() + .setArgumentValue("--dest", TKit.createTempDirectory("appimage")); + + // Add an additional launcher + AdditionalLauncher testAL = new AdditionalLauncher("testAL"); + testAL.applyTo(appImageCmd); + + signAppImage.ifPresentOrElse(signOption -> { + MacSign.withKeychain(keychain -> { + signOption.addTo(appImageCmd); + appImageCmd.execute(); + MacSignVerify.verifyAppImageSigned(appImageCmd, signOption.certRequest()); + }, signOption.keychain()); + }, appImageCmd::execute); + + var cmd = new JPackageCommand() + .setPackageType(PackageType.IMAGE) + .addArguments("--app-image", appImageCmd.outputBundle()) + .mutate(sign::addTo); + + cmd.executeAndAssertHelloAppImageCreated(); + MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + } + } + + public static Collection test() { + + List data = new ArrayList<>(); + + for (var appImageSign : withAndWithout(SignKeyOption.Type.SIGN_KEY_IDENTITY)) { + var builder = TestSpec.build(); + appImageSign.ifPresent(signIdentityType -> { + // Sign the input app image bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of the input app image bundle. + builder.signIdentityType(signIdentityType) + .certRequest(SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD) + .signAppImage(); + }); + for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + builder.signIdentityType(signIdentityType) + .certRequest(SigningBase.StandardCertificateRequest.CODESIGN); + data.add(builder.sign().create()); } } - // Add addtional launcher - AdditionalLauncher testAL = new AdditionalLauncher("testAL"); - testAL.applyTo(appImageCmd); + return data.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } - // Generate app image - appImageCmd.executeAndAssertHelloAppImageCreated(); - - // Double check if it is signed or unsigned based on signAppImage - SigningBase.verifyAppImageSignature(appImageCmd, signAppImage, "testAL"); - - // Sign app image - JPackageCommand cmd = new JPackageCommand(); - cmd.setPackageType(PackageType.IMAGE) - .addArguments("--app-image", appImageCmd.outputBundle().toAbsolutePath()) - .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", keychain.name()); - if (signingKey) { - cmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); - } else { - cmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(SigningBase.DEFAULT_INDEX)); - } - cmd.executeAndAssertImageCreated(); - - // Should be signed app image - SigningBase.verifyAppImageSignature(appImageCmd, true, "testAL"); + private static List> withAndWithout(T value) { + return List.of(Optional.empty(), Optional.of(value)); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningBase.java b/test/jdk/tools/jpackage/macosx/SigningBase.java new file mode 100644 index 00000000000..5f4367e6096 --- /dev/null +++ b/test/jdk/tools/jpackage/macosx/SigningBase.java @@ -0,0 +1,178 @@ +/* + * 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 + * 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.security.cert.X509Certificate; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; +import jdk.jpackage.test.MacHelper.NamedCertificateRequestSupplier; +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.TKit; + + +/* + * @test + * @summary Setup the environment for jpackage macos signing tests. + * Creates required keychains and signing identities. + * Does NOT run any jpackag tests. + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @compile -Xlint:all -Werror SigningBase.java + * @requires (jpackage.test.MacSignTests == "setup") + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SigningBase.setUp + */ + +/* + * @test + * @summary Tear down the environment for jpackage macos signing tests. + * Deletes required keychains and signing identities. + * Does NOT run any jpackag tests. + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @compile -Xlint:all -Werror SigningBase.java + * @requires (jpackage.test.MacSignTests == "teardown") + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SigningBase.tearDown + */ + +public class SigningBase { + + public enum StandardCertificateRequest implements NamedCertificateRequestSupplier { + CODESIGN(cert().userName(NAME_ASCII)), + CODESIGN_COPY(cert().days(100).userName(NAME_ASCII)), + CODESIGN_ACME_TECH_LTD(cert().days(100).userName("ACME Technologies Limited (ABC12345)")), + PKG(cert().type(CertificateType.INSTALLER).userName(NAME_ASCII)), + PKG_COPY(cert().type(CertificateType.INSTALLER).days(100).userName(NAME_ASCII)), + CODESIGN_UNICODE(cert().userName(NAME_UNICODE)), + PKG_UNICODE(cert().type(CertificateType.INSTALLER).userName(NAME_UNICODE)), + CODESIGN_EXPIRED(cert().expired().userName("expired jpackage test")), + PKG_EXPIRED(cert().expired().type(CertificateType.INSTALLER).userName("expired jpackage test")); + + StandardCertificateRequest(CertificateRequest.Builder specBuilder) { + this.spec = specBuilder.create(); + } + + @Override + public CertificateRequest certRequest() { + return spec; + } + + private static CertificateRequest.Builder cert() { + return new CertificateRequest.Builder(); + } + + private final CertificateRequest spec; + } + + /** + * Standard keychains used in signing tests. + */ + public enum StandardKeychain { + /** + * The primary keychain with good certificates. + */ + MAIN("jpackagerTest.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG, + StandardCertificateRequest.CODESIGN_UNICODE, + StandardCertificateRequest.PKG_UNICODE, + StandardCertificateRequest.CODESIGN_ACME_TECH_LTD), + /** + * A keychain with some good and some expired certificates. + */ + EXPIRED("jpackagerTest-expired.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG, + StandardCertificateRequest.CODESIGN_EXPIRED, + StandardCertificateRequest.PKG_EXPIRED), + /** + * A keychain with duplicated certificates. + */ + DUPLICATE("jpackagerTest-duplicate.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG, + StandardCertificateRequest.CODESIGN_COPY, + StandardCertificateRequest.PKG_COPY), + ; + + StandardKeychain(String keychainName, StandardCertificateRequest... certs) { + this(keychainName, + certs[0].certRequest(), + Stream.of(certs).skip(1).map(StandardCertificateRequest::certRequest).toArray(CertificateRequest[]::new)); + } + + StandardKeychain(String keychainName, CertificateRequest cert, CertificateRequest... otherCerts) { + final var builder = keychain(keychainName).addCert(cert); + List.of(otherCerts).forEach(builder::addCert); + this.keychain = new ResolvedKeychain(builder.create()); + } + + public ResolvedKeychain keychain() { + return keychain; + } + + public X509Certificate mapCertificateRequest(CertificateRequest certRequest) { + return Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest)); + } + + public boolean contains(StandardCertificateRequest certRequest) { + return keychain.spec().certificateRequests().contains(certRequest.spec); + } + + private static KeychainWithCertsSpec.Builder keychain(String name) { + return new KeychainWithCertsSpec.Builder().name(name); + } + + private static List signingEnv() { + return Stream.of(values()).map(StandardKeychain::keychain).map(ResolvedKeychain::spec).toList(); + } + + private final ResolvedKeychain keychain; + } + + public static void setUp() { + MacSign.setUp(StandardKeychain.signingEnv()); + } + + public static void tearDown() { + MacSign.tearDown(StandardKeychain.signingEnv()); + } + + public static void verifySignTestEnvReady() { + if (!Inner.SIGN_ENV_READY) { + TKit.throwSkippedException(new IllegalStateException("Misconfigured signing test environment")); + } + } + + private final class Inner { + private static final boolean SIGN_ENV_READY = MacSign.isDeployed(StandardKeychain.signingEnv()); + } + + private static final String NAME_ASCII = "jpackage.openjdk.java.net"; + private static final String NAME_UNICODE = "jpackage.openjdk.java.net (ö)"; +} diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index b1e9155dacb..8a93ce5f749 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.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 @@ -21,180 +21,247 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.nio.file.Path; -import jdk.jpackage.test.Annotations.Parameter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.SequencedSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.ApplicationLayout; -import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; +import jdk.jpackage.test.MacHelper.SignKeyOption; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; /** - * Tests generation of dmg and pkg with --mac-sign and related arguments. - * Test will generate pkg and verifies its signature. It verifies that dmg - * is not signed, but app image inside dmg is signed. This test requires that - * the machine is configured with test certificate for - * "Developer ID Installer: jpackage.openjdk.java.net" in - * jpackagerTest keychain with - * always allowed access to this keychain for user which runs test. - * note: - * "jpackage.openjdk.java.net" can be over-ridden by system property - * "jpackage.mac.signing.key.user.name", and - * "jpackagerTest" can be over-ridden by system property - * "jpackage.mac.signing.keychain" + * Tests bundling of .pkg and .dmg packages with various signing options. + * + *

+ * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ + /* * @test * @summary jpackage with --type pkg,dmg --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningPackageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest != null) * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningPackageTest - * --jpt-space-subst=* - * --jpt-include=SigningPackageTest.test(true,*true,*true,*ASCII_INDEX) - * --jpt-before-run=SigningBase.verifySignTestEnvReady + * --jpt-run=SigningPackageTest.test + * --jpt-space-subst=* + * --jpt-include=({--mac-signing-key-user-name:*CODESIGN},*{--mac-signing-key-user-name:*PKG},*MAC_DMG+MAC_PKG) + * --jpt-before-run=SigningBase.verifySignTestEnvReady */ /* * @test * @summary jpackage with --type pkg,dmg --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningPackageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest == null) - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningPackageTest + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SigningPackageTest.test * --jpt-before-run=SigningBase.verifySignTestEnvReady */ public class SigningPackageTest { - private static boolean isAppImageSigned(JPackageCommand cmd) { - return cmd.hasArgument("--mac-signing-key-user-name") || - cmd.hasArgument("--mac-app-image-sign-identity"); + @Test + @ParameterSupplier + public static void test(TestSpec spec) { + MacSign.withKeychain(_ -> { + spec.test(); + }, spec.keychain()); } - private static boolean isPKGSigned(JPackageCommand cmd) { - return cmd.hasArgument("--mac-signing-key-user-name") || - cmd.hasArgument("--mac-installer-sign-identity"); + public static Collection test() { + return TestSpec.testCases(true).stream().map(v -> { + return new Object[] {v}; + }).toList(); } - private static void verifyPKG(JPackageCommand cmd) { - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyPkgutil(outputBundle, isPKGSigned(cmd), getCertIndex(cmd)); - if (isPKGSigned(cmd)) { - SigningBase.verifySpctl(outputBundle, "install", getCertIndex(cmd)); - } - } + record TestSpec( + Optional appImageSignOption, + Optional packageSignOption, + Set packageTypes) { - private static void verifyDMG(JPackageCommand cmd) { - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyDMG(outputBundle); - } + TestSpec { + Objects.requireNonNull(appImageSignOption); + Objects.requireNonNull(packageSignOption); + Objects.requireNonNull(packageTypes); - private static void verifyAppImageInDMG(JPackageCommand cmd) { - MacHelper.withExplodedDmg(cmd, dmgImage -> { - Path launcherPath = ApplicationLayout.platformAppImage() - .resolveAt(dmgImage).launchersDirectory().resolve(cmd.name()); - // We will be called with all folders in DMG since JDK-8263155, but - // we only need to verify app. - if (dmgImage.endsWith(cmd.name() + ".app")) { - SigningBase.verifyCodesign(launcherPath, isAppImageSigned(cmd), - getCertIndex(cmd)); - SigningBase.verifyCodesign(dmgImage, isAppImageSigned(cmd), - getCertIndex(cmd)); - if (isAppImageSigned(cmd)) { - SigningBase.verifySpctl(dmgImage, "exec", getCertIndex(cmd)); + if (appImageSignOption.isEmpty() && packageSignOption.isEmpty()) { + // No signing. + throw new IllegalArgumentException(); + } + + if (packageTypes.isEmpty() || !PackageType.MAC.containsAll(packageTypes)) { + // Invalid package types. + throw new IllegalArgumentException(); + } + + if (packageSignOption.isPresent()) { + if (!packageTypes.contains(PackageType.MAC_PKG)) { + // .pkg installer should be signed, but .pkg type is missing. + throw new IllegalArgumentException(); + } + + if (appImageSignOption.isEmpty()) { + if (packageSignOption.get().type() != SignKeyOption.Type.SIGN_KEY_IDENTITY) { + // 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. + // This is wrong because only the "--mac-installer-sign-identity" option + // allows signing a .pkg installer without signing its packaged app image. + throw new IllegalArgumentException(); + } + } else if (appImageSignOption.get().type() != packageSignOption.get().type()) { + // Signing option types should be the same. + throw new IllegalArgumentException(); } } - }); - } - private static int getCertIndex(JPackageCommand cmd) { - if (cmd.hasArgument("--mac-signing-key-user-name")) { - String devName = cmd.getArgumentValue("--mac-signing-key-user-name"); - return SigningBase.getDevNameIndex(devName); - } else { - // Signing-indentity - return SigningBase.CertIndex.UNICODE_INDEX.value(); + if (!(packageTypes instanceof SequencedSet)) { + packageTypes = new TreeSet<>(packageTypes); + } + } + + TestSpec( + Optional appImageSignOption, + Optional packageSignOption, + PackageType... packageTypes) { + this(appImageSignOption, packageSignOption, Set.of(packageTypes)); + } + + @Override + public String toString() { + return Stream.of( + signKeyOptions(), + Stream.of(packageTypes.stream().map(Object::toString).collect(Collectors.joining("+"))) + ).flatMap(x -> x).map(Object::toString).collect(Collectors.joining(", ")); + } + + Stream signKeyOptions() { + return Stream.concat(appImageSignOption.stream(), packageSignOption.stream()); + } + + Optional bundleSignIdentity(PackageType type) { + switch (type) { + case MAC_DMG -> { + return Optional.empty(); + } + case MAC_PKG -> { + return packageSignOption.map(SignKeyOption::certRequest); + } + default -> { + throw new IllegalArgumentException(); + } + } + } + + void test() { + initTest().configureHelloApp().addInstallVerifier(cmd -> { + appImageSignOption.map(SignKeyOption::certRequest).ifPresent(signIdentity -> { + MacSignVerify.verifyAppImageSigned(cmd, signIdentity); + }); + }).run(); + } + + PackageTest initTest() { + return new PackageTest().forTypes(packageTypes).mutate(test -> { + appImageSignOption.ifPresent(signOption -> { + test.addInitializer(signOption::setTo); + }); + packageSignOption.ifPresent(signOption -> { + test.forTypes(PackageType.MAC_PKG, () -> { + test.addInitializer(signOption::setTo); + }); + }); + }).addBundleVerifier(cmd -> { + bundleSignIdentity(cmd.packageType()).ifPresent(signIdentity -> { + MacSignVerify.verifyPkgSigned(cmd, signIdentity); + }); + }).addInitializer(MacHelper.useKeychain(keychain())::accept); + } + + MacSign.ResolvedKeychain keychain() { + return SigningBase.StandardKeychain.MAIN.keychain(); + } + + static List testCases(boolean withUnicode) { + + List data = new ArrayList<>(); + + List> certRequestGroups; + if (withUnicode) { + certRequestGroups = List.of( + List.of(SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.PKG), + List.of(SigningBase.StandardCertificateRequest.CODESIGN_UNICODE, SigningBase.StandardCertificateRequest.PKG_UNICODE) + ); + } else { + certRequestGroups = List.of( + List.of(SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.PKG) + ); + } + + for (var certRequests : certRequestGroups) { + for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + var keychain = SigningBase.StandardKeychain.MAIN.keychain(); + var appImageSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getFirst(), keychain); + var pkgSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getLast(), keychain); + + switch (signIdentityType) { + case SIGN_KEY_IDENTITY -> { + // 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 -> { + // 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. + data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.empty(), PackageType.MAC_DMG)); + } + case SIGN_KEY_USER_FULL_NAME -> { + // Use "--mac-signing-key-user-name" signing option with the full user name. + // Like SIGN_KEY_USER_SHORT_NAME, jpackage will try to use it to sign both + // the packaged app image and the installer (.pkg). + // It will fail to sign the installer, though, because the signing identity is unsuitable. + // That is why, use it with .dmg packaging only and not with .pkg packaging. + data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.empty(), PackageType.MAC_DMG)); + continue; + } + default -> { + // SignKeyOption.Type.defaultValues() should return + // such a sequence that makes this code location unreachable. + throw ExceptionBox.reachedUnreachable(); + } + } + data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.of(pkgSignKeyOption), PackageType.MAC)); + } + } + + return data; } } - - @Test - // ("signing-key or sign-identity", "sign app-image", "sign pkg", "certificate index"}) - // Signing-key and ASCII certificate - @Parameter({"true", "true", "true", "ASCII_INDEX"}) - // Signing-key and UNICODE certificate - @Parameter({"true", "true", "true", "UNICODE_INDEX"}) - // Signing-indentity and UNICODE certificate - @Parameter({"false", "true", "true", "UNICODE_INDEX"}) - // Signing-indentity, but sign app-image only and UNICODE certificate - @Parameter({"false", "true", "false", "UNICODE_INDEX"}) - // Signing-indentity, but sign pkg only and UNICODE certificate - @Parameter({"false", "false", "true", "UNICODE_INDEX"}) - public static void test(boolean signingKey, boolean signAppImage, boolean signPKG, SigningBase.CertIndex certEnum) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, signingKey, signAppImage, signPKG, certEnum); - }), SigningBase.StandardKeychain.MAIN.keychain()); - } - - private static void test(MacSign.ResolvedKeychain keychain, boolean signingKey, boolean signAppImage, boolean signPKG, SigningBase.CertIndex certEnum) throws Exception { - final var certIndex = certEnum.value(); - - new PackageTest() - .configureHelloApp() - .forTypes(PackageType.MAC) - .addInitializer(cmd -> { - cmd.addArguments("--mac-sign", - "--mac-signing-keychain", keychain.name()); - if (signingKey) { - cmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(certIndex)); - } else { - if (signAppImage) { - cmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(certIndex)); - } - if (signPKG) { - cmd.addArguments("--mac-installer-sign-identity", - SigningBase.getInstallerCert(certIndex)); - } - } - }) - .forTypes(PackageType.MAC_PKG) - .addBundleVerifier(SigningPackageTest::verifyPKG) - .forTypes(PackageType.MAC_DMG) - .addInitializer(cmd -> { - if (!signingKey) { - // jpackage throws expected error with - // --mac-installer-sign-identity and DMG type - cmd.removeArgumentWithValue("--mac-installer-sign-identity"); - // In case of not signing app image and DMG we need to - // remove signing completely, otherwise we will default - // to --mac-signing-key-user-name once - // --mac-installer-sign-identity is removed. - if (!signAppImage) { - cmd.removeArgumentWithValue("--mac-signing-keychain"); - cmd.removeArgument("--mac-sign"); - } - } - }) - .addBundleVerifier(SigningPackageTest::verifyDMG) - .addBundleVerifier(SigningPackageTest::verifyAppImageInDMG) - .run(); - } } diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java index 586d8d68444..70ec0e600de 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.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 @@ -21,25 +21,22 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; -import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageFile; @@ -52,22 +49,23 @@ import jdk.jpackage.test.TKit; * signed/unsigned .pkg or .dmg package. * *

- * Prerequisites: A keychain with self-signed certificates as specified in - * {@link SigningBase.StandardKeychain#MAIN}. + * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type pkg,dmg --app-image * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningPackageTwoStepTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java + * @compile -Xlint:all -Werror SigningPackageTwoStepTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest == null) - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningPackageTwoStepTest * --jpt-before-run=SigningBase.verifySignTestEnvReady */ @@ -75,176 +73,148 @@ public class SigningPackageTwoStepTest { @Test @ParameterSupplier - public static void test(TestSpec spec) { - MacSign.withKeychain(toConsumer(keychain -> { - spec.test(keychain); - }), SigningBase.StandardKeychain.MAIN.keychain()); + public static void test(TwoStepsTestSpec spec) { + spec.test(); } - public record TestSpec(Optional signAppImage, Map signPackage) { + @Test + public static void testBundleSignedAppImage() { - public TestSpec { - Objects.requireNonNull(signAppImage); - Objects.requireNonNull(signPackage); + var appImageCmd = JPackageCommand.helloAppImage(); - if ((signAppImage.isEmpty() && signPackage.isEmpty()) || !PackageType.MAC.containsAll(signPackage.keySet())) { - // Unexpected package types. - throw new IllegalArgumentException(); - } + var predefinedAppImageSignOption = predefinedAppImageSignOption(); - // Ensure stable result of toString() call. - if (!SortedMap.class.isInstance(signPackage)) { - signPackage = new TreeMap<>(signPackage); - } - } - - @Override - public String toString() { - var sb = new StringBuilder(); - - signAppImage.ifPresent(signOption -> { - sb.append(String.format("app-image=%s", signOption)); - }); - - if (!sb.isEmpty() && !signPackage.isEmpty()) { - sb.append("; "); - } - - if (!signPackage.isEmpty()) { - sb.append(signPackage); - } - - return sb.toString(); - } - - boolean signNativeBundle() { - return signPackage.isEmpty(); - } - - static Builder build() { - return new Builder(); - } - - static class Builder { - - TestSpec create() { - return new TestSpec(Optional.ofNullable(signAppImage), signPackage); - } - - Builder certRequest(SigningBase.StandardCertificateRequest v) { - return certRequest(v.spec()); - } - - Builder certRequest(MacSign.CertificateRequest v) { - certRequest = Objects.requireNonNull(v); - return this; - } - - Builder signIdentityType(SignKeyOption.Type v) { - signIdentityType = Objects.requireNonNull(v); - return this; - } - - Builder signAppImage() { - signAppImage = createSignKeyOption(); - return this; - } - - Builder signPackage(PackageType type) { - Objects.requireNonNull(type); - signPackage.put(type, createSignKeyOption()); - return this; - } - - Builder signPackage() { - PackageType.MAC.forEach(this::signPackage); - return this; - } - - private SignKeyOption createSignKeyOption() { - return new SignKeyOption(signIdentityType, certRequest); - } - - private MacSign.CertificateRequest certRequest = SigningBase.StandardCertificateRequest.CODESIGN.spec(); - private SignKeyOption.Type signIdentityType = SignKeyOption.Type.SIGN_KEY_IDENTITY; - - private SignKeyOption signAppImage; - private Map signPackage = new HashMap<>(); - } - - void test(MacSign.ResolvedKeychain keychain) { - - var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime(); - MacHelper.useKeychain(appImageCmd, keychain); - signAppImage.ifPresent(signOption -> { - signOption.setTo(appImageCmd); - }); - - var test = new PackageTest(); - - signAppImage.map(SignKeyOption::certRequest).ifPresent(certRequest -> { - // The predefined app image is signed, verify bundled app image is signed too. - test.addInstallVerifier(cmd -> { - MacSignVerify.verifyAppImageSigned(cmd, certRequest, keychain); - }); - }); - - Optional.ofNullable(signPackage.get(PackageType.MAC_PKG)).map(SignKeyOption::certRequest).ifPresent(certRequest -> { - test.forTypes(PackageType.MAC_PKG, () -> { - test.addBundleVerifier(cmd -> { - MacSignVerify.verifyPkgSigned(cmd, certRequest, keychain); - }); - }); - }); - - test.forTypes(signPackage.keySet()).addRunOnceInitializer(() -> { - appImageCmd.setArgumentValue("--dest", TKit.createTempDirectory("appimage")).execute(0); - }).usePredefinedAppImage(appImageCmd).addInitializer(cmd -> { - MacHelper.useKeychain(cmd, keychain); - Optional.ofNullable(signPackage.get(cmd.packageType())).ifPresent(signOption -> { - signOption.setTo(cmd); - }); - - if (signAppImage.isPresent()) { - // Predefined app image is signed. Expect a warning. - cmd.validateOutput(JPackageStringBundle.MAIN.cannedFormattedString( - "warning.per.user.app.image.signed", - PackageFile.getPathInAppImage(Path.of("")))); - } else if (cmd.packageType() == PackageType.MAC_PKG && signPackage.containsKey(cmd.packageType())) { - // Create signed ".pkg" bundle from the unsigned predefined app image. Expect a warning. - cmd.validateOutput(JPackageStringBundle.MAIN.cannedFormattedString("warning.unsigned.app.image", "pkg")); - } - }) - .run(); - } + new PackageTest().addRunOnceInitializer(() -> { + createPredefinedAppImage(appImageCmd, Optional.of(predefinedAppImageSignOption)); + }).usePredefinedAppImage(appImageCmd).addInitializer(cmd -> { + configureOutputValidator(cmd, true, false); + }).addInstallVerifier(cmd -> { + MacSignVerify.verifyAppImageSigned(cmd, predefinedAppImageSignOption.certRequest()); + }).run(); } public static Collection test() { - List data = new ArrayList<>(); + List data = new ArrayList<>(); - Stream.of(SignKeyOption.Type.values()).flatMap(signIdentityType -> { - return Stream.of( - // Sign both predefined app image and native package. - TestSpec.build().signIdentityType(signIdentityType) - .signAppImage() - .signPackage() - .certRequest(SigningBase.StandardCertificateRequest.PKG) - .signPackage(PackageType.MAC_PKG), + for (var signAppImage : List.of(true, false)) { + Optional appImageSignOption; + if (signAppImage) { + // Sign the predefined app image bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of + // the predefined app image bundle when backing it in the pkg or dmg installer. + appImageSignOption = Optional.of(predefinedAppImageSignOption()); + } else { + appImageSignOption = Optional.empty(); + } - // Don't sign predefined app image, sign native package. - TestSpec.build().signIdentityType(signIdentityType) - .signPackage() - .certRequest(SigningBase.StandardCertificateRequest.PKG) - .signPackage(PackageType.MAC_PKG), + for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + data.add(new TwoStepsTestSpec(appImageSignOption, signPackage)); + } + } - // Sign predefined app image, don't sign native package. - TestSpec.build().signIdentityType(signIdentityType).signAppImage() - ); - }).forEach(data::add); - - return data.stream().map(TestSpec.Builder::create).map(v -> { + return data.stream().map(v -> { return new Object[] {v}; }).toList(); } + + record TwoStepsTestSpec(Optional signAppImage, SigningPackageTest.TestSpec signPackage) { + + TwoStepsTestSpec { + Objects.requireNonNull(signAppImage); + Objects.requireNonNull(signPackage); + } + + @Override + public String toString() { + return Stream.of( + String.format("app-image=%s", signAppImage.map(Objects::toString).orElse("unsigned")), + signPackage.toString() + ).collect(Collectors.joining("; ")); + } + + Optional packagedAppImageSignIdentity() { + return signAppImage.map(SignKeyOptionWithKeychain::certRequest); + } + + void test() { + + var appImageCmd = JPackageCommand.helloAppImage(); + + var test = signPackage.initTest().addRunOnceInitializer(() -> { + createPredefinedAppImage(appImageCmd, signAppImage); + }).usePredefinedAppImage(appImageCmd).addInitializer(cmd -> { + configureOutputValidator(cmd, + signAppImage.isPresent(), + (cmd.packageType() == PackageType.MAC_PKG) && signPackage.packageSignOption().isPresent()); + }).addInstallVerifier(cmd -> { + packagedAppImageSignIdentity().ifPresent(certRequest -> { + MacSignVerify.verifyAppImageSigned(cmd, certRequest); + }); + }); + + MacSign.withKeychain(_ -> { + test.run(); + }, signPackage.keychain()); + } + } + + private static SignKeyOptionWithKeychain predefinedAppImageSignOption() { + // Sign the predefined app image bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of the input app image bundle. + return new SignKeyOptionWithKeychain( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD, + SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static void createPredefinedAppImage(JPackageCommand appImageCmd, Optional signAppImage) { + Objects.requireNonNull(appImageCmd); + Objects.requireNonNull(signAppImage); + + appImageCmd.setFakeRuntime().setArgumentValue("--dest", TKit.createTempDirectory("appimage")); + + signAppImage.ifPresentOrElse(signOption -> { + signOption.setTo(appImageCmd); + + MacSign.withKeychain(_ -> { + appImageCmd.execute(0); + }, signOption.keychain()); + + // Verify that the predefined app image is signed. + MacSignVerify.verifyAppImageSigned(appImageCmd, signOption.certRequest()); + }, () -> { + appImageCmd.execute(0); + }); + } + + private static void configureOutputValidator(JPackageCommand cmd, boolean signAppImage, boolean signPackage) { + var signedPredefinedAppImageWarning = JPackageStringBundle.MAIN.cannedFormattedString( + "warning.per.user.app.image.signed", + PackageFile.getPathInAppImage(Path.of(""))); + + var signedInstallerFromUnsignedPredefinedAppImageWarning = + JPackageStringBundle.MAIN.cannedFormattedString("warning.unsigned.app.image", "pkg"); + + // The warnings are mutually exclusive + final Optional expected; + final List unexpected = new ArrayList<>(); + + if (signAppImage) { + expected = Optional.of(signedPredefinedAppImageWarning); + } else { + unexpected.add(signedPredefinedAppImageWarning); + if (signPackage) { + expected = Optional.of(signedInstallerFromUnsignedPredefinedAppImageWarning); + } else { + expected = Optional.empty(); + unexpected.add(signedInstallerFromUnsignedPredefinedAppImageWarning); + } + } + + expected.ifPresent(cmd::validateOutput); + unexpected.forEach(str -> { + cmd.validateOutput(TKit.assertTextStream(cmd.getValue(str)).negate()); + }); + } } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index ccc39f7a367..604399ebd7a 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.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 @@ -21,17 +21,31 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.test.JPackageCommand.RuntimeImageType.RUNTIME_TYPE_FAKE; import java.nio.file.Path; -import java.util.function.Predicate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.internal.util.MacBundle; +import jdk.jpackage.internal.util.Slot; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; +import jdk.jpackage.test.MacSignVerify.SpctlType; import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.TKit; /** * Tests generation of dmg and pkg with --mac-sign and related arguments. Test @@ -43,126 +57,182 @@ import jdk.jpackage.test.PackageTest; * app image signing and it will be covered by SigningPackageTest. * *

- * Following combinations are tested: - *

    - *
  1. "--runtime-image" points to unsigned JDK bundle and --mac-sign is not - * provided. Expected result: runtime image ad-hoc signed. - *
  2. "--runtime-image" points to unsigned JDK bundle and --mac-sign is - * provided. Expected result: Everything is signed with provided certificate. - *
  3. "--runtime-image" points to signed JDK bundle and --mac-sign is not - * provided. Expected result: runtime image is signed with original certificate. - *
  4. "--runtime-image" points to signed JDK bundle and --mac-sign is provided. - * Expected result: runtime image is signed with provided certificate. - *
  5. "--runtime-image" points to JDK image and --mac-sign is not provided. - * Expected result: runtime image ad-hoc signed. - *
  6. "--runtime-image" points to JDK image and --mac-sign is provided. - * Expected result: Everything is signed with provided certificate. - *
- * - * This test requires that the machine is configured with test certificate for - * "Developer ID Installer: jpackage.openjdk.java.net" in jpackagerTest keychain - * with always allowed access to this keychain for user which runs test. note: - * "jpackage.openjdk.java.net" can be over-ridden by system property - * "jpackage.mac.signing.key.user.name" + * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type pkg,dmg --runtime-image --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningRuntimeImagePackageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java + * @compile -Xlint:all -Werror SigningRuntimeImagePackageTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest == null) - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningRuntimeImagePackageTest * --jpt-before-run=SigningBase.verifySignTestEnvReady */ public class SigningRuntimeImagePackageTest { - private static JPackageCommand addSignOptions(JPackageCommand cmd, MacSign.ResolvedKeychain keychain, int certIndex) { - if (certIndex != SigningBase.CertIndex.INVALID_INDEX.value()) { - cmd.addArguments( - "--mac-sign", - "--mac-signing-keychain", keychain.name(), - "--mac-signing-key-user-name", SigningBase.getDevName(certIndex)); - } - return cmd; - } - - private static Path createInputRuntimeBundle(MacSign.ResolvedKeychain keychain, int certIndex) { - return MacHelper.createRuntimeBundle(cmd -> { - addSignOptions(cmd, keychain, certIndex); - }); + @Test + @ParameterSupplier + public static void test(RuntimeTestSpec spec) { + spec.test(); } @Test - // useJDKBundle - If "true" predefined runtime image will be converted to - // JDK bundle. If "false" JDK image will be used. - // JDKBundleCert - Certificate to sign JDK bundle before calling jpackage. - // signCert - Certificate to sign bundle produced by jpackage. - // 1) unsigned JDK bundle and --mac-sign is not provided - @Parameter({"true", "INVALID_INDEX", "INVALID_INDEX"}) - // 2) unsigned JDK bundle and --mac-sign is provided - @Parameter({"true", "INVALID_INDEX", "ASCII_INDEX"}) - // 3) signed JDK bundle and --mac-sign is not provided - @Parameter({"true", "UNICODE_INDEX", "INVALID_INDEX"}) - // 4) signed JDK bundle and --mac-sign is provided - @Parameter({"true", "UNICODE_INDEX", "ASCII_INDEX"}) - // 5) JDK image and --mac-sign is not provided - @Parameter({"false", "INVALID_INDEX", "INVALID_INDEX"}) - // 6) JDK image and --mac-sign is provided - @Parameter({"false", "INVALID_INDEX", "ASCII_INDEX"}) - public static void test(boolean useJDKBundle, - SigningBase.CertIndex jdkBundleCert, - SigningBase.CertIndex signCert) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, useJDKBundle, jdkBundleCert, signCert); - }), SigningBase.StandardKeychain.MAIN.keychain()); + public static void testBundleSignedRuntime() { + + Slot predefinedRuntime = Slot.createEmpty(); + + var signRuntime = runtimeImageSignOption(); + + new PackageTest().addRunOnceInitializer(() -> { + predefinedRuntime.set(createRuntime(Optional.of(signRuntime), RuntimeType.BUNDLE)); + }).addInitializer(cmd -> { + cmd.ignoreDefaultRuntime(true); + cmd.removeArgumentWithValue("--input"); + cmd.setArgumentValue("--runtime-image", predefinedRuntime.get()); + }).addInstallVerifier(cmd -> { + MacSignVerify.verifyAppImageSigned(cmd, signRuntime.certRequest()); + }).run(); } - private static void test(MacSign.ResolvedKeychain keychain, boolean useJDKBundle, - SigningBase.CertIndex jdkBundleCert, - SigningBase.CertIndex signCert) throws Exception { + public static Collection test() { - final Path inputRuntime[] = new Path[1]; + List data = new ArrayList<>(); - new PackageTest() - .addRunOnceInitializer(() -> { - if (useJDKBundle) { - inputRuntime[0] = createInputRuntimeBundle(keychain, jdkBundleCert.value()); - } else { - inputRuntime[0] = JPackageCommand.createInputRuntimeImage(); - } - }) - .addInitializer(cmd -> { - cmd.addArguments("--runtime-image", inputRuntime[0]); - // Remove --input parameter from jpackage command line as we don't - // create input directory in the test and jpackage fails - // if --input references non existent directory. - cmd.removeArgumentWithValue("--input"); - addSignOptions(cmd, keychain, signCert.value()); - }) - .addInstallVerifier(cmd -> { - final var certIndex = Stream.of(signCert, jdkBundleCert) - .filter(Predicate.isEqual(SigningBase.CertIndex.INVALID_INDEX).negate()) - .findFirst().orElse(SigningBase.CertIndex.INVALID_INDEX).value(); + for (var runtimeSpec : List.of( + Map.entry(RuntimeType.IMAGE, false /* unsigned */), + Map.entry(RuntimeType.BUNDLE, false /* unsigned */), + Map.entry(RuntimeType.BUNDLE, true /* signed */) + )) { + var runtimeType = runtimeSpec.getKey(); + var signRuntime = runtimeSpec.getValue(); - final var signed = certIndex != SigningBase.CertIndex.INVALID_INDEX.value(); + Optional runtimeSignOption; + if (signRuntime) { + // Sign the runtime bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of + // the predefined runtime bundle when backing it in the pkg or dmg installer. + runtimeSignOption = Optional.of(runtimeImageSignOption()); + } else { + runtimeSignOption = Optional.empty(); + } - final var unfoldedBundleDir = cmd.appRuntimeDirectory(); + for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + data.add(new RuntimeTestSpec(runtimeSignOption, runtimeType, signPackage)); + } + } - final var libjli = unfoldedBundleDir.resolve("Contents/MacOS/libjli.dylib"); + return data.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } - SigningBase.verifyCodesign(libjli, signed, certIndex); - SigningBase.verifyCodesign(unfoldedBundleDir, signed, certIndex); - if (signed) { - SigningBase.verifySpctl(unfoldedBundleDir, "exec", certIndex); - } - }) - .run(); + enum RuntimeType { + IMAGE, + BUNDLE, + ; + } + + record RuntimeTestSpec( + Optional signRuntime, + RuntimeType runtimeType, + SigningPackageTest.TestSpec signPackage) { + + RuntimeTestSpec { + Objects.requireNonNull(signRuntime); + Objects.requireNonNull(runtimeType); + Objects.requireNonNull(signPackage); + } + + @Override + public String toString() { + var runtimeToken = new StringBuilder(); + runtimeToken.append("runtime={").append(runtimeType); + signRuntime.ifPresent(v -> { + runtimeToken.append(", ").append(v); + }); + runtimeToken.append('}'); + return Stream.of(runtimeToken, signPackage).map(Objects::toString).collect(Collectors.joining("; ")); + } + + Optional packagedAppImageSignIdentity() { + if (runtimeType == RuntimeType.IMAGE) { + return signPackage.appImageSignOption().map(SignKeyOption::certRequest); + } else { + return signPackage.appImageSignOption().or(() -> { + return signRuntime.map(SignKeyOptionWithKeychain::signKeyOption); + }).map(SignKeyOption::certRequest); + } + } + + void test() { + + Slot predefinedRuntime = Slot.createEmpty(); + + var test = signPackage.initTest().addRunOnceInitializer(() -> { + predefinedRuntime.set(createRuntime(signRuntime, runtimeType)); + }).addInitializer(cmd -> { + cmd.ignoreDefaultRuntime(true); + cmd.removeArgumentWithValue("--input"); + cmd.setArgumentValue("--runtime-image", predefinedRuntime.get()); + }).addInstallVerifier(cmd -> { + packagedAppImageSignIdentity().ifPresent(certRequest -> { + MacSignVerify.verifyAppImageSigned(cmd, certRequest); + }); + }); + + MacSign.withKeychain(_ -> { + test.run(); + }, signPackage.keychain()); + } + } + + private static SignKeyOptionWithKeychain runtimeImageSignOption() { + // Sign the runtime bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of + // the predefined runtime bundle when backing it in the pkg or dmg installer. + return new SignKeyOptionWithKeychain( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD, + SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static Path createRuntime(Optional signRuntime, RuntimeType runtimeType) { + if (runtimeType == RuntimeType.IMAGE && signRuntime.isEmpty()) { + return JPackageCommand.createInputRuntimeImage(RUNTIME_TYPE_FAKE); + } else { + Slot runtimeBundle = Slot.createEmpty(); + + MacSign.withKeychain(keychain -> { + var runtimeBundleBuilder = MacHelper.buildRuntimeBundle(); + signRuntime.ifPresent(signingOption -> { + runtimeBundleBuilder.mutator(signingOption::setTo); + }); + runtimeBundle.set(runtimeBundleBuilder.type(RUNTIME_TYPE_FAKE).create()); + }, SigningBase.StandardKeychain.MAIN.keychain()); + + if (runtimeType == RuntimeType.IMAGE) { + return MacBundle.fromPath(runtimeBundle.get()).orElseThrow().homeDir(); + } else { + // Verify the runtime bundle is properly signed/unsigned. + signRuntime.map(SignKeyOptionWithKeychain::certRequest).ifPresentOrElse(certRequest -> { + MacSignVerify.assertSigned(runtimeBundle.get(), certRequest); + var signOrigin = MacSignVerify.findSpctlSignOrigin(SpctlType.EXEC, runtimeBundle.get()).orElse(null); + TKit.assertEquals(certRequest.name(), signOrigin, + String.format("Check [%s] has sign origin as expected", runtimeBundle.get())); + }, () -> { + MacSignVerify.assertAdhocSigned(runtimeBundle.get()); + }); + return runtimeBundle.get(); + } + } } } diff --git a/test/jdk/tools/jpackage/macosx/base/SigningBase.java b/test/jdk/tools/jpackage/macosx/base/SigningBase.java deleted file mode 100644 index b1a709c9cf0..00000000000 --- a/test/jdk/tools/jpackage/macosx/base/SigningBase.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2019, 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.nio.file.Path; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; -import jdk.jpackage.test.JPackageCommand; -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.MacSignVerify; -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. - * @library /test/jdk/tools/jpackage/helpers - * @build jdk.jpackage.test.* - * @compile -Xlint:all -Werror SigningBase.java - * @requires (jpackage.test.MacSignTests == "setup") - * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningBase.setUp - */ - -/* - * @test - * @summary Tear down the environment for jpackage macos signing tests. - * Deletes required keychains and signing identities. - * Does NOT run any jpackag tests. - * @library /test/jdk/tools/jpackage/helpers - * @build jdk.jpackage.test.* - * @compile -Xlint:all -Werror SigningBase.java - * @requires (jpackage.test.MacSignTests == "teardown") - * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningBase.tearDown - */ - -public class SigningBase { - - public enum StandardCertificateRequest { - CODESIGN(cert().userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - CODESIGN_COPY(cert().days(100).userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - CODESIGN_ACME_TECH_LTD(cert().days(100).userName("ACME Technologies Limited (ABC12345)")), - PKG(cert().type(CertificateType.INSTALLER).userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - PKG_COPY(cert().type(CertificateType.INSTALLER).days(100).userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - CODESIGN_UNICODE(cert().userName(DEV_NAMES[CertIndex.UNICODE_INDEX.value()])), - PKG_UNICODE(cert().type(CertificateType.INSTALLER).userName(DEV_NAMES[CertIndex.UNICODE_INDEX.value()])), - CODESIGN_EXPIRED(cert().expired().userName("expired jpackage test")), - PKG_EXPIRED(cert().expired().type(CertificateType.INSTALLER).userName("expired jpackage test")); - - StandardCertificateRequest(CertificateRequest.Builder specBuilder) { - this.spec = specBuilder.create(); - } - - public CertificateRequest spec() { - return spec; - } - - private static CertificateRequest.Builder cert() { - return new CertificateRequest.Builder(); - } - - private final CertificateRequest spec; - } - - /** - * Standard keychains used in signing tests. - */ - public enum StandardKeychain { - /** - * The primary keychain with good certificates. - */ - MAIN("jpackagerTest.keychain", - StandardCertificateRequest.CODESIGN, - StandardCertificateRequest.PKG, - StandardCertificateRequest.CODESIGN_UNICODE, - StandardCertificateRequest.PKG_UNICODE, - StandardCertificateRequest.CODESIGN_ACME_TECH_LTD), - /** - * A keychain with some good and some expired certificates. - */ - EXPIRED("jpackagerTest-expired.keychain", - StandardCertificateRequest.CODESIGN, - StandardCertificateRequest.PKG, - StandardCertificateRequest.CODESIGN_EXPIRED, - StandardCertificateRequest.PKG_EXPIRED), - /** - * A keychain with duplicated certificates. - */ - DUPLICATE("jpackagerTest-duplicate.keychain", - StandardCertificateRequest.CODESIGN, - StandardCertificateRequest.PKG, - StandardCertificateRequest.CODESIGN_COPY, - StandardCertificateRequest.PKG_COPY); - - StandardKeychain(String keychainName, StandardCertificateRequest... certs) { - this(keychainName, certs[0].spec(), Stream.of(certs).skip(1).map(StandardCertificateRequest::spec).toArray(CertificateRequest[]::new)); - } - - StandardKeychain(String keychainName, CertificateRequest cert, CertificateRequest... otherCerts) { - final var builder = keychain(keychainName).addCert(cert); - List.of(otherCerts).forEach(builder::addCert); - this.keychain = new ResolvedKeychain(builder.create()); - } - - public ResolvedKeychain keychain() { - return keychain; - } - - public X509Certificate mapCertificateRequest(CertificateRequest certRequest) { - return Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest)); - } - - private static KeychainWithCertsSpec.Builder keychain(String name) { - return new KeychainWithCertsSpec.Builder().name(name); - } - - private static List signingEnv() { - return Stream.of(values()).map(StandardKeychain::keychain).map(ResolvedKeychain::spec).toList(); - } - - private final ResolvedKeychain keychain; - } - - public static void setUp() { - MacSign.setUp(StandardKeychain.signingEnv()); - } - - public static void tearDown() { - MacSign.tearDown(StandardKeychain.signingEnv()); - } - - public static void verifySignTestEnvReady() { - if (!Inner.SIGN_ENV_READY) { - TKit.throwSkippedException(new IllegalStateException("Misconfigured signing test environment")); - } - } - - private final class Inner { - private static final boolean SIGN_ENV_READY = MacSign.isDeployed(StandardKeychain.signingEnv()); - } - - enum CertIndex { - ASCII_INDEX(0), - UNICODE_INDEX(1), - INVALID_INDEX(-1); - - CertIndex(int value) { - this.value = value; - } - - int value() { - return value; - } - - private final int value; - } - - public static int DEFAULT_INDEX = 0; - private static String [] DEV_NAMES = { - "jpackage.openjdk.java.net", - "jpackage.openjdk.java.net (ö)", - }; - - public static String getDevName(int certIndex) { - // Always use values from system properties if set - String value = System.getProperty("jpackage.mac.signing.key.user.name"); - if (value != null) { - return value; - } - - return DEV_NAMES[certIndex]; - } - - public static int getDevNameIndex(String devName) { - return Arrays.binarySearch(DEV_NAMES, devName); - } - - public static String getAppCert(int certIndex) { - return "Developer ID Application: " + getDevName(certIndex); - } - - public static String getInstallerCert(int certIndex) { - return "Developer ID Installer: " + getDevName(certIndex); - } - - public static void verifyCodesign(Path target, boolean signed, int certIndex) { - if (signed) { - final var certRequest = getCertRequest(certIndex); - MacSignVerify.assertSigned(target, certRequest); - } else { - MacSignVerify.assertAdhocSigned(target); - } - } - - // Since we no longer have unsigned app image, but we need to check - // DMG which is not adhoc or certificate signed and we cannot use verifyCodesign - // for this. verifyDMG() is introduced to check that DMG is unsigned. - // Should not be used to validated anything else. - public static void verifyDMG(Path target) { - if (!target.toString().toLowerCase().endsWith(".dmg")) { - throw new IllegalArgumentException("Unexpected target: " + target); - } - - MacSignVerify.assertUnsigned(target); - } - - public static void verifySpctl(Path target, String type, int certIndex) { - final var standardCertIndex = Stream.of(CertIndex.values()).filter(v -> { - return v.value() == certIndex; - }).findFirst().orElseThrow(); - - final var standardType = Stream.of(MacSignVerify.SpctlType.values()).filter(v -> { - return v.value().equals(type); - }).findFirst().orElseThrow(); - - final String expectedSignOrigin; - if (standardCertIndex == CertIndex.INVALID_INDEX) { - expectedSignOrigin = null; - } else if (standardType == MacSignVerify.SpctlType.EXEC) { - expectedSignOrigin = getCertRequest(certIndex).name(); - } else if (standardType == MacSignVerify.SpctlType.INSTALL) { - expectedSignOrigin = getPkgCertRequest(certIndex).name(); - } else { - throw new IllegalArgumentException(); - } - - final var signOrigin = MacSignVerify.findSpctlSignOrigin(standardType, target).orElse(null); - - TKit.assertEquals(signOrigin, expectedSignOrigin, - String.format("Check [%s] has sign origin as expected", target)); - } - - public static void verifyPkgutil(Path target, boolean signed, int certIndex) { - if (signed) { - final var certRequest = getPkgCertRequest(certIndex); - MacSignVerify.assertPkgSigned(target, certRequest, StandardKeychain.MAIN.mapCertificateRequest(certRequest)); - } else { - MacSignVerify.assertUnsigned(target); - } - } - - public static void verifyAppImageSignature(JPackageCommand appImageCmd, - boolean isSigned, String... launchers) throws Exception { - Path launcherPath = appImageCmd.appLauncherPath(); - SigningBase.verifyCodesign(launcherPath, isSigned, SigningBase.DEFAULT_INDEX); - - final List launchersList = List.of(launchers); - launchersList.forEach(launcher -> { - Path testALPath = launcherPath.getParent().resolve(launcher); - SigningBase.verifyCodesign(testALPath, isSigned, SigningBase.DEFAULT_INDEX); - }); - - Path appImage = appImageCmd.outputBundle(); - SigningBase.verifyCodesign(appImage, isSigned, SigningBase.DEFAULT_INDEX); - if (isSigned) { - SigningBase.verifySpctl(appImage, "exec", SigningBase.DEFAULT_INDEX); - } - } - - private static CertificateRequest getCertRequest(int certIndex) { - switch (CertIndex.values()[certIndex]) { - case ASCII_INDEX -> { - return StandardCertificateRequest.CODESIGN.spec(); - } - case UNICODE_INDEX -> { - return StandardCertificateRequest.CODESIGN_UNICODE.spec(); - } - default -> { - throw new IllegalArgumentException(); - } - } - } - - private static CertificateRequest getPkgCertRequest(int certIndex) { - switch (CertIndex.values()[certIndex]) { - case ASCII_INDEX -> { - return StandardCertificateRequest.PKG.spec(); - } - case UNICODE_INDEX -> { - return StandardCertificateRequest.PKG_UNICODE.spec(); - } - default -> { - throw new IllegalArgumentException(); - } - } - } -} From 0dd5b59194f32f54c2ec6572833f45e1402515ba Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Sat, 17 Jan 2026 04:30:02 +0000 Subject: [PATCH 040/126] 8375370: XRBackendNative.c reported variable uninitialized by clang23 Reviewed-by: prr --- .../unix/native/libawt_xawt/java2d/x11/XRBackendNative.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c index 0ea86e3e9b3..ebb3e32890e 100644 --- a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c +++ b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020, 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 @@ -339,7 +339,7 @@ Java_sun_java2d_xr_XRBackendNative_createPixmap(JNIEnv *env, jobject this, JNIEXPORT jint JNICALL Java_sun_java2d_xr_XRBackendNative_createPictureNative (JNIEnv *env, jclass cls, jint drawable, jlong formatPtr) { - XRenderPictureAttributes pict_attr; + XRenderPictureAttributes pict_attr = {0}; return XRenderCreatePicture(awt_display, (Drawable) drawable, (XRenderPictFormat *) jlong_to_ptr(formatPtr), 0, &pict_attr); From 436c62afd285a3ce2be9aef59876df4b9f0955ff Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sat, 17 Jan 2026 06:24:31 +0000 Subject: [PATCH 041/126] 8373867: Improve robustness of Attach API for finding tmp directory Reviewed-by: sspitsyn, amenkov --- .../sun/tools/attach/VirtualMachineImpl.java | 55 +++++++--- .../attach/AttachNotSupportedException.java | 13 ++- .../attach/TestWithoutDumpableProcess.java | 102 ++++++++++++++++++ 3 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java diff --git a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java index af8870ecf64..998e8d037b4 100644 --- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java +++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.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 @@ -41,6 +41,8 @@ import java.util.regex.Pattern; import static java.nio.charset.StandardCharsets.UTF_8; +import sun.jvmstat.monitor.MonitoredHost; + /* * Linux implementation of HotSpotVirtualMachine */ @@ -228,7 +230,7 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // Return the socket file for the given process. private File findSocketFile(long pid, long ns_pid) throws AttachNotSupportedException, IOException { - return new File(findTargetProcessTmpDirectory(pid, ns_pid), ".java_pid" + ns_pid); + return new File(findTargetProcessTmpDirectory(pid), ".java_pid" + ns_pid); } // On Linux a simple handshake is used to start the attach mechanism @@ -243,14 +245,14 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // Do not canonicalize the file path, or we will fail to attach to a VM in a container. f.createNewFile(); } catch (IOException _) { - f = new File(findTargetProcessTmpDirectory(pid, ns_pid), fn.toString()); + f = new File(findTargetProcessTmpDirectory(pid), fn.toString()); f.createNewFile(); } return f; } - private String findTargetProcessTmpDirectory(long pid, long ns_pid) throws AttachNotSupportedException, IOException { - final var procPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP); + private String findTargetProcessTmpDirectory(long pid) throws AttachNotSupportedException { + final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP); /* We need to handle at least 4 different cases: * 1. Caller and target processes share PID namespace and root filesystem (host to host or container to @@ -261,21 +263,44 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { * 4. Caller and target processes share neither PID namespace nor root filesystem (host to container) * * if target is elevated, we cant use /proc//... so we have to fallback to /tmp, but that may not be shared - * with the target/attachee process, we can try, except in the case where the ns_pid also exists in this pid ns - * which is ambiguous, if we share /tmp with the intended target, the attach will succeed, if we do not, - * then we will potentially attempt to attach to some arbitrary process with the same pid (in this pid ns) - * as that of the intended target (in its * pid ns). + * with the target/attachee process, so we should check whether /tmp on both is same. This method would throw + * AttachNotSupportedException if they are different because we cannot make a connection with target VM. * - * so in that case we should prehaps throw - or risk sending SIGQUIT to some arbitrary process... which could kill it - * - * however we can also check the target pid's signal masks to see if it catches SIGQUIT and only do so if in + * In addition, we can also check the target pid's signal masks to see if it catches SIGQUIT and only do so if in * fact it does ... this reduces the risk of killing an innocent process in the current ns as opposed to * attaching to the actual target JVM ... c.f: checkCatchesAndSendQuitTo() below. - * - * note that if pid == ns_pid we are in a shared pid ns with the target and may (potentially) share /tmp */ - return (Files.isWritable(procPidRoot) ? procPidRoot : TMPDIR).toString(); + try { + if (Files.isWritable(tmpOnProcPidRoot)) { + return tmpOnProcPidRoot.toString(); + } else if (Files.isSameFile(tmpOnProcPidRoot, TMPDIR)) { + return TMPDIR.toString(); + } else { + throw new AttachNotSupportedException("Unable to access the filesystem of the target process"); + } + } catch (IOException ioe) { + try { + boolean found = MonitoredHost.getMonitoredHost("//localhost") + .activeVms() + .stream() + .anyMatch(i -> pid == i.intValue()); + if (found) { + // We can use /tmp because target process is on same host + // even if we cannot access /proc//root. + // The process with capsh/setcap would fall this pattern. + return TMPDIR.toString(); + } else { + throw new AttachNotSupportedException("Unable to access the filesystem of the target process", ioe); + } + } catch (AttachNotSupportedException e) { + // AttachNotSupportedException happened in above should go through + throw e; + } catch (Exception e) { + // Other exceptions would be wrapped with AttachNotSupportedException + throw new AttachNotSupportedException("Unable to access the filesystem of the target process", e); + } + } } // Return the inner most namespaced PID if there is one, diff --git a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java index 382c8a57b26..9d62badb94c 100644 --- a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java +++ b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, 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 @@ -62,4 +62,15 @@ public class AttachNotSupportedException extends Exception { super(s); } + /** + * Constructs an AttachNotSupportedException with + * the specified cause. + * + * @param message the detail message. + * @param cause the cause of this exception. + */ + public AttachNotSupportedException(String message, Throwable cause) { + super(message, cause); + } + } diff --git a/test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java b/test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java new file mode 100644 index 00000000000..be9d0c8790c --- /dev/null +++ b/test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java @@ -0,0 +1,102 @@ +/* + * 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. + */ + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; + +import com.sun.tools.attach.VirtualMachine; + +import jdk.test.lib.Asserts; +import jdk.test.lib.thread.ProcessThread; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8226919 8373867 + * @summary Test to make sure attach target process which is not dumpable. + * @library /test/lib + * @modules jdk.attach + * @requires os.family == "linux" + * + * @run main/timeout=200 TestWithoutDumpableProcess + */ +public class TestWithoutDumpableProcess { + + private static final String EXPECTED_PROP_KEY = "attach.test"; + private static final String EXPECTED_PROP_VALUE = "true"; + + public static class Debuggee { + + // Disable dumpable attribute via prctl(2) + private static void disableDumpable() throws Throwable { + var linker = Linker.nativeLinker(); + var prctl = linker.downcallHandle(linker.defaultLookup().findOrThrow("prctl"), + FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG), + Linker.Option.firstVariadicArg(1), Linker.Option.captureCallState("errno")); + var errnoSeg = Arena.global().allocate(Linker.Option.captureStateLayout()); + final int PR_SET_DUMPABLE = 4; // from linux/prctl.h + + int ret = (int)prctl.invoke(errnoSeg, PR_SET_DUMPABLE, 0L); + if (ret == -1){ + var hndErrno = Linker.Option + .captureStateLayout() + .varHandle(MemoryLayout.PathElement.groupElement("errno")); + int errno = (int)hndErrno.get(errnoSeg, 0L); + throw new RuntimeException("prctl: errno=" + errno); + } + } + + public static void main(String[] args) throws Throwable { + disableDumpable(); + IO.println(Application.READY_MSG); + + while (IO.readln().equals(Application.SHUTDOWN_MSG)); + } + + public static ProcessThread start() { + var args = new String[]{ + "--enable-native-access=ALL-UNNAMED", + String.format("-D%s=%s", EXPECTED_PROP_KEY, EXPECTED_PROP_VALUE), Debuggee.class.getName() + }; + var pb = ProcessTools.createLimitedTestJavaProcessBuilder(args); + var pt = new ProcessThread("runApplication", Application.READY_MSG::equals, pb); + pt.start(); + return pt; + } + + } + + public static void main(String[] args) throws Exception { + var pt = Debuggee.start(); + var vm = VirtualMachine.attach(Long.toString(pt.getPid())); + var val = vm.getSystemProperties().getProperty(EXPECTED_PROP_KEY); + + Asserts.assertNotNull(val, "Expected sysprop not found"); + Asserts.assertEquals(val, "true", "Unexpected sysprop value"); + } + +} From a0e6f028a8952f61d9115f7bdf04b8a87f8ebba4 Mon Sep 17 00:00:00 2001 From: Shawn M Emery Date: Sat, 17 Jan 2026 11:08:30 +0000 Subject: [PATCH 042/126] 8360934: Add AVX-512 intrinsics for ML-KEM - enhancement on AVX512_VBMI Co-authored-by: Sandhya Viswanathan Reviewed-by: jbhateja, vpaprotski --- .../cpu/x86/stubGenerator_x86_64_kyber.cpp | 92 ++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index 3e5593322d5..7d5dee6a5df 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.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 @@ -64,6 +64,39 @@ static address kyberAvx512ConstsAddr(int offset) { const Register scratch = r10; +ATTRIBUTE_ALIGNED(64) static const uint8_t kyberAvx512_12To16Dup[] = { +// 0 - 63 + 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 13, 13, 14, 15, 16, + 16, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, + 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 40, 41, 42, 43, 43, 44, + 45, 46, 46, 47 + }; + +static address kyberAvx512_12To16DupAddr() { + return (address) kyberAvx512_12To16Dup; +} + +ATTRIBUTE_ALIGNED(64) static const uint16_t kyberAvx512_12To16Shift[] = { +// 0 - 31 + 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, + 4, 0, 4, 0, 4, 0, 4 + }; + +static address kyberAvx512_12To16ShiftAddr() { + return (address) kyberAvx512_12To16Shift; +} + +ATTRIBUTE_ALIGNED(64) static const uint64_t kyberAvx512_12To16And[] = { +// 0 - 7 + 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, + 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, + 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF + }; + +static address kyberAvx512_12To16AndAddr() { + return (address) kyberAvx512_12To16And; +} + ATTRIBUTE_ALIGNED(64) static const uint16_t kyberAvx512NttPerms[] = { // 0 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, @@ -822,10 +855,65 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, const Register perms = r11; - Label Loop; + Label Loop, VBMILoop; __ addptr(condensed, condensedOffs); + if (VM_Version::supports_avx512_vbmi()) { + // mask load for the first 48 bytes of each vector + __ mov64(rax, 0x0000FFFFFFFFFFFF); + __ kmovql(k1, rax); + + __ lea(perms, ExternalAddress(kyberAvx512_12To16DupAddr())); + __ evmovdqub(xmm20, Address(perms), Assembler::AVX_512bit); + + __ lea(perms, ExternalAddress(kyberAvx512_12To16ShiftAddr())); + __ evmovdquw(xmm21, Address(perms), Assembler::AVX_512bit); + + __ lea(perms, ExternalAddress(kyberAvx512_12To16AndAddr())); + __ evmovdquq(xmm22, Address(perms), Assembler::AVX_512bit); + + __ align(OptoLoopAlignment); + __ BIND(VBMILoop); + + __ evmovdqub(xmm0, k1, Address(condensed, 0), false, + Assembler::AVX_512bit); + __ evmovdqub(xmm1, k1, Address(condensed, 48), false, + Assembler::AVX_512bit); + __ evmovdqub(xmm2, k1, Address(condensed, 96), false, + Assembler::AVX_512bit); + __ evmovdqub(xmm3, k1, Address(condensed, 144), false, + Assembler::AVX_512bit); + + __ evpermb(xmm4, k0, xmm20, xmm0, false, Assembler::AVX_512bit); + __ evpermb(xmm5, k0, xmm20, xmm1, false, Assembler::AVX_512bit); + __ evpermb(xmm6, k0, xmm20, xmm2, false, Assembler::AVX_512bit); + __ evpermb(xmm7, k0, xmm20, xmm3, false, Assembler::AVX_512bit); + + __ evpsrlvw(xmm4, xmm4, xmm21, Assembler::AVX_512bit); + __ evpsrlvw(xmm5, xmm5, xmm21, Assembler::AVX_512bit); + __ evpsrlvw(xmm6, xmm6, xmm21, Assembler::AVX_512bit); + __ evpsrlvw(xmm7, xmm7, xmm21, Assembler::AVX_512bit); + + __ evpandq(xmm0, xmm22, xmm4, Assembler::AVX_512bit); + __ evpandq(xmm1, xmm22, xmm5, Assembler::AVX_512bit); + __ evpandq(xmm2, xmm22, xmm6, Assembler::AVX_512bit); + __ evpandq(xmm3, xmm22, xmm7, Assembler::AVX_512bit); + + store4regs(parsed, 0, xmm0_3, _masm); + + __ addptr(condensed, 192); + __ addptr(parsed, 256); + __ subl(parsedLength, 128); + __ jcc(Assembler::greater, VBMILoop); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ mov64(rax, 0); // return 0 + __ ret(0); + + return start; + } + __ lea(perms, ExternalAddress(kyberAvx512_12To16PermsAddr())); load4regs(xmm24_27, perms, 0, _masm); From 1cdb8174220e52c055406e0e927bc982c91ac595 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sun, 18 Jan 2026 07:35:12 +0000 Subject: [PATCH 043/126] 8375575: AttachNotSupportedException constructor missing @since 27 Reviewed-by: liach --- .../com/sun/tools/attach/AttachNotSupportedException.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java index 9d62badb94c..725db3e7732 100644 --- a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java +++ b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java @@ -68,6 +68,8 @@ public class AttachNotSupportedException extends Exception { * * @param message the detail message. * @param cause the cause of this exception. + * + * @since 27 */ public AttachNotSupportedException(String message, Throwable cause) { super(message, cause); From a67979c4e6dcea70e63cc79a105be12a9306c660 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Mon, 19 Jan 2026 02:33:18 +0000 Subject: [PATCH 044/126] 8375125: assert(false) failed: "Attempting to acquire lock NativeHeapTrimmer_lock/nosafepoint out of order with lock ConcurrentHashTableResize_lock/nosafepoint-2 -- possible deadlock" when using native heap trimmer Reviewed-by: dholmes, stuefe --- src/hotspot/share/classfile/stringTable.cpp | 7 +- src/hotspot/share/classfile/symbolTable.cpp | 7 +- ...stTrimNativeHeapIntervalTablesCleanup.java | 107 ++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index c775014cfac..20dfad0d980 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.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 @@ -614,6 +614,10 @@ struct StringTableDeleteCheck : StackObj { }; void StringTable::clean_dead_entries(JavaThread* jt) { + // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2). + // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint). + // Take SuspendMark first to keep lock order and avoid deadlock. + NativeHeapTrimmer::SuspendMark sm("stringtable"); StringTableHash::BulkDeleteTask bdt(_local_table); if (!bdt.prepare(jt)) { return; @@ -621,7 +625,6 @@ void StringTable::clean_dead_entries(JavaThread* jt) { StringTableDeleteCheck stdc; StringTableDoDelete stdd; - NativeHeapTrimmer::SuspendMark sm("stringtable"); { TraceTime timer("Clean", TRACETIME_LOG(Debug, stringtable, perf)); while(bdt.do_task(jt, stdc, stdd)) { diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index ec639a2b4d3..c49aa10fa0d 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.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 @@ -763,6 +763,10 @@ struct SymbolTableDeleteCheck : StackObj { }; void SymbolTable::clean_dead_entries(JavaThread* jt) { + // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2). + // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint). + // Take SuspendMark first to keep lock order and avoid deadlock. + NativeHeapTrimmer::SuspendMark sm("symboltable"); SymbolTableHash::BulkDeleteTask bdt(_local_table); if (!bdt.prepare(jt)) { return; @@ -770,7 +774,6 @@ void SymbolTable::clean_dead_entries(JavaThread* jt) { SymbolTableDeleteCheck stdc; SymbolTableDoDelete stdd; - NativeHeapTrimmer::SuspendMark sm("symboltable"); { TraceTime timer("Clean", TRACETIME_LOG(Debug, symboltable, perf)); while (bdt.do_task(jt, stdc, stdd)) { diff --git a/test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java b/test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java new file mode 100644 index 00000000000..3ced98b616d --- /dev/null +++ b/test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java @@ -0,0 +1,107 @@ +/* + * 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 8375125 + * @summary Trigger StringTable::clean_dead_entries or SymbolTable::clean_dead_entries + * with -XX:TrimNativeHeapInterval enabled,should not violate lock ordering. + * @requires vm.debug + * @requires vm.gc != "Epsilon" + * @library /test/lib + * @modules java.compiler + * @run main/othervm -Xms128m -Xmx128m + * -XX:TrimNativeHeapInterval=300000 + * TestTrimNativeHeapIntervalTablesCleanup string + * @run main/othervm -Xms128m -Xmx128m + * -XX:TrimNativeHeapInterval=300000 + * TestTrimNativeHeapIntervalTablesCleanup symbol + */ + +import java.util.LinkedList; +import jdk.test.lib.compiler.InMemoryJavaCompiler; + +public class TestTrimNativeHeapIntervalTablesCleanup { + + public static void main(String[] args) throws Exception{ + if (args.length != 1) { + throw new IllegalArgumentException("Expected 1 argument: string|symbol"); + } + switch (args[0]) { + case "string": + testStringTableCleanup(); + break; + case "symbol": + testSymbolTableCleanup(); + break; + default: + throw new IllegalArgumentException("Unknown mode: " + args[0]); + } + System.out.println("passed: " + args[0]); + } + + static void testStringTableCleanup() throws Exception{ + final int rounds = 30; + final int maxSize = 200_000; + final int pruneEvery = 50_000; + final int pruneCount = 25_000; + long stringNum = 0; + + for (int round = 0; round < rounds; round++) { + LinkedList list = new LinkedList<>(); + for (int i = 0; i < maxSize; i++, stringNum++) { + if (i != 0 && (i % pruneEvery) == 0) { + int toRemove = Math.min(pruneCount, list.size()); + list.subList(0, toRemove).clear(); + } + list.push(Long.toString(stringNum).intern()); + } + System.gc(); + Thread.sleep(1000); + } + } + + static void testSymbolTableCleanup() throws Exception { + final int rounds = 10; + final int classesPerRound = 100; + + for (int r = 0; r < rounds; r++) { + for (int i = 0; i < classesPerRound; i++) { + String cn = "C" + r + "_" + i; + byte[] bytes = InMemoryJavaCompiler.compile( + cn, + "public class " + cn + " { int m" + i + "() { return " + i + "; } }" + ); + new ClassLoader(null) { + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (!name.equals(cn)) throw new ClassNotFoundException(name); + return defineClass(name, bytes, 0, bytes.length); + } + }.loadClass(cn).getDeclaredConstructor().newInstance(); + } + System.gc(); + Thread.sleep(1000); + } + } +} \ No newline at end of file From f8fb78042639d4c436fdad7f501ca4ca28dfe9e3 Mon Sep 17 00:00:00 2001 From: Valerie Peng Date: Fri, 18 Jul 2025 23:49:30 +0000 Subject: [PATCH 045/126] 8265429: Improve GCM encryption Co-authored-by: Daniel Jelinski Reviewed-by: rhalade, pkumaraswamy, ahgross, jnimeh, djelinski --- .../share/native/libj2pkcs11/p11_crypt.c | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c index d969fabffd0..052c7011860 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -184,9 +184,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -194,7 +197,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -208,7 +211,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt (CK_BYTE_PTR)(outBufP + jOutOfs), &ckEncryptedLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -251,9 +254,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -261,7 +267,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -275,7 +281,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate (CK_BYTE_PTR)(outBufP + jOutOfs), &ckEncryptedPartLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -462,9 +468,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -472,7 +481,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -485,7 +494,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt (CK_BYTE_PTR)(outBufP + jOutOfs), &ckOutLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -528,9 +537,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -538,7 +550,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -551,7 +563,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate (CK_BYTE_PTR)(outBufP + jOutOfs), &ckDecryptedPartLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { From 9f3f960b364bad96bfcd469d7993d2aedbc020a4 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Mon, 18 Aug 2025 10:25:12 +0000 Subject: [PATCH 046/126] 8364214: Enhance polygon data support Reviewed-by: rhalade, psadhukhan, mschoene, prr --- .../share/classes/sun/java2d/SunGraphics2D.java | 6 +++--- .../share/classes/sun/java2d/pipe/SpanClipRenderer.java | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 1bebf379997..e92c485a363 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1901,9 +1901,9 @@ public final class SunGraphics2D if (usrClip == null) { clipState = CLIP_DEVICE; clipRegion = devClip; - } else if (usrClip instanceof Rectangle2D) { + } else if (usrClip instanceof Rectangle2D clip) { clipState = CLIP_RECTANGULAR; - clipRegion = devClip.getIntersection((Rectangle2D) usrClip); + clipRegion = devClip.getIntersection(clip); } else { PathIterator cpi = usrClip.getPathIterator(null); int[] box = new int[4]; diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java b/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java index 62d1f119f33..69c4954cac4 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,8 @@ package sun.java2d.pipe; import java.awt.Rectangle; import java.awt.Shape; + +import sun.java2d.InvalidPipeException; import sun.java2d.SunGraphics2D; /** @@ -67,7 +69,9 @@ public class SpanClipRenderer implements CompositePipe public Object startSequence(SunGraphics2D sg, Shape s, Rectangle devR, int[] abox) { RegionIterator ri = sg.clipRegion.getIterator(); - + if (ri.region.isRectangular()) { + throw new InvalidPipeException("Invalid clip data"); + } return new SCRcontext(ri, outpipe.startSequence(sg, s, devR, abox)); } From 3b6ac2af9c8637891092955474b27e5400650dfc Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 20 Aug 2025 03:17:34 +0000 Subject: [PATCH 047/126] 8362308: Enhance Bitmap operations Reviewed-by: mschoene, rhalade, psadhukhan, prr --- .../libmlib_image/mlib_ImageConvMxN_Fp.c | 9 +++++++- .../libmlib_image/mlib_ImageConvMxN_ext.c | 6 ++++- .../libmlib_image/mlib_ImageConv_16ext.c | 23 ++++++++++++++++++- .../libmlib_image/mlib_ImageConv_16nw.c | 7 +++++- .../libmlib_image/mlib_ImageConv_32nw.c | 7 +++++- .../libmlib_image/mlib_ImageConv_8ext.c | 23 ++++++++++++++++++- .../native/libmlib_image/mlib_ImageConv_8nw.c | 7 +++++- .../libmlib_image/mlib_ImageConv_u16ext.c | 23 ++++++++++++++++++- .../libmlib_image/mlib_ImageConv_u16nw.c | 7 +++++- .../libmlib_image/mlib_ImageLookUp_Bit.c | 12 +++++++++- .../native/libmlib_image/mlib_ImageScanPoly.c | 11 ++++++++- 11 files changed, 124 insertions(+), 11 deletions(-) diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c index fa9a186d6d7..bc2df9b0b1a 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -76,6 +76,7 @@ #include "mlib_ImageCheck.h" #include "mlib_SysMath.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ static void mlib_ImageConvMxNMulAdd_F32(mlib_f32 *dst, @@ -272,6 +273,9 @@ mlib_status mlib_convMxNext_f32(mlib_image *dst, mlib_s32 nch = mlib_ImageGetChannels(dst); mlib_s32 i, j, j1, k; + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); @@ -629,6 +633,9 @@ mlib_status mlib_convMxNext_d64(mlib_image *dst, mlib_s32 nch = mlib_ImageGetChannels(dst); mlib_s32 i, j, j1, k; + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c index ee15935dcfe..5869b0a54af 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -82,6 +82,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ static void mlib_ImageConvMxNMulAdd_S32(mlib_d64 *dst, @@ -229,6 +230,9 @@ mlib_status mlib_convMxNext_s32(mlib_image *dst, /* internal buffer */ + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c index 57486b1cae5..00469d25719 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -260,8 +261,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -509,8 +516,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -919,8 +934,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c index 3b6985b7876..2e035d12453 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -32,6 +32,7 @@ #include "mlib_image.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -466,6 +467,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c index 380ed044878..bb264d9dcd2 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ #define CACHE_SIZE (64*1024) @@ -335,6 +336,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 2), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 2)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c index c8b58e6f138..136d5a2b814 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -245,8 +246,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -494,8 +501,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -904,8 +919,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c index f65fda45c58..c144404b0f4 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -467,6 +468,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c index b2757979a84..81a06f2fc28 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -270,8 +271,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -519,8 +526,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -927,8 +942,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c index a3234cf8959..49412c7d7ef 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -32,6 +32,7 @@ #include "mlib_image.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -466,6 +467,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c index 2e77c20aa57..cfd5e3e671e 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -50,6 +50,7 @@ #include "mlib_image.h" #include "mlib_ImageLookUp.h" +#include "safe_math.h" /***************************************************************/ #define MAX_WIDTH 512 @@ -302,6 +303,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_2(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 val0, val1; + if (!SAFE_TO_MULT(xsize, 2)) { + return MLIB_FAILURE; + } size = xsize * 2; if (size > MAX_WIDTH) { @@ -440,6 +444,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_3(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 l0, h0, v0, l1, h1, v1, l2, h2, v2; + if (!SAFE_TO_MULT(3, xsize)) { + return MLIB_FAILURE; + } size = 3 * xsize; if (size > MAX_WIDTH) { @@ -583,6 +590,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_4(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 l, h; + if (!SAFE_TO_MULT(xsize, 4)) { + return MLIB_FAILURE; + } size = xsize * 4; if (size > MAX_WIDTH) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c index a6f4cfdd36e..72adc212af6 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,6 +101,11 @@ mlib_status mlib_AffineEdges(mlib_affine_param *param, return MLIB_FAILURE; } + int intSize = sizeof(mlib_s32); + if (!SAFE_TO_MULT(dstHeight, intSize) || + !SAFE_TO_ADD(dstHeight * intSize, 7)) { + return MLIB_FAILURE; + } bsize0 = (dstHeight * sizeof(mlib_s32) + 7) & ~7; if (lineAddr == NULL) { @@ -109,6 +114,10 @@ mlib_status mlib_AffineEdges(mlib_affine_param *param, param->buff_malloc = NULL; + if (!SAFE_TO_MULT(4, bsize0) || !SAFE_TO_ADD(4 * bsize0, bsize1)) { + return MLIB_FAILURE; + } + if ((4 * bsize0 + bsize1) > buff_size) { buff = param->buff_malloc = mlib_malloc(4 * bsize0 + bsize1); From 97bd4458416dffd901ad07be028a08b3d6dc4881 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 26 Aug 2025 03:07:27 +0000 Subject: [PATCH 048/126] 8365271: Improve Swing supports Reviewed-by: tr, prr, rhalade, aivanov --- .../classes/javax/swing/plaf/basic/BasicOptionPaneUI.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index a6d2a448186..8ab31b1a2ad 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -466,12 +466,12 @@ public class BasicOptionPaneUI extends OptionPaneUI { str = s.substring(index2 + "".length()); s = s.substring(index1, index2 + + "".length()); } - JLabel label; - label = new JLabel(s, JLabel.LEADING); + JLabel label = new JLabel(); if (Boolean.TRUE.equals( this.optionPane.getClientProperty("html.disable"))) { label.putClientProperty("html.disable", true); } + label.setText(s); label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); From dc46a17f1e569e2ae6857eaed4b1365b6cab02e1 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 3 Sep 2025 17:23:16 +0000 Subject: [PATCH 049/126] 8365058: Enhance CopyOnWriteArraySet Reviewed-by: rhalade, skoivu, vklang, rriggs --- .../util/concurrent/CopyOnWriteArraySet.java | 41 +++++++++ .../SerializationTest.java | 84 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java index cef1682b0b1..abc9fdb348c 100644 --- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java +++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java @@ -35,6 +35,13 @@ package java.util.concurrent; +import jdk.internal.misc.Unsafe; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamException; +import java.io.Serial; +import java.io.StreamCorruptedException; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; @@ -445,4 +452,38 @@ public class CopyOnWriteArraySet extends AbstractSet return Spliterators.spliterator (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT); } + + /** + * De-serialization without data not supported for this class. + */ + @Serial + private void readObjectNoData() throws ObjectStreamException { + throw new StreamCorruptedException("Deserialized CopyOnWriteArraySet requires data"); + } + + /** + * Reconstitutes the {@code CopyOnWriteArraySet} instance from a stream + * (that is, deserializes it). + * @throws StreamCorruptedException if the object read from the stream is invalid. + */ + @Serial + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + CopyOnWriteArrayList newAl; // Set during the duplicate check + + @SuppressWarnings("unchecked") + CopyOnWriteArrayList inAl = (CopyOnWriteArrayList) in.readFields().get("al", null); + + if (inAl == null + || inAl.getClass() != CopyOnWriteArrayList.class + || (newAl = new CopyOnWriteArrayList<>()).addAllAbsent(inAl) != inAl.size()) { + throw new StreamCorruptedException("Content is invalid"); + } + + final Unsafe U = Unsafe.getUnsafe(); + U.putReference( + this, + U.objectFieldOffset(CopyOnWriteArraySet.class, "al"), + newAl + ); + } } diff --git a/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java b/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java new file mode 100644 index 00000000000..7700eda6cd6 --- /dev/null +++ b/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 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. + */ + +/* + * @test + * @bug 8365058 + * @summary Check basic correctness of de-serialization + * @run junit SerializationTest + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SerializationTest { + + // Ensure basic serialization round trip correctness + @ParameterizedTest + @MethodSource + void roundTripTest(CopyOnWriteArraySet expected) { + var bytes = ser(expected); + var actual = deSer(bytes); + assertEquals(CopyOnWriteArraySet.class, actual.getClass()); + assertEquals(expected, actual); + } + + private static Stream> roundTripTest() { + return Stream.of( + new CopyOnWriteArraySet<>(), + new CopyOnWriteArraySet<>(List.of(1, 2, 3)), + new CopyOnWriteArraySet<>(Set.of("Foo", "Bar", "Baz")) + ); + } + + private static byte[] ser(Object obj) { + return assertDoesNotThrow(() -> { + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream)) { + oos.writeObject(obj); + return byteArrayOutputStream.toByteArray(); + } + }, "Unexpected error during serialization"); + } + + private static Object deSer(byte[] bytes) { + return assertDoesNotThrow(() -> { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream)) { + return ois.readObject(); + } + }, "Unexpected error during de-serialization"); + } +} From 3afb831ae45182e4219decacc355fae100a41b05 Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Thu, 4 Sep 2025 18:11:37 +0000 Subject: [PATCH 050/126] 8341496: Improve JMX connections Co-authored-by: Daniel Fuchs Reviewed-by: skoivu, rhalade, coffeys, dfuchs, kevinw, jnimeh --- .../rmi/ssl/SslRMIClientSocketFactory.java | 13 +++++++++- .../management/security/SecurityTest.java | 2 ++ .../rmi/ssl/SSLSocketParametersTest.java | 1 + .../bootstrap/JMXInterfaceBindingTest.java | 24 +++++++++++++++++++ .../jmxremote/bootstrap/RmiBootstrapTest.java | 3 ++- .../bootstrap/RmiRegistrySslTest.java | 1 + 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java b/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java index ab6664b9d8f..a4475c8bd1e 100644 --- a/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java +++ b/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ import java.net.Socket; import java.rmi.server.RMIClientSocketFactory; import java.util.StringTokenizer; import javax.net.SocketFactory; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -119,6 +120,16 @@ public class SslRMIClientSocketFactory // final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port); + + if (Boolean.parseBoolean( + System.getProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "true"))) { + SSLParameters params = sslSocket.getSSLParameters(); + if (params == null) { + params = new SSLParameters(); + } + params.setEndpointIdentificationAlgorithm("HTTPS"); + sslSocket.setSSLParameters(params); + } // Set the SSLSocket Enabled Cipher Suites // final String enabledCipherSuites = diff --git a/test/jdk/javax/management/security/SecurityTest.java b/test/jdk/javax/management/security/SecurityTest.java index 7212aea883f..b46bbfa759f 100644 --- a/test/jdk/javax/management/security/SecurityTest.java +++ b/test/jdk/javax/management/security/SecurityTest.java @@ -402,6 +402,8 @@ public class SecurityTest { opts.add(JDKToolFinder.getJDKTool("java")); opts.addAll(Arrays.asList(jdk.test.lib.Utils.getTestJavaOpts())); + opts.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); + // We need to forward some properties to the client side opts.add("-Dtest.src=" + System.getProperty("test.src")); diff --git a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java index 3aa7a98c394..7616b112e39 100644 --- a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java +++ b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java @@ -137,6 +137,7 @@ public class SSLSocketParametersTest extends SSLContextTemplate { } public static void main(String[] args) throws Exception { + System.setProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "false"); SSLSocketParametersTest test = new SSLSocketParametersTest(); test.runTest(Integer.parseInt(args[0])); } diff --git a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java index 61359084297..c331cb9050c 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java @@ -21,6 +21,29 @@ * questions. */ +/* + * Copyright (c) 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.io.File; import java.io.PrintWriter; import java.net.InetAddress; @@ -205,6 +228,7 @@ public class JMXInterfaceBindingTest { // This is needed for testing on loopback args.add("-Djava.rmi.server.hostname=" + address); if (useSSL) { + args.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); args.add("-Dcom.sun.management.jmxremote.registry.ssl=true"); args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC); args.add("-Djavax.net.ssl.trustStore=" + TRUSTSTORE_LOC); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java index 2a99c5e7d52..b62d3f1bbd7 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -169,6 +169,7 @@ public class RmiBootstrapTest extends RmiTestBase { final List credentialFiles = prepareTestFiles(args[0]); Security.setProperty("jdk.tls.disabledAlgorithms", ""); + System.setProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "false"); try { MAX_GET_FREE_PORT_TRIES = Integer.parseInt(System.getProperty("test.getfreeport.max.tries", "10")); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java index 1aa20937962..b4ef5e224f8 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java @@ -179,6 +179,7 @@ public class RmiRegistrySslTest { initTestEnvironment(); List command = new ArrayList<>(); + command.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); command.add("-Dtest.src=" + TEST_SRC); command.add("-Dtest.rmi.port=" + port); command.addAll(Arrays.asList(args)); From 84ee4f976b1580944bd77bdbd8ccd23569bce3ac Mon Sep 17 00:00:00 2001 From: Renjith Kannath Pariyangad Date: Wed, 10 Sep 2025 11:56:45 +0000 Subject: [PATCH 051/126] 8366446: Test java/awt/geom/ConcurrentDrawPolygonTest.java fails intermittently Reviewed-by: jdv, aivanov, prr, rhalade --- .../share/classes/sun/java2d/SunGraphics2D.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index e92c485a363..9815d657eee 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1898,14 +1898,16 @@ public final class SunGraphics2D protected void validateCompClip() { int origClipState = clipState; - if (usrClip == null) { + final Shape clip = usrClip; + + if (clip == null) { clipState = CLIP_DEVICE; clipRegion = devClip; - } else if (usrClip instanceof Rectangle2D clip) { + } else if (clip instanceof Rectangle2D rect2d) { clipState = CLIP_RECTANGULAR; - clipRegion = devClip.getIntersection(clip); + clipRegion = devClip.getIntersection(rect2d); } else { - PathIterator cpi = usrClip.getPathIterator(null); + PathIterator cpi = clip.getPathIterator(null); int[] box = new int[4]; ShapeSpanIterator sr = LoopPipe.getFillSSI(this); try { From 7e3e35abef13ddf38d4268e1269c1d18566149ab Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Wed, 10 Sep 2025 16:40:58 +0000 Subject: [PATCH 052/126] 8367277: Fix copyright header in JMXInterfaceBindingTest.java Reviewed-by: dfuchs, rhalade, iris, coffeys --- .../bootstrap/JMXInterfaceBindingTest.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java index c331cb9050c..1f4707fab3b 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java @@ -1,27 +1,5 @@ /* * Copyright (c) 2015, Red Hat Inc - * 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. - */ - -/* * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * From f24fadc6240e2dcb5bcd732c91ccc03d1aa19e8a Mon Sep 17 00:00:00 2001 From: Michael McMahon Date: Mon, 15 Sep 2025 13:31:30 +0000 Subject: [PATCH 053/126] 8362632: Improve HttpServer Request handling Reviewed-by: djelinski, dfuchs --- .../com/sun/net/httpserver/Headers.java | 37 +++++-------------- .../sun/net/httpserver/ExchangeImpl.java | 19 +++++++--- .../classes/sun/net/httpserver/Utils.java | 33 +++++++++++++++++ 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java index 2decd48c806..9389aae8691 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java @@ -36,6 +36,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; import sun.net.httpserver.UnmodifiableHeaders; +import sun.net.httpserver.Utils; /** * HTTP request and response headers are represented by this class which @@ -216,8 +217,13 @@ public class Headers implements Map> { @Override public List put(String key, List value) { + // checkHeader is called in this class to fail fast + // It also must be called in sendResponseHeaders because + // Headers instances internal state can be modified + // external to these methods. + Utils.checkHeader(key, false); for (String v : value) - checkValue(v); + Utils.checkHeader(v, true); return map.put(normalize(key), value); } @@ -229,7 +235,8 @@ public class Headers implements Map> { * @param value the value to add to the header */ public void add(String key, String value) { - checkValue(value); + Utils.checkHeader(key, false); + Utils.checkHeader(value, true); String k = normalize(key); List l = map.get(k); if (l == null) { @@ -239,30 +246,6 @@ public class Headers implements Map> { l.add(value); } - private static void checkValue(String value) { - int len = value.length(); - for (int i=0; i= len - 2) { - throw new IllegalArgumentException("Illegal CR found in header"); - } - char c1 = value.charAt(i+1); - char c2 = value.charAt(i+2); - if (c1 != '\n') { - throw new IllegalArgumentException("Illegal char found after CR in header"); - } - if (c2 != ' ' && c2 != '\t') { - throw new IllegalArgumentException("No whitespace found after CRLF in header"); - } - i+=2; - } else if (c == '\n') { - throw new IllegalArgumentException("Illegal LF found in header"); - } - } - } - /** * Sets the given {@code value} as the sole header value for the given * {@code key}. If the mapping does not already exist, then it is created. @@ -304,7 +287,7 @@ public class Headers implements Map> { public void replaceAll(BiFunction, ? extends List> function) { var f = function.andThen(values -> { Objects.requireNonNull(values); - values.forEach(Headers::checkValue); + values.forEach(value -> Utils.checkHeader(value, true)); return values; }); Map.super.replaceAll(f); diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java index 8da98cdcfa5..ad6805938a2 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java @@ -207,6 +207,8 @@ class ExchangeImpl { return uos_orig; } + private static final byte[] CRLF = new byte[] {0x0D, 0x0A}; + public void sendResponseHeaders(int rCode, long contentLen) throws IOException { @@ -215,10 +217,11 @@ class ExchangeImpl { throw new IOException("headers already sent"); } this.rcode = rCode; - String statusLine = "HTTP/1.1 " + rCode + Code.msg(rCode) + "\r\n"; + String statusLine = "HTTP/1.1 " + rCode + Code.msg(rCode); ByteArrayOutputStream tmpout = new ByteArrayOutputStream(); PlaceholderOutputStream o = getPlaceholderResponseBody(); - tmpout.write(bytes(statusLine, 0), 0, statusLine.length()); + tmpout.write(bytes(statusLine, false, 0), 0, statusLine.length()); + tmpout.write(CRLF); boolean noContentToSend = false; // assume there is content boolean noContentLengthHeader = false; // must not send Content-length is set rspHdrs.set("Date", FORMATTER.format(Instant.now())); @@ -305,11 +308,11 @@ class ExchangeImpl { List values = entry.getValue(); for (String val : values) { int i = key.length(); - buf = bytes(key, 2); + buf = bytes(key, true, 2); buf[i++] = ':'; buf[i++] = ' '; os.write(buf, 0, i); - buf = bytes(val, 2); + buf = bytes(val, false, 2); i = val.length(); buf[i++] = '\r'; buf[i++] = '\n'; @@ -327,8 +330,14 @@ class ExchangeImpl { * Make sure that at least "extra" bytes are free at end * of rspbuf. Reallocate rspbuf if not big enough. * caller must check return value to see if rspbuf moved + * + * Header values are supposed to be limited to 7-bit ASCII + * but 8-bit has to be allowed (for ISO_8859_1). For efficiency + * we just down cast 16 bit Java chars to byte. We don't allow + * any character that can't be encoded in 8 bits. */ - private byte[] bytes(String s, int extra) { + private byte[] bytes(String s, boolean isKey, int extra) throws IOException { + Utils.checkHeader(s, !isKey); int slen = s.length(); if (slen+extra > rspbuf.length) { int diff = slen + extra - rspbuf.length; diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java index 43dadb84a90..41834172f27 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java @@ -88,4 +88,37 @@ public class Utils { } return true; } + + /* Throw IAE if illegal character found. isValue is true if String is + * a value. Otherwise it is header name + */ + public static void checkHeader(String str, boolean isValue) { + int len = str.length(); + for (int i=0; i= len - 2) { + throw new IllegalArgumentException("Illegal CR found in header"); + } + char c1 = str.charAt(i+1); + char c2 = str.charAt(i+2); + if (c1 != '\n') { + throw new IllegalArgumentException("Illegal char found after CR in header"); + } + if (c2 != ' ' && c2 != '\t') { + throw new IllegalArgumentException("No whitespace found after CRLF in header"); + } + i+=2; + } else if (c == '\n') { + throw new IllegalArgumentException("Illegal LF found in header"); + } else if (c > 255) { + throw new IllegalArgumentException("Illegal character found in header"); + } + } + } + } From eddbd359654cf6e2a437367461231ba37ee76918 Mon Sep 17 00:00:00 2001 From: Harshitha Onkar Date: Wed, 24 Sep 2025 18:05:45 +0000 Subject: [PATCH 054/126] 8359501: Enhance Handling of URIs Reviewed-by: rhalade, ahgross, azvegint, prr --- .../sun/lwawt/macosx/CDesktopPeer.java | 57 ++++++-- .../native/libawt_lwawt/awt/CDesktopPeer.m | 124 ++++++++++++++---- .../classes/sun/awt/windows/WDesktopPeer.java | 53 +++++++- .../native/libawt/windows/awt_Desktop.cpp | 49 ++++++- test/jdk/java/awt/Desktop/BrowseTest.java | 26 +++- .../EditAndPrintTest/EditAndPrintTest.java | 2 +- 6 files changed, 258 insertions(+), 53 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java index a4ec0767298..cc0e253f23b 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -34,7 +34,10 @@ import java.awt.peer.DesktopPeer; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.annotation.Native; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; /** @@ -44,6 +47,12 @@ import java.net.URI; */ public final class CDesktopPeer implements DesktopPeer { + @Native private static final int OPEN = 0; + @Native private static final int BROWSE = 1; + @Native private static final int EDIT = 2; + @Native private static final int PRINT = 3; + @Native private static final int MAIL = 4; + @Override public boolean isSupported(Action action) { return true; @@ -51,27 +60,27 @@ public final class CDesktopPeer implements DesktopPeer { @Override public void open(File file) throws IOException { - this.lsOpenFile(file, false); + this.lsOpenFile(file, OPEN); } @Override public void edit(File file) throws IOException { - this.lsOpenFile(file, false); + this.lsOpenFile(file, EDIT); } @Override public void print(File file) throws IOException { - this.lsOpenFile(file, true); + this.lsOpenFile(file, PRINT); } @Override public void mail(URI uri) throws IOException { - this.lsOpen(uri); + this.lsOpen(uri, MAIL); } @Override public void browse(URI uri) throws IOException { - this.lsOpen(uri); + this.lsOpen(uri, BROWSE); } @Override @@ -162,24 +171,44 @@ public final class CDesktopPeer implements DesktopPeer { } } - private void lsOpen(URI uri) throws IOException { - int status = _lsOpenURI(uri.toString()); + private void lsOpen(URI uri, int action) throws IOException { + int status = _lsOpenURI(uri.toString(), action); if (status != 0 /* noErr */) { - throw new IOException("Failed to mail or browse " + uri + ". Error code: " + status); + String actionString = (action == MAIL) ? "mail" : "browse"; + throw new IOException("Failed to " + actionString + " " + uri + + ". Error code: " + status); } } - private void lsOpenFile(File file, boolean print) throws IOException { - int status = _lsOpenFile(file.getCanonicalPath(), print); + private void lsOpenFile(File file, int action) throws IOException { + int status = -1; + Path tmpFile = null; + String tmpTxtPath = null; + try { + if (action == EDIT) { + tmpFile = Files.createTempFile("TmpFile", ".txt"); + tmpTxtPath = tmpFile.toAbsolutePath().toString(); + } + status = _lsOpenFile(file.getCanonicalPath(), action, tmpTxtPath); + } catch (Exception e) { + throw new IOException("Failed to create tmp file: ", e); + } finally { + if (tmpFile != null) { + Files.deleteIfExists(tmpFile); + } + } if (status != 0 /* noErr */) { - throw new IOException("Failed to open, edit or print " + file + ". Error code: " + status); + String actionString = (action == OPEN) ? "open" + : (action == EDIT) ? "edit" : "print"; + throw new IOException("Failed to " + actionString + " " + file + + ". Error code: " + status); } } - private static native int _lsOpenURI(String uri); + private static native int _lsOpenURI(String uri, int action); - private static native int _lsOpenFile(String path, boolean print); + private static native int _lsOpenFile(String path, int action, String tmpTxtPath); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m index 7555c7990c4..e1841c9398c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,27 +27,60 @@ #import "JNIUtilities.h" #import #import +#import "sun_lwawt_macosx_CDesktopPeer.h" /* * Class: sun_lwawt_macosx_CDesktopPeer * Method: _lsOpenURI - * Signature: (Ljava/lang/String;)I; + * Signature: (Ljava/lang/String;I)I */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenURI -(JNIEnv *env, jclass clz, jstring uri) +(JNIEnv *env, jclass clz, jstring uri, jint action) { - OSStatus status = noErr; + __block OSStatus status = noErr; JNI_COCOA_ENTER(env); - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. - // So we use LaunchServices directly. + NSURL *urlToOpen = [NSURL URLWithString:JavaStringToNSString(env, uri)]; + NSURL *appURI = nil; - NSURL *url = [NSURL URLWithString:JavaStringToNSString(env, uri)]; + if (action == sun_lwawt_macosx_CDesktopPeer_BROWSE) { + // To get the defaultBrowser + NSURL *httpsURL = [NSURL URLWithString:@"https://"]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + appURI = [workspace URLForApplicationToOpenURL:httpsURL]; + } else if (action == sun_lwawt_macosx_CDesktopPeer_MAIL) { + // To get the default mailer + NSURL *mailtoURL = [NSURL URLWithString:@"mailto://"]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + appURI = [workspace URLForApplicationToOpenURL:mailtoURL]; + } - LSLaunchFlags flags = kLSLaunchDefaults; + if (appURI == nil) { + return -1; + } - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); + // Prepare NSOpenConfig object + NSArray *urls = @[urlToOpen]; + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; + configuration.activates = YES; // To bring app to foreground + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts + + // dispatch semaphores used to wait for the completion handler to update and return status + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout + + // Asynchronous call to openURL + [[NSWorkspace sharedWorkspace] openURLs:urls + withApplicationAtURL:appURI + configuration:configuration + completionHandler:^(NSRunningApplication *app, NSError *error) { + if (error) { + status = (OSStatus) error.code; + } + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, timeout); JNI_COCOA_EXIT(env); return status; @@ -56,32 +89,73 @@ JNI_COCOA_EXIT(env); /* * Class: sun_lwawt_macosx_CDesktopPeer * Method: _lsOpenFile - * Signature: (Ljava/lang/String;Z)I; + * Signature: (Ljava/lang/String;I;Ljava/lang/String;)I; */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenFile -(JNIEnv *env, jclass clz, jstring jpath, jboolean print) +(JNIEnv *env, jclass clz, jstring jpath, jint action, jstring jtmpTxtPath) { - OSStatus status = noErr; + __block OSStatus status = noErr; JNI_COCOA_ENTER(env); - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. - // So we use LaunchServices directly. - NSString *path = NormalizedPathNSStringFromJavaString(env, jpath); - - NSURL *url = [NSURL fileURLWithPath:(NSString *)path]; + NSURL *urlToOpen = [NSURL fileURLWithPath:(NSString *)path]; // This byzantine workaround is necessary, or else directories won't open in Finder - url = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[url path], kCFURLPOSIXPathStyle, false); + urlToOpen = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[urlToOpen path], + kCFURLPOSIXPathStyle, false); - LSLaunchFlags flags = kLSLaunchDefaults; - if (print) flags |= kLSLaunchAndPrint; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSURL *appURI = [workspace URLForApplicationToOpenURL:urlToOpen]; + NSURL *defaultTerminalApp = [workspace URLForApplicationToOpenURL:[NSURL URLWithString:@"file:///bin/sh"]]; - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); - [url release]; + // Prepare NSOpenConfig object + NSArray *urls = @[urlToOpen]; + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; + configuration.activates = YES; // To bring app to foreground + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts + + // pre-checks for open/print/edit before calling openURLs API + if (action == sun_lwawt_macosx_CDesktopPeer_OPEN + || action == sun_lwawt_macosx_CDesktopPeer_PRINT) { + if (appURI == nil + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]] + || [[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { + return -1; + } + // Additionally set forPrinting=TRUE for print + if (action == sun_lwawt_macosx_CDesktopPeer_PRINT) { + configuration.forPrinting = YES; + } + } else if (action == sun_lwawt_macosx_CDesktopPeer_EDIT) { + if (appURI == nil + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]]) { + return -1; + } + // for EDIT: if (defaultApp = TerminalApp) then set appURI = DefaultTextEditor + if ([[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { + NSString *path = NormalizedPathNSStringFromJavaString(env, jtmpTxtPath); + NSURL *tempFilePath = [NSURL fileURLWithPath:(NSString *)path]; + appURI = [workspace URLForApplicationToOpenURL:tempFilePath]; + } + } + + // dispatch semaphores used to wait for the completion handler to update and return status + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout + + // Asynchronous call - openURLs:withApplicationAtURL + [[NSWorkspace sharedWorkspace] openURLs:urls + withApplicationAtURL:appURI + configuration:configuration + completionHandler:^(NSRunningApplication *app, NSError *error) { + if (error) { + status = (OSStatus) error.code; + } + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, timeout); JNI_COCOA_EXIT(env); return status; } - diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java index 788c1477265..e5b628dd74b 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -38,6 +38,9 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.swing.event.EventListenerList; import sun.awt.shell.ShellFolder; @@ -50,9 +53,11 @@ import sun.awt.shell.ShellFolder; */ final class WDesktopPeer implements DesktopPeer { /* Constants for the operation verbs */ - private static String ACTION_OPEN_VERB = "open"; - private static String ACTION_EDIT_VERB = "edit"; - private static String ACTION_PRINT_VERB = "print"; + private static final String ACTION_OPEN_VERB = "open"; + private static final String ACTION_EDIT_VERB = "edit"; + private static final String ACTION_PRINT_VERB = "print"; + private static final String ACTION_BROWSE_VERB = "browse"; + private static final String ACTION_MAIL_VERB = "mail"; private static native void init(); @@ -95,12 +100,12 @@ final class WDesktopPeer implements DesktopPeer { @Override public void mail(URI uri) throws IOException { - this.ShellExecute(uri, ACTION_OPEN_VERB); + this.ShellExecute(uri, ACTION_MAIL_VERB); } @Override public void browse(URI uri) throws IOException { - this.ShellExecute(uri, ACTION_OPEN_VERB); + this.launchUriInBrowser(uri); } private void ShellExecute(File file, String verb) throws IOException { @@ -121,6 +126,42 @@ final class WDesktopPeer implements DesktopPeer { } } + private void launchUriInBrowser(URI uri) throws IOException { + String defaultBrowser = getDefaultBrowser(); + if (defaultBrowser == null) { + throw new IOException("Failed to get default browser"); + } + + List cmdLineTokens = getCmdLineTokens(uri, defaultBrowser); + try { + ProcessBuilder pb = new ProcessBuilder(cmdLineTokens); + pb.start(); + } catch (Exception e) { + throw new IOException("Error launching Browser: ", e); + } + } + + private static List getCmdLineTokens(URI uri, String defaultBrowser) { + if (defaultBrowser.contains("%1")) { + defaultBrowser = defaultBrowser.replace("%1", uri.toString()); + } else { + defaultBrowser = defaultBrowser + " " + uri.toString(); + } + + List cmdLineTokens = new ArrayList<>(); + int firstIndex = defaultBrowser.indexOf("\""); + int secondIndex = defaultBrowser.indexOf("\"", firstIndex + 1); + + if (firstIndex == 0 && secondIndex != firstIndex) { + cmdLineTokens.add(defaultBrowser.substring(firstIndex, secondIndex + 1)); + defaultBrowser = defaultBrowser.substring(secondIndex + 1).trim(); + } + cmdLineTokens.addAll(Arrays.asList(defaultBrowser.split(" "))); + return cmdLineTokens; + } + + private static native String getDefaultBrowser(); + private static native String ShellExecute(String fileOrUri, String verb); private static final EventListenerList listenerList = new EventListenerList(); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp index ba79523249c..ebb43b2f078 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,12 @@ #include #include #include "awt_Toolkit.h" +#include +#include // for AssocQueryStringW +#include +#include +#include +#include // for SaferiIsExecutableFileType #define BUFFER_LIMIT MAX_PATH+1 @@ -78,14 +84,23 @@ JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute LPCWSTR fileOrUri_c = JNU_GetStringPlatformChars(env, fileOrUri_j, NULL); CHECK_NULL_RETURN(fileOrUri_c, NULL); LPCWSTR verb_c = JNU_GetStringPlatformChars(env, verb_j, NULL); + if (verb_c == NULL) { JNU_ReleaseStringPlatformChars(env, fileOrUri_j, fileOrUri_c); return NULL; } + if (wcscmp(verb_c, L"open") == 0) { + BOOL isExecutable = SaferiIsExecutableFileType(fileOrUri_c, FALSE); + if (isExecutable) { + return env->NewStringUTF("Unsupported URI content"); + } + } + // set action verb for mail() to open before calling ShellExecute + LPCWSTR actionVerb = wcscmp(verb_c, L"mail") == 0 ? L"open" : verb_c; // 6457572: ShellExecute possibly changes FPU control word - saving it here unsigned oldcontrol87 = _control87(0, 0); - HINSTANCE retval = ::ShellExecute(NULL, verb_c, fileOrUri_c, NULL, NULL, + HINSTANCE retval = ::ShellExecute(NULL, actionVerb, fileOrUri_c, NULL, NULL, SW_SHOWNORMAL); DWORD error = ::GetLastError(); _control87(oldcontrol87, 0xffffffff); @@ -113,10 +128,38 @@ JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute return errmsg; } } - return NULL; } +/* + * Class: sun_awt_windows_WDesktopPeer + * Method: getDefaultBrowser + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_getDefaultBrowser +(JNIEnv *env, jclass cls) +{ + LPCWSTR fileExtension = L"https"; + WCHAR defaultBrowser_c [MAX_PATH]; + DWORD cchBuffer = MAX_PATH; + + // Use AssocQueryString to get the default browser + HRESULT hr = AssocQueryStringW( + ASSOCF_NONE, // No special flags + ASSOCSTR_COMMAND, // Request the command string + fileExtension, // File extension + NULL, // pszExtra (optional) + defaultBrowser_c, // Output buffer - result + &cchBuffer // Size of the output buffer + ); + + if (FAILED(hr)) { + return NULL; + } + + return JNU_NewStringPlatform(env, defaultBrowser_c); +} + /* * Class: sun_awt_windows_WDesktopPeer * Method: moveToTrash diff --git a/test/jdk/java/awt/Desktop/BrowseTest.java b/test/jdk/java/awt/Desktop/BrowseTest.java index 33de1ecdca7..28e08fe16c7 100644 --- a/test/jdk/java/awt/Desktop/BrowseTest.java +++ b/test/jdk/java/awt/Desktop/BrowseTest.java @@ -40,10 +40,27 @@ import jtreg.SkippedException; public class BrowseTest extends JPanel { static final String INSTRUCTIONS = """ - This test could launch default file manager to open user's home - directory, and default web browser to show the URL of java vendor. - After test execution close the native file manager and web browser + Set your default browser as per the test platform. + macOS - Safari + windows - MS Edge + linux - Firefox + + This test checks 2 cases: + + 1) Directory URI: + On macOS and windows, verify that a browser window opens and + EITHER the browser OR native file manager shows the user's + home directory. + + On Linux verify that the user's home directory is shown by the + default file manager. + + 2) Web URI: + Verify that the Web URI (URL of java vendor) opens in the browser. + + After test execution close the native file manager and any web browser windows if they were launched by test. + Also check output for any unexpected EXCEPTIONS, if you see any failure messages press Fail otherwise press Pass. """; @@ -53,7 +70,7 @@ public class BrowseTest extends JPanel { URI dirURI = new File(System.getProperty("user.home")).toURI(); URI webURI = URI.create(System.getProperty("java.vendor.url", "http://www.java.com")); - boolean failed = false; + PassFailJFrame.log("Testing 1st case: Directory URI ..."); try { PassFailJFrame.log("Try to browse " + dirURI + " ..."); desktop.browse(dirURI); @@ -62,6 +79,7 @@ public class BrowseTest extends JPanel { PassFailJFrame.log("EXCEPTION: " + e.getMessage()); } + PassFailJFrame.log("Testing 2nd case: Web URI ..."); try { PassFailJFrame.log("Try to browse " + webURI + " ..."); desktop.browse(webURI); diff --git a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java index b2d7ef28df1..77d86ceb42a 100644 --- a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java +++ b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java @@ -48,7 +48,7 @@ public class EditAndPrintTest extends JPanel { This test tries to edit and print a directory, which will expectedly raise IOException. Then this test would edit and print a .txt file, which should be successful. After test execution close the editor if it was launched by test. - If you see any EXCEPTION messages in the output press FAIL. + If you see any EXCEPTION messages in case of .txt file in the output press FAIL. """; static Desktop desktop; From 82e5771b0be205c2ef9500ffa750bf97da21823c Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 9 Oct 2025 04:40:38 +0000 Subject: [PATCH 055/126] 8365280: Enhance JOptionPane Reviewed-by: rhalade, prr, tr, aivanov --- .../swing/plaf/basic/BasicOptionPaneUI.java | 102 +++++++----------- .../swing/JOptionPane/TestJOptionHTMLTag.java | 68 ------------ 2 files changed, 39 insertions(+), 131 deletions(-) delete mode 100644 test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index 8ab31b1a2ad..8c7ac94b04e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -452,78 +452,54 @@ public class BasicOptionPaneUI extends OptionPaneUI { } else if ((nl = s.indexOf('\n')) >= 0) { nll = 1; } - if (s.contains("")) { - /* line break in html text is done by
tag - * and not by /n so it's incorrect to address newline - * same as non-html text. - * Text between tags are extracted - * and rendered as JLabel text - */ - int index1 = s.indexOf(""); - int index2 = s.indexOf(""); - String str = ""; - if (index2 >= 0) { - str = s.substring(index2 + "".length()); - s = s.substring(index1, index2 + + "".length()); + if (nl >= 0) { + // break up newlines + if (nl == 0) { + JPanel breakPanel = new JPanel() { + public Dimension getPreferredSize() { + Font f = getFont(); + + if (f != null) { + return new Dimension(1, f.getSize() + 2); + } + return new Dimension(0, 0); + } + }; + breakPanel.setName("OptionPane.break"); + addMessageComponents(container, cons, breakPanel, maxll, + true); + } else { + addMessageComponents(container, cons, s.substring(0, nl), + maxll, false); } + // Prevent recursion of more than + // 200 successive newlines in a message + // and indicate message is truncated via ellipsis + if (recursionCount++ > 200) { + recursionCount = 0; + addMessageComponents(container, cons, new String("..."), + maxll, false); + return; + } + addMessageComponents(container, cons, s.substring(nl + nll), maxll, + false); + + } else if (len > maxll) { + Container c = Box.createVerticalBox(); + c.setName("OptionPane.verticalBox"); + burstStringInto(c, s, maxll); + addMessageComponents(container, cons, c, maxll, true); + + } else { JLabel label = new JLabel(); if (Boolean.TRUE.equals( - this.optionPane.getClientProperty("html.disable"))) { + optionPane.getClientProperty("html.disable"))) { label.putClientProperty("html.disable", true); } label.setText(s); label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); - if (!str.isEmpty()) { - addMessageComponents(container, cons, str, maxll, false); - } - } else { - if (nl >= 0) { - // break up newlines - if (nl == 0) { - JPanel breakPanel = new JPanel() { - public Dimension getPreferredSize() { - Font f = getFont(); - - if (f != null) { - return new Dimension(1, f.getSize() + 2); - } - return new Dimension(0, 0); - } - }; - breakPanel.setName("OptionPane.break"); - addMessageComponents(container, cons, breakPanel, maxll, - true); - } else { - addMessageComponents(container, cons, s.substring(0, nl), - maxll, false); - } - // Prevent recursion of more than - // 200 successive newlines in a message - // and indicate message is truncated via ellipsis - if (recursionCount++ > 200) { - recursionCount = 0; - addMessageComponents(container, cons, new String("..."), - maxll, false); - return; - } - addMessageComponents(container, cons, s.substring(nl + nll), maxll, - false); - - } else if (len > maxll) { - Container c = Box.createVerticalBox(); - c.setName("OptionPane.verticalBox"); - burstStringInto(c, s, maxll); - addMessageComponents(container, cons, c, maxll, true); - - } else { - JLabel label; - label = new JLabel(s, JLabel.LEADING); - label.setName("OptionPane.label"); - configureMessageLabel(label); - addMessageComponents(container, cons, label, maxll, true); - } } } } diff --git a/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java b/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java deleted file mode 100644 index 94318492bd9..00000000000 --- a/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. - * 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 5074006 - * @key headful - * @library /java/awt/regtesthelpers - * @build PassFailJFrame - * @summary Swing JOptionPane shows tag as a string after newline - * @run main/manual TestJOptionHTMLTag -*/ - -import javax.swing.JDialog; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; - -public class TestJOptionHTMLTag { - static String instructions - = """ - INSTRUCTIONS: - A dialog will be shown. - If it does not contain string, press Pass else press Fail. - """; - static PassFailJFrame passFailJFrame; - - public static void main(String[] args) throws Exception { - - SwingUtilities.invokeAndWait(() -> { - try { - String message = "" + "This is a test\n" + ""; - JOptionPane optionPane = new JOptionPane(); - optionPane.setMessage(message); - optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE); - JDialog dialog = new JDialog(); - dialog.setContentPane(optionPane); - dialog.pack(); - dialog.setVisible(true); - - passFailJFrame = new PassFailJFrame(instructions); - PassFailJFrame.addTestWindow(dialog); - PassFailJFrame.positionTestWindow(dialog, PassFailJFrame.Position.HORIZONTAL); - } catch (Exception e) { - e.printStackTrace(); - } - }); - passFailJFrame.awaitAndCheck(); - } -} - From 07f981f6b0bb8a7e444fd744791f73853e9fa325 Mon Sep 17 00:00:00 2001 From: Jamil Nimeh Date: Mon, 3 Nov 2025 14:53:21 +0000 Subject: [PATCH 056/126] 8368032: Enhance Certificate Checking Reviewed-by: ahgross, coffeys, rhalade, mullan, abarashev --- .../provider/certpath/URICertStore.java | 358 +++++++++++++++++- .../share/conf/security/java.security | 45 +++ .../x509/URICertStore/AIACertTimeout.java | 2 + .../x509/URICertStore/ExtensionsWithLDAP.java | 94 ++--- 4 files changed, 452 insertions(+), 47 deletions(-) diff --git a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java index 28729a56dbd..3e1fc8db164 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -29,6 +29,7 @@ import java.io.InputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URISyntaxException; import java.net.URLConnection; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -48,8 +49,11 @@ import java.security.cert.X509CRL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Optional; +import java.util.Set; import sun.security.x509.AccessDescription; import sun.security.x509.GeneralNameInterface; @@ -58,6 +62,8 @@ import sun.security.util.Cache; import sun.security.util.Debug; import sun.security.util.SecurityProperties; +import javax.security.auth.x500.X500Principal; + /** * A CertStore that retrieves Certificates or * CRLs from a URI, for example, as specified in an X.509 @@ -182,6 +188,166 @@ class URICertStore extends CertStoreSpi { return timeoutVal; } + /** + * Enumeration for the allowed schemes we support when following a + * URI from an authorityInfoAccess extension on a certificate. + */ + private enum AllowedScheme { + HTTP(HttpFtpRuleMatcher.HTTP), + HTTPS(HttpFtpRuleMatcher.HTTPS), + LDAP(LdapRuleMatcher.LDAP), + LDAPS(LdapRuleMatcher.LDAPS), + FTP(HttpFtpRuleMatcher.FTP); + + final URIRuleMatcher ruleMatcher; + + AllowedScheme(URIRuleMatcher matcher) { + ruleMatcher = matcher; + } + + /** + * Return an {@code AllowedScheme} based on a case-insensitive match + * @param name the scheme name to be matched + * @return the {@code AllowedScheme} that corresponds to the + * {@code name} provided, or null if there is no match. + */ + static AllowedScheme nameOf(String name) { + if (name == null) { + return null; + } + + try { + return AllowedScheme.valueOf(name.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException _) { + return null; + } + } + } + + private static Set CA_ISS_URI_FILTERS = null; + private static final boolean CA_ISS_ALLOW_ANY; + + static { + boolean allowAny = false; + try { + if (Builder.USE_AIA) { + CA_ISS_URI_FILTERS = new LinkedHashSet<>(); + String aiaPropVal = Optional.ofNullable( + SecurityProperties.getOverridableProperty( + "com.sun.security.allowedAIALocations")). + map(String::trim).orElse(""); + if (aiaPropVal.equalsIgnoreCase("any")) { + allowAny = true; + if (debug != null) { + debug.println("allowedAIALocations: Warning: " + + "Allow-All URI filtering enabled!"); + } + } else { + // Load all the valid rules from the Security property + if (!aiaPropVal.isEmpty()) { + String[] aiaUriStrs = aiaPropVal.trim().split("\\s+"); + addCaIssUriFilters(aiaUriStrs); + } + + if (CA_ISS_URI_FILTERS.isEmpty()) { + if (debug != null) { + debug.println("allowedAIALocations: Warning: " + + "No valid filters found. Deny-all URI " + + "filtering is active."); + } + } + } + } + } finally { + CA_ISS_ALLOW_ANY = allowAny; + } + } + + /** + * Populate the filter collection from the list of AIA CA issuer URIs + * found in the {@code com.sun.security.allowedAIALocations} security + * or system property. + * + * @param aiaUriStrs array containing String URI filters + */ + private static void addCaIssUriFilters(String[] aiaUriStrs) { + for (String aiaStr : aiaUriStrs) { + if (aiaStr != null && !aiaStr.isEmpty()) { + try { + AllowedScheme scheme; + URI aiaUri = new URI(aiaStr).normalize(); + // It must be absolute and non-opaque + if (!aiaUri.isAbsolute() || aiaUri.isOpaque()) { + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "non-absolute or opaque URI " + aiaUri); + } + } else if (aiaUri.getHost() == null) { + // We do not allow rules with URIs that omit a hostname + // or address. + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "URI rule with no hostname or address: " + + aiaUri); + } + } else if ((scheme = AllowedScheme.nameOf( + aiaUri.getScheme())) != null) { + // When it is an LDAP type, we can check the path + // portion (the DN) for proper structure and reject + // the rule early if it isn't correct. + if (scheme == AllowedScheme.LDAP || + scheme == AllowedScheme.LDAPS) { + try { + new X500Principal(aiaUri.getPath(). + replaceFirst("^/+", "")); + } catch (IllegalArgumentException iae) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Skipping LDAP rule: " + iae); + } + continue; + } + } + + // When a URI has a non-null query or fragment + // warn the user upon adding the rule that those + // components will be ignored + if (aiaUri.getQuery() != null) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Rule will ignore non-null query"); + } + } + if (aiaUri.getFragment() != null) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Rule will ignore non-null fragment"); + } + } + + CA_ISS_URI_FILTERS.add(aiaUri); + if (debug != null) { + debug.println("allowedAIALocations: Added " + + aiaUri + " to URI filters"); + } + } else { + if (debug != null) { + debug.println("allowedAIALocations: Disallowed " + + "filter URI scheme: " + + aiaUri.getScheme()); + } + } + } catch (URISyntaxException urise) { + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "filter URI entry " + aiaStr + + ": parse failure at index " + urise.getIndex()); + } + } + } + } + } + /** * Creates a URICertStore. * @@ -244,6 +410,39 @@ class URICertStore extends CertStoreSpi { return null; } URI uri = ((URIName) gn).getURI(); + + // Before performing any instantiation make sure that + // the URI passes any filtering rules. This processing should + // only occur if the com.sun.security.enableAIAcaIssuers is true + // and the "any" rule has not been specified. + if (Builder.USE_AIA && !CA_ISS_ALLOW_ANY) { + URI normAIAUri = uri.normalize(); + AllowedScheme scheme = AllowedScheme.nameOf(normAIAUri.getScheme()); + + if (scheme == null) { + if (debug != null) { + debug.println("allowedAIALocations: No matching ruleset " + + "for scheme " + normAIAUri.getScheme()); + } + return null; + } + + // Go through each of the filter rules and see if any will + // make a positive match against the caIssuer URI. If nothing + // matches then we won't instantiate a URICertStore. + if (CA_ISS_URI_FILTERS.stream().noneMatch(rule -> + scheme.ruleMatcher.matchRule(rule, normAIAUri))) { + if (debug != null) { + debug.println("allowedAIALocations: Warning - " + + "The caIssuer URI " + normAIAUri + + " in the AuthorityInfoAccess extension is denied " + + "access. Use the com.sun.security.allowedAIALocations" + + " security/system property to allow access."); + } + return null; + } + } + try { return URICertStore.getInstance(new URICertStoreParameters(uri)); } catch (Exception ex) { @@ -270,7 +469,7 @@ class URICertStore extends CertStoreSpi { @Override @SuppressWarnings("unchecked") public synchronized Collection engineGetCertificates - (CertSelector selector) throws CertStoreException { + (CertSelector selector) throws CertStoreException { if (ldap) { // caching mechanism, see the class description for more info. @@ -462,4 +661,159 @@ class URICertStore extends CertStoreSpi { super(spi, p, type, params); } } + + /** + * URIRuleMatcher - abstract base class for the rule sets used for + * various URI schemes. + */ + static abstract class URIRuleMatcher { + protected final int wellKnownPort; + + protected URIRuleMatcher(int port) { + wellKnownPort = port; + } + + /** + * Attempt to match the scheme, host and port between a filter + * rule URI and a URI coming from an AIA extension. + * + * @param filterRule the filter rule to match against + * @param caIssuer the AIA URI being compared + * @return true if the scheme, host and port numbers match, false if + * any of the components do not match. If a port number is omitted in + * either the filter rule or AIA URI, the well-known port for that + * scheme is used in the comparison. + */ + boolean schemeHostPortCheck(URI filterRule, URI caIssuer) { + if (!filterRule.getScheme().equalsIgnoreCase( + caIssuer.getScheme())) { + return false; + } else if (!filterRule.getHost().equalsIgnoreCase( + caIssuer.getHost())) { + return false; + } else { + try { + // Check for port matching, taking into consideration + // default ports + int fPort = (filterRule.getPort() == -1) ? wellKnownPort : + filterRule.getPort(); + int caiPort = (caIssuer.getPort() == -1) ? wellKnownPort : + caIssuer.getPort(); + if (fPort != caiPort) { + return false; + } + } catch (IllegalArgumentException iae) { + return false; + } + } + return true; + } + + /** + * Attempt to match an AIA URI against a specific filter rule. The + * specific rules to apply are implementation dependent. + * + * @param filterRule the filter rule to match against + * @param caIssuer the AIA URI being compared + * @return true if all matching rules pass, false if any fail. + */ + abstract boolean matchRule(URI filterRule, URI caIssuer); + } + + static class HttpFtpRuleMatcher extends URIRuleMatcher { + static final HttpFtpRuleMatcher HTTP = new HttpFtpRuleMatcher(80); + static final HttpFtpRuleMatcher HTTPS = new HttpFtpRuleMatcher(443); + static final HttpFtpRuleMatcher FTP = new HttpFtpRuleMatcher(21); + + private HttpFtpRuleMatcher(int port) { + super(port); + } + + @Override + boolean matchRule(URI filterRule, URI caIssuer) { + // Check for scheme/host/port matching + if (!schemeHostPortCheck(filterRule, caIssuer)) { + return false; + } + + // Check the path component to make sure the filter is at + // least a root of the AIA caIssuer URI's path. It must be + // a case-sensitive match for all platforms. + if (!isRootOf(filterRule, caIssuer)) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed: " + + "AIA URI is not within the rule's path hierarchy."); + } + return false; + } + return true; + } + + /** + * Performs a hierarchical containment check, ensuring that the + * base URI's path is a root component of the candidate path. The + * path comparison is case-sensitive. If the base path ends in a + * slash (/) then all candidate paths that begin with the base + * path are allowed. If it does not end in a slash, then it is + * assumed that the leaf node in the base path is a file component + * and both paths must match exactly. + * + * @param base the URI that contains the root path + * @param candidate the URI that contains the path being evaluated + * @return true if {@code candidate} is a child path of {@code base}, + * false otherwise. + */ + private static boolean isRootOf(URI base, URI candidate) { + // Note: The URIs have already been normalized at this point and + // HTTP URIs cannot have null paths. If it's an empty path + // then consider the path to be "/". + String basePath = Optional.of(base.getPath()). + filter(p -> !p.isEmpty()).orElse("/"); + String candPath = Optional.of(candidate.getPath()). + filter(p -> !p.isEmpty()).orElse("/"); + return (basePath.endsWith("/")) ? candPath.startsWith(basePath) : + candPath.equals(basePath); + } + } + + static class LdapRuleMatcher extends URIRuleMatcher { + static final LdapRuleMatcher LDAP = new LdapRuleMatcher(389); + static final LdapRuleMatcher LDAPS = new LdapRuleMatcher(636); + + private LdapRuleMatcher(int port) { + super(port); + } + + @Override + boolean matchRule(URI filterRule, URI caIssuer) { + // Check for scheme/host/port matching + if (!schemeHostPortCheck(filterRule, caIssuer)) { + return false; + } + + // Obtain the base DN component and compare + try { + X500Principal filterBaseDn = new X500Principal( + filterRule.getPath().replaceFirst("^/+", "")); + X500Principal caIssBaseDn = new X500Principal( + caIssuer.getPath().replaceFirst("^/+", "")); + if (!filterBaseDn.equals(caIssBaseDn)) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed: " + + "Base DN mismatch (" + filterBaseDn + " vs " + + caIssBaseDn + ")"); + } + return false; + } + } catch (IllegalArgumentException iae) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed on DN: " + + iae); + } + return false; + } + + return true; + } + } } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index b5cbce413b2..9a81ba86268 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1655,3 +1655,48 @@ jdk.tls.alpnCharset=ISO_8859_1 # withEncryption method. # jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128 + +# +# X.509 AuthorityInfoAccess caIssuer URI Filtering +# +# This property defines a whitespace-separated list of filters that +# are applied to URIs found in the authorityInfoAccess extension in +# X.509 certificates. Any caIssuers URIs in X.509 certificates are only +# followed when the com.sun.security.enableAIAcaIssuers System property is +# enabled and the filter allows the URI. By default this property imposes a +# deny-all ruleset. This property may be overridden by a System property +# of the same name. +# +# The filters must take the form of absolute, hierarchical URIs as defined by +# the java.net.URI class. Additionally, only the following protocols are +# allowed as filters: http, https, ldap and ftp. +# See RFC 5280, section 4.2.2.1 for details about the types of URIs allowed for +# the extension and their specific requirements. +# The filter matching rules are applied to each CA issuer URI as follows: +# 1. The scheme must match (case-insensitive). +# 2. A hostname or address must be specified in the filter URI. It must match +# the host or address in the caIssuers URI (case-insensitive). No name +# resolution is performed on hostnames to match IP addresses. +# 3. The port number must match. For filter and caIssuer URIs, when a port +# number is omitted, the well-known port for that scheme will be used in the +# comparison. +# 4. For hierarchical filesystem schemes (e.g. http[s], ftp): +# a. The normalized path portion of the filter URI is matched in a +# case-sensitive manner. If the final component of the path does not end +# in a slash (/), it is considered to be a file path component and must +# be an exact match of the caIssuer's URI file path component. If the +# final filter component ends in a slash, then it must either match or be +# a prefix of the caIssuer's URI path component (e.g. a filter path of +# /ab/cd/ will match a caIssuer path of /ab/cd/, /ab/cd/ef and +# /ab/cd/ef/ghi). +# b. Query strings will be ignored in filter rules and caIssuer URIs. +# c. Fragments will be ignored in filter rules and caIssuer URIs. +# 5. For ldap URIs: +# a. The base DN must be an exact match (case-insensitive). +# b. Any query string in the rule, if specified, is ignored. +# 6. A single value "any" (case-insensitive) will create an allow-all rule. +# +# As an example, here is a valid filter policy consisting of two rules: +# com.sun.security.allowedAIALocations=http://some.company.com/cacert \ +# ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary +com.sun.security.allowedAIALocations= diff --git a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java index cabb225bf1c..5491d7b0d7a 100644 --- a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java +++ b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java @@ -47,6 +47,7 @@ import com.sun.net.httpserver.*; import java.io.*; import java.math.BigInteger; import java.net.InetSocketAddress; +import java.security.Security; import java.security.cert.*; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -69,6 +70,7 @@ public class AIACertTimeout { private static X509Certificate eeCert; public static void main(String[] args) throws Exception { + Security.setProperty("com.sun.security.allowedAIALocations", "any"); int servTimeoutMsec = (args != null && args.length >= 1) ? Integer.parseInt(args[0]) : -1; boolean expectedPass = args != null && args.length >= 2 && diff --git a/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java b/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java index 3b598d78d9f..2214e9256c3 100644 --- a/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java +++ b/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -38,6 +38,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; +import java.security.Security; import java.security.cert.CertPath; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; @@ -47,7 +48,6 @@ import java.security.cert.PKIXParameters; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -67,25 +67,27 @@ public class ExtensionsWithLDAP { * Not After : Jan 17 18:03:59 2043 GMT * Subject: CN=Root */ - private static final String CA_CERT = "" - + "-----BEGIN CERTIFICATE-----\n" - + "MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV\n" - + "BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD\n" - + "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm\n" - + "bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH\n" - + "V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv\n" - + "BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b\n" - + "QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t\n" - + "svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6\n" - + "bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb\n" - + "QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB\n" - + "/zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp\n" - + "3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES\n" - + "q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ\n" - + "nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj\n" - + "jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc\n" - + "IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg==\n" - + "-----END CERTIFICATE-----"; + private static final String CA_CERT = + """ + -----BEGIN CERTIFICATE----- + MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV + BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD + VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm + bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH + V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv + BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b + QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t + svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6 + bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb + QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB + /zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp + 3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES + q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ + nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj + jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc + IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg== + -----END CERTIFICATE----- + """; /* * Certificate: @@ -106,39 +108,41 @@ public class ExtensionsWithLDAP { * Authority Information Access: * CA Issuers - URI:ldap://ldap.host.for.aia/dc=Root?cACertificate */ - private static final String EE_CERT = "" - + "-----BEGIN CERTIFICATE-----\n" - + "MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290\n" - + "MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw\n" - + "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi\n" - + "T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK\n" - + "mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz\n" - + "7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR\n" - + "v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM\n" - + "ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19\n" - + "AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m\n" - + "b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s\n" - + "ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G\n" - + "CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/\n" - + "gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc\n" - + "iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn\n" - + "ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1\n" - + "/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL\n" - + "hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY\n" - + "-----END CERTIFICATE-----"; + private static final String EE_CERT = + """ + -----BEGIN CERTIFICATE----- + MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290 + MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw + ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi + T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK + mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz + 7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR + v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM + ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19 + AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m + b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s + ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G + CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/ + gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc + iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn + ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1 + /a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL + hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY + -----END CERTIFICATE-----"""; public static void main(String[] args) throws Exception { String extension = args[0]; String targetHost = args[1]; - + Security.setProperty("com.sun.security.allowedAIALocations", + "ldap://" + targetHost + "/dc=Root"); X509Certificate trustedCert = loadCertificate(CA_CERT); X509Certificate eeCert = loadCertificate(EE_CERT); Set trustedCertsSet = new HashSet<>(); trustedCertsSet.add(new TrustAnchor(trustedCert, null)); - CertPath cp = (CertPath) CertificateFactory.getInstance("X509") - .generateCertPath(Arrays.asList(eeCert)); + CertPath cp = CertificateFactory.getInstance("X509") + .generateCertPath(List.of(eeCert)); // CertPath validator should try to parse CRLDP and AIA extensions, // and load CRLs/certs which they point to. @@ -151,7 +155,7 @@ public class ExtensionsWithLDAP { = (InetSocketAddress) socket.getRemoteSocketAddress(); hosts.add(remoteAddress.getHostName()); }; - try (SocksProxy proxy = SocksProxy.startProxy(socketConsumer)) { + try (SocksProxy _ = SocksProxy.startProxy(socketConsumer)) { CertPathValidator.getInstance("PKIX").validate(cp, new PKIXParameters(trustedCertsSet)); throw new RuntimeException("CertPathValidatorException not thrown"); From 75172e06585060e5efca080a11d8a8a51b40afed Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Mon, 19 Jan 2026 07:45:21 +0000 Subject: [PATCH 057/126] 8374717: Unclear wording in docs for recursion for List, Map and LazyConstant Reviewed-by: rriggs --- src/java.base/share/classes/java/lang/LazyConstant.java | 7 +++---- src/java.base/share/classes/java/util/List.java | 4 ++-- src/java.base/share/classes/java/util/Map.java | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/lang/LazyConstant.java b/src/java.base/share/classes/java/lang/LazyConstant.java index 703d67b8abf..85f9d0e82fd 100644 --- a/src/java.base/share/classes/java/lang/LazyConstant.java +++ b/src/java.base/share/classes/java/lang/LazyConstant.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 @@ -87,9 +87,8 @@ import java.util.function.Supplier; * is thrown. Hence, a lazy constant can never hold a {@code null} value. Clients who * want to use a nullable constant can wrap the value into an {@linkplain Optional} holder. *

- * If the computing function recursively invokes itself (directly or indirectly via - * the lazy constant), an {@linkplain IllegalStateException} is thrown, and the lazy - * constant is not initialized. + * If the computing function recursively invokes itself via the lazy constant, an + * {@linkplain IllegalStateException} is thrown, and the lazy constant is not initialized. * *

Composing lazy constants

* A lazy constant can depend on other lazy constants, forming a dependency graph diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java index 43408de292a..5f9a90e1748 100644 --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.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 @@ -1224,7 +1224,7 @@ public interface List extends SequencedCollection { * The returned list and its {@link List#subList(int, int) subList()} or * {@link List#reversed()} views implement the {@link RandomAccess} interface. *

- * If the provided computing function recursively calls itself or the returned + * If the provided computing function recursively calls itself via the returned * lazy list for the same index, an {@linkplain IllegalStateException} * will be thrown. *

diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java index 177f0522b1b..fa16fb89050 100644 --- a/src/java.base/share/classes/java/util/Map.java +++ b/src/java.base/share/classes/java/util/Map.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 @@ -1777,7 +1777,7 @@ public interface Map { * The values of any {@link Map#values()} or {@link Map#entrySet()} views of * the returned map are also lazily computed. *

- * If the provided computing function recursively calls itself or + * If the provided computing function recursively calls itself via * the returned lazy map for the same key, an {@linkplain IllegalStateException} * will be thrown. *

From 9d7ecd51d72a1a9f34a19c07813e8b5530e6a944 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 19 Jan 2026 08:32:03 +0000 Subject: [PATCH 058/126] 8375437: G1: Convert G1EvacFailureRegions to use Atomic Reviewed-by: stefank, iwalulya --- src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp | 6 +++--- src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp | 6 ++++-- src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp index ffcb5a0022f..37553e2aa56 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Huawei Technologies Co., Ltd. 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 @@ -28,7 +29,6 @@ #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/bitMap.inline.hpp" G1EvacFailureRegions::G1EvacFailureRegions() : @@ -43,7 +43,7 @@ G1EvacFailureRegions::~G1EvacFailureRegions() { } void G1EvacFailureRegions::pre_collection(uint max_regions) { - AtomicAccess::store(&_num_regions_evac_failed, 0u); + _num_regions_evac_failed.store_relaxed(0u); _regions_evac_failed.resize(max_regions); _regions_pinned.resize(max_regions); _regions_alloc_failed.resize(max_regions); @@ -69,6 +69,6 @@ void G1EvacFailureRegions::par_iterate(G1HeapRegionClosure* closure, G1CollectedHeap::heap()->par_iterate_regions_array(closure, hrclaimer, _evac_failed_regions, - AtomicAccess::load(&_num_regions_evac_failed), + num_regions_evac_failed(), worker_id); } diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp index 9d29957b782..f752a3f8ab7 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Huawei Technologies Co., Ltd. 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 @@ -25,6 +26,7 @@ #ifndef SHARE_GC_G1_G1EVACFAILUREREGIONS_HPP #define SHARE_GC_G1_G1EVACFAILUREREGIONS_HPP +#include "runtime/atomic.hpp" #include "utilities/bitMap.hpp" class G1AbstractSubTask; @@ -53,14 +55,14 @@ class G1EvacFailureRegions { // Evacuation failed regions (indexes) in the current collection. uint* _evac_failed_regions; // Number of regions evacuation failed in the current collection. - volatile uint _num_regions_evac_failed; + Atomic _num_regions_evac_failed; public: G1EvacFailureRegions(); ~G1EvacFailureRegions(); uint get_region_idx(uint idx) const { - assert(idx < _num_regions_evac_failed, "precondition"); + assert(idx < _num_regions_evac_failed.load_relaxed(), "precondition"); return _evac_failed_regions[idx]; } diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp index 6eec9b63e6b..fb456475b56 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Huawei Technologies Co., Ltd. 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 @@ -29,10 +30,9 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" -#include "runtime/atomicAccess.hpp" uint G1EvacFailureRegions::num_regions_evac_failed() const { - return AtomicAccess::load(&_num_regions_evac_failed); + return _num_regions_evac_failed.load_relaxed(); } bool G1EvacFailureRegions::has_regions_evac_failed() const { @@ -57,7 +57,7 @@ bool G1EvacFailureRegions::record(uint worker_id, uint region_idx, bool cause_pi bool success = _regions_evac_failed.par_set_bit(region_idx, memory_order_relaxed); if (success) { - size_t offset = AtomicAccess::fetch_then_add(&_num_regions_evac_failed, 1u); + size_t offset = _num_regions_evac_failed.fetch_then_add(1u); _evac_failed_regions[offset] = region_idx; G1CollectedHeap* g1h = G1CollectedHeap::heap(); From 30f39d88e5af36bb6db458c03215e9fa6a31d6f3 Mon Sep 17 00:00:00 2001 From: David Briemann Date: Mon, 19 Jan 2026 08:54:18 +0000 Subject: [PATCH 059/126] 8375530: PPC64: incorrect quick verify_method_data_pointer check causes poor performance in debug build Reviewed-by: mdoerr, shade --- src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index fc865be015e..f7bf457f72c 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -1109,11 +1109,11 @@ void InterpreterMacroAssembler::verify_method_data_pointer() { lhz(R11_scratch1, in_bytes(DataLayout::bci_offset()), R28_mdx); ld(R12_scratch2, in_bytes(Method::const_offset()), R19_method); addi(R11_scratch1, R11_scratch1, in_bytes(ConstMethod::codes_offset())); - add(R11_scratch1, R12_scratch2, R12_scratch2); + add(R11_scratch1, R11_scratch1, R12_scratch2); cmpd(CR0, R11_scratch1, R14_bcp); beq(CR0, verify_continue); - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp ), R19_method, R14_bcp, R28_mdx); + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), R19_method, R14_bcp, R28_mdx); bind(verify_continue); #endif From 3e181485709d108ef3d1e6b595fbd95ecc8ef74a Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 19 Jan 2026 09:02:33 +0000 Subject: [PATCH 060/126] 8375439: G1: Convert G1MonotonicArena class to use Atomic Reviewed-by: stefank, iwalulya --- src/hotspot/share/gc/g1/g1MonotonicArena.cpp | 55 +++++++++---------- src/hotspot/share/gc/g1/g1MonotonicArena.hpp | 47 ++++++++-------- .../share/gc/g1/g1MonotonicArena.inline.hpp | 11 ++-- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp b/src/hotspot/share/gc/g1/g1MonotonicArena.cpp index a9c6462680f..3f97870a67f 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.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. * 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,6 @@ #include "gc/g1/g1MonotonicArena.inline.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/vmOperations.hpp" #include "utilities/globalCounter.inline.hpp" @@ -61,13 +60,13 @@ void G1MonotonicArena::SegmentFreeList::bulk_add(Segment& first, size_t num, size_t mem_size) { _list.prepend(first, last); - AtomicAccess::add(&_num_segments, num, memory_order_relaxed); - AtomicAccess::add(&_mem_size, mem_size, memory_order_relaxed); + _num_segments.add_then_fetch(num, memory_order_relaxed); + _mem_size.add_then_fetch(mem_size, memory_order_relaxed); } void G1MonotonicArena::SegmentFreeList::print_on(outputStream* out, const char* prefix) { out->print_cr("%s: segments %zu size %zu", - prefix, AtomicAccess::load(&_num_segments), AtomicAccess::load(&_mem_size)); + prefix, _num_segments.load_relaxed(), _mem_size.load_relaxed()); } G1MonotonicArena::Segment* G1MonotonicArena::SegmentFreeList::get_all(size_t& num_segments, @@ -75,12 +74,12 @@ G1MonotonicArena::Segment* G1MonotonicArena::SegmentFreeList::get_all(size_t& nu GlobalCounter::CriticalSection cs(Thread::current()); Segment* result = _list.pop_all(); - num_segments = AtomicAccess::load(&_num_segments); - mem_size = AtomicAccess::load(&_mem_size); + num_segments = _num_segments.load_relaxed(); + mem_size = _mem_size.load_relaxed(); if (result != nullptr) { - AtomicAccess::sub(&_num_segments, num_segments, memory_order_relaxed); - AtomicAccess::sub(&_mem_size, mem_size, memory_order_relaxed); + _num_segments.sub_then_fetch(num_segments, memory_order_relaxed); + _mem_size.sub_then_fetch(mem_size, memory_order_relaxed); } return result; } @@ -96,8 +95,8 @@ void G1MonotonicArena::SegmentFreeList::free_all() { Segment::delete_segment(cur); } - AtomicAccess::sub(&_num_segments, num_freed, memory_order_relaxed); - AtomicAccess::sub(&_mem_size, mem_size_freed, memory_order_relaxed); + _num_segments.sub_then_fetch(num_freed, memory_order_relaxed); + _mem_size.sub_then_fetch(mem_size_freed, memory_order_relaxed); } G1MonotonicArena::Segment* G1MonotonicArena::new_segment(Segment* const prev) { @@ -115,7 +114,7 @@ G1MonotonicArena::Segment* G1MonotonicArena::new_segment(Segment* const prev) { } // Install it as current allocation segment. - Segment* old = AtomicAccess::cmpxchg(&_first, prev, next); + Segment* old = _first.compare_exchange(prev, next); if (old != prev) { // Somebody else installed the segment, use that one. Segment::delete_segment(next); @@ -126,9 +125,9 @@ G1MonotonicArena::Segment* G1MonotonicArena::new_segment(Segment* const prev) { _last = next; } // Successfully installed the segment into the list. - AtomicAccess::inc(&_num_segments, memory_order_relaxed); - AtomicAccess::add(&_mem_size, next->mem_size(), memory_order_relaxed); - AtomicAccess::add(&_num_total_slots, next->num_slots(), memory_order_relaxed); + _num_segments.add_then_fetch(1u, memory_order_relaxed); + _mem_size.add_then_fetch(next->mem_size(), memory_order_relaxed); + _num_total_slots.add_then_fetch(next->num_slots(), memory_order_relaxed); return next; } } @@ -155,7 +154,7 @@ uint G1MonotonicArena::slot_size() const { } void G1MonotonicArena::drop_all() { - Segment* cur = AtomicAccess::load_acquire(&_first); + Segment* cur = _first.load_acquire(); if (cur != nullptr) { assert(_last != nullptr, "If there is at least one segment, there must be a last one."); @@ -175,25 +174,25 @@ void G1MonotonicArena::drop_all() { cur = next; } #endif - assert(num_segments == _num_segments, "Segment count inconsistent %u %u", num_segments, _num_segments); - assert(mem_size == _mem_size, "Memory size inconsistent"); + assert(num_segments == _num_segments.load_relaxed(), "Segment count inconsistent %u %u", num_segments, _num_segments.load_relaxed()); + assert(mem_size == _mem_size.load_relaxed(), "Memory size inconsistent"); assert(last == _last, "Inconsistent last segment"); - _segment_free_list->bulk_add(*first, *_last, _num_segments, _mem_size); + _segment_free_list->bulk_add(*first, *_last, _num_segments.load_relaxed(), _mem_size.load_relaxed()); } - _first = nullptr; + _first.store_relaxed(nullptr); _last = nullptr; - _num_segments = 0; - _mem_size = 0; - _num_total_slots = 0; - _num_allocated_slots = 0; + _num_segments.store_relaxed(0); + _mem_size.store_relaxed(0); + _num_total_slots.store_relaxed(0); + _num_allocated_slots.store_relaxed(0); } void* G1MonotonicArena::allocate() { assert(slot_size() > 0, "instance size not set."); - Segment* cur = AtomicAccess::load_acquire(&_first); + Segment* cur = _first.load_acquire(); if (cur == nullptr) { cur = new_segment(cur); } @@ -201,7 +200,7 @@ void* G1MonotonicArena::allocate() { while (true) { void* slot = cur->allocate_slot(); if (slot != nullptr) { - AtomicAccess::inc(&_num_allocated_slots, memory_order_relaxed); + _num_allocated_slots.add_then_fetch(1u, memory_order_relaxed); guarantee(is_aligned(slot, _alloc_options->slot_alignment()), "result " PTR_FORMAT " not aligned at %u", p2i(slot), _alloc_options->slot_alignment()); return slot; @@ -213,7 +212,7 @@ void* G1MonotonicArena::allocate() { } uint G1MonotonicArena::num_segments() const { - return AtomicAccess::load(&_num_segments); + return _num_segments.load_relaxed(); } #ifdef ASSERT @@ -238,7 +237,7 @@ uint G1MonotonicArena::calculate_length() const { template void G1MonotonicArena::iterate_segments(SegmentClosure& closure) const { - Segment* cur = AtomicAccess::load_acquire(&_first); + Segment* cur = _first.load_acquire(); assert((cur != nullptr) == (_last != nullptr), "If there is at least one segment, there must be a last one"); diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp b/src/hotspot/share/gc/g1/g1MonotonicArena.hpp index 211820c5254..d8e658b5a64 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.hpp @@ -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, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,6 +28,7 @@ #include "gc/shared/freeListAllocator.hpp" #include "nmt/memTag.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/lockFreeStack.hpp" @@ -65,27 +66,27 @@ private: // AllocOptions provides parameters for Segment sizing and expansion. const AllocOptions* _alloc_options; - Segment* volatile _first; // The (start of the) list of all segments. - Segment* _last; // The last segment of the list of all segments. - volatile uint _num_segments; // Number of assigned segments to this allocator. - volatile size_t _mem_size; // Memory used by all segments. + Atomic _first; // The (start of the) list of all segments. + Segment* _last; // The last segment of the list of all segments. + Atomic _num_segments; // Number of assigned segments to this allocator. + Atomic _mem_size; // Memory used by all segments. SegmentFreeList* _segment_free_list; // The global free segment list to preferentially // get new segments from. - volatile uint _num_total_slots; // Number of slots available in all segments (allocated + not yet used). - volatile uint _num_allocated_slots; // Number of total slots allocated ever (including free and pending). + Atomic _num_total_slots; // Number of slots available in all segments (allocated + not yet used). + Atomic _num_allocated_slots; // Number of total slots allocated ever (including free and pending). inline Segment* new_segment(Segment* const prev); DEBUG_ONLY(uint calculate_length() const;) public: - const Segment* first_segment() const { return AtomicAccess::load(&_first); } + const Segment* first_segment() const { return _first.load_relaxed(); } - uint num_total_slots() const { return AtomicAccess::load(&_num_total_slots); } + uint num_total_slots() const { return _num_total_slots.load_relaxed(); } uint num_allocated_slots() const { - uint allocated = AtomicAccess::load(&_num_allocated_slots); + uint allocated = _num_allocated_slots.load_relaxed(); assert(calculate_length() == allocated, "Must be"); return allocated; } @@ -116,11 +117,11 @@ static constexpr uint SegmentPayloadMaxAlignment = 8; class alignas(SegmentPayloadMaxAlignment) G1MonotonicArena::Segment { const uint _slot_size; const uint _num_slots; - Segment* volatile _next; + Atomic _next; // Index into the next free slot to allocate into. Full if equal (or larger) // to _num_slots (can be larger because we atomically increment this value and // check only afterwards if the allocation has been successful). - uint volatile _next_allocate; + Atomic _next_allocate; const MemTag _mem_tag; static size_t header_size() { return align_up(sizeof(Segment), SegmentPayloadMaxAlignment); } @@ -139,21 +140,21 @@ class alignas(SegmentPayloadMaxAlignment) G1MonotonicArena::Segment { Segment(uint slot_size, uint num_slots, Segment* next, MemTag mem_tag); ~Segment() = default; public: - Segment* volatile* next_addr() { return &_next; } + Atomic* next_addr() { return &_next; } void* allocate_slot(); uint num_slots() const { return _num_slots; } - Segment* next() const { return _next; } + Segment* next() const { return _next.load_relaxed(); } void set_next(Segment* next) { assert(next != this, " loop condition"); - _next = next; + _next.store_relaxed(next); } void reset(Segment* next) { - _next_allocate = 0; + _next_allocate.store_relaxed(0); assert(next != this, " loop condition"); set_next(next); memset(payload(0), 0, payload_size()); @@ -166,7 +167,7 @@ public: uint length() const { // _next_allocate might grow larger than _num_slots in multi-thread environments // due to races. - return MIN2(_next_allocate, _num_slots); + return MIN2(_next_allocate.load_relaxed(), _num_slots); } static size_t size_in_bytes(uint slot_size, uint num_slots) { @@ -176,7 +177,7 @@ public: static Segment* create_segment(uint slot_size, uint num_slots, Segment* next, MemTag mem_tag); static void delete_segment(Segment* segment); - bool is_full() const { return _next_allocate >= _num_slots; } + bool is_full() const { return _next_allocate.load_relaxed() >= _num_slots; } }; static_assert(alignof(G1MonotonicArena::Segment) >= SegmentPayloadMaxAlignment, "assert alignment of Segment (and indirectly its payload)"); @@ -186,15 +187,15 @@ static_assert(alignof(G1MonotonicArena::Segment) >= SegmentPayloadMaxAlignment, // performed by multiple threads concurrently. // Counts and memory usage are current on a best-effort basis if accessed concurrently. class G1MonotonicArena::SegmentFreeList { - static Segment* volatile* next_ptr(Segment& segment) { + static Atomic* next_ptr(Segment& segment) { return segment.next_addr(); } using SegmentStack = LockFreeStack; SegmentStack _list; - volatile size_t _num_segments; - volatile size_t _mem_size; + Atomic _num_segments; + Atomic _mem_size; public: SegmentFreeList() : _list(), _num_segments(0), _mem_size(0) { } @@ -210,8 +211,8 @@ public: void print_on(outputStream* out, const char* prefix = ""); - size_t num_segments() const { return AtomicAccess::load(&_num_segments); } - size_t mem_size() const { return AtomicAccess::load(&_mem_size); } + size_t num_segments() const { return _num_segments.load_relaxed(); } + size_t mem_size() const { return _mem_size.load_relaxed(); } }; // Configuration for G1MonotonicArena, e.g slot size, slot number of next Segment. diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp b/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp index dd9ccae1849..cf1b35ccead 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp @@ -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, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,14 +28,13 @@ #include "gc/g1/g1MonotonicArena.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/globalCounter.inline.hpp" inline void* G1MonotonicArena::Segment::allocate_slot() { - if (_next_allocate >= _num_slots) { + if (_next_allocate.load_relaxed() >= _num_slots) { return nullptr; } - uint result = AtomicAccess::fetch_then_add(&_next_allocate, 1u, memory_order_relaxed); + uint result = _next_allocate.fetch_then_add(1u, memory_order_relaxed); if (result >= _num_slots) { return nullptr; } @@ -48,8 +47,8 @@ inline G1MonotonicArena::Segment* G1MonotonicArena::SegmentFreeList::get() { Segment* result = _list.pop(); if (result != nullptr) { - AtomicAccess::dec(&_num_segments, memory_order_relaxed); - AtomicAccess::sub(&_mem_size, result->mem_size(), memory_order_relaxed); + _num_segments.sub_then_fetch(1u, memory_order_relaxed); + _mem_size.sub_then_fetch(result->mem_size(), memory_order_relaxed); } return result; } From e0edc656240d18b4468212c38f136084a50be301 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 19 Jan 2026 12:57:44 +0000 Subject: [PATCH 061/126] 8375463: G1: Remove AtomicAccess include from files that do not use it Reviewed-by: stefank, iwalulya --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 1 - src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp | 3 +-- src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp | 3 +-- src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp | 1 - src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp | 3 +-- src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp | 3 +-- src/hotspot/share/gc/g1/g1ParScanThreadState.cpp | 3 +-- 7 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 3a0c4a04441..b6c3c0b0907 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -103,7 +103,6 @@ #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/cpuTimeCounters.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" diff --git a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp index 83c846e84d4..c02b028112b 100644 --- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp @@ -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 @@ -37,7 +37,6 @@ #include "gc/shared/weakProcessor.inline.hpp" #include "logging/log.hpp" #include "memory/iterator.inline.hpp" -#include "runtime/atomicAccess.hpp" class G1AdjustLiveClosure : public StackObj { G1AdjustClosure* _adjust_closure; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index fae73a2c6bf..13c7a6a8d3e 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp @@ -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 @@ -31,7 +31,6 @@ #include "memory/allocation.hpp" #include "memory/padded.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 878d35397aa..950098c706e 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -30,7 +30,6 @@ #include "gc/g1/g1CodeRootSet.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/g1/g1FromCardCache.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" #include "utilities/bitMap.hpp" diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp index fbd529cb1d3..f621b1318c1 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.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,7 +30,6 @@ #include "gc/g1/g1CardSet.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/bitMap.inline.hpp" void G1HeapRegionRemSet::set_state_untracked() { diff --git a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp index d7e0c6e394f..529ef62b44d 100644 --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * 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 "nmt/memTracker.hpp" #include "oops/markWord.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/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index e7b02ed68e7..75a8ef1a336 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,6 @@ #include "memory/allocation.inline.hpp" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/prefetch.inline.hpp" #include "utilities/globalDefinitions.hpp" From 6942bb2b313c2d81e95f692dd947733b1149e8b8 Mon Sep 17 00:00:00 2001 From: Andreas Steiner Date: Mon, 19 Jan 2026 13:54:06 +0000 Subject: [PATCH 062/126] 8374802: java/net/DatagramSocket/SendReceiveMaxSize.java fails on AIX due to small default RCVBUF size Reviewed-by: alanb --- test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java index 3cea9073199..ded087d35e8 100644 --- a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java +++ b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, 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 @@ -55,6 +55,8 @@ import java.net.MulticastSocket; import java.nio.channels.DatagramChannel; import java.util.Random; +import static java.net.StandardSocketOptions.SO_RCVBUF; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.expectThrows; @@ -102,6 +104,10 @@ public class SendReceiveMaxSize { DatagramSocketSupplier supplier, Class exception) throws IOException { try (var receiver = new DatagramSocket(new InetSocketAddress(HOST_ADDR, 0))) { + assertTrue(receiver.getOption(SO_RCVBUF) >= capacity, + receiver.getOption(SO_RCVBUF) + + " for UDP receive buffer too small to hold capacity " + + capacity); var port = receiver.getLocalPort(); var addr = new InetSocketAddress(HOST_ADDR, port); try (var sender = supplier.open()) { From e7f1f16a88ce239f22f86e479a5e806f531fbe31 Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Mon, 19 Jan 2026 14:02:02 +0000 Subject: [PATCH 063/126] 8375271: [IR Framework] Rename IREncoding to ApplicableIRRules and driver/flag/test VM to Driver/Flag/Test VM Reviewed-by: dfenacci, thartmann, mhaessig --- .../lib/ir_framework/AbstractInfo.java | 4 +- .../compiler/lib/ir_framework/CompLevel.java | 4 +- .../jtreg/compiler/lib/ir_framework/IR.java | 4 +- .../jtreg/compiler/lib/ir_framework/README.md | 24 +++---- .../compiler/lib/ir_framework/Scenario.java | 8 +-- .../lib/ir_framework/TestFramework.java | 46 ++++++------- .../ir_framework/driver/FlagVMProcess.java | 26 ++++---- .../ir_framework/driver/TestVMException.java | 8 +-- .../ir_framework/driver/TestVMProcess.java | 35 +++++----- ...rser.java => ApplicableIRRulesParser.java} | 65 ++++++++++--------- .../irmatching/parser/IRMethodBuilder.java | 5 +- .../irmatching/parser/TestClassParser.java | 14 ++-- .../driver/irmatching/parser/TestMethod.java | 6 +- .../driver/irmatching/parser/TestMethods.java | 8 +-- .../driver/irmatching/parser/VMInfo.java | 4 +- .../irmatching/parser/VMInfoParser.java | 18 ++--- .../parser/hotspot/CompileQueueMessages.java | 8 +-- .../parser/hotspot/HotSpotPidFileParser.java | 12 ++-- .../flag/CompilePhaseCollector.java | 4 +- .../lib/ir_framework/flag/FlagVM.java | 14 ++-- .../shared/NoTestsRunException.java | 10 +-- .../shared/TestFrameworkSocket.java | 34 +++++----- ...ter.java => ApplicableIRRulesPrinter.java} | 25 +++---- .../lib/ir_framework/test/TestVM.java | 20 +++--- .../lib/ir_framework/test/VMInfoPrinter.java | 4 +- .../ir_framework/tests/TestIRMatching.java | 9 +-- .../tests/TestPhaseIRMatching.java | 4 +- 27 files changed, 213 insertions(+), 210 deletions(-) rename test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/{IREncodingParser.java => ApplicableIRRulesParser.java} (60%) rename test/hotspot/jtreg/compiler/lib/ir_framework/test/{IREncodingPrinter.java => ApplicableIRRulesPrinter.java} (96%) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.java b/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.java index 20a865b1337..b57b209112c 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.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 @@ -103,7 +103,7 @@ abstract public class AbstractInfo { } /** - * Returns a boolean indicating if the test VM runs with flags that allow C2 compilations. + * Returns a boolean indicating if the Test VM runs with flags that allow C2 compilations. * * @return {@code true} if C2 compilations are allowed; * {@code false} otherwise (run with {@code -XX:TieredStopAtLevel={1,2,3}, -XX:-UseCompiler}). diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.java b/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.java index b6e55c0f617..f8760b4dfad 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.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,7 +51,7 @@ public enum CompLevel { * Can only be used at {@link Test#compLevel()}. After the warm-up, the framework keeps invoking the test over a span * of 10s (configurable by setting the property flag {@code -DWaitForCompilationTimeout}) until HotSpot compiles the * {@link Test} method. If the method was not compiled after 10s, an exception is thrown. The framework does not wait - * for the compilation if the test VM is run with {@code -Xcomp}, {@code -XX:-UseCompiler}, or + * for the compilation if the Test VM is run with {@code -Xcomp}, {@code -XX:-UseCompiler}, or * {@code -DExcludeRandom=true}. */ WAIT_FOR_COMPILATION(-4), diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IR.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IR.java index fd2cd69056a..96df71dacd7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IR.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IR.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, 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 @@ -55,7 +55,7 @@ import java.lang.annotation.RetentionPolicy; * For any other flag specified either by user code (e.g. {@link Scenario#Scenario(int, String...)}, * {@link TestFramework#runWithFlags(String...) etc.} or as part of the JTreg whitelist, IR verification is applied. * To restrict the application of IR rules when certain flags are present that could change the IR, each {@code @IR} - * annotation can specify additional preconditions on the allowed test VM flags that must hold when an IR rule is applied. + * annotation can specify additional preconditions on the allowed Test VM flags that must hold when an IR rule is applied. * If the specified preconditions fail, then the framework does not apply the IR rule. These preconditions can be * set with {@link #applyIf()}, {@link #applyIfNot()}, {@link #applyIfAnd()}, or {@link #applyIfOr()}. *

diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md index c0df8b54658..da59bd61b13 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md @@ -115,7 +115,7 @@ The [@IR](./IR.java) annotation provides two kinds of checks: - `counts`: A list of one or more "IR node/user-defined regex - counter" pairs which specify how often each IR node/user-defined regex should be matched on the compilation output of each compile phase. #### Disable/Enable IR Rules based on VM Flags -One might also want to restrict the application of certain `@IR` rules depending on the used flags in the test VM. These could be flags defined by the user or by JTreg. In the latter case, the flags must be whitelisted in `JTREG_WHITELIST_FLAGS` in [TestFramework](./TestFramework.java) (i.e. have no unexpected impact on the IR except if the flag simulates a specific machine setup like `UseAVX={1,2,3}` etc.) to enable an IR verification by the framework. The `@IR` rules thus have an option to restrict their application: +One might also want to restrict the application of certain `@IR` rules depending on the used flags in the Test VM. These could be flags defined by the user or by JTreg. In the latter case, the flags must be whitelisted in `JTREG_WHITELIST_FLAGS` in [TestFramework](./TestFramework.java) (i.e. have no unexpected impact on the IR except if the flag simulates a specific machine setup like `UseAVX={1,2,3}` etc.) to enable an IR verification by the framework. The `@IR` rules thus have an option to restrict their application: - `applyIf`: Only apply a rule if a flag has the specified value/range of values. - `applyIfNot`: Only apply a rule if a flag has **not** a specified value/range of values @@ -144,7 +144,7 @@ An IR verification cannot always be performed. Certain VM flags explicitly disab More information about IR matching can be found in the Javadocs of [IR](./IR.java). Concrete examples on how to specify IR constraint/rules can be found in [IRExample](../../../testlibrary_tests/ir_framework/examples/IRExample.java), [TestIRMatching](../../../testlibrary_tests/ir_framework/tests/TestIRMatching.java) (internal framework test), and [TestPhaseIRMatching](../../../testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java) (internal framework test). ### 2.3 Test VM Flags and Scenarios -The recommended way to use the framework is by defining a single `@run driver` statement in the JTreg header which, however, does not allow the specification of additional test VM flags. Instead, the user has the possibility to provide VM flags by calling `TestFramework.runWithFlags()` or by creating a `TestFramework` builder object on which `addFlags()` can be called. +The recommended way to use the framework is by defining a single `@run driver` statement in the JTreg header which, however, does not allow the specification of additional Test VM flags. Instead, the user has the possibility to provide VM flags by calling `TestFramework.runWithFlags()` or by creating a `TestFramework` builder object on which `addFlags()` can be called. If a user wants to provide multiple flag combinations for a single test, he or she has the option to provide different scenarios. A scenario based flag will always have precedence over other user defined flags. More information about scenarios can be found in the Javadocs of [Scenario](./Scenario.java). If a user wants to test all combinations of multiple sets of flags, they can use `TestFramework.addCrossProductScenarios()`. @@ -174,16 +174,16 @@ The framework provides various stress and debug flags. They should mainly be use - `-DExclude=test3`: Provide a list of `@Test` method names which should be excluded from execution. - `-DScenarios=1,2`: Provide a list of scenario indexes to specify which scenarios should be executed. - `-DWarmup=200`: Provide a new default value of the number of warm-up iterations (framework default is 2000). This might have an influence on the resulting IR and could lead to matching failures (the user can also set a fixed default warm-up value in a test with `testFrameworkObject.setDefaultWarmup(200)`). -- `-DReportStdout=true`: Print the standard output of the test VM. +- `-DReportStdout=true`: Print the standard output of the Test VM. - `-DVerbose=true`: Enable more fine-grained logging (slows the execution down). -- `-DReproduce=true`: Flag to use when directly running a test VM to bypass dependencies to the driver VM state (for example, when reproducing an issue). +- `-DReproduce=true`: Flag to use when directly running a Test VM to bypass dependencies to the Driver VM state (for example, when reproducing an issue). - `-DPrintTimes=true`: Print the execution time measurements of each executed test. - `-DPrintRuleMatchingTime=true`: Print the time of matching IR rules per method. Slows down the execution as the rules are warmed up before measurement. -- `-DVerifyVM=true`: The framework runs the test VM with additional verification flags (slows the execution down). +- `-DVerifyVM=true`: The framework runs the Test VM with additional verification flags (slows the execution down). - `-DExcludeRandom=true`: The framework randomly excludes some methods from compilation. IR verification is disabled completely with this flag. - `-DFlipC1C2=true`: The framework compiles all `@Test` annotated method with C1 if a C2 compilation would have been applied and vice versa. IR verification is disabled completely with this flag. - `-DShuffleTests=false`: Disables the random execution order of all tests (such a shuffling is always done by default). -- `-DDumpReplay=true`: Add the `DumpReplay` directive to the test VM. +- `-DDumpReplay=true`: Add the `DumpReplay` directive to the Test VM. - `-DGCAfter=true`: Perform `System.gc()` after each test (slows the execution down). - `-DTestCompilationTimeout=20`: Change the default waiting time (default: 10s) for a compilation of a normal `@Test` annotated method. - `-DWaitForCompilationTimeout=20`: Change the default waiting time (default: 10s) for a compilation of a `@Test` annotated method with compilation level [WAIT\_FOR\_COMPILATION](./CompLevel.java). @@ -193,12 +193,12 @@ The framework provides various stress and debug flags. They should mainly be use ## 3. Test Framework Execution This section gives an overview of how the framework is executing a JTreg test that calls the framework from within its `main()` method. -The framework will spawn a new "test VM" to execute the user defined tests. The test VM collects all tests of the test class specified by the user code in `main()` and ensures that there is no violation of the required format by the framework. In a next step, the framework does the following for each test in general: +The framework will spawn a new "Test VM" to execute the user defined tests. The Test VM collects all tests of the test class specified by the user code in `main()` and ensures that there is no violation of the required format by the framework. In a next step, the framework does the following for each test in general: 1. Warm the test up for a predefined number of times (default 2000). This can also be adapted for all tests by using `testFrameworkobject.setDefaultWarmup(100)` or for individual tests with an additional [@Warmup](./Warmup.java) annotation. 2. After the warm-up is finished, the framework compiles the associated `@Test` annotated method at the specified compilation level (default: C2). 3. After the compilation, the test is invoked one more time. -Once the test VM terminates, IR verification (if possible) is performed on the output of the test VM. If any test throws an exception during its execution or if IR matching fails, the failures are collected and reported in a pretty format. Check the standard error and output for more information and how to reproduce these failures. +Once the Test VM terminates, IR verification (if possible) is performed on the output of the Test VM. If any test throws an exception during its execution or if IR matching fails, the failures are collected and reported in a pretty format. Check the standard error and output for more information and how to reproduce these failures. Some of the steps above can be different due to the kind of the test or due to using non-default annotation properties. These details and differences are described in the Javadocs for the three tests (see section 2.1 Different Tests). @@ -212,10 +212,10 @@ Additional testing was performed by converting all compiler Inline Types tests t ## 5. Framework Package Structure A user only needs to import classes from the package `compiler.lib.ir_framework` (e.g. `import compiler.lib.ir_framework.*;`) which represents the interface classes to the framework. The remaining framework internal classes are kept in separate subpackages and should not directly be imported: -- `compiler.lib.ir_framework.driver`: These classes are used while running the driver VM (same VM as the one running the user code's `main()` method of a JTreg test). -- `compiler.lib.ir_framework.flag`: These classes are used while running the flag VM to determine additional flags for the test VM which are required for IR verification. -- `compiler.lib.ir_framework.test`: These classes are used while running the test VM (i.e. the actual execution of the user tests as described in section 3). -- `compiler.lib.ir_framework.shared`: These classes can be called from either the driver, flag, or test VM. +- `compiler.lib.ir_framework.driver`: These classes are used while running the Driver VM (same VM as the one running the user code's `main()` method of a JTreg test). +- `compiler.lib.ir_framework.flag`: These classes are used while running the Flag VM to determine additional flags for the Test VM which are required for IR verification. +- `compiler.lib.ir_framework.test`: These classes are used while running the Test VM (i.e. the actual execution of the user tests as described in section 3). +- `compiler.lib.ir_framework.shared`: These classes can be called from either the driver, flag, or Test VM. ## 6. Summary The initial design and feature set was kept simple and straight forward and serves well for small to medium sized tests. There are a lot of possibilities to further enhance the framework and make it more powerful. This can be tackled in additional RFEs. A few ideas can be found as subtasks of the [initial RFE](https://bugs.openjdk.org/browse/JDK-8254129) for this framework. diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java b/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java index 17776f7285c..65f61173e2a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.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 @@ -121,16 +121,16 @@ public class Scenario { } /** - * Get the test VM output (stdout + stderr) of this scenario from the last execution of the framework. + * Get the Test VM output (stdout + stderr) of this scenario from the last execution of the framework. * - * @return the test VM output. + * @return the Test VM output. */ public String getTestVMOutput() { return testVMOutput; } /** - * Set the test VM output, called by the framework. + * Set the Test VM output, called by the framework. */ void setTestVMOutput(String testVMOutput) { this.testVMOutput = testVMOutput; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 09e291ce5a4..1fea5da52ef 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.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 @@ -85,19 +85,19 @@ import java.util.stream.Stream; * {@code runXX()} methods of {@link TestFramework}. The second way, which gives more control, is to create a new * {@code TestFramework} builder object on which {@link #start()} needs to be eventually called to start the testing. *

- * The framework is called from the driver VM in which the JTreg test is initially run by specifying {@code + * The framework is called from the Driver VM in which the JTreg test is initially run by specifying {@code * @run driver} in the JTreg header. This strips all additionally specified JTreg VM and Javaoptions. - * The framework creates a new flag VM with all these flags added again in order to figure out which flags are + * The framework creates a new Flag VM with all these flags added again in order to figure out which flags are * required to run the tests specified in the test class (e.g. {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly} * for IR matching). *

- * After the flag VM terminates, it starts a new test VM which performs the execution of the specified + * After the Flag VM terminates, it starts a new Test VM which performs the execution of the specified * tests in the test class as described in {@link Test}, {@link Check}, and {@link Run}. *

- * In a last step, once the test VM has terminated without exceptions, IR matching is performed if there are any IR + * In a last step, once the Test VM has terminated without exceptions, IR matching is performed if there are any IR * rules and if no VM flags disable it (e.g. not running with {@code -Xint}, see {@link IR} for more details). * The IR regex matching is done on the output of {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly} by parsing - * the hotspot_pid file of the test VM. Failing IR rules are reported by throwing a {@link IRViolationException}. + * the hotspot_pid file of the Test VM. Failing IR rules are reported by throwing a {@link IRViolationException}. * * @see Test * @see Check @@ -166,7 +166,7 @@ public class TestFramework { ############################################################# - To only run the failed tests use -DTest, -DExclude, and/or -DScenarios. - - To also get the standard output of the test VM run with + - To also get the standard output of the Test VM run with -DReportStdout=true or for even more fine-grained logging use -DVerbose=true. ############################################################# @@ -236,10 +236,10 @@ public class TestFramework { } /** - * Tests the class from which this method was invoked from. The test VM is called with the specified {@code flags}. + * Tests the class from which this method was invoked from. The Test VM is called with the specified {@code flags}. *

    *
  • The {@code flags} override any set VM or Javaoptions flags by JTreg by default.

    - * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the JTreg VM and Javaoptions flags over + * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the JTreg VM and Javaoptions flags over * the specified {@code flags} of this method.

  • *
  • If you want to run your entire JTreg test with additional flags, use this method.

  • *
  • If you want to run your entire JTreg test with additional flags but for another test class then the one @@ -248,7 +248,7 @@ public class TestFramework { * {@link #addScenarios(Scenario...)}

  • *
* - * @param flags VM flags to be used for the test VM. + * @param flags VM flags to be used for the Test VM. */ public static void runWithFlags(String... flags) { StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); @@ -258,13 +258,13 @@ public class TestFramework { } /** - * Add VM flags to be used for the test VM. These flags override any VM or Javaoptions set by JTreg by default.

+ * Add VM flags to be used for the Test VM. These flags override any VM or Javaoptions set by JTreg by default.

* Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM or Javaoptions over the scenario flags. * *

* The testing can be started by invoking {@link #start()} * - * @param flags VM options to be applied to the test VM. + * @param flags VM options to be applied to the Test VM. * @return the same framework instance. */ public TestFramework addFlags(String... flags) { @@ -306,7 +306,7 @@ public class TestFramework { } /** - * Add scenarios to be used for the test VM. A test VM is called for each scenario in {@code scenarios} by using the + * Add scenarios to be used for the Test VM. A Test VM is called for each scenario in {@code scenarios} by using the * specified VM flags in the scenario. The scenario flags override any flags set by {@link #addFlags(String...)} * and thus also override any VM or Javaoptions set by JTreg by default.

* Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM and Javaoptions over the scenario flags. @@ -314,7 +314,7 @@ public class TestFramework { *

* The testing can be started by invoking {@link #start()} * - * @param scenarios scenarios which specify specific flags for the test VM. + * @param scenarios scenarios which specify specific flags for the Test VM. * @return the same framework instance. */ public TestFramework addScenarios(Scenario... scenarios) { @@ -503,10 +503,10 @@ public class TestFramework { } /** - * Get the VM output of the test VM. Use {@code -DVerbose=true} to enable more debug information. If scenarios + * Get the VM output of the Test VM. Use {@code -DVerbose=true} to enable more debug information. If scenarios * were run, use {@link Scenario#getTestVMOutput()}. * - * @return the last test VM output. + * @return the last Test VM output. */ public static String getLastTestVMOutput() { return TestVMProcess.getLastTestVMOutput(); @@ -796,9 +796,9 @@ public class TestFramework { } /** - * Execute a separate "flag" VM with White Box access to determine all test VM flags. The flag VM sends an encoding of - * all required flags for the test VM to the driver VM over a socket. Once the flag VM exits, this driver VM parses the - * test VM flags, which also determine if IR matching should be done, and then starts the test VM to execute all tests. + * Execute a separate Flag VM with White Box access to determine all Test VM flags. The Flag VM sends an encoding of + * all required flags for the Test VM to the Driver VM over a socket. Once the Flag VM exits, this Driver VM parses the + * Test VM flags, which also determine if IR matching should be done, and then starts the Test VM to execute all tests. */ private void start(Scenario scenario) { if (scenario != null && !scenario.isEnabled()) { @@ -823,12 +823,12 @@ public class TestFramework { "" : " - [" + String.join(", ", additionalFlags) + "]"; if (shouldVerifyIR) { - // Only need to use flag VM if an IR verification is possibly done. + // Only need to use Flag VM if an IR verification is possibly done. System.out.println("Run Flag VM:"); FlagVMProcess flagVMProcess = new FlagVMProcess(testClass, additionalFlags); shouldVerifyIR = flagVMProcess.shouldVerifyIR(); if (shouldVerifyIR) { - // Add more flags for the test VM which are required to do IR verification. + // Add more flags for the Test VM which are required to do IR verification. additionalFlags.addAll(flagVMProcess.getTestVMFlags()); } // else: Flag VM found a reason to not do IR verification. } else { @@ -882,7 +882,7 @@ public class TestFramework { try { TestClassParser testClassParser = new TestClassParser(testClass, isAllowNotCompilable); Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), - testVMProcess.getIrEncoding()); + testVMProcess.getApplicableIRRules()); IRMatcher matcher = new IRMatcher(testClassMatchable); matcher.match(); } catch (IRViolationException e) { @@ -892,7 +892,7 @@ public class TestFramework { } else { System.out.println("IR verification disabled either due to no @IR annotations, through explicitly setting " + "-DVerify=false, due to not running a debug build, using a non-whitelisted JTreg VM or " + - "Javaopts flag like -Xint, or running the test VM with other VM flags added by user code " + + "Javaopts flag like -Xint, or running the Test VM with other VM flags added by user code " + "that make the IR verification impossible (e.g. -XX:-UseCompile, " + "-XX:TieredStopAtLevel=[1,2,3], etc.)."); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java index 0b7d1db125c..86eb1935207 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,9 +41,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * This class prepares, creates, and runs the "flag" VM with verification of proper termination. The flag VM determines - * the flags required for the "test" VM. The flag VM writes these flags to a dedicated file which is then parsed by this - * class after the termination of the flag VM. + * This class prepares, creates, and runs the Flag VM with verification of proper termination. The Flag VM determines + * the flags required for the Test VM. The Flag VM writes these flags to a dedicated file which is then parsed by this + * class after the termination of the Flag VM. * * @see FlagVM */ @@ -73,7 +73,7 @@ public class FlagVMProcess { String patternString = "(.*DShouldDoIRVerification=(true|false).*)"; Pattern pattern = Pattern.compile(patternString); Matcher matcher = pattern.matcher(flags); - TestFramework.check(matcher.find(), "Invalid flag encoding emitted by flag VM"); + TestFramework.check(matcher.find(), "Invalid flag encoding emitted by Flag VM"); // Maybe we run with flags that make IR verification impossible shouldVerifyIR = Boolean.parseBoolean(matcher.group(2)); testVMFlags.addAll(Arrays.asList(matcher.group(1).split(FlagVM.TEST_VM_FLAGS_DELIMITER))); @@ -91,8 +91,8 @@ public class FlagVMProcess { } /** - * The flag VM needs White Box access to prepare all test VM flags. The flag VM will write the test VM flags to - * a dedicated file which is afterwards parsed by the driver VM and added as flags to the test VM. + * The Flag VM needs White Box access to prepare all Test VM flags. The Flag VM will write the Test VM flags to + * a dedicated file which is afterwards parsed by the Driver VM and added as flags to the Test VM. */ private void prepareVMFlags(Class testClass, List additionalFlags) { cmds.add("-Dtest.jdk=" + Utils.TEST_JDK); @@ -103,7 +103,7 @@ public class FlagVMProcess { cmds.add("-Xbootclasspath/a:."); cmds.add("-XX:+UnlockDiagnosticVMOptions"); cmds.add("-XX:+WhiteBoxAPI"); - // TestFramework and scenario flags might have an influence on the later used test VM flags. Add them as well. + // TestFramework and scenario flags might have an influence on the later used Test VM flags. Add them as well. cmds.addAll(additionalFlags); cmds.add(FlagVM.class.getCanonicalName()); cmds.add(testClass.getCanonicalName()); @@ -111,10 +111,10 @@ public class FlagVMProcess { private void start() { try { - // Run "flag" VM with White Box access to determine the test VM flags and if IR verification should be done. + // Run Flag VM with White Box access to determine the Test VM flags and if IR verification should be done. oa = ProcessTools.executeTestJava(cmds); } catch (Exception e) { - throw new TestRunException("Failed to execute TestFramework flag VM", e); + throw new TestRunException("Failed to execute TestFramework Flag VM", e); } testVMFlagsFile = FlagVM.TEST_VM_FLAGS_FILE_PREFIX + oa.pid() + FlagVM.FILE_POSTFIX; @@ -125,14 +125,14 @@ public class FlagVMProcess { String flagVMOutput = oa.getOutput(); int exitCode = oa.getExitValue(); if (VERBOSE && exitCode == 0) { - System.out.println("--- OUTPUT TestFramework flag VM ---"); + System.out.println("--- OUTPUT TestFramework Flag VM ---"); System.out.println(flagVMOutput); } if (exitCode != 0) { - System.err.println("--- OUTPUT TestFramework flag VM ---"); + System.err.println("--- OUTPUT TestFramework Flag VM ---"); System.err.println(flagVMOutput); - throw new RuntimeException("TestFramework flag VM exited with " + exitCode); + throw new RuntimeException("TestFramework Flag VM exited with " + exitCode); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.java index 63b3d522b86..8de9ac8e348 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.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 @@ -26,7 +26,7 @@ package compiler.lib.ir_framework.driver; import compiler.lib.ir_framework.shared.TestFormatException; /** - * Exception that is thrown if the test VM has thrown any kind of exception (except for {@link TestFormatException}). + * Exception that is thrown if the Test VM has thrown any kind of exception (except for {@link TestFormatException}). */ public class TestVMException extends RuntimeException { private final String exceptionInfo; @@ -37,9 +37,9 @@ public class TestVMException extends RuntimeException { } /** - * Get some more detailed information about the exception thrown in the test VM and how to reproduce it. + * Get some more detailed information about the exception thrown in the Test VM and how to reproduce it. * - * @return a formatted string containing information about the exception of the test VM and how to reproduce it. + * @return a formatted string containing information about the exception of the Test VM and how to reproduce it. */ public String getExceptionInfo() { return exceptionInfo; 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 2b7fd2a1eea..a172dce1991 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.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 @@ -41,9 +41,9 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; /** - * This class prepares, creates, and runs the "test" VM with verification of proper termination. The class also stores - * information about the test VM which is later queried for IR matching. The communication between this driver VM - * and the test VM is done over a dedicated socket. + * This class prepares, creates, and runs the Test VM with verification of proper termination. The class also stores + * information about the Test VM which is later queried for IR matching. The communication between this Driver VM + * and the Test VM is done over a dedicated socket. * * @see TestVM * @see TestFrameworkSocket @@ -62,7 +62,7 @@ public class TestVMProcess { private String hotspotPidFileName; private String commandLine; private OutputAnalyzer oa; - private String irEncoding; + private String applicableIRRules; public TestVMProcess(List additionalFlags, Class testClass, Set> helperClasses, int defaultWarmup, boolean allowNotCompilable, boolean testClassesOnBootClassPath) { @@ -81,8 +81,8 @@ public class TestVMProcess { return commandLine; } - public String getIrEncoding() { - return irEncoding; + public String getApplicableIRRules() { + return applicableIRRules; } public String getHotspotPidFileName() { @@ -98,7 +98,7 @@ public class TestVMProcess { boolean testClassesOnBootClassPath) { // Set java.library.path so JNI tests which rely on jtreg nativepath setting work cmds.add("-Djava.library.path=" + Utils.TEST_NATIVE_PATH); - // Need White Box access in test VM. + // Need White Box access in Test VM. String bootClassPath = "-Xbootclasspath/a:."; if (testClassesOnBootClassPath) { // Add test classes themselves to boot classpath to make them privileged. @@ -112,7 +112,8 @@ public class TestVMProcess { if (!PREFER_COMMAND_LINE_FLAGS) { cmds.addAll(jtregVMFlags); } - // Add server property flag that enables test VM to print encoding for IR verification last and debug messages. + // Add server property flag that enables the Test VM to print the Applicable IR Rules for IR verification and + // debug messages. cmds.add(socket.getPortPropertyFlag()); cmds.addAll(additionalFlags); cmds.addAll(Arrays.asList(getDefaultFlags())); @@ -142,7 +143,7 @@ public class TestVMProcess { } /** - * Default flags that are added used for the test VM. + * Default flags that are added used for the Test VM. */ private static String[] getDefaultFlags() { return new String[] {"-XX:-BackgroundCompilation", "-XX:CompileCommand=quiet"}; @@ -169,7 +170,7 @@ public class TestVMProcess { throw new TestFrameworkException("Error while executing Test VM", e); } - process.command().add(1, "-DReproduce=true"); // Add after "/path/to/bin/java" in order to rerun the test VM directly + process.command().add(1, "-DReproduce=true"); // Add after "/path/to/bin/java" in order to rerun the Test VM directly commandLine = "Command Line:" + System.lineSeparator() + String.join(" ", process.command()) + System.lineSeparator(); hotspotPidFileName = String.format("hotspot_pid%d.log", oa.pid()); @@ -178,7 +179,7 @@ public class TestVMProcess { /** * Process the socket output: All prefixed lines are dumped to the standard output while the remaining lines - * represent the IR encoding used for IR matching later. + * represent the Applicable IR Rules used for IR matching later. */ private void processSocketOutput(TestFrameworkSocket socket) { String output = socket.getOutput(); @@ -215,16 +216,16 @@ public class TestVMProcess { System.out.println("---------------------"); System.out.println(messagesBuilder); } - irEncoding = nonStdOutBuilder.toString(); + applicableIRRules = nonStdOutBuilder.toString(); } else { - irEncoding = output; + applicableIRRules = output; } } private void checkTestVMExitCode() { final int exitCode = oa.getExitValue(); if (EXCLUDE_RANDOM || REPORT_STDOUT || (VERBOSE && exitCode == 0)) { - System.out.println("--- OUTPUT TestFramework test VM ---"); + System.out.println("--- OUTPUT TestFramework Test VM ---"); System.out.println(oa.getOutput()); } @@ -234,7 +235,7 @@ public class TestVMProcess { } /** - * Exit code was non-zero of test VM. Check the stderr to determine what kind of exception that should be thrown to + * Exit code was non-zero of Test VM. Check the stderr to determine what kind of exception that should be thrown to * react accordingly later. */ private void throwTestVMException() { @@ -266,7 +267,7 @@ public class TestVMProcess { stdOut = System.lineSeparator() + System.lineSeparator() + "Standard Output" + System.lineSeparator() + "---------------" + System.lineSeparator() + oa.getOutput(); } - return "TestFramework test VM exited with code " + exitCode + System.lineSeparator() + stdOut + return "TestFramework Test VM exited with code " + exitCode + System.lineSeparator() + stdOut + System.lineSeparator() + commandLine + System.lineSeparator() + System.lineSeparator() + "Error Output" + System.lineSeparator() + "------------" + System.lineSeparator() + stdErr + System.lineSeparator() + System.lineSeparator(); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IREncodingParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java similarity index 60% rename from test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IREncodingParser.java rename to test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java index 28c4213f82d..7aaf0c2b285 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IREncodingParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -28,7 +28,7 @@ import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFrameworkException; -import compiler.lib.ir_framework.test.IREncodingPrinter; +import compiler.lib.ir_framework.test.ApplicableIRRulesPrinter; import java.lang.reflect.Method; import java.util.HashMap; @@ -37,34 +37,34 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Class to parse the IR encoding emitted by the test VM and creating {@link TestMethod} objects for each entry. + * Class to parse the Applicable IR Rules emitted by the Test VM and creating {@link TestMethod} objects for each entry. * * @see TestMethod */ -public class IREncodingParser { +public class ApplicableIRRulesParser { - private static final boolean PRINT_IR_ENCODING = Boolean.parseBoolean(System.getProperty("PrintIREncoding", "false")); - private static final Pattern IR_ENCODING_PATTERN = - Pattern.compile("(?<=" + IREncodingPrinter.START + "\r?\n).*\\R([\\s\\S]*)(?=" + IREncodingPrinter.END + ")"); + 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; - public IREncodingParser(Class testClass) { + public ApplicableIRRulesParser(Class testClass) { this.testClass = testClass; this.testMethods = new HashMap<>(); } /** - * Parse the IR encoding passed as parameter and return a "test name" -> TestMethod map that contains an entry - * for each method that needs to be IR matched on. + * Parse the Applicable IR rules passed as parameter and return a "test name" -> TestMethod map that contains an + * entry for each method that needs to be IR matched on. */ - public TestMethods parse(String irEncoding) { - if (TestFramework.VERBOSE || PRINT_IR_ENCODING) { - System.out.println("Read IR encoding from test VM:"); - System.out.println(irEncoding); + 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(irEncoding, testClass); + createTestMethodMap(applicableIRRules, testClass); // We could have found format errors in @IR annotations. Report them now with an exception. TestFormat.throwIfAnyFailures(); return new TestMethods(testMethods); @@ -74,22 +74,22 @@ public class IREncodingParser { * Sets up a map testname -> TestMethod map. The TestMethod object will later be filled with the ideal and opto * assembly output in {@link HotSpotPidFileParser}. */ - private void createTestMethodMap(String irEncoding, Class testClass) { - Map irRulesMap = parseIREncoding(irEncoding); - createTestMethodsWithEncoding(testClass, irRulesMap); + private void createTestMethodMap(String applicableIRRules, Class testClass) { + Map irRulesMap = parseApplicableIRRules(applicableIRRules); + createTestMethodsWithApplicableIRRules(testClass, irRulesMap); } /** - * Read the IR encoding emitted by the test VM to decide if an @IR rule must be checked for a method. + * Read the Applicable IR Rules emitted by the Test VM to decide if an @IR rule must be checked for a method. */ - private Map parseIREncoding(String irEncoding) { + private Map parseApplicableIRRules(String applicableIRRules) { Map irRulesMap = new HashMap<>(); - String[] irEncodingLines = getIREncodingLines(irEncoding); - for (String s : irEncodingLines) { + String[] applicableIRRulesLines = getApplicableIRRulesLines(applicableIRRules); + for (String s : applicableIRRulesLines) { String line = s.trim(); String[] splitLine = line.split(","); if (splitLine.length < 2) { - throw new TestFrameworkException("Invalid IR match rule encoding. No comma found: " + splitLine[0]); + throw new TestFrameworkException("Invalid Applicable IR Rules format. No comma found: " + splitLine[0]); } String testName = splitLine[0]; int[] irRulesIdx = getRuleIndexes(splitLine); @@ -99,11 +99,12 @@ public class IREncodingParser { } /** - * Parse the IR encoding lines without header, explanation line and footer and return them in an array. + * Parse the Applicable IR Rules lines without header, explanation line and footer and return them in an array. */ - private String[] getIREncodingLines(String irEncoding) { - Matcher matcher = IR_ENCODING_PATTERN.matcher(irEncoding); - TestFramework.check(matcher.find(), "Did not find IR encoding in:" + System.lineSeparator() + irEncoding); + 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()) { // Nothing to IR match. @@ -113,7 +114,7 @@ public class IREncodingParser { } /** - * Parse rule indexes from IR encoding line of the format: + * Parse rule indexes from a single line of the Applicable IR Rules in the format: */ private int[] getRuleIndexes(String[] splitLine) { int[] irRulesIdx = new int[splitLine.length - 1]; @@ -121,13 +122,13 @@ public class IREncodingParser { try { irRulesIdx[i - 1] = Integer.parseInt(splitLine[i]); } catch (NumberFormatException e) { - throw new TestFrameworkException("Invalid IR match rule encoding. No number found: " + splitLine[i]); + throw new TestFrameworkException("Invalid Applicable IR Rules format. No number found: " + splitLine[i]); } } return irRulesIdx; } - private void createTestMethodsWithEncoding(Class testClass, Map irRulesMap) { + private void createTestMethodsWithApplicableIRRules(Class testClass, Map irRulesMap) { for (Method m : testClass.getDeclaredMethods()) { IR[] irAnnos = m.getAnnotationsByType(IR.class); if (irAnnos.length > 0) { @@ -144,7 +145,7 @@ public class IREncodingParser { private void validateIRRuleIds(Method m, IR[] irAnnos, int[] ids) { TestFramework.check(ids != null, "Should find method name in validIrRulesMap for " + m); TestFramework.check(ids.length > 0, "Did not find any rule indices for " + m); - TestFramework.check((ids[0] >= 1 || ids[0] == IREncodingPrinter.NO_RULE_APPLIED) + TestFramework.check((ids[0] >= 1 || ids[0] == ApplicableIRRulesPrinter.NO_RULE_APPLIED) && ids[ids.length - 1] <= irAnnos.length, "Invalid IR rule index found in validIrRulesMap for " + m); } @@ -153,6 +154,6 @@ public class IREncodingParser { * Does the list of IR rules contain any applicable IR rules for the given conditions? */ private boolean hasAnyApplicableIRRules(int[] irRuleIds) { - return irRuleIds[0] != IREncodingPrinter.NO_RULE_APPLIED; + return irRuleIds[0] != ApplicableIRRulesPrinter.NO_RULE_APPLIED; } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java index 538680496e7..d7a10acd1cd 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.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,7 +24,6 @@ package compiler.lib.ir_framework.driver.irmatching.parser; import compiler.lib.ir_framework.Test; -import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.Compilation; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethod; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethodMatchable; @@ -53,7 +52,7 @@ class IRMethodBuilder { } /** - * Create IR methods for all test methods identified by {@link IREncodingParser} by combining them with the parsed + * Create IR methods for all test methods identified by {@link ApplicableIRRulesParser} by combining them with the parsed * compilation output from {@link HotSpotPidFileParser}. */ public SortedSet build(VMInfo vmInfo) { 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 b6c9920c2d1..2329b41afbe 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 @@ -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 @@ -50,13 +50,13 @@ public class TestClassParser { } /** - * Parse the IR encoding and hotspot_pid* file to create a collection of {@link IRMethod} objects. + * Parse the Applicable IR Rules and hotspot_pid* file to create a collection of {@link IRMethod} objects. * Return a default/empty TestClass object if there are no applicable @IR rules in any method of the test class. */ - public Matchable parse(String hotspotPidFileName, String irEncoding) { - IREncodingParser irEncodingParser = new IREncodingParser(testClass); - TestMethods testMethods = irEncodingParser.parse(irEncoding); - VMInfo vmInfo = VMInfoParser.parseVMInfo(irEncoding); + public Matchable parse(String hotspotPidFileName, String applicableIRRules) { + ApplicableIRRulesParser applicableIRRulesParser = new ApplicableIRRulesParser(testClass); + TestMethods testMethods = applicableIRRulesParser.parse(applicableIRRules); + VMInfo vmInfo = VMInfoParser.parseVMInfo(applicableIRRules); if (testMethods.hasTestMethods()) { HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); LoggedMethods loggedMethods = hotSpotPidFileParser.parse(hotspotPidFileName); @@ -66,7 +66,7 @@ public class TestClassParser { } /** - * Create test class with IR methods for all test methods identified by {@link IREncodingParser} by combining them + * Create test class with IR methods for all test methods identified by {@link ApplicableIRRulesParser} by combining them * with the parsed compilation output from {@link HotSpotPidFileParser}. */ private Matchable createTestClass(TestMethods testMethods, LoggedMethods loggedMethods, VMInfo vmInfo) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java index 8491b8d61eb..43f27c80ac6 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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,10 +30,10 @@ import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethod; import java.lang.reflect.Method; /** - * This class represents a test method parsed by {@link IREncodingParser}. In combination with the associated + * This class represents a test method parsed by {@link ApplicableIRRulesParser}. In combination with the associated * {@link LoggedMethod}, a new {@link IRMethod} is created to IR match on later. * - * @see IREncodingParser + * @see ApplicableIRRulesParser * @see LoggedMethod * @see IRMethod */ diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.java index ecc8fb4e0b9..d1edfb08fdf 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.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 @@ -29,15 +29,15 @@ import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFile import java.util.Map; /** - * This class stores all test methods that need to be IR matched as identified by {@link IREncodingParser}. + * This class stores all test methods that need to be IR matched as identified by {@link ApplicableIRRulesParser}. * - * @see IREncodingParser + * @see ApplicableIRRulesParser * @see HotSpotPidFileParser * @see IRMethod */ public class TestMethods { /** - * "Method name" -> TestMethod map created by {@link IREncodingParser} which contains an entry for each method that + * "Method name" -> TestMethod map created by {@link ApplicableIRRulesParser} which contains an entry for each method that * needs to be IR matched on. */ private final Map testMethods; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java index db70e7892a2..89b6d610496 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.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 @@ -33,7 +33,7 @@ import java.util.regex.Pattern; /** * This class stores the key value mapping from the VMInfo. * - * @see IREncodingParser + * @see ApplicableIRRulesParser */ public class VMInfo { /** 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 a8e6782ab12..2b17303f1a7 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 @@ -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 @@ -33,7 +33,7 @@ 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. + * Class to parse the VMInfo emitted by the Test VM and creating {@link VMInfo} objects for each entry. * * @see VMInfo */ @@ -43,11 +43,11 @@ public class VMInfoParser { Pattern.compile("(?<=" + VMInfoPrinter.START_VM_INFO + "\r?\n).*\\R([\\s\\S]*)(?=" + VMInfoPrinter.END_VM_INFO + ")"); /** - * Extract VMInfo from the irEncoding. + * Extract VMInfo from the applicableIRRules. */ - public static VMInfo parseVMInfo(String irEncoding) { + public static VMInfo parseVMInfo(String applicableIRRules) { Map map = new HashMap<>(); - String[] lines = getVMInfoLines(irEncoding); + String[] lines = getVMInfoLines(applicableIRRules); for (String s : lines) { String line = s.trim(); String[] splitLine = line.split(":", 2); @@ -62,11 +62,11 @@ public class VMInfoParser { } /** - * Extract the VMInfo from the irEncoding string, strip away the header and return the individual key-value lines. + * Extract the VMInfo from the applicableIRRules string, strip away the header and return the individual key-value lines. */ - private static String[] getVMInfoLines(String irEncoding) { - Matcher matcher = VM_INFO_PATTERN.matcher(irEncoding); - TestFramework.check(matcher.find(), "Did not find VMInfo in:" + System.lineSeparator() + irEncoding); + 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()) { // Nothing to IR match. diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.java index 9ccb78ea892..f9fa0d1433e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.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 @@ -24,7 +24,7 @@ package compiler.lib.ir_framework.driver.irmatching.parser.hotspot; import compiler.lib.ir_framework.TestFramework; -import compiler.lib.ir_framework.driver.irmatching.parser.IREncodingParser; +import compiler.lib.ir_framework.driver.irmatching.parser.ApplicableIRRulesParser; import compiler.lib.ir_framework.driver.irmatching.parser.TestMethods; import java.util.HashMap; @@ -34,9 +34,9 @@ import java.util.regex.Pattern; /** * This class parses compile queue messages found in the hotspot_pid* files and keeps track of those that need to be - * IR matched (i.e. identified by {@link IREncodingParser}. + * IR matched (i.e. identified by {@link ApplicableIRRulesParser}. * - * @see IREncodingParser + * @see ApplicableIRRulesParser */ class CompileQueueMessages { private static final Pattern COMPILE_ID_PATTERN = Pattern.compile("compile_id='(\\d+)'"); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java index a5a5a0b20a9..f39f7cc9a83 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -24,7 +24,7 @@ package compiler.lib.ir_framework.driver.irmatching.parser.hotspot; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethod; -import compiler.lib.ir_framework.driver.irmatching.parser.IREncodingParser; +import compiler.lib.ir_framework.driver.irmatching.parser.ApplicableIRRulesParser; import compiler.lib.ir_framework.driver.irmatching.parser.TestMethods; import compiler.lib.ir_framework.shared.TestFrameworkException; @@ -34,10 +34,10 @@ import java.nio.file.Paths; /** * Class to parse the ideal compile phases and PrintOptoAssembly outputs of the test class from the hotspot_pid* file - * of all methods identified by {@link IREncodingParser}. + * of all methods identified by {@link ApplicableIRRulesParser}. * * @see IRMethod - * @see IREncodingParser + * @see ApplicableIRRulesParser */ public class HotSpotPidFileParser { private final State state; @@ -47,8 +47,8 @@ public class HotSpotPidFileParser { } /** - * Parse the hotspot_pid*.log file from the test VM. Read the ideal compile phase and PrintOptoAssembly outputs for - * all methods defined by the IR encoding. + * Parse the hotspot_pid*.log file from the Test VM. Read the ideal compile phase and PrintOptoAssembly outputs for + * all methods defined by the Applicable IR Rules. */ public LoggedMethods parse(String hotspotPidFileName) { try (var reader = Files.newBufferedReader(Paths.get(hotspotPidFileName))) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java index 86ef31c6277..0488b3f58e6 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 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 @@ -53,7 +53,7 @@ class CompilePhaseCollector { collectCompilePhases(method)); } } catch (TestFormatException e) { - // Create default map and let the IR matcher report the format failures later in the driver VM. + // Create default map and let the IR matcher report the format failures later in the Driver VM. return createDefaultMap(testClass); } return methodNameToCompilePhasesMap; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.java index 42e4c85a499..4b033eb2c0e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, 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 @@ -38,8 +38,8 @@ import java.util.ArrayList; import java.util.Arrays; /** - * This class' main method is called from {@link TestFramework} and represents the so-called "flag VM". It uses the - * Whitebox API to determine the necessary additional flags to run the test VM (e.g. to do IR matching). It returns + * This class' main method is called from {@link TestFramework} and represents the so-called "Flag VM". It uses the + * Whitebox API to determine the necessary additional flags to run the Test VM (e.g. to do IR matching). It returns * the flags over the dedicated TestFramework socket. */ public class FlagVM { @@ -79,12 +79,12 @@ public class FlagVM { private static final boolean VERIFY_IR = REQUESTED_VERIFY_IR && USE_COMPILER && !EXCLUDE_RANDOM && !FLIP_C1_C2 && !TEST_C1 && Platform.isServer(); /** - * Main entry point of the flag VM. + * Main entry point of the Flag VM. */ public static void main(String[] args) { String testClassName = args[0]; if (VERBOSE) { - System.out.println("FlagVM main() called. Prepare test VM flags to run class " + testClassName); + System.out.println("FlagVM main() called. Prepare Test VM flags to run class " + testClassName); } Class testClass; try { @@ -96,8 +96,8 @@ public class FlagVM { } /** - * Emit test VM flags to the dedicated test VM flags file to parse them from the TestFramework "driver" VM again - * which adds them to the test VM. + * Emit Test VM flags to the dedicated Test VM flags file to parse them from the TestFramework Driver VM again + * which adds them to the Test VM. */ private static void emitTestVMFlags(ArrayList flags) { try (var bw = Files.newBufferedWriter(Paths.get(TEST_VM_FLAGS_FILE))) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.java index 02afdd4ee4f..4b616f53e4d 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.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 @@ -24,17 +24,17 @@ package compiler.lib.ir_framework.shared; /** - * Exception that is thrown by the test VM if no tests are run as a result of specifying {@code -DTest} and/or - * {@code -DExclude} defining an empty set with the used test VM flags. + * Exception that is thrown by the Test VM if no tests are run as a result of specifying {@code -DTest} and/or + * {@code -DExclude} defining an empty set with the used Test VM flags. */ public class NoTestsRunException extends RuntimeException { /** - * Default constructor used by test VM + * Default constructor used by Test VM */ public NoTestsRunException() {} /** - * Constructor used to eventually throw the exception in the driver VM. + * Constructor used to eventually throw the exception in the Driver VM. */ public NoTestsRunException(String message) { super(message); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java index 1fdcb34a6b8..f10540ebc5b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.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 @@ -37,7 +37,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** - * Dedicated socket to send data from the flag and test VM back to the driver VM. + * Dedicated socket to send data from the flag and Test VM back to the Driver VM. */ public class TestFrameworkSocket implements AutoCloseable { public static final String STDOUT_PREFIX = "[STDOUT]"; @@ -46,7 +46,7 @@ public class TestFrameworkSocket implements AutoCloseable { public static final String PRINT_TIMES_TAG = "[PRINT_TIMES]"; public static final String NOT_COMPILABLE_TAG = "[NOT_COMPILABLE]"; - // Static fields used for test VM only. + // Static fields used for Test VM only. private static final String SERVER_PORT_PROPERTY = "ir.framework.server.port"; private static final int SERVER_PORT = Integer.getInteger(SERVER_PORT_PROPERTY, -1); @@ -85,7 +85,7 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Waits for a client (created by flag or test VM) to connect. Return the messages received from the client. + * Waits for a client (created by flag or Test VM) to connect. Return the messages received from the client. */ private FutureTask initSocketTask() { return new FutureTask<>(() -> { @@ -117,18 +117,18 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Only called by test VM to write to server socket. + * Only called by Test VM to write to server socket. */ public static void write(String msg, String tag) { write(msg, tag, false); } /** - * Only called by test VM to write to server socket. + * Only called by Test VM to write to server socket. *

- * The test VM is spawned by the main jtreg VM. The stdout of the test VM is hidden + * The Test VM is spawned by the main jtreg VM. The stdout of the Test VM is hidden * unless the Verbose or ReportStdout flag is used. TestFrameworkSocket is used by the parent jtreg - * VM and the test VM to communicate. By sending the prints through the TestFrameworkSocket with the + * VM and the Test VM to communicate. By sending the prints through the TestFrameworkSocket with the * parameter stdout set to true, the parent VM will print the received messages to its stdout, making it * visible to the user. */ @@ -137,10 +137,10 @@ public class TestFrameworkSocket implements AutoCloseable { System.out.println("Debugging Test VM: Skip writing due to -DReproduce"); return; } - TestFramework.check(SERVER_PORT != -1, "Server port was not set correctly for flag and/or test VM " - + "or method not called from flag or test VM"); + TestFramework.check(SERVER_PORT != -1, "Server port was not set correctly for flag and/or Test VM " + + "or method not called from flag or Test VM"); try { - // Keep the client socket open until the test VM terminates (calls closeClientSocket before exiting main()). + // Keep the client socket open until the Test VM terminates (calls closeClientSocket before exiting main()). if (clientSocket == null) { clientSocket = new Socket(InetAddress.getLoopbackAddress(), SERVER_PORT); clientWriter = new PrintWriter(clientSocket.getOutputStream(), true); @@ -150,11 +150,11 @@ public class TestFrameworkSocket implements AutoCloseable { } clientWriter.println(msg); } catch (Exception e) { - // When the test VM is directly run, we should ignore all messages that would normally be sent to the - // driver VM. + // When the Test VM is directly run, we should ignore all messages that would normally be sent to the + // Driver VM. String failMsg = System.lineSeparator() + System.lineSeparator() + """ ########################################################### - Did you directly run the test VM (TestVM class) + Did you directly run the Test VM (TestVM class) to reproduce a bug? => Append the flag -DReproduce=true and try again! ########################################################### @@ -169,7 +169,7 @@ public class TestFrameworkSocket implements AutoCloseable { /** * Closes (and flushes) the printer to the socket and the socket itself. Is called as last thing before exiting - * the main() method of the flag and the test VM. + * the main() method of the flag and the Test VM. */ public static void closeClientSocket() { if (clientSocket != null) { @@ -183,7 +183,7 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Get the socket output of the flag VM. + * Get the socket output of the Flag VM. */ public String getOutput() { try { @@ -197,7 +197,7 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Return whether test VM sent messages to be put on stdout (starting with {@link ::STDOUT_PREFIX}). + * Return whether Test VM sent messages to be put on stdout (starting with {@link ::STDOUT_PREFIX}). */ public boolean hasStdOut() { return receivedStdOut; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java similarity index 96% rename from test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java rename to test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index a24cfbd3e37..4fa1f8f3fe5 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.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 @@ -39,12 +39,13 @@ import java.util.Objects; import java.util.function.Function; /** - * Prints an encoding to the dedicated test framework socket whether @IR rules of @Test methods should be applied or not. - * This is done during the execution of the test VM by checking the active VM flags. This encoding is eventually parsed - * and checked by the IRMatcher class in the driver VM after the termination of the test VM. IR rule indices start at 1. + * Prints all applicable IR rules to the dedicated test framework socket whether @IR rules of @Test methods should be + * applied or not. This is done during the execution of the Test VM by checking the active VM flags. This + * Applicable IR Rules message is eventually parsed and checked by the IRMatcher class in the Driver VM after the + * termination of the Test VM. IR rule indices start at 1. */ -public class IREncodingPrinter { - public static final String START = "##### IRMatchRulesEncoding - used by TestFramework #####"; +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; @@ -60,7 +61,7 @@ public class IREncodingPrinter { // Platforms for use in IR preconditions. Please verify that e.g. there is // a corresponding use in a jtreg @requires annotation before adding new platforms, // as adding non-existent platforms can lead to skipped tests. - private static final List irTestingPlatforms = new ArrayList(Arrays.asList( + private static final List irTestingPlatforms = new ArrayList<>(Arrays.asList( // os.family "aix", "linux", @@ -85,7 +86,7 @@ public class IREncodingPrinter { // Please verify new CPU features before adding them. If we allow non-existent features // on this list, we will ignore tests and never execute them. Consult CPU_FEATURE_FLAGS // in corresponding vm_version_.hpp file to find correct cpu feature's name. - private static final List verifiedCPUFeatures = new ArrayList( Arrays.asList( + private static final List verifiedCPUFeatures = new ArrayList<>( Arrays.asList( // x86 "fma", "f16c", @@ -126,7 +127,7 @@ public class IREncodingPrinter { "zvkn" )); - public IREncodingPrinter() { + public ApplicableIRRulesPrinter() { output.append(START).append(System.lineSeparator()); output.append(",{comma separated applied @IR rule ids}").append(System.lineSeparator()); } @@ -136,7 +137,7 @@ public class IREncodingPrinter { * - indices of all @IR rules that should be applied, separated by a comma * - "-1" if no @IR rule should not be applied */ - public void emitRuleEncoding(Method m, boolean skipped) { + public void emitApplicableIRRules(Method m, boolean skipped) { method = m; int i = 0; ArrayList validRules = new ArrayList<>(); @@ -170,7 +171,7 @@ public class IREncodingPrinter { private void printDisableReason(String method, String reason, String[] apply, int ruleIndex, int ruleMax) { TestFrameworkSocket.write("Disabling IR matching for rule " + ruleIndex + " of " + ruleMax + " in " + method + ": " + reason + ": " + String.join(", ", apply), - "[IREncodingPrinter]", true); + "[ApplicableIRRules]", true); } private boolean shouldApplyIrRule(IR irAnno, String m, int ruleIndex, int ruleMax) { @@ -521,7 +522,7 @@ public class IREncodingPrinter { public void emit() { output.append(END); - TestFrameworkSocket.write(output.toString(), "IR rule application encoding"); + TestFrameworkSocket.write(output.toString(), "ApplicableIRRules"); } } 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 8ccd0faa013..c5ab318d67d 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.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 @@ -41,7 +41,7 @@ import java.util.stream.Stream; import static compiler.lib.ir_framework.shared.TestFrameworkSocket.PRINT_TIMES_TAG; /** - * This class' main method is called from {@link TestFramework} and represents the so-called "test VM". The class is + * This class' main method is called from {@link TestFramework} and represents the so-called "Test VM". The class is * the heart of the framework and is responsible for executing all the specified tests in the test class. It uses the * Whitebox API and reflection to achieve this task. */ @@ -58,7 +58,7 @@ public class TestVM { TestFramework in main() of your test? Make sure to only call setup/run methods and no checks or assertions from main() of your test! - - Are you rerunning the test VM (TestVM class) + - Are you rerunning the Test VM (TestVM class) directly after a JTreg run? Make sure to start it from within JTwork/scratch and with the flag -DReproduce=true! @@ -100,7 +100,7 @@ public class TestVM { private static final boolean DUMP_REPLAY = Boolean.getBoolean("DumpReplay"); private static final boolean GC_AFTER = Boolean.getBoolean("GCAfter"); private static final boolean SHUFFLE_TESTS = Boolean.parseBoolean(System.getProperty("ShuffleTests", "true")); - // Use separate flag as VERIFY_IR could have been set by user but due to other flags it was disabled by flag VM. + // Use separate flag as VERIFY_IR could have been set by user but due to other flags it was disabled by Flag VM. private static final boolean PRINT_VALID_IR_RULES = Boolean.getBoolean("ShouldDoIRVerification"); protected static final long PER_METHOD_TRAP_LIMIT = (Long)WHITE_BOX.getVMFlag("PerMethodTrapLimit"); protected static final boolean PROFILE_INTERPRETER = (Boolean)WHITE_BOX.getVMFlag("ProfileInterpreter"); @@ -114,7 +114,7 @@ public class TestVM { private final List excludeList; private final List testList; private Set> helperClasses = null; // Helper classes that contain framework annotations to be processed. - private final IREncodingPrinter irMatchRulePrinter; + private final ApplicableIRRulesPrinter irMatchRulePrinter; private final Class testClass; private final Map forceCompileMap = new HashMap<>(); @@ -125,7 +125,7 @@ public class TestVM { this.excludeList = createTestFilterList(EXCLUDELIST, testClass); if (PRINT_VALID_IR_RULES) { - irMatchRulePrinter = new IREncodingPrinter(); + irMatchRulePrinter = new ApplicableIRRulesPrinter(); } else { irMatchRulePrinter = null; } @@ -155,7 +155,7 @@ public class TestVM { } /** - * Main entry point of the test VM. + * Main entry point of the Test VM. */ public static void main(String[] args) { try { @@ -293,7 +293,7 @@ public class TestVM { BaseTest baseTest = new BaseTest(test, shouldExcludeTest(m.getName())); allTests.add(baseTest); if (PRINT_VALID_IR_RULES) { - irMatchRulePrinter.emitRuleEncoding(m, baseTest.isSkipped()); + irMatchRulePrinter.emitApplicableIRRules(m, baseTest.isSkipped()); } } catch (TestFormatException e) { // Failure logged. Continue and report later. @@ -687,7 +687,7 @@ public class TestVM { allTests.add(checkedTest); if (PRINT_VALID_IR_RULES) { // Only need to emit IR verification information if IR verification is actually performed. - irMatchRulePrinter.emitRuleEncoding(testMethod, checkedTest.isSkipped()); + irMatchRulePrinter.emitApplicableIRRules(testMethod, checkedTest.isSkipped()); } } @@ -755,7 +755,7 @@ public class TestVM { CustomRunTest customRunTest = new CustomRunTest(m, getAnnotation(m, Warmup.class), runAnno, tests, shouldExcludeTest); allTests.add(customRunTest); if (PRINT_VALID_IR_RULES) { - tests.forEach(test -> irMatchRulePrinter.emitRuleEncoding(test.getTestMethod(), customRunTest.isSkipped())); + tests.forEach(test -> irMatchRulePrinter.emitApplicableIRRules(test.getTestMethod(), customRunTest.isSkipped())); } } 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 14636da4cbe..470569122dd 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.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 @@ import compiler.lib.ir_framework.shared.TestFrameworkSocket; import jdk.test.whitebox.WhiteBox; /** - * Prints some test VM info to the socket. + * Prints some Test VM info to the socket. */ public class VMInfoPrinter { public static final String START_VM_INFO = "##### IRMatchingVMInfo - used by TestFramework #####"; diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java index 142a30f34a9..07a6a03f2a6 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.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 @@ -38,13 +38,13 @@ import java.util.regex.Pattern; /* * @test * @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler1.enabled & vm.compiler2.enabled & vm.flagless - * @summary Test IR matcher with different default IR node regexes. Use -DPrintIREncoding. + * @summary Test IR matcher with different default IR node regexes. Use -DPrintApplicableIRRules. * Normally, the framework should be called with driver. * @library /test/lib /testlibrary_tests / * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=240 -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI -DPrintIREncoding=true ir_framework.tests.TestIRMatching + * -XX:+WhiteBoxAPI -DPrintApplicableIRRules=true ir_framework.tests.TestIRMatching */ public class TestIRMatching { @@ -440,7 +440,8 @@ public class TestIRMatching { } } if (!output.contains(builder.toString())) { - addException(new RuntimeException("Could not find encoding: \"" + builder + System.lineSeparator())); + addException(new RuntimeException("Could not find line in Applicable IR Rules: \"" + builder + + System.lineSeparator())); } } } diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java index fe21dce73b5..70e4c463c55 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.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 @@ -70,7 +70,7 @@ public class TestPhaseIRMatching { TestVMProcess testVMProcess = new TestVMProcess(testVMFlags, testClass, null, -1, false, false); TestClassParser testClassParser = new TestClassParser(testClass, false); Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), - testVMProcess.getIrEncoding()); + testVMProcess.getApplicableIRRules()); MatchResult result = testClassMatchable.match(); List expectedFails = new ExpectedFailsBuilder().build(testClass); List foundFailures = new FailureBuilder().build(result); From c44a99a758f38ceea84e03905d2ffb9c1fd1987a Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Mon, 19 Jan 2026 14:20:18 +0000 Subject: [PATCH 064/126] 8374180: C2 crash in PhaseCCP::verify_type - fatal error: Not monotonic Reviewed-by: hgreule, bmaillard, epeter --- src/hotspot/share/opto/rangeinference.hpp | 41 ++++++++++---- src/hotspot/share/opto/type.hpp | 8 ++- .../gtest/opto/test_rangeinference.cpp | 8 +-- .../compiler/ccp/TestWrongXorIWiden.java | 54 +++++++++++++++++++ 4 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.java diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index ebfd98ca4a6..66ea741a2da 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.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 @@ -147,7 +147,7 @@ public: static const Type* int_type_xmeet(const CT* i1, const Type* t2); template - static CTP int_type_union(CTP t1, CTP t2) { + static auto int_type_union(CTP t1, CTP t2) { using CT = std::conditional_t, std::remove_pointer_t, CTP>; using S = std::remove_const_t; using U = std::remove_const_t; @@ -209,7 +209,7 @@ public: KnownBits _bits; int _widen = 0; // dummy field to mimic the same field in TypeInt, useful in testing - static TypeIntMirror make(const TypeIntPrototype& t, int widen) { + static TypeIntMirror make(const TypeIntPrototype& t, int widen = 0) { auto canonicalized_t = t.canonicalize_constraints(); assert(!canonicalized_t.empty(), "must not be empty"); return TypeIntMirror{canonicalized_t._data._srange._lo, canonicalized_t._data._srange._hi, @@ -217,11 +217,15 @@ public: canonicalized_t._data._bits}; } + TypeIntMirror meet(const TypeIntMirror& o) const { + return TypeIntHelper::int_type_union(this, &o); + } + // These allow TypeIntMirror to mimick the behaviors of TypeInt* and TypeLong*, so they can be // passed into RangeInference methods. These are only used in testing, so they are implemented in // the test file. + static TypeIntMirror make(const TypeIntMirror& t, int widen); const TypeIntMirror* operator->() const; - TypeIntMirror meet(const TypeIntMirror& o) const; bool contains(U u) const; bool contains(const TypeIntMirror& o) const; bool operator==(const TypeIntMirror& o) const; @@ -322,7 +326,7 @@ private: // Infer a result given the input types of a binary operation template static CTP infer_binary(CTP t1, CTP t2, Inference infer) { - CTP res; + TypeIntMirror, U> res; bool is_init = false; SimpleIntervalIterable t1_simple_intervals(t1); @@ -330,10 +334,10 @@ private: for (auto& st1 : t1_simple_intervals) { for (auto& st2 : t2_simple_intervals) { - CTP current = infer(st1, st2); + TypeIntMirror, U> current = infer(st1, st2); if (is_init) { - res = res->meet(current)->template cast>(); + res = res.meet(current); } else { is_init = true; res = current; @@ -342,7 +346,22 @@ private: } assert(is_init, "must be initialized"); - return res; + // It is important that widen is computed on the whole result instead of during each step. This + // is because we normalize the widen of small Type instances to 0, so computing the widen value + // for each step and taking the union of them may return a widen value that conflicts with + // other computations, trigerring the monotonicity assert during CCP. + // + // For example, let us consider the operation r = x ^ y: + // - During the first step of CCP, type(x) = {0}, type(y) = [-2, 2], w = 3. + // Since x is a constant that is the identity element of the xor operation, type(r) = type(y) = [-2, 2], w = 3 + // - During the second step, type(x) is widened to [0, 2], w = 0. + // We then compute the range for: + // r1 = x ^ y1, type(x) = [0, 2], w = 0, type(y1) = [0, 2], w = 0 + // r2 = x ^ y2, type(x) = [0, 2], w = 0, type(y2) = [-2, -1], w = 0 + // This results in type(r1) = [0, 3], w = 0, and type(r2) = [-4, -1], w = 0 + // So the union of type(r1) and type(r2) is [-4, 3], w = 0. This widen value is smaller than + // that of the previous step, triggering the monotonicity assert. + return CT::make(res, MAX2(t1->_widen, t2->_widen)); } public: @@ -357,7 +376,7 @@ public: U uhi = MIN2(st1._uhi, st2._uhi); U zeros = st1._bits._zeros | st2._bits._zeros; U ones = st1._bits._ones & st2._bits._ones; - return CT::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen)); + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } @@ -372,7 +391,7 @@ public: U uhi = std::numeric_limits>::max(); U zeros = st1._bits._zeros & st2._bits._zeros; U ones = st1._bits._ones | st2._bits._ones; - return CT::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen)); + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } @@ -385,7 +404,7 @@ public: U uhi = std::numeric_limits>::max(); U zeros = (st1._bits._zeros & st2._bits._zeros) | (st1._bits._ones & st2._bits._ones); U ones = (st1._bits._zeros & st2._bits._ones) | (st1._bits._ones & st2._bits._zeros); - return CT::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen)); + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } }; diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 73e2ba0045a..285b4984cd1 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.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 @@ -799,6 +799,9 @@ public: static const TypeInt* make(jint lo, jint hi, int widen); static const Type* make_or_top(const TypeIntPrototype& t, int widen); static const TypeInt* make(const TypeIntPrototype& t, int widen) { return make_or_top(t, widen)->is_int(); } + static const TypeInt* make(const TypeIntMirror& t, int widen) { + return (new TypeInt(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen, false))->hashcons()->is_int(); + } // Check for single integer bool is_con() const { return _lo == _hi; } @@ -881,6 +884,9 @@ public: static const TypeLong* make(jlong lo, jlong hi, int widen); static const Type* make_or_top(const TypeIntPrototype& t, int widen); static const TypeLong* make(const TypeIntPrototype& t, int widen) { return make_or_top(t, widen)->is_long(); } + static const TypeLong* make(const TypeIntMirror& t, int widen) { + return (new TypeLong(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen, false))->hashcons()->is_long(); + } // Check for single integer bool is_con() const { return _lo == _hi; } diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 42767e9fcab..fd49050d022 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -216,13 +216,13 @@ TEST_VM(opto, canonicalize_constraints) { // Implementations of TypeIntMirror methods for testing purposes template -const TypeIntMirror* TypeIntMirror::operator->() const { - return this; +TypeIntMirror TypeIntMirror::make(const TypeIntMirror& t, int widen) { + return t; } template -TypeIntMirror TypeIntMirror::meet(const TypeIntMirror& o) const { - return TypeIntHelper::int_type_union(*this, o); +const TypeIntMirror* TypeIntMirror::operator->() const { + return this; } template diff --git a/test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.java b/test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.java new file mode 100644 index 00000000000..3b058758beb --- /dev/null +++ b/test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.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.ccp; + +/* + * @test + * @bug 8374180 + * @summary Test that _widen is set correctly in XorI::add_ring() to ensure monotonicity. + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}::* -Xcomp ${test.main.class} + */ +public class TestWrongXorIWiden { + static byte byFld; + + public static void main(String[] strArr) { + test(); + } + + static void test() { + int k, i17 = 0; + long lArr[] = new long[400]; + for (int i = 9; i < 54; ++i) { + for (int j = 7; j > 1; j--) { + for (k = 1; k < 2; k++) { + i17 >>= i; + } + byFld += j ^ i17; + for (int a = 1; a < 2; a++) { + i17 = k; + } + } + } + } +} From f2d5290c29b0b832e64ab2b4dc04cd892a627ca2 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Mon, 19 Jan 2026 14:44:37 +0000 Subject: [PATCH 065/126] 8367319: Add os interfaces to get machine and container values separately Reviewed-by: eosterlund, sgehwolf --- src/hotspot/os/aix/os_aix.cpp | 26 +++- src/hotspot/os/bsd/os_bsd.cpp | 26 +++- .../os/linux/cgroupSubsystem_linux.cpp | 22 ++- .../os/linux/cgroupSubsystem_linux.hpp | 23 +-- src/hotspot/os/linux/cgroupUtil_linux.cpp | 27 ++-- src/hotspot/os/linux/cgroupUtil_linux.hpp | 7 +- .../os/linux/cgroupV1Subsystem_linux.cpp | 6 +- .../os/linux/cgroupV1Subsystem_linux.hpp | 10 +- .../os/linux/cgroupV2Subsystem_linux.cpp | 6 +- .../os/linux/cgroupV2Subsystem_linux.hpp | 10 +- src/hotspot/os/linux/osContainer_linux.cpp | 16 +- src/hotspot/os/linux/osContainer_linux.hpp | 7 +- src/hotspot/os/linux/os_linux.cpp | 141 ++++++++++++++---- src/hotspot/os/windows/os_windows.cpp | 26 +++- src/hotspot/share/jfr/jni/jfrJniMethod.cpp | 27 +--- src/hotspot/share/prims/jvm.cpp | 9 +- src/hotspot/share/prims/whitebox.cpp | 9 +- src/hotspot/share/runtime/os.cpp | 55 +++++-- src/hotspot/share/runtime/os.hpp | 48 +++++- .../containers/docker/TestCPUAwareness.java | 12 +- 20 files changed, 356 insertions(+), 157 deletions(-) diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index f88729cdc66..d7c1911a914 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -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. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -258,10 +258,18 @@ bool os::free_memory(physical_memory_size_type& value) { return Aix::available_memory(value); } +bool os::Machine::free_memory(physical_memory_size_type& value) { + return Aix::available_memory(value); +} + bool os::available_memory(physical_memory_size_type& value) { return Aix::available_memory(value); } +bool os::Machine::available_memory(physical_memory_size_type& value) { + return Aix::available_memory(value); +} + bool os::Aix::available_memory(physical_memory_size_type& value) { os::Aix::meminfo_t mi; if (os::Aix::get_meminfo(&mi)) { @@ -273,6 +281,10 @@ bool os::Aix::available_memory(physical_memory_size_type& value) { } bool os::total_swap_space(physical_memory_size_type& value) { + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { perfstat_memory_total_t memory_info; if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) { return false; @@ -282,6 +294,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return Machine::free_swap_space(value); +} + +bool os::Machine::free_swap_space(physical_memory_size_type& value) { perfstat_memory_total_t memory_info; if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) { return false; @@ -294,6 +310,10 @@ physical_memory_size_type os::physical_memory() { return Aix::physical_memory(); } +physical_memory_size_type os::Machine::physical_memory() { + return Aix::physical_memory(); +} + size_t os::rss() { return (size_t)0; } // Cpu architecture string @@ -2264,6 +2284,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); return online_cpus; diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 61de48bb7fa..667810bd6ca 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -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 @@ -137,10 +137,18 @@ bool os::available_memory(physical_memory_size_type& value) { return Bsd::available_memory(value); } +bool os::Machine::available_memory(physical_memory_size_type& value) { + return Bsd::available_memory(value); +} + bool os::free_memory(physical_memory_size_type& value) { return Bsd::available_memory(value); } +bool os::Machine::free_memory(physical_memory_size_type& value) { + return Bsd::available_memory(value); +} + // Available here means free. Note that this number is of no much use. As an estimate // for future memory pressure it is far too conservative, since MacOS will use a lot // of unused memory for caches, and return it willingly in case of needs. @@ -181,6 +189,10 @@ void os::Bsd::print_uptime_info(outputStream* st) { } bool os::total_swap_space(physical_memory_size_type& value) { + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { #if defined(__APPLE__) struct xsw_usage vmusage; size_t size = sizeof(vmusage); @@ -195,6 +207,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return Machine::free_swap_space(value); +} + +bool os::Machine::free_swap_space(physical_memory_size_type& value) { #if defined(__APPLE__) struct xsw_usage vmusage; size_t size = sizeof(vmusage); @@ -212,6 +228,10 @@ physical_memory_size_type os::physical_memory() { return Bsd::physical_memory(); } +physical_memory_size_type os::Machine::physical_memory() { + return Bsd::physical_memory(); +} + size_t os::rss() { size_t rss = 0; #ifdef __APPLE__ @@ -2189,6 +2209,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { return _processor_count; } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index f9c6f794ebd..e49d070890e 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.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 @@ -631,22 +631,20 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { * return: * true if there were no errors. false otherwise. */ -bool CgroupSubsystem::active_processor_count(int& value) { - int cpu_count; - int result = -1; - +bool CgroupSubsystem::active_processor_count(double& value) { // We use a cache with a timeout to avoid performing expensive // computations in the event this function is called frequently. // [See 8227006]. - CachingCgroupController* contrl = cpu_controller(); - CachedMetric* cpu_limit = contrl->metrics_cache(); + CachingCgroupController* contrl = cpu_controller(); + CachedMetric* cpu_limit = contrl->metrics_cache(); if (!cpu_limit->should_check_metric()) { - value = (int)cpu_limit->value(); - log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %d", value); + value = cpu_limit->value(); + log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %.2f", value); return true; } - cpu_count = os::Linux::active_processor_count(); + int cpu_count = os::Linux::active_processor_count(); + double result = -1; if (!CgroupUtil::processor_count(contrl->controller(), cpu_count, result)) { return false; } @@ -671,8 +669,8 @@ bool CgroupSubsystem::active_processor_count(int& value) { */ bool CgroupSubsystem::memory_limit_in_bytes(physical_memory_size_type upper_bound, physical_memory_size_type& value) { - CachingCgroupController* contrl = memory_controller(); - CachedMetric* memory_limit = contrl->metrics_cache(); + CachingCgroupController* contrl = memory_controller(); + CachedMetric* memory_limit = contrl->metrics_cache(); if (!memory_limit->should_check_metric()) { value = memory_limit->value(); return true; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 522b64f8816..8aafbf49c63 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.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 @@ -181,9 +181,10 @@ class CgroupController: public CHeapObj { static bool limit_from_str(char* limit_str, physical_memory_size_type& value); }; +template class CachedMetric : public CHeapObj{ private: - volatile physical_memory_size_type _metric; + volatile MetricType _metric; volatile jlong _next_check_counter; public: CachedMetric() { @@ -193,8 +194,8 @@ class CachedMetric : public CHeapObj{ bool should_check_metric() { return os::elapsed_counter() > _next_check_counter; } - physical_memory_size_type value() { return _metric; } - void set_value(physical_memory_size_type value, jlong timeout) { + MetricType value() { return _metric; } + void set_value(MetricType value, jlong timeout) { _metric = value; // Metric is unlikely to change, but we want to remain // responsive to configuration changes. A very short grace time @@ -205,19 +206,19 @@ class CachedMetric : public CHeapObj{ } }; -template +template class CachingCgroupController : public CHeapObj { private: T* _controller; - CachedMetric* _metrics_cache; + CachedMetric* _metrics_cache; public: CachingCgroupController(T* cont) { _controller = cont; - _metrics_cache = new CachedMetric(); + _metrics_cache = new CachedMetric(); } - CachedMetric* metrics_cache() { return _metrics_cache; } + CachedMetric* metrics_cache() { return _metrics_cache; } T* controller() { return _controller; } }; @@ -277,7 +278,7 @@ class CgroupMemoryController: public CHeapObj { class CgroupSubsystem: public CHeapObj { public: bool memory_limit_in_bytes(physical_memory_size_type upper_bound, physical_memory_size_type& value); - bool active_processor_count(int& value); + bool active_processor_count(double& value); virtual bool pids_max(uint64_t& value) = 0; virtual bool pids_current(uint64_t& value) = 0; @@ -286,8 +287,8 @@ class CgroupSubsystem: public CHeapObj { virtual char * cpu_cpuset_cpus() = 0; virtual char * cpu_cpuset_memory_nodes() = 0; virtual const char * container_type() = 0; - virtual CachingCgroupController* memory_controller() = 0; - virtual CachingCgroupController* cpu_controller() = 0; + virtual CachingCgroupController* memory_controller() = 0; + virtual CachingCgroupController* cpu_controller() = 0; virtual CgroupCpuacctController* cpuacct_controller() = 0; bool cpu_quota(int& value); diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index 7aa07d53148..570b335940b 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, 2025, Red Hat, Inc. + * 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 @@ -25,9 +26,8 @@ #include "cgroupUtil_linux.hpp" #include "os_linux.hpp" -bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, int& value) { +bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, double& value) { assert(upper_bound > 0, "upper bound of cpus must be positive"); - int limit_count = upper_bound; int quota = -1; int period = -1; if (!cpu_ctrl->cpu_quota(quota)) { @@ -37,20 +37,15 @@ bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, return false; } int quota_count = 0; - int result = upper_bound; + double result = upper_bound; - if (quota > -1 && period > 0) { - quota_count = ceilf((float)quota / (float)period); - log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count); + if (quota > 0 && period > 0) { // Use quotas + double cpu_quota = static_cast(quota) / period; + log_trace(os, container)("CPU Quota based on quota/period: %.2f", cpu_quota); + result = MIN2(result, cpu_quota); } - // Use quotas - if (quota_count != 0) { - limit_count = quota_count; - } - - result = MIN2(upper_bound, limit_count); - log_trace(os, container)("OSContainer::active_processor_count: %d", result); + log_trace(os, container)("OSContainer::active_processor_count: %.2f", result); value = result; return true; } @@ -73,11 +68,11 @@ physical_memory_size_type CgroupUtil::get_updated_mem_limit(CgroupMemoryControll // Get an updated cpu limit. The return value is strictly less than or equal to the // passed in 'lowest' value. -int CgroupUtil::get_updated_cpu_limit(CgroupCpuController* cpu, +double CgroupUtil::get_updated_cpu_limit(CgroupCpuController* cpu, int lowest, int upper_bound) { assert(lowest > 0 && lowest <= upper_bound, "invariant"); - int cpu_limit_val = -1; + double cpu_limit_val = -1; if (CgroupUtil::processor_count(cpu, upper_bound, cpu_limit_val) && cpu_limit_val != upper_bound) { assert(cpu_limit_val <= upper_bound, "invariant"); if (lowest > cpu_limit_val) { @@ -172,7 +167,7 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { assert(cg_path[0] == '/', "cgroup path must start with '/'"); int host_cpus = os::Linux::active_processor_count(); int lowest_limit = host_cpus; - int cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); + double cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); int orig_limit = lowest_limit != host_cpus ? lowest_limit : host_cpus; char* limit_cg_path = nullptr; while ((last_slash = strrchr(cg_path, '/')) != cg_path) { diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index d72bbd1cf1e..1fd2a7d872b 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Red Hat, Inc. + * 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 @@ -31,7 +32,7 @@ class CgroupUtil: AllStatic { public: - static bool processor_count(CgroupCpuController* cpu, int upper_bound, int& value); + static bool processor_count(CgroupCpuController* cpu, int upper_bound, double& value); // Given a memory controller, adjust its path to a point in the hierarchy // that represents the closest memory limit. static void adjust_controller(CgroupMemoryController* m); @@ -42,9 +43,7 @@ class CgroupUtil: AllStatic { static physical_memory_size_type get_updated_mem_limit(CgroupMemoryController* m, physical_memory_size_type lowest, physical_memory_size_type upper_bound); - static int get_updated_cpu_limit(CgroupCpuController* c, - int lowest, - int upper_bound); + static double get_updated_cpu_limit(CgroupCpuController* c, int lowest, int upper_bound); }; #endif // CGROUP_UTIL_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 2df604083d2..c8f5a290c99 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.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 @@ -328,8 +328,8 @@ CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset, _pids(pids) { CgroupUtil::adjust_controller(memory); CgroupUtil::adjust_controller(cpu); - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); } bool CgroupV1Subsystem::is_containerized() { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index f556bc57f26..af8d0efd378 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.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 @@ -214,15 +214,15 @@ class CgroupV1Subsystem: public CgroupSubsystem { const char * container_type() override { return "cgroupv1"; } - CachingCgroupController* memory_controller() override { return _memory; } - CachingCgroupController* cpu_controller() override { return _cpu; } + CachingCgroupController* memory_controller() override { return _memory; } + CachingCgroupController* cpu_controller() override { return _cpu; } CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; } private: /* controllers */ - CachingCgroupController* _memory = nullptr; + CachingCgroupController* _memory = nullptr; CgroupV1Controller* _cpuset = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _cpu = nullptr; CgroupV1CpuacctController* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index c61d30e9236..30e1affc646 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2025, Red Hat Inc. - * 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 @@ -156,8 +156,8 @@ CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory, _unified(unified) { CgroupUtil::adjust_controller(memory); CgroupUtil::adjust_controller(cpu); - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 39a4fabe9f6..998145c0ff9 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2024, Red Hat Inc. - * 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 @@ -152,8 +152,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { /* One unified controller */ CgroupV2Controller _unified; /* Caching wrappers for cpu/memory metrics */ - CachingCgroupController* _memory = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _memory = nullptr; + CachingCgroupController* _cpu = nullptr; CgroupCpuacctController* _cpuacct = nullptr; @@ -175,8 +175,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { const char * container_type() override { return "cgroupv2"; } - CachingCgroupController* memory_controller() override { return _memory; } - CachingCgroupController* cpu_controller() override { return _cpu; } + CachingCgroupController* memory_controller() override { return _memory; } + CachingCgroupController* cpu_controller() override { return _cpu; } CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; }; }; diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index 15a6403d07f..fe1dbc17239 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -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 @@ -86,8 +86,8 @@ void OSContainer::init() { // 2.) On a physical Linux system with a limit enforced by other means (like systemd slice) physical_memory_size_type mem_limit_val = value_unlimited; (void)memory_limit_in_bytes(mem_limit_val); // discard error and use default - int host_cpus = os::Linux::active_processor_count(); - int cpus = host_cpus; + double host_cpus = os::Linux::active_processor_count(); + double cpus = host_cpus; (void)active_processor_count(cpus); // discard error and use default any_mem_cpu_limit_present = mem_limit_val != value_unlimited || host_cpus != cpus; if (any_mem_cpu_limit_present) { @@ -127,8 +127,7 @@ bool OSContainer::available_memory_in_bytes(physical_memory_size_type& value) { return false; } -bool OSContainer::available_swap_in_bytes(physical_memory_size_type host_free_swap, - physical_memory_size_type& value) { +bool OSContainer::available_swap_in_bytes(physical_memory_size_type& value) { physical_memory_size_type mem_limit = 0; physical_memory_size_type mem_swap_limit = 0; if (memory_limit_in_bytes(mem_limit) && @@ -179,8 +178,7 @@ bool OSContainer::available_swap_in_bytes(physical_memory_size_type host_free_sw assert(num < 25, "buffer too small"); mem_limit_buf[num] = '\0'; log_trace(os,container)("OSContainer::available_swap_in_bytes: container_swap_limit=%s" - " container_mem_limit=%s, host_free_swap: " PHYS_MEM_TYPE_FORMAT, - mem_swap_buf, mem_limit_buf, host_free_swap); + " container_mem_limit=%s", mem_swap_buf, mem_limit_buf); } return false; } @@ -252,7 +250,7 @@ char * OSContainer::cpu_cpuset_memory_nodes() { return cgroup_subsystem->cpu_cpuset_memory_nodes(); } -bool OSContainer::active_processor_count(int& value) { +bool OSContainer::active_processor_count(double& value) { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); return cgroup_subsystem->active_processor_count(value); } @@ -291,11 +289,13 @@ template struct metric_fmt; template<> struct metric_fmt { static constexpr const char* fmt = "%llu"; }; template<> struct metric_fmt { static constexpr const char* fmt = "%lu"; }; template<> struct metric_fmt { static constexpr const char* fmt = "%d"; }; +template<> struct metric_fmt { static constexpr const char* fmt = "%.2f"; }; template<> struct metric_fmt { static constexpr const char* fmt = "%s"; }; template void OSContainer::print_container_metric(outputStream*, const char*, unsigned long long int, const char*); template void OSContainer::print_container_metric(outputStream*, const char*, unsigned long int, const char*); template void OSContainer::print_container_metric(outputStream*, const char*, int, const char*); +template void OSContainer::print_container_metric(outputStream*, const char*, double, const char*); template void OSContainer::print_container_metric(outputStream*, const char*, const char*, const char*); template diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp index 11c3e086feb..96b59b98db8 100644 --- a/src/hotspot/os/linux/osContainer_linux.hpp +++ b/src/hotspot/os/linux/osContainer_linux.hpp @@ -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 @@ -72,8 +72,7 @@ class OSContainer: AllStatic { static const char * container_type(); static bool available_memory_in_bytes(physical_memory_size_type& value); - static bool available_swap_in_bytes(physical_memory_size_type host_free_swap, - physical_memory_size_type& value); + static bool available_swap_in_bytes(physical_memory_size_type& value); static bool memory_limit_in_bytes(physical_memory_size_type& value); static bool memory_and_swap_limit_in_bytes(physical_memory_size_type& value); static bool memory_and_swap_usage_in_bytes(physical_memory_size_type& value); @@ -84,7 +83,7 @@ class OSContainer: AllStatic { static bool rss_usage_in_bytes(physical_memory_size_type& value); static bool cache_usage_in_bytes(physical_memory_size_type& value); - static bool active_processor_count(int& value); + static bool active_processor_count(double& value); static char * cpu_cpuset_cpus(); static char * cpu_cpuset_memory_nodes(); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 6a2a3974a16..48529c6ce17 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -211,15 +211,58 @@ static bool suppress_primordial_thread_resolution = false; // utility functions +bool os::is_containerized() { + return OSContainer::is_containerized(); +} + +bool os::Container::memory_limit(physical_memory_size_type& value) { + physical_memory_size_type result = 0; + if (OSContainer::memory_limit_in_bytes(result) && result != value_unlimited) { + value = result; + return true; + } + return false; +} + +bool os::Container::memory_soft_limit(physical_memory_size_type& value) { + physical_memory_size_type result = 0; + if (OSContainer::memory_soft_limit_in_bytes(result) && result != 0 && result != value_unlimited) { + value = result; + return true; + } + return false; +} + +bool os::Container::memory_throttle_limit(physical_memory_size_type& value) { + physical_memory_size_type result = 0; + if (OSContainer::memory_throttle_limit_in_bytes(result) && result != value_unlimited) { + value = result; + return true; + } + return false; +} + +bool os::Container::used_memory(physical_memory_size_type& value) { + return OSContainer::memory_usage_in_bytes(value); +} + bool os::available_memory(physical_memory_size_type& value) { - if (OSContainer::is_containerized() && OSContainer::available_memory_in_bytes(value)) { + if (is_containerized() && Container::available_memory(value)) { log_trace(os)("available container memory: " PHYS_MEM_TYPE_FORMAT, value); return true; } + return Machine::available_memory(value); +} + +bool os::Machine::available_memory(physical_memory_size_type& value) { return Linux::available_memory(value); } +bool os::Container::available_memory(physical_memory_size_type& value) { + return OSContainer::available_memory_in_bytes(value); +} + bool os::Linux::available_memory(physical_memory_size_type& value) { physical_memory_size_type avail_mem = 0; @@ -251,11 +294,15 @@ bool os::Linux::available_memory(physical_memory_size_type& value) { } bool os::free_memory(physical_memory_size_type& value) { - if (OSContainer::is_containerized() && OSContainer::available_memory_in_bytes(value)) { + if (is_containerized() && Container::available_memory(value)) { log_trace(os)("free container memory: " PHYS_MEM_TYPE_FORMAT, value); return true; } + return Machine::free_memory(value); +} + +bool os::Machine::free_memory(physical_memory_size_type& value) { return Linux::free_memory(value); } @@ -274,21 +321,30 @@ bool os::Linux::free_memory(physical_memory_size_type& value) { } bool os::total_swap_space(physical_memory_size_type& value) { - if (OSContainer::is_containerized()) { - physical_memory_size_type mem_swap_limit = value_unlimited; - physical_memory_size_type memory_limit = value_unlimited; - if (OSContainer::memory_and_swap_limit_in_bytes(mem_swap_limit) && - OSContainer::memory_limit_in_bytes(memory_limit)) { - if (memory_limit != value_unlimited && mem_swap_limit != value_unlimited && - mem_swap_limit >= memory_limit /* ensure swap is >= 0 */) { - value = mem_swap_limit - memory_limit; - return true; - } - } - } // fallback to the host swap space if the container returned unlimited + if (is_containerized() && Container::total_swap_space(value)) { + return true; + } // fallback to the host swap space if the container value fails + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { return Linux::host_swap(value); } +bool os::Container::total_swap_space(physical_memory_size_type& value) { + physical_memory_size_type mem_swap_limit = value_unlimited; + physical_memory_size_type memory_limit = value_unlimited; + if (OSContainer::memory_and_swap_limit_in_bytes(mem_swap_limit) && + OSContainer::memory_limit_in_bytes(memory_limit)) { + if (memory_limit != value_unlimited && mem_swap_limit != value_unlimited && + mem_swap_limit >= memory_limit /* ensure swap is >= 0 */) { + value = mem_swap_limit - memory_limit; + return true; + } + } + return false; +} + static bool host_free_swap_f(physical_memory_size_type& value) { struct sysinfo si; int ret = sysinfo(&si); @@ -309,32 +365,45 @@ bool os::free_swap_space(physical_memory_size_type& value) { return false; } physical_memory_size_type host_free_swap_val = MIN2(total_swap_space, host_free_swap); - if (OSContainer::is_containerized()) { - if (OSContainer::available_swap_in_bytes(host_free_swap_val, value)) { + if (is_containerized()) { + if (Container::free_swap_space(value)) { return true; } // Fall through to use host value log_trace(os,container)("os::free_swap_space: containerized value unavailable" " returning host value: " PHYS_MEM_TYPE_FORMAT, host_free_swap_val); } + value = host_free_swap_val; return true; } +bool os::Machine::free_swap_space(physical_memory_size_type& value) { + return host_free_swap_f(value); +} + +bool os::Container::free_swap_space(physical_memory_size_type& value) { + return OSContainer::available_swap_in_bytes(value); +} + physical_memory_size_type os::physical_memory() { - if (OSContainer::is_containerized()) { + if (is_containerized()) { physical_memory_size_type mem_limit = value_unlimited; - if (OSContainer::memory_limit_in_bytes(mem_limit) && mem_limit != value_unlimited) { + if (Container::memory_limit(mem_limit) && mem_limit != value_unlimited) { log_trace(os)("total container memory: " PHYS_MEM_TYPE_FORMAT, mem_limit); return mem_limit; } } - physical_memory_size_type phys_mem = Linux::physical_memory(); + physical_memory_size_type phys_mem = Machine::physical_memory(); log_trace(os)("total system memory: " PHYS_MEM_TYPE_FORMAT, phys_mem); return phys_mem; } +physical_memory_size_type os::Machine::physical_memory() { + return Linux::physical_memory(); +} + // Returns the resident set size (RSS) of the process. // Falls back to using VmRSS from /proc/self/status if /proc/self/smaps_rollup is unavailable. // Note: On kernels with memory cgroups or shared memory, VmRSS may underreport RSS. @@ -2439,20 +2508,21 @@ bool os::Linux::print_container_info(outputStream* st) { OSContainer::print_container_metric(st, "cpu_memory_nodes", p != nullptr ? p : "not supported"); free(p); - int i = -1; - bool supported = OSContainer::active_processor_count(i); + double cpus = -1; + bool supported = OSContainer::active_processor_count(cpus); if (supported) { - assert(i > 0, "must be"); + assert(cpus > 0, "must be"); if (ActiveProcessorCount > 0) { OSContainer::print_container_metric(st, "active_processor_count", ActiveProcessorCount, "(from -XX:ActiveProcessorCount)"); } else { - OSContainer::print_container_metric(st, "active_processor_count", i); + OSContainer::print_container_metric(st, "active_processor_count", cpus); } } else { OSContainer::print_container_metric(st, "active_processor_count", "not supported"); } + int i = -1; supported = OSContainer::cpu_quota(i); if (supported && i > 0) { OSContainer::print_container_metric(st, "cpu_quota", i); @@ -4737,15 +4807,26 @@ int os::active_processor_count() { return ActiveProcessorCount; } - int active_cpus = -1; - if (OSContainer::is_containerized() && OSContainer::active_processor_count(active_cpus)) { - log_trace(os)("active_processor_count: determined by OSContainer: %d", - active_cpus); - } else { - active_cpus = os::Linux::active_processor_count(); + if (is_containerized()) { + double cpu_quota; + if (Container::processor_count(cpu_quota)) { + int active_cpus = ceilf(cpu_quota); // Round fractional CPU quota up. + assert(active_cpus <= Machine::active_processor_count(), "must be"); + log_trace(os)("active_processor_count: determined by OSContainer: %d", + active_cpus); + return active_cpus; + } } - return active_cpus; + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { + return os::Linux::active_processor_count(); +} + +bool os::Container::processor_count(double& value) { + return OSContainer::active_processor_count(value); } static bool should_warn_invalid_processor_id() { diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index efbd1fe7c68..b0b7ae18106 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -839,10 +839,18 @@ bool os::available_memory(physical_memory_size_type& value) { return win32::available_memory(value); } +bool os::Machine::available_memory(physical_memory_size_type& value) { + return win32::available_memory(value); +} + bool os::free_memory(physical_memory_size_type& value) { return win32::available_memory(value); } +bool os::Machine::free_memory(physical_memory_size_type& value) { + return win32::available_memory(value); +} + bool os::win32::available_memory(physical_memory_size_type& value) { // Use GlobalMemoryStatusEx() because GlobalMemoryStatus() may return incorrect // value if total memory is larger than 4GB @@ -858,7 +866,11 @@ bool os::win32::available_memory(physical_memory_size_type& value) { } } -bool os::total_swap_space(physical_memory_size_type& value) { +bool os::total_swap_space(physical_memory_size_type& value) { + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); BOOL res = GlobalMemoryStatusEx(&ms); @@ -872,6 +884,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return Machine::free_swap_space(value); +} + +bool os::Machine::free_swap_space(physical_memory_size_type& value) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); BOOL res = GlobalMemoryStatusEx(&ms); @@ -888,6 +904,10 @@ physical_memory_size_type os::physical_memory() { return win32::physical_memory(); } +physical_memory_size_type os::Machine::physical_memory() { + return win32::physical_memory(); +} + size_t os::rss() { size_t rss = 0; PROCESS_MEMORY_COUNTERS_EX pmex; @@ -911,6 +931,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { bool schedules_all_processor_groups = win32::is_windows_11_or_greater() || win32::is_windows_server_2022_or_greater(); if (UseAllWindowsProcessorGroups && !schedules_all_processor_groups && !win32::processor_group_warning_displayed()) { win32::set_processor_group_warning_displayed(true); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index d8a30f9b5ee..885484020bd 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -66,10 +66,6 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" -#ifdef LINUX -#include "os_linux.hpp" -#include "osContainer_linux.hpp" -#endif #define NO_TRANSITION(result_type, header) extern "C" { result_type JNICALL header { #define NO_TRANSITION_END } } @@ -400,35 +396,18 @@ JVM_ENTRY_NO_ENV(jboolean, jfr_is_class_instrumented(JNIEnv* env, jclass jvm, jc JVM_END JVM_ENTRY_NO_ENV(jboolean, jfr_is_containerized(JNIEnv* env, jclass jvm)) -#ifdef LINUX - return OSContainer::is_containerized(); -#else - return false; -#endif + return os::is_containerized(); JVM_END JVM_ENTRY_NO_ENV(jlong, jfr_host_total_memory(JNIEnv* env, jclass jvm)) -#ifdef LINUX - // We want the host memory, not the container limit. - // os::physical_memory() would return the container limit. - return static_cast(os::Linux::physical_memory()); -#else - return static_cast(os::physical_memory()); -#endif + return static_cast(os::Machine::physical_memory()); JVM_END JVM_ENTRY_NO_ENV(jlong, jfr_host_total_swap_memory(JNIEnv* env, jclass jvm)) -#ifdef LINUX - // We want the host swap memory, not the container value. - physical_memory_size_type host_swap = 0; - (void)os::Linux::host_swap(host_swap); // Discard return value and treat as no swap - return static_cast(host_swap); -#else physical_memory_size_type total_swap_space = 0; // Return value ignored - defaulting to 0 on failure. - (void)os::total_swap_space(total_swap_space); + (void)os::Machine::total_swap_space(total_swap_space); return static_cast(total_swap_space); -#endif JVM_END JVM_ENTRY_NO_ENV(void, jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes)) diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index ef5aca96a57..6d6937a97e9 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.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 @@ -115,9 +115,6 @@ #if INCLUDE_MANAGEMENT #include "services/finalizerService.hpp" #endif -#ifdef LINUX -#include "osContainer_linux.hpp" -#endif #include @@ -500,11 +497,9 @@ JVM_LEAF(jboolean, JVM_IsUseContainerSupport(void)) JVM_END JVM_LEAF(jboolean, JVM_IsContainerized(void)) -#ifdef LINUX - if (OSContainer::is_containerized()) { + if (os::is_containerized()) { return JNI_TRUE; } -#endif return JNI_FALSE; JVM_END diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 1c2f5527ff9..de5bc9ea58f 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.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 @@ -129,7 +129,6 @@ #ifdef LINUX #include "cgroupSubsystem_linux.hpp" #include "os_linux.hpp" -#include "osContainer_linux.hpp" #endif #define CHECK_JNI_EXCEPTION_(env, value) \ @@ -2582,14 +2581,12 @@ WB_ENTRY(jboolean, WB_CheckLibSpecifiesNoexecstack(JNIEnv* env, jobject o, jstri WB_END WB_ENTRY(jboolean, WB_IsContainerized(JNIEnv* env, jobject o)) - LINUX_ONLY(return OSContainer::is_containerized();) - return false; + return os::is_containerized(); WB_END // Physical memory of the host machine (including containers) WB_ENTRY(jlong, WB_HostPhysicalMemory(JNIEnv* env, jobject o)) - LINUX_ONLY(return static_cast(os::Linux::physical_memory());) - return static_cast(os::physical_memory()); + return static_cast(os::Machine::physical_memory()); WB_END // Available memory of the host machine (container-aware) diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index cf18388c625..1c06bf3c521 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.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 @@ -81,10 +81,6 @@ #include "utilities/permitForbiddenFunctions.hpp" #include "utilities/powerOfTwo.hpp" -#ifdef LINUX -#include "osContainer_linux.hpp" -#endif - #ifndef _WINDOWS # include #endif @@ -2205,11 +2201,14 @@ static void assert_nonempty_range(const char* addr, size_t bytes) { } bool os::used_memory(physical_memory_size_type& value) { -#ifdef LINUX - if (OSContainer::is_containerized()) { - return OSContainer::memory_usage_in_bytes(value); + if (is_containerized()) { + return Container::used_memory(value); } -#endif + + return Machine::used_memory(value); +} + +bool os::Machine::used_memory(physical_memory_size_type& value) { physical_memory_size_type avail_mem = 0; // Return value ignored - defaulting to 0 on failure. (void)os::available_memory(avail_mem); @@ -2218,6 +2217,44 @@ bool os::used_memory(physical_memory_size_type& value) { return true; } +#ifndef LINUX +bool os::is_containerized() { + return false; +} + +bool os::Container::processor_count(double& value) { + return false; +} + +bool os::Container::available_memory(physical_memory_size_type& value) { + return false; +} + +bool os::Container::used_memory(physical_memory_size_type& value) { + return false; +} + +bool os::Container::total_swap_space(physical_memory_size_type& value) { + return false; +} + +bool os::Container::free_swap_space(physical_memory_size_type& value) { + return false; +} + +bool os::Container::memory_limit(physical_memory_size_type& value) { + return false; +} + +bool os::Container::memory_soft_limit(physical_memory_size_type& value) { + return false; +} + +bool os::Container::memory_throttle_limit(physical_memory_size_type& value) { + return false; +} +#endif + bool os::commit_memory(char* addr, size_t bytes, bool executable) { assert_nonempty_range(addr, bytes); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index d585d3e5fc0..29c872157fd 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.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 @@ -342,6 +342,52 @@ class os: AllStatic { static bool is_server_class_machine(); static size_t rss(); + // On platforms with container support (currently only Linux) we combine machine values with + // potential container values in os:: methods, abstracting which value is actually used. + // The os::Machine and os::Container classes and containing methods are used to get machine + // and container values (when available) separately. + static bool is_containerized(); + + // The os::Machine class reports system resource metrics from the perspective of the operating + // system, without considering container-imposed limits. The values returned by these methods + // reflect the resources visible to the process as reported by the OS, and may already be + // affected by mechanisms such as virtualization, hypervisor limits, or process affinity, + // but do NOT consider further restrictions imposed by container runtimes (e.g., cgroups) + class Machine : AllStatic { + public: + static int active_processor_count(); + + [[nodiscard]] static bool available_memory(physical_memory_size_type& value); + [[nodiscard]] static bool used_memory(physical_memory_size_type& value); + [[nodiscard]] static bool free_memory(physical_memory_size_type& value); + + [[nodiscard]] static bool total_swap_space(physical_memory_size_type& value); + [[nodiscard]] static bool free_swap_space(physical_memory_size_type& value); + + static physical_memory_size_type physical_memory(); + }; + + // The os::Container class reports resource limits as imposed by a supported container runtime + // (currently only cgroup-based Linux runtimes). If the process is running inside a + // containerized environment, methods from this class report the effective limits imposed + // by the container, which may be more restrictive than what os::Machine reports. + // Methods return true and set the out-parameter if a limit is found, + // or false if no limit exists or it cannot be determined. + class Container : AllStatic { + public: + [[nodiscard]] static bool processor_count(double& value); // Returns the core-equivalent CPU quota + + [[nodiscard]] static bool available_memory(physical_memory_size_type& value); + [[nodiscard]] static bool used_memory(physical_memory_size_type& value); + + [[nodiscard]] static bool total_swap_space(physical_memory_size_type& value); + [[nodiscard]] static bool free_swap_space(physical_memory_size_type& value); + + [[nodiscard]] static bool memory_limit(physical_memory_size_type& value); + [[nodiscard]] static bool memory_soft_limit(physical_memory_size_type& value); + [[nodiscard]] static bool memory_throttle_limit(physical_memory_size_type& value); + }; + // Returns the id of the processor on which the calling thread is currently executing. // The returned value is guaranteed to be between 0 and (os::processor_count() - 1). static uint processor_id(); diff --git a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java index 6b0a536e4d4..3064b320c0d 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.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 @@ -143,9 +143,9 @@ public class TestCPUAwareness { // Expected active processor count can not exceed available CPU count - private static int adjustExpectedAPCForAvailableCPUs(int expectedAPC) { - if (expectedAPC > availableCPUs) { - expectedAPC = availableCPUs; + private static double adjustExpectedAPCForAvailableCPUs(double expectedAPC) { + if (expectedAPC > (double)availableCPUs) { + expectedAPC = (double)availableCPUs; System.out.println("Adjusted expectedAPC = " + expectedAPC); } return expectedAPC; @@ -158,7 +158,7 @@ public class TestCPUAwareness { System.out.println("quota = " + quota); System.out.println("period = " + period); - int expectedAPC = (int) Math.ceil((float) quota / (float) period); + double expectedAPC = (double) quota / (double) period; System.out.println("expectedAPC = " + expectedAPC); expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); @@ -178,7 +178,7 @@ public class TestCPUAwareness { private static void testAPCCombo(String cpuset, int quota, int period, int shares, - int expectedAPC) throws Exception { + double expectedAPC) throws Exception { Common.logNewTestCase("test APC Combo"); System.out.println("cpuset = " + cpuset); System.out.println("quota = " + quota); From 496af3cf4769b78fa0928450a87928d259511c51 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Mon, 19 Jan 2026 18:05:22 +0000 Subject: [PATCH 066/126] 8375093: Convert GlobalCounter to use Atomic Reviewed-by: dholmes, iwalulya --- src/hotspot/share/runtime/thread.cpp | 5 +++-- src/hotspot/share/runtime/thread.hpp | 7 ++++--- src/hotspot/share/utilities/globalCounter.cpp | 12 ++++++------ src/hotspot/share/utilities/globalCounter.hpp | 5 +++-- .../share/utilities/globalCounter.inline.hpp | 15 +++++++-------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index f3c08ae62f7..bfbd4727e9a 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.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) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -36,6 +36,7 @@ #include "memory/resourceArea.hpp" #include "nmt/memTracker.hpp" #include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.inline.hpp" @@ -82,7 +83,7 @@ Thread::Thread(MemTag mem_tag) { _threads_hazard_ptr = nullptr; _threads_list_ptr = nullptr; _nested_threads_hazard_ptr_cnt = 0; - _rcu_counter = 0; + _rcu_counter.store_relaxed(0); // the handle mark links itself to last_handle_mark new HandleMark(this); diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 181dfc46f87..dcd6fb2d3fd 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.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) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,6 +30,7 @@ #include "gc/shared/threadLocalAllocBuffer.hpp" #include "jni.h" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" @@ -238,9 +239,9 @@ class Thread: public ThreadShadow { // Support for GlobalCounter private: - volatile uintx _rcu_counter; + Atomic _rcu_counter; public: - volatile uintx* get_rcu_counter() { + Atomic* get_rcu_counter() { return &_rcu_counter; } diff --git a/src/hotspot/share/utilities/globalCounter.cpp b/src/hotspot/share/utilities/globalCounter.cpp index 7019273d937..114d3b5fed1 100644 --- a/src/hotspot/share/utilities/globalCounter.cpp +++ b/src/hotspot/share/utilities/globalCounter.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 @@ -23,7 +23,7 @@ */ #include "memory/iterator.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/threadSMR.inline.hpp" #include "runtime/vmThread.hpp" @@ -41,7 +41,7 @@ class GlobalCounter::CounterThreadCheck : public ThreadClosure { SpinYield yield; // Loops on this thread until it has exited the critical read section. while(true) { - uintx cnt = AtomicAccess::load_acquire(thread->get_rcu_counter()); + uintx cnt = thread->get_rcu_counter()->load_acquire(); // This checks if the thread's counter is active. And if so is the counter // for a pre-existing reader (belongs to this grace period). A pre-existing // reader will have a lower counter than the global counter version for this @@ -57,9 +57,9 @@ class GlobalCounter::CounterThreadCheck : public ThreadClosure { }; void GlobalCounter::write_synchronize() { - assert((*Thread::current()->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "must be outside a critcal section"); - // AtomicAccess::add must provide fence since we have storeload dependency. - uintx gbl_cnt = AtomicAccess::add(&_global_counter._counter, COUNTER_INCREMENT); + assert((Thread::current()->get_rcu_counter()->load_relaxed() & COUNTER_ACTIVE) == 0x0, "must be outside a critcal section"); + // Atomic add must provide fence since we have storeload dependency. + uintx gbl_cnt = _global_counter._counter.add_then_fetch(COUNTER_INCREMENT); // Do all RCU threads. CounterThreadCheck ctc(gbl_cnt); diff --git a/src/hotspot/share/utilities/globalCounter.hpp b/src/hotspot/share/utilities/globalCounter.hpp index c78831acfa5..80fc1b3e94e 100644 --- a/src/hotspot/share/utilities/globalCounter.hpp +++ b/src/hotspot/share/utilities/globalCounter.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 @@ -27,6 +27,7 @@ #include "memory/allStatic.hpp" #include "memory/padded.hpp" +#include "runtime/atomic.hpp" class Thread; @@ -47,7 +48,7 @@ class GlobalCounter : public AllStatic { // counter is on a separate cacheline. struct PaddedCounter { DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0); - volatile uintx _counter; + Atomic _counter; DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(volatile uintx)); }; diff --git a/src/hotspot/share/utilities/globalCounter.inline.hpp b/src/hotspot/share/utilities/globalCounter.inline.hpp index 9cc746173b8..ed37b8a878d 100644 --- a/src/hotspot/share/utilities/globalCounter.inline.hpp +++ b/src/hotspot/share/utilities/globalCounter.inline.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,30 +27,29 @@ #include "utilities/globalCounter.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" inline GlobalCounter::CSContext GlobalCounter::critical_section_begin(Thread *thread) { assert(thread == Thread::current(), "must be current thread"); - uintx old_cnt = AtomicAccess::load(thread->get_rcu_counter()); + uintx old_cnt = thread->get_rcu_counter()->load_relaxed(); // Retain the old counter value if already active, e.g. nested. // Otherwise, set the counter to the current version + active bit. uintx new_cnt = old_cnt; if ((new_cnt & COUNTER_ACTIVE) == 0) { - new_cnt = AtomicAccess::load(&_global_counter._counter) | COUNTER_ACTIVE; + new_cnt = _global_counter._counter.load_relaxed() | COUNTER_ACTIVE; } - AtomicAccess::release_store_fence(thread->get_rcu_counter(), new_cnt); + thread->get_rcu_counter()->release_store_fence(new_cnt); return static_cast(old_cnt); } inline void GlobalCounter::critical_section_end(Thread *thread, CSContext context) { assert(thread == Thread::current(), "must be current thread"); - assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == COUNTER_ACTIVE, "must be in critical section"); + assert((thread->get_rcu_counter()->load_relaxed() & COUNTER_ACTIVE) == COUNTER_ACTIVE, "must be in critical section"); // Restore the counter value from before the associated begin. - AtomicAccess::release_store(thread->get_rcu_counter(), - static_cast(context)); + thread->get_rcu_counter()->release_store(static_cast(context)); } class GlobalCounter::CriticalSection { From 303de9a3f2ba93f0bbe42044483a0b48c82b70cb Mon Sep 17 00:00:00 2001 From: Xiaohong Gong Date: Tue, 20 Jan 2026 01:43:40 +0000 Subject: [PATCH 067/126] 8370666: VectorAPI: Add clear comments for vector relative code in c2 Reviewed-by: epeter, jbhateja, qamai --- src/hotspot/share/opto/matcher.hpp | 4 +- src/hotspot/share/opto/node.hpp | 5 +- src/hotspot/share/opto/type.cpp | 8 +- src/hotspot/share/opto/type.hpp | 15 +- src/hotspot/share/opto/vectornode.hpp | 343 ++++++++++++++------------ 5 files changed, 214 insertions(+), 161 deletions(-) diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index a071cff9e3c..9579af84f24 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.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 @@ -327,6 +327,8 @@ public: // e.g. Op_ vector nodes and other intrinsics while guarding with vlen static bool match_rule_supported_vector(int opcode, int vlen, BasicType bt); + // Returns true if the platform efficiently implements the given masked vector + // operation using predicate features, false otherwise. static bool match_rule_supported_vector_masked(int opcode, int vlen, BasicType bt); // Determines if a vector operation needs to be partially implemented with a mask diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index f1d9785a746..5ddc4236b3e 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.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) 2024, 2025, Alibaba Group Holding Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -845,7 +845,8 @@ public: Flag_has_swapped_edges = 1ULL << 11, Flag_is_scheduled = 1ULL << 12, Flag_is_expensive = 1ULL << 13, - Flag_is_predicated_vector = 1ULL << 14, + Flag_is_predicated_vector = 1ULL << 14, // Marked on a vector node that has an additional + // mask input controlling the lane operations. Flag_for_post_loop_opts_igvn = 1ULL << 15, Flag_for_merge_stores_igvn = 1ULL << 16, Flag_is_removed_by_peephole = 1ULL << 17, diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index ecb8c2c1cd8..d3271e79f2f 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.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 @@ -2443,6 +2443,12 @@ const TypeVect* TypeVect::make(BasicType elem_bt, uint length, bool is_mask) { return nullptr; } +// Create a vector mask type with the given element basic type and length. +// - Returns "TypeVectMask" (PVectMask) for platforms that support the predicate +// feature and it is implemented properly in the backend, allowing the mask to +// be stored in a predicate/mask register. +// - Returns a normal vector type "TypeVectA ~ TypeVectZ" (NVectMask) otherwise, +// where the vector mask is stored in a vector register. const TypeVect* TypeVect::makemask(BasicType elem_bt, uint length) { if (Matcher::has_predicated_vectors() && Matcher::match_rule_supported_vector_masked(Op_VectorLoadMask, length, elem_bt)) { diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 285b4984cd1..7c7ff035a54 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -1018,7 +1018,7 @@ public: }; //------------------------------TypeVect--------------------------------------- -// Class of Vector Types +// Basic class of vector (mask) types. class TypeVect : public Type { const BasicType _elem_bt; // Vector's element type const uint _length; // Elements in vector (power of 2) @@ -1058,6 +1058,16 @@ public: #endif }; +// TypeVect subclasses representing vectors or vector masks with "BVectMask" or "NVectMask" +// layout (see vectornode.hpp for detailed notes on vector mask representations), mapped +// to vector registers and distinguished by vector register size: +// +// - TypeVectA: Scalable vector type (variable size, e.g., AArch64 SVE, RISC-V RVV) +// - TypeVectS: 32-bit vector type +// - TypeVectD: 64-bit vector type +// - TypeVectX: 128-bit vector type +// - TypeVectY: 256-bit vector type +// - TypeVectZ: 512-bit vector type class TypeVectA : public TypeVect { friend class TypeVect; TypeVectA(BasicType elem_bt, uint length) : TypeVect(VectorA, elem_bt, length) {} @@ -1088,6 +1098,9 @@ class TypeVectZ : public TypeVect { TypeVectZ(BasicType elem_bt, uint length) : TypeVect(VectorZ, elem_bt, length) {} }; +// Class of TypeVectMask, representing vector masks with "PVectMask" layout (see +// vectornode.hpp for detailed notes on vector mask representations), mapped to +// dedicated hardware predicate/mask registers. class TypeVectMask : public TypeVect { public: friend class TypeVect; diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index dc7aa13cf36..b1976d8f0db 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.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 @@ -33,7 +33,63 @@ #include "opto/opcodes.hpp" #include "prims/vectorSupport.hpp" -//------------------------------VectorNode------------------------------------- +//=======================Notes-for-VectorMask-Representation======================= +// +// There are three distinct representations for vector masks based on platform and +// use scenarios: +// +// - BVectMask: Platform-independent mask stored in a vector register with 8-bit +// lanes containing 1/0 values. The corresponding type is TypeVectA ~ TypeVectZ. +// +// - NVectMask: Platform-specific mask stored in vector registers with N-bit lanes, +// where all bits in each lane are either set (true) or unset (false). Generated +// on architectures without predicate/mask feature, such as AArch64 NEON, x86 +// AVX2, etc. The corresponding type is TypeVectA ~ TypeVectZ. +// +// - PVectMask: Platform-specific mask stored in predicate/mask registers. Generated +// on architectures with predicate/mask feature, such as AArch64 SVE, x86 AVX-512, +// and RISC-V Vector Extension (RVV). The corresponding type is TypeVectMask. +// +// NVectMask and PVectMask encode element data type and vector length information. +// They are the primary mask representations used in most mask and masked vector +// operations. BVectMask primarily represents mask values loaded from or stored to +// Java boolean memory (mask backing storage). VectorLoadMask/VectorStoreMask nodes +// are needed to transform it to/from P/NVectMask. It is also used in certain mask +// operations (e.g. VectorMaskOpNode). +// +//=========================Notes-for-Masked-Vector-Nodes=========================== +// +// Each lane-wise and cross-lane (reduction) ALU node supports both non-masked +// and masked operations. +// +// Currently masked vector nodes are only used to implement the Vector API's masked +// operations (which might also be used for auto-vectorization in future), such as: +// Vector lanewise(VectorOperators.Binary op, Vector v, VectorMask m) +// +// They are generated during intrinsification for Vector API, only on architectures +// that support the relevant predicated instructions. The compiler uses +// "Matcher::match_rule_supported_vector_masked()" to check whether the current +// platform supports the predicated/masked vector instructions for an operation. It +// generates the masked vector node for the operation if supported. Otherwise, it +// generates the unpredicated vector node and implements the masked operation with +// the help of a VectorBlendNode. Please see more details from API intrinsification +// in vectorIntrinsics.cpp. +// +// To differentiate the masked and non-masked nodes, flag "Flag_is_predicated_vector" +// is set for the masked version. Meanwhile, there is an additional mask input for +// the masked nodes. +// +// For example: +// - Non-masked version: +// in1 in2 +// \ / +// AddVBNode +// +// - Masked version (with "Flag_is_predicated_vector" being set): +// in1 in2 mask +// \ | / +// AddVBNode + // Vector Operation class VectorNode : public TypeNode { public: @@ -103,6 +159,8 @@ class VectorNode : public TypeNode { static bool implemented(int opc, uint vlen, BasicType bt); static bool is_shift(Node* n); static bool is_vshift_cnt(Node* n); + // Returns true if the lower vlen bits (bits [0, vlen-1]) of the long value + // are all 1s or all 0s, indicating a "mask all" or "mask none" pattern. static bool is_maskall_type(const TypeLong* type, int vlen); static bool is_muladds2i(const Node* n); static bool is_roundopD(Node* n); @@ -176,7 +234,6 @@ class SaturatingVectorNode : public VectorNode { bool is_unsigned() { return _is_unsigned; } }; -//------------------------------AddVBNode-------------------------------------- // Vector add byte class AddVBNode : public VectorNode { public: @@ -184,7 +241,6 @@ class AddVBNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AddVSNode-------------------------------------- // Vector add char/short class AddVSNode : public VectorNode { public: @@ -192,7 +248,6 @@ class AddVSNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AddVINode-------------------------------------- // Vector add int class AddVINode : public VectorNode { public: @@ -200,7 +255,6 @@ class AddVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AddVLNode-------------------------------------- // Vector add long class AddVLNode : public VectorNode { public: @@ -208,7 +262,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddVHFNode-------------------------------------- // Vector add float class AddVHFNode : public VectorNode { public: @@ -216,7 +269,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddVFNode-------------------------------------- // Vector add float class AddVFNode : public VectorNode { public: @@ -224,7 +276,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddVDNode-------------------------------------- // Vector add double class AddVDNode : public VectorNode { public: @@ -232,7 +283,6 @@ public: virtual int Opcode() const; }; -//------------------------------ReductionNode------------------------------------ // Perform reduction of a vector class ReductionNode : public Node { private: @@ -294,7 +344,6 @@ class ReductionNode : public Node { #endif }; -//------------------------------AddReductionVINode-------------------------------------- // Vector add byte, short and int as a reduction class AddReductionVINode : public ReductionNode { public: @@ -302,7 +351,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddReductionVLNode-------------------------------------- // Vector add long as a reduction class AddReductionVLNode : public ReductionNode { public: @@ -310,7 +358,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddReductionVFNode-------------------------------------- // Vector add float as a reduction class AddReductionVFNode : public ReductionNode { private: @@ -337,7 +384,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------AddReductionVDNode-------------------------------------- // Vector add double as a reduction class AddReductionVDNode : public ReductionNode { private: @@ -364,7 +410,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------SubVBNode-------------------------------------- // Vector subtract byte class SubVBNode : public VectorNode { public: @@ -372,7 +417,6 @@ class SubVBNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVSNode-------------------------------------- // Vector subtract short class SubVSNode : public VectorNode { public: @@ -380,7 +424,6 @@ class SubVSNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVINode-------------------------------------- // Vector subtract int class SubVINode : public VectorNode { public: @@ -388,7 +431,6 @@ class SubVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVLNode-------------------------------------- // Vector subtract long class SubVLNode : public VectorNode { public: @@ -396,7 +438,6 @@ class SubVLNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SaturatingAddVNode----------------------------- // Vector saturating addition. class SaturatingAddVNode : public SaturatingVectorNode { public: @@ -404,7 +445,6 @@ class SaturatingAddVNode : public SaturatingVectorNode { virtual int Opcode() const; }; -//------------------------------SaturatingSubVNode----------------------------- // Vector saturating subtraction. class SaturatingSubVNode : public SaturatingVectorNode { public: @@ -412,7 +452,6 @@ class SaturatingSubVNode : public SaturatingVectorNode { virtual int Opcode() const; }; -//------------------------------SubVHFNode-------------------------------------- // Vector subtract half float class SubVHFNode : public VectorNode { public: @@ -420,8 +459,6 @@ public: virtual int Opcode() const; }; - -//------------------------------SubVFNode-------------------------------------- // Vector subtract float class SubVFNode : public VectorNode { public: @@ -429,7 +466,6 @@ class SubVFNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVDNode-------------------------------------- // Vector subtract double class SubVDNode : public VectorNode { public: @@ -437,7 +473,6 @@ class SubVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVBNode-------------------------------------- // Vector multiply byte class MulVBNode : public VectorNode { public: @@ -445,7 +480,6 @@ class MulVBNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVSNode-------------------------------------- // Vector multiply short class MulVSNode : public VectorNode { public: @@ -453,7 +487,6 @@ class MulVSNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVINode-------------------------------------- // Vector multiply int class MulVINode : public VectorNode { public: @@ -461,7 +494,6 @@ class MulVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVLNode-------------------------------------- // Vector multiply long class MulVLNode : public VectorNode { public: @@ -473,7 +505,6 @@ public: bool has_uint_inputs() const; }; -//------------------------------MulVFNode-------------------------------------- // Vector multiply half float class MulVHFNode : public VectorNode { public: @@ -481,7 +512,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulVFNode-------------------------------------- // Vector multiply float class MulVFNode : public VectorNode { public: @@ -489,7 +519,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulVDNode-------------------------------------- // Vector multiply double class MulVDNode : public VectorNode { public: @@ -497,7 +526,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulAddVS2VINode-------------------------------- // Vector multiply shorts to int and add adjacent ints. class MulAddVS2VINode : public VectorNode { public: @@ -505,7 +533,6 @@ class MulAddVS2VINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------FmaVNode-------------------------------------- // Vector fused-multiply-add class FmaVNode : public VectorNode { public: @@ -515,7 +542,6 @@ public: virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; -//------------------------------FmaVDNode-------------------------------------- // Vector fused-multiply-add double class FmaVDNode : public FmaVNode { public: @@ -523,7 +549,6 @@ public: virtual int Opcode() const; }; -//------------------------------FmaVFNode-------------------------------------- // Vector fused-multiply-add float class FmaVFNode : public FmaVNode { public: @@ -531,7 +556,6 @@ public: virtual int Opcode() const; }; -//------------------------------FmaVHFNode------------------------------------- // Vector fused-multiply-add half-precision float class FmaVHFNode : public FmaVNode { public: @@ -539,7 +563,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulReductionVINode-------------------------------------- // Vector multiply byte, short and int as a reduction class MulReductionVINode : public ReductionNode { public: @@ -547,7 +570,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulReductionVLNode-------------------------------------- // Vector multiply int as a reduction class MulReductionVLNode : public ReductionNode { public: @@ -555,7 +577,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulReductionVFNode-------------------------------------- // Vector multiply float as a reduction class MulReductionVFNode : public ReductionNode { // True if mul reduction operation for floats requires strict ordering. @@ -581,7 +602,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------MulReductionVDNode-------------------------------------- // Vector multiply double as a reduction class MulReductionVDNode : public ReductionNode { // True if mul reduction operation for doubles requires strict ordering. @@ -607,7 +627,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------DivVHFNode------------------------------------- // Vector divide half float class DivVHFNode : public VectorNode { public: @@ -615,7 +634,6 @@ public: virtual int Opcode() const; }; -//------------------------------DivVFNode-------------------------------------- // Vector divide float class DivVFNode : public VectorNode { public: @@ -623,7 +641,6 @@ class DivVFNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------DivVDNode-------------------------------------- // Vector Divide double class DivVDNode : public VectorNode { public: @@ -631,7 +648,6 @@ class DivVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVBNode-------------------------------------- // Vector Abs byte class AbsVBNode : public VectorNode { public: @@ -639,7 +655,6 @@ public: virtual int Opcode() const; }; -//------------------------------AbsVSNode-------------------------------------- // Vector Abs short class AbsVSNode : public VectorNode { public: @@ -647,7 +662,6 @@ public: virtual int Opcode() const; }; -//------------------------------MinVNode-------------------------------------- // Vector Min class MinVNode : public VectorNode { public: @@ -655,7 +669,6 @@ public: virtual int Opcode() const; }; -//------------------------------MinVHFNode------------------------------------ // Vector Min for half floats class MinVHFNode : public VectorNode { public: @@ -663,7 +676,6 @@ public: virtual int Opcode() const; }; -//------------------------------MaxVHFNode------------------------------------ // Vector Max for half floats class MaxVHFNode : public VectorNode { public: @@ -671,6 +683,7 @@ public: virtual int Opcode() const; }; +// Vector Unsigned Min class UMinVNode : public VectorNode { public: UMinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2 ,vt) { @@ -681,8 +694,6 @@ class UMinVNode : public VectorNode { virtual int Opcode() const; }; - -//------------------------------MaxVNode-------------------------------------- // Vector Max class MaxVNode : public VectorNode { public: @@ -690,6 +701,7 @@ class MaxVNode : public VectorNode { virtual int Opcode() const; }; +// Vector Unsigned Max class UMaxVNode : public VectorNode { public: UMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) { @@ -700,7 +712,6 @@ class UMaxVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVINode-------------------------------------- // Vector Abs int class AbsVINode : public VectorNode { public: @@ -708,7 +719,6 @@ class AbsVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVLNode-------------------------------------- // Vector Abs long class AbsVLNode : public VectorNode { public: @@ -716,7 +726,6 @@ public: virtual int Opcode() const; }; -//------------------------------AbsVFNode-------------------------------------- // Vector Abs float class AbsVFNode : public VectorNode { public: @@ -724,7 +733,6 @@ class AbsVFNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVDNode-------------------------------------- // Vector Abs double class AbsVDNode : public VectorNode { public: @@ -732,7 +740,6 @@ class AbsVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------NegVNode--------------------------------------- // Vector Neg parent class (not for code generation). class NegVNode : public VectorNode { public: @@ -746,7 +753,6 @@ class NegVNode : public VectorNode { Node* degenerate_integral_negate(PhaseGVN* phase, bool is_predicated); }; -//------------------------------NegVINode-------------------------------------- // Vector Neg byte/short/int class NegVINode : public NegVNode { public: @@ -754,7 +760,6 @@ class NegVINode : public NegVNode { virtual int Opcode() const; }; -//------------------------------NegVLNode-------------------------------------- // Vector Neg long class NegVLNode : public NegVNode { public: @@ -762,7 +767,6 @@ class NegVLNode : public NegVNode { virtual int Opcode() const; }; -//------------------------------NegVFNode-------------------------------------- // Vector Neg float class NegVFNode : public NegVNode { public: @@ -770,7 +774,6 @@ class NegVFNode : public NegVNode { virtual int Opcode() const; }; -//------------------------------NegVDNode-------------------------------------- // Vector Neg double class NegVDNode : public NegVNode { public: @@ -778,7 +781,6 @@ class NegVDNode : public NegVNode { virtual int Opcode() const; }; -//------------------------------PopCountVINode--------------------------------- // Vector popcount integer bits class PopCountVINode : public VectorNode { public: @@ -786,7 +788,6 @@ class PopCountVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------PopCountVLNode--------------------------------- // Vector popcount long bits class PopCountVLNode : public VectorNode { public: @@ -796,7 +797,6 @@ class PopCountVLNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SqrtVHFNode------------------------------------- // Vector Sqrt half-precision float class SqrtVHFNode : public VectorNode { public: @@ -804,14 +804,13 @@ public: virtual int Opcode() const; }; -//------------------------------SqrtVFNode-------------------------------------- // Vector Sqrt float class SqrtVFNode : public VectorNode { public: SqrtVFNode(Node* in, const TypeVect* vt) : VectorNode(in,vt) {} virtual int Opcode() const; }; -//------------------------------RoundDoubleVNode-------------------------------- + // Vector round double class RoundDoubleModeVNode : public VectorNode { public: @@ -819,7 +818,6 @@ class RoundDoubleModeVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SqrtVDNode-------------------------------------- // Vector Sqrt double class SqrtVDNode : public VectorNode { public: @@ -827,8 +825,7 @@ class SqrtVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------ShiftVNode----------------------------------- -// Class ShiftV functionality. This covers the common behaviors for all kinds +// Class ShiftV functionality. This covers the common behaviors for all kinds // of vector shifts. class ShiftVNode : public VectorNode { private: @@ -848,7 +845,6 @@ class ShiftVNode : public VectorNode { virtual uint size_of() const { return sizeof(ShiftVNode); } }; -//------------------------------LShiftVBNode----------------------------------- // Vector left shift bytes class LShiftVBNode : public ShiftVNode { public: @@ -857,7 +853,6 @@ class LShiftVBNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftVSNode----------------------------------- // Vector left shift shorts class LShiftVSNode : public ShiftVNode { public: @@ -866,7 +861,6 @@ class LShiftVSNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftVINode----------------------------------- // Vector left shift ints class LShiftVINode : public ShiftVNode { public: @@ -875,7 +869,6 @@ class LShiftVINode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftVLNode----------------------------------- // Vector left shift longs class LShiftVLNode : public ShiftVNode { public: @@ -884,7 +877,6 @@ class LShiftVLNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVBNode----------------------------------- // Vector right arithmetic (signed) shift bytes class RShiftVBNode : public ShiftVNode { public: @@ -893,7 +885,6 @@ class RShiftVBNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVSNode----------------------------------- // Vector right arithmetic (signed) shift shorts class RShiftVSNode : public ShiftVNode { public: @@ -902,7 +893,6 @@ class RShiftVSNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVINode----------------------------------- // Vector right arithmetic (signed) shift ints class RShiftVINode : public ShiftVNode { public: @@ -911,7 +901,6 @@ class RShiftVINode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVLNode----------------------------------- // Vector right arithmetic (signed) shift longs class RShiftVLNode : public ShiftVNode { public: @@ -920,7 +909,6 @@ class RShiftVLNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVBNode---------------------------------- // Vector right logical (unsigned) shift bytes class URShiftVBNode : public ShiftVNode { public: @@ -929,7 +917,6 @@ class URShiftVBNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVSNode---------------------------------- // Vector right logical (unsigned) shift shorts class URShiftVSNode : public ShiftVNode { public: @@ -938,7 +925,6 @@ class URShiftVSNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVINode---------------------------------- // Vector right logical (unsigned) shift ints class URShiftVINode : public ShiftVNode { public: @@ -947,7 +933,6 @@ class URShiftVINode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVLNode---------------------------------- // Vector right logical (unsigned) shift longs class URShiftVLNode : public ShiftVNode { public: @@ -956,7 +941,6 @@ class URShiftVLNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftCntVNode--------------------------------- // Vector left shift count class LShiftCntVNode : public VectorNode { public: @@ -964,7 +948,6 @@ class LShiftCntVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------RShiftCntVNode--------------------------------- // Vector right shift count class RShiftCntVNode : public VectorNode { public: @@ -972,7 +955,6 @@ class RShiftCntVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AndVNode--------------------------------------- // Vector and integer class AndVNode : public VectorNode { public: @@ -981,7 +963,6 @@ class AndVNode : public VectorNode { virtual Node* Identity(PhaseGVN* phase); }; -//------------------------------AndReductionVNode-------------------------------------- // Vector and byte, short, int, long as a reduction class AndReductionVNode : public ReductionNode { public: @@ -989,8 +970,7 @@ class AndReductionVNode : public ReductionNode { virtual int Opcode() const; }; -//------------------------------OrVNode--------------------------------------- -// Vector or byte, short, int, long as a reduction +// Vector or integer class OrVNode : public VectorNode { public: OrVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} @@ -998,15 +978,13 @@ class OrVNode : public VectorNode { virtual Node* Identity(PhaseGVN* phase); }; -//------------------------------OrReductionVNode-------------------------------------- -// Vector xor byte, short, int, long as a reduction +// Vector or byte, short, int, long as a reduction class OrReductionVNode : public ReductionNode { public: OrReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------XorVNode--------------------------------------- // Vector xor integer class XorVNode : public VectorNode { public: @@ -1016,15 +994,13 @@ class XorVNode : public VectorNode { Node* Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape); }; -//------------------------------XorReductionVNode-------------------------------------- -// Vector and int, long as a reduction +// Vector xor byte, short, int, long as a reduction class XorReductionVNode : public ReductionNode { public: XorReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------MinReductionVNode-------------------------------------- // Vector min byte, short, int, long, float, double as a reduction class MinReductionVNode : public ReductionNode { public: @@ -1032,15 +1008,13 @@ public: virtual int Opcode() const; }; -//------------------------------MaxReductionVNode-------------------------------------- -// Vector min byte, short, int, long, float, double as a reduction +// Vector max byte, short, int, long, float, double as a reduction class MaxReductionVNode : public ReductionNode { public: MaxReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------CompressVNode-------------------------------------- // Vector compress class CompressVNode: public VectorNode { public: @@ -1051,6 +1025,7 @@ class CompressVNode: public VectorNode { virtual int Opcode() const; }; +// Vector mask compress class CompressMNode: public VectorNode { public: CompressMNode(Node* mask, const TypeVect* vt) : @@ -1060,7 +1035,6 @@ class CompressMNode: public VectorNode { virtual int Opcode() const; }; -//------------------------------ExpandVNode-------------------------------------- // Vector expand class ExpandVNode: public VectorNode { public: @@ -1073,7 +1047,6 @@ class ExpandVNode: public VectorNode { //================================= M E M O R Y =============================== -//------------------------------LoadVectorNode--------------------------------- // Load Vector from memory class LoadVectorNode : public LoadNode { private: @@ -1115,7 +1088,6 @@ class LoadVectorNode : public LoadNode { #endif }; -//------------------------------LoadVectorGatherNode------------------------------ // Load Vector from memory via index map class LoadVectorGatherNode : public LoadVectorNode { public: @@ -1138,7 +1110,6 @@ class LoadVectorGatherNode : public LoadVectorNode { } }; -//------------------------------StoreVectorNode-------------------------------- // Store Vector to memory class StoreVectorNode : public StoreNode { private: @@ -1180,9 +1151,7 @@ class StoreVectorNode : public StoreNode { #endif }; -//------------------------------StoreVectorScatterNode------------------------------ // Store Vector into memory via index map - class StoreVectorScatterNode : public StoreVectorNode { public: enum { Indices = 4 }; @@ -1200,7 +1169,6 @@ class StoreVectorNode : public StoreNode { virtual Node* indices() const { return in(Indices); } }; -//------------------------------StoreVectorMaskedNode-------------------------------- // Store Vector to memory under the influence of a predicate register(mask). class StoreVectorMaskedNode : public StoreVectorNode { public: @@ -1221,7 +1189,6 @@ class StoreVectorMaskedNode : public StoreVectorNode { virtual Node* mask() const { return in(Mask); } }; -//------------------------------LoadVectorMaskedNode-------------------------------- // Load Vector from memory under the influence of a predicate register(mask). class LoadVectorMaskedNode : public LoadVectorNode { public: @@ -1245,7 +1212,6 @@ class LoadVectorMaskedNode : public LoadVectorNode { } }; -//-------------------------------LoadVectorGatherMaskedNode--------------------------------- // Load Vector from memory via index map under the influence of a predicate register(mask). class LoadVectorGatherMaskedNode : public LoadVectorNode { public: @@ -1268,7 +1234,6 @@ class LoadVectorGatherMaskedNode : public LoadVectorNode { } }; -//------------------------------StoreVectorScatterMaskedNode-------------------------------- // Store Vector into memory via index map under the influence of a predicate register(mask). class StoreVectorScatterMaskedNode : public StoreVectorNode { public: @@ -1312,7 +1277,6 @@ public: virtual const Type *bottom_type() const { return in(1)->bottom_type(); } }; -//------------------------------VectorCmpMaskedNode-------------------------------- // Vector Comparison under the influence of a predicate register(mask). class VectorCmpMaskedNode : public TypeNode { public: @@ -1325,7 +1289,8 @@ class VectorCmpMaskedNode : public TypeNode { virtual int Opcode() const; }; -//------------------------------VectorMaskGenNode---------------------------------- +// Generate a vector mask based on the given length. Lanes with indices in +// [0, length) are set to true, while the remaining lanes are set to false. class VectorMaskGenNode : public TypeNode { public: VectorMaskGenNode(Node* length, const Type* ty): TypeNode(ty, 2) { @@ -1338,7 +1303,8 @@ class VectorMaskGenNode : public TypeNode { static Node* make(Node* length, BasicType vmask_bt, int vmask_len); }; -//------------------------------VectorMaskOpNode----------------------------------- +// Base class for certain vector mask operations. The supported input mask can +// be either "BVectMask" or "PVectMask" depending on the platform. class VectorMaskOpNode : public TypeNode { private: int _mopc; @@ -1359,6 +1325,7 @@ class VectorMaskOpNode : public TypeNode { static Node* make(Node* mask, const Type* ty, int mopc); }; +// Count the number of true (set) lanes in the vector mask. class VectorMaskTrueCountNode : public VectorMaskOpNode { public: VectorMaskTrueCountNode(Node* mask, const Type* ty): @@ -1366,6 +1333,8 @@ class VectorMaskTrueCountNode : public VectorMaskOpNode { virtual int Opcode() const; }; +// Returns the index of the first true (set) lane in the vector mask. +// If no lanes are set, returns the vector length. class VectorMaskFirstTrueNode : public VectorMaskOpNode { public: VectorMaskFirstTrueNode(Node* mask, const Type* ty): @@ -1373,6 +1342,8 @@ class VectorMaskFirstTrueNode : public VectorMaskOpNode { virtual int Opcode() const; }; +// Returns the index of the last true (set) lane in the vector mask. +// If no lanes are set, returns -1 . class VectorMaskLastTrueNode : public VectorMaskOpNode { public: VectorMaskLastTrueNode(Node* mask, const Type* ty): @@ -1380,6 +1351,10 @@ class VectorMaskLastTrueNode : public VectorMaskOpNode { virtual int Opcode() const; }; +// Pack the mask lane values into a long value, supporting at most the +// first 64 lanes. Each mask lane is packed into one bit in the long +// value, ordered from the least significant bit to the most significant +// bit. class VectorMaskToLongNode : public VectorMaskOpNode { public: VectorMaskToLongNode(Node* mask, const Type* ty): @@ -1391,6 +1366,9 @@ class VectorMaskToLongNode : public VectorMaskOpNode { virtual Node* Identity(PhaseGVN* phase); }; +// Unpack bits from a long value into vector mask lane values. Each bit +// in the long value is unpacked into one mask lane, ordered from the +// least significant bit to the sign bit. class VectorLongToMaskNode : public VectorNode { public: VectorLongToMaskNode(Node* mask, const TypeVect* ty): @@ -1400,36 +1378,40 @@ class VectorLongToMaskNode : public VectorNode { virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; -//-------------------------- Vector mask broadcast ----------------------------------- +// Broadcast a scalar value to all lanes of a vector mask. All lanes are set +// to true if the input value is non-zero, or false if the input value is +// zero. This node is only used to generate a mask with "PVectMask" layout. class MaskAllNode : public VectorNode { public: MaskAllNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) {} virtual int Opcode() const; }; -//--------------------------- Vector mask logical and -------------------------------- +// Perform a bitwise AND operation between two vector masks. This node is +// only used for vector masks with "PVectMask" layout. class AndVMaskNode : public AndVNode { public: AndVMaskNode(Node* in1, Node* in2, const TypeVect* vt) : AndVNode(in1, in2, vt) {} virtual int Opcode() const; }; -//--------------------------- Vector mask logical or --------------------------------- +// Perform a bitwise OR operation between two vector masks. This node is +// only used for vector masks with "PVectMask" layout. class OrVMaskNode : public OrVNode { public: OrVMaskNode(Node* in1, Node* in2, const TypeVect* vt) : OrVNode(in1, in2, vt) {} virtual int Opcode() const; }; -//--------------------------- Vector mask logical xor -------------------------------- +// Perform a bitwise XOR operation between two vector masks. This node is +// only used for vector masks with "PVectMask" layout. class XorVMaskNode : public XorVNode { public: XorVMaskNode(Node* in1, Node* in2, const TypeVect* vt) : XorVNode(in1, in2, vt) {} virtual int Opcode() const; }; -//=========================Promote_Scalar_to_Vector============================ - +// Replicate a scalar value to all lanes of a vector. class ReplicateNode : public VectorNode { public: ReplicateNode(Node* in1, const TypeVect* vt) : VectorNode(in1, vt) { @@ -1439,7 +1421,7 @@ class ReplicateNode : public VectorNode { virtual int Opcode() const; }; -//======================Populate_Indices_into_a_Vector========================= +// Populate indices into a vector. class PopulateIndexNode : public VectorNode { public: PopulateIndexNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} @@ -1448,7 +1430,6 @@ class PopulateIndexNode : public VectorNode { //========================Pack_Scalars_into_a_Vector=========================== -//------------------------------PackNode--------------------------------------- // Pack parent class (not for code generation). class PackNode : public VectorNode { public: @@ -1466,7 +1447,6 @@ class PackNode : public VectorNode { static PackNode* make(Node* s, uint vlen, BasicType bt); }; -//------------------------------PackBNode-------------------------------------- // Pack byte scalars into vector class PackBNode : public PackNode { public: @@ -1474,7 +1454,6 @@ class PackBNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackSNode-------------------------------------- // Pack short scalars into a vector class PackSNode : public PackNode { public: @@ -1483,7 +1462,6 @@ class PackSNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackINode-------------------------------------- // Pack integer scalars into a vector class PackINode : public PackNode { public: @@ -1492,7 +1470,6 @@ class PackINode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackLNode-------------------------------------- // Pack long scalars into a vector class PackLNode : public PackNode { public: @@ -1501,7 +1478,6 @@ class PackLNode : public PackNode { virtual int Opcode() const; }; -//------------------------------Pack2LNode------------------------------------- // Pack 2 long scalars into a vector class Pack2LNode : public PackNode { public: @@ -1509,7 +1485,6 @@ class Pack2LNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackFNode-------------------------------------- // Pack float scalars into vector class PackFNode : public PackNode { public: @@ -1518,7 +1493,6 @@ class PackFNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackDNode-------------------------------------- // Pack double scalars into a vector class PackDNode : public PackNode { public: @@ -1527,7 +1501,6 @@ class PackDNode : public PackNode { virtual int Opcode() const; }; -//------------------------------Pack2DNode------------------------------------- // Pack 2 double scalars into a vector class Pack2DNode : public PackNode { public: @@ -1535,7 +1508,10 @@ class Pack2DNode : public PackNode { virtual int Opcode() const; }; - +// Load the IOTA constant vector containing sequential indices starting from 0 +// and incrementing by 1 up to "VLENGTH - 1". So far, the first input is an int +// constant 0. For example, a 128-bit vector with int (32-bit) elements produces +// a vector like "[0, 1, 2, 3]". class VectorLoadConstNode : public VectorNode { public: VectorLoadConstNode(Node* in1, const TypeVect* vt) : VectorNode(in1, vt) {} @@ -1544,8 +1520,7 @@ class VectorLoadConstNode : public VectorNode { //========================Extract_Scalar_from_Vector=========================== -//------------------------------ExtractNode------------------------------------ -// Extract a scalar from a vector at position "pos" +// The base class for all extract nodes. class ExtractNode : public Node { public: ExtractNode(Node* src, Node* pos) : Node(nullptr, src, pos) {} @@ -1554,8 +1529,7 @@ class ExtractNode : public Node { static int opcode(BasicType bt); }; -//------------------------------ExtractBNode----------------------------------- -// Extract a byte from a vector at position "pos" +// Extract a byte from a vector at position "pos". class ExtractBNode : public ExtractNode { public: ExtractBNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1564,8 +1538,7 @@ class ExtractBNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractUBNode---------------------------------- -// Extract a boolean from a vector at position "pos" +// Extract a boolean from a vector at position "pos". class ExtractUBNode : public ExtractNode { public: ExtractUBNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1574,8 +1547,7 @@ class ExtractUBNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractCNode----------------------------------- -// Extract a char from a vector at position "pos" +// Extract a char from a vector at position "pos". class ExtractCNode : public ExtractNode { public: ExtractCNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1584,8 +1556,7 @@ class ExtractCNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractSNode----------------------------------- -// Extract a short from a vector at position "pos" +// Extract a short from a vector at position "pos". class ExtractSNode : public ExtractNode { public: ExtractSNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1594,8 +1565,7 @@ class ExtractSNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractINode----------------------------------- -// Extract an int from a vector at position "pos" +// Extract an int from a vector at position "pos". class ExtractINode : public ExtractNode { public: ExtractINode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1604,8 +1574,7 @@ class ExtractINode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractLNode----------------------------------- -// Extract a long from a vector at position "pos" +// Extract a long from a vector at position "pos". class ExtractLNode : public ExtractNode { public: ExtractLNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1614,8 +1583,7 @@ class ExtractLNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegL; } }; -//------------------------------ExtractFNode----------------------------------- -// Extract a float from a vector at position "pos" +// Extract a float from a vector at position "pos". class ExtractFNode : public ExtractNode { public: ExtractFNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1624,8 +1592,7 @@ class ExtractFNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegF; } }; -//------------------------------ExtractDNode----------------------------------- -// Extract a double from a vector at position "pos" +// Extract a double from a vector at position "pos". class ExtractDNode : public ExtractNode { public: ExtractDNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1634,7 +1601,6 @@ class ExtractDNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegD; } }; -//------------------------------MacroLogicVNode------------------------------- // Vector logical operations packing node. class MacroLogicVNode : public VectorNode { private: @@ -1653,6 +1619,8 @@ public: Node* mask, uint truth_table, const TypeVect* vt); }; +// Compare two vectors lane-wise using the specified predicate and produce a +// vector mask. class VectorMaskCmpNode : public VectorNode { private: BoolTest::mask _predicate; @@ -1697,6 +1665,8 @@ class VectorMaskWrapperNode : public VectorNode { Node* vector_mask() const { return in(2); } }; +// Test whether all or any lanes in the first input vector mask is true, +// based on the specified predicate. class VectorTestNode : public CmpNode { private: BoolTest::mask _predicate; @@ -1719,6 +1689,9 @@ class VectorTestNode : public CmpNode { } }; +// Blend two vectors based on a vector mask. For each lane, select the value +// from the first input vector (vec1) if the corresponding mask lane is set, +// otherwise select from the second input vector (vec2). class VectorBlendNode : public VectorNode { public: VectorBlendNode(Node* vec1, Node* vec2, Node* mask) @@ -1732,11 +1705,14 @@ class VectorBlendNode : public VectorNode { Node* vec_mask() const { return in(3); } }; +// Rearrange lane elements from a source vector under the control of a shuffle +// (indexes) vector. Each lane in the shuffle vector specifies which lane from +// the source vector to select for the corresponding output lane. All indexes +// are in the range [0, VLENGTH). class VectorRearrangeNode : public VectorNode { public: VectorRearrangeNode(Node* vec1, Node* shuffle) : VectorNode(vec1, shuffle, vec1->bottom_type()->is_vect()) { - // assert(mask->is_VectorMask(), "VectorBlendNode requires that third argument be a mask"); } virtual int Opcode() const; @@ -1744,9 +1720,12 @@ class VectorRearrangeNode : public VectorNode { Node* vec_shuffle() const { return in(2); } }; - -// Select elements from two source vectors based on the wrapped indexes held in -// the first vector. +// Select lane elements from two source vectors ("src1" and "src2") under the +// control of an "indexes" vector. The two source vectors are logically concatenated +// to form a table of 2*VLENGTH elements, where src1 occupies indices [0, VLENGTH) +// and src2 occupies indices [VLENGTH, 2*VLENGTH). Each lane in the "indexes" +// vector specifies which element from this table to select for the corresponding +// output lane. class SelectFromTwoVectorNode : public VectorNode { public: SelectFromTwoVectorNode(Node* indexes, Node* src1, Node* src2, const TypeVect* vt) @@ -1758,13 +1737,19 @@ public: virtual int Opcode() const; }; -// The target may not directly support the rearrange operation for an element type. In those cases, -// we can transform the rearrange into a different element type. For example, on x86 before AVX512, -// there is no rearrange instruction for short elements, what we will then do is to transform the -// shuffle vector into one that we can do byte rearrange such that it would provide the same -// result. This could have been done in VectorRearrangeNode during code emission but we eagerly -// expand this out because it is often the case that an index vector is reused in many rearrange -// operations. This allows the index preparation to be GVN-ed as well as hoisted out of loops, etc. +// Transform a shuffle vector when the target does not directly support rearrange +// operations for the original element type. In such cases, the rearrange can be +// transformed to use a different element type. +// +// For example, on x86 before AVX512, there are no rearrange instructions for short +// elements. The shuffle vector is transformed into one suitable for byte rearrange +// that produces the same result. This could have been done in VectorRearrangeNode +// during code emission, but we eagerly expand it out because shuffle vectors are +// often reused in many rearrange operations. This allows the transformation to be +// GVN-ed and hoisted out of loops. +// +// Input: Original shuffle vector (indices for the desired element type) +// Output: Transformed shuffle vector (indices for the supported element type) class VectorLoadShuffleNode : public VectorNode { public: VectorLoadShuffleNode(Node* in, const TypeVect* vt) @@ -1773,6 +1758,8 @@ class VectorLoadShuffleNode : public VectorNode { virtual int Opcode() const; }; +// Convert a "BVectMask" into a platform-specific vector mask (either "NVectMask" +// or "PVectMask"). class VectorLoadMaskNode : public VectorNode { public: VectorLoadMaskNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { @@ -1784,6 +1771,8 @@ class VectorLoadMaskNode : public VectorNode { Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Convert a platform-specific vector mask (either "NVectMask" or "PVectMask") +// into a "BVectMask". class VectorStoreMaskNode : public VectorNode { protected: VectorStoreMaskNode(Node* in1, ConINode* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} @@ -1795,6 +1784,8 @@ class VectorStoreMaskNode : public VectorNode { static VectorStoreMaskNode* make(PhaseGVN& gvn, Node* in, BasicType in_type, uint num_elem); }; +// Lane-wise type cast a vector mask to the given vector type. The vector length +// of the input and output must be the same. class VectorMaskCastNode : public VectorNode { public: VectorMaskCastNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { @@ -1831,6 +1822,8 @@ class VectorReinterpretNode : public VectorNode { virtual int Opcode() const; }; +// Lane-wise type cast a vector to the given vector type. This is the base +// class for all vector type cast operations. class VectorCastNode : public VectorNode { public: VectorCastNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) {} @@ -1843,6 +1836,7 @@ class VectorCastNode : public VectorNode { virtual Node* Identity(PhaseGVN* phase); }; +// Cast a byte vector to the given vector type. class VectorCastB2XNode : public VectorCastNode { public: VectorCastB2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1851,6 +1845,7 @@ class VectorCastB2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a short vector to the given vector type. class VectorCastS2XNode : public VectorCastNode { public: VectorCastS2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1859,6 +1854,7 @@ class VectorCastS2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast an int vector to the given vector type. class VectorCastI2XNode : public VectorCastNode { public: VectorCastI2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1867,6 +1863,7 @@ class VectorCastI2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a long vector to the given vector type. class VectorCastL2XNode : public VectorCastNode { public: VectorCastL2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1875,6 +1872,7 @@ class VectorCastL2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a float vector to the given vector type. class VectorCastF2XNode : public VectorCastNode { public: VectorCastF2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1883,6 +1881,7 @@ class VectorCastF2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a double vector to the given vector type. class VectorCastD2XNode : public VectorCastNode { public: VectorCastD2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1891,6 +1890,7 @@ class VectorCastD2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a half float vector to float vector type. class VectorCastHF2FNode : public VectorCastNode { public: VectorCastHF2FNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1899,6 +1899,7 @@ class VectorCastHF2FNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a float vector to a half float vector type. class VectorCastF2HFNode : public VectorCastNode { public: VectorCastF2HFNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1907,8 +1908,12 @@ class VectorCastF2HFNode : public VectorCastNode { virtual int Opcode() const; }; -// So far, VectorUCastNode can only be used in Vector API unsigned extensions -// between integral types. E.g., extending byte to float is not supported now. +// Unsigned vector cast operations can only be used in unsigned (zero) +// extensions between integral types so far. E.g., extending byte to +// float is not supported now. + +// Unsigned cast a byte vector to the given vector type with short, int, +// or long element type. class VectorUCastB2XNode : public VectorCastNode { public: VectorUCastB2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1920,6 +1925,8 @@ class VectorUCastB2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Unsigned cast a short vector to the given vector type with int or long +// element type. class VectorUCastS2XNode : public VectorCastNode { public: VectorUCastS2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1930,6 +1937,7 @@ class VectorUCastS2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Unsigned cast an int vector to the given vector type with long element type. class VectorUCastI2XNode : public VectorCastNode { public: VectorUCastI2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1939,6 +1947,7 @@ class VectorUCastI2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Vector round float to nearest integer. class RoundVFNode : public VectorNode { public: RoundVFNode(Node* in, const TypeVect* vt) :VectorNode(in, vt) { @@ -1947,6 +1956,7 @@ class RoundVFNode : public VectorNode { virtual int Opcode() const; }; +// Vector round double to nearest integer. class RoundVDNode : public VectorNode { public: RoundVDNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { @@ -1955,6 +1965,7 @@ class RoundVDNode : public VectorNode { virtual int Opcode() const; }; +// Insert a new value into a vector lane at the specified position. class VectorInsertNode : public VectorNode { public: VectorInsertNode(Node* vsrc, Node* new_val, ConINode* pos, const TypeVect* vt) : VectorNode(vsrc, new_val, (Node*)pos, vt) { @@ -1968,6 +1979,9 @@ class VectorInsertNode : public VectorNode { static Node* make(Node* vec, Node* new_val, int position, PhaseGVN& gvn); }; +// Box a vector value into a Vector API object (e.g., IntMaxVector). +// This is a macro node that gets expanded during vector optimization +// phase. class VectorBoxNode : public Node { private: const TypeInstPtr* const _box_type; @@ -1995,6 +2009,8 @@ class VectorBoxNode : public Node { static const TypeFunc* vec_box_type(const TypeInstPtr* box_type); }; +// Allocate storage for boxing a vector value. This is used during vector +// box expansion. class VectorBoxAllocateNode : public CallStaticJavaNode { public: VectorBoxAllocateNode(Compile* C, const TypeInstPtr* vbox_type) @@ -2009,6 +2025,9 @@ class VectorBoxAllocateNode : public CallStaticJavaNode { #endif // !PRODUCT }; +// Unbox a Vector API object (e.g., IntMaxVector) to extract the underlying +// vector value. This is a macro node expanded during vector optimization +// phase. class VectorUnboxNode : public VectorNode { protected: uint size_of() const { return sizeof(*this); } @@ -2027,6 +2046,7 @@ class VectorUnboxNode : public VectorNode { Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Lane-wise right rotation of the first input by the second input. class RotateRightVNode : public VectorNode { public: RotateRightVNode(Node* in1, Node* in2, const TypeVect* vt) @@ -2036,6 +2056,7 @@ public: Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Lane-wise left rotation of the first input by the second input. class RotateLeftVNode : public VectorNode { public: RotateLeftVNode(Node* in1, Node* in2, const TypeVect* vt) @@ -2045,6 +2066,7 @@ public: Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Count the number of leading zeros in each lane of the input. class CountLeadingZerosVNode : public VectorNode { public: CountLeadingZerosVNode(Node* in, const TypeVect* vt) @@ -2056,6 +2078,7 @@ class CountLeadingZerosVNode : public VectorNode { virtual int Opcode() const; }; +// Count the number of trailing zeros in each lane of the input. class CountTrailingZerosVNode : public VectorNode { public: CountTrailingZerosVNode(Node* in, const TypeVect* vt) @@ -2067,6 +2090,7 @@ class CountTrailingZerosVNode : public VectorNode { virtual int Opcode() const; }; +// Reverse the bits within each lane (e.g., 0b10110010 becomes 0b01001101). class ReverseVNode : public VectorNode { public: ReverseVNode(Node* in, const TypeVect* vt) @@ -2076,6 +2100,7 @@ public: virtual int Opcode() const; }; +// Reverse the byte order within each lane (e.g., 0x12345678 becomes 0x78563412). class ReverseBytesVNode : public VectorNode { public: ReverseBytesVNode(Node* in, const TypeVect* vt) @@ -2085,6 +2110,7 @@ public: virtual int Opcode() const; }; +// Vector signum float. class SignumVFNode : public VectorNode { public: SignumVFNode(Node* in1, Node* zero, Node* one, const TypeVect* vt) @@ -2093,6 +2119,7 @@ public: virtual int Opcode() const; }; +// Vector signum double. class SignumVDNode : public VectorNode { public: SignumVDNode(Node* in1, Node* zero, Node* one, const TypeVect* vt) @@ -2101,6 +2128,8 @@ public: virtual int Opcode() const; }; +// Compress (extract and pack) bits in each lane of the first input +// based on the mask input. class CompressBitsVNode : public VectorNode { public: CompressBitsVNode(Node* in, Node* mask, const TypeVect* vt) @@ -2108,6 +2137,8 @@ public: virtual int Opcode() const; }; +// Expand (deposit) bits in each lane of the first input based on the +// mask input. class ExpandBitsVNode : public VectorNode { public: ExpandBitsVNode(Node* in, Node* mask, const TypeVect* vt) From ca6925ec6bf44cf7d4704becc194389e4c87b74f Mon Sep 17 00:00:00 2001 From: David Holmes Date: Tue, 20 Jan 2026 06:18:07 +0000 Subject: [PATCH 068/126] 8370112: Remove VM_Version::supports_fast_class_init_checks() in platform-specific code Reviewed-by: shade, fyang --- .../cpu/aarch64/sharedRuntime_aarch64.cpp | 29 ++++++++------- .../cpu/aarch64/templateTable_aarch64.cpp | 9 ++--- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 35 +++++++++--------- src/hotspot/cpu/ppc/templateTable_ppc_64.cpp | 9 ++--- src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 29 ++++++++------- src/hotspot/cpu/riscv/templateTable_riscv.cpp | 9 ++--- src/hotspot/cpu/s390/sharedRuntime_s390.cpp | 31 ++++++++-------- src/hotspot/cpu/s390/templateTable_s390.cpp | 9 ++--- src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 36 +++++++++---------- src/hotspot/cpu/x86/templateTable_x86.cpp | 9 ++--- 10 files changed, 102 insertions(+), 103 deletions(-) diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 89ae6bc10e0..73b631029a0 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.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. * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -722,22 +722,20 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ ldrh(rscratch1, Address(rmethod, Method::access_flags_offset())); - __ andsw(zr, rscratch1, JVM_ACC_STATIC); - __ br(Assembler::EQ, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ ldrh(rscratch1, Address(rmethod, Method::access_flags_offset())); + __ andsw(zr, rscratch1, JVM_ACC_STATIC); + __ br(Assembler::EQ, L_skip_barrier); // non-static - __ load_method_holder(rscratch2, rmethod); - __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier); - __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); + __ load_method_holder(rscratch2, rmethod); + __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier); + __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm); @@ -1508,7 +1506,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // SVC, HVC, or SMC. Make it a NOP. __ nop(); - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; __ mov_metadata(rscratch2, method->method_holder()); // InstanceKlass* __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index cde142b39ac..07b469650f0 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.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. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2290,7 +2290,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ subs(zr, temp, (int) code); // have we resolved this bytecode? // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); __ br(Assembler::NE, L_clinit_barrier_slow); __ ldr(temp, Address(Rcache, in_bytes(ResolvedMethodEntry::method_offset()))); __ load_method_holder(temp, temp); @@ -2340,8 +2341,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ subs(zr, temp, (int) code); // have we resolved this bytecode? // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = temp; __ br(Assembler::NE, L_clinit_barrier_slow); diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 4e427ace404..4eb2028f529 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_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. * @@ -1237,26 +1237,24 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ lhz(R0, in_bytes(Method::access_flags_offset()), R19_method); - __ andi_(R0, R0, JVM_ACC_STATIC); - __ beq(CR0, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ lhz(R0, in_bytes(Method::access_flags_offset()), R19_method); + __ andi_(R0, R0, JVM_ACC_STATIC); + __ beq(CR0, L_skip_barrier); // non-static - Register klass = R11_scratch1; - __ load_method_holder(klass, R19_method); - __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/); + Register klass = R11_scratch1; + __ load_method_holder(klass, R19_method); + __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/); - __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0); - __ mtctr(klass); - __ bctr(); + __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0); + __ mtctr(klass); + __ bctr(); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm, /* tmp register*/ ic_klass, /* tmp register*/ receiver_klass, /* tmp register*/ code); @@ -2210,7 +2208,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // -------------------------------------------------------------------------- vep_start_pc = (intptr_t)__ pc(); - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; Register klass = r_temp_1; // Notify OOP recorder (don't need the relocation) diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 8d61ba1b2d7..8a3af748fa1 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2199,7 +2199,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, Register Rca __ isync(); // Order load wrt. succeeding loads. // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register method = Rscratch; const Register klass = Rscratch; @@ -2244,8 +2245,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, Register Rcac __ isync(); // Order load wrt. succeeding loads. // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = R4_ARG2; // InterpreterRuntime::resolve_get_put sets field_holder and finally release-stores put_code. diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index eeb6fad1b59..44a6f6c0dc0 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.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. * Copyright (c) 2014, 2020, Red Hat Inc. 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. @@ -637,22 +637,20 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ load_unsigned_short(t0, Address(xmethod, Method::access_flags_offset())); - __ test_bit(t1, t0, exact_log2(JVM_ACC_STATIC)); - __ beqz(t1, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ load_unsigned_short(t0, Address(xmethod, Method::access_flags_offset())); + __ test_bit(t1, t0, exact_log2(JVM_ACC_STATIC)); + __ beqz(t1, L_skip_barrier); // non-static - __ load_method_holder(t1, xmethod); - __ clinit_barrier(t1, t0, &L_skip_barrier); - __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); + __ load_method_holder(t1, xmethod); + __ clinit_barrier(t1, t0, &L_skip_barrier); + __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm); @@ -1443,7 +1441,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ nop(); // 4 bytes } - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; __ mov_metadata(t1, method->method_holder()); // InstanceKlass* __ clinit_barrier(t1, t0, &L_skip_barrier); diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index ca41583e4bc..5a3644f70bb 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.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. * Copyright (c) 2014, Red Hat Inc. 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. @@ -2192,7 +2192,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ mv(t0, (int) code); // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); __ bne(temp, t0, L_clinit_barrier_slow); // have we resolved this bytecode? __ ld(temp, Address(Rcache, in_bytes(ResolvedMethodEntry::method_offset()))); __ load_method_holder(temp, temp); @@ -2243,8 +2244,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ mv(t0, (int) code); // have we resolved this bytecode? // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = temp; __ bne(temp, t0, L_clinit_barrier_slow); diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 5b6f7dcd984..00a830a80cd 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1567,7 +1567,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, //--------------------------------------------------------------------- wrapper_VEPStart = __ offset(); - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; Register klass = Z_R1_scratch; // Notify OOP recorder (don't need the relocation) @@ -2378,24 +2379,22 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ testbit_ushort(Address(Z_method, Method::access_flags_offset()), JVM_ACC_STATIC_BIT); - __ z_bfalse(L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ testbit_ushort(Address(Z_method, Method::access_flags_offset()), JVM_ACC_STATIC_BIT); + __ z_bfalse(L_skip_barrier); // non-static - Register klass = Z_R11; - __ load_method_holder(klass, Z_method); - __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/); + Register klass = Z_R11; + __ load_method_holder(klass, Z_method); + __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/); - __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub()); - __ z_br(klass); + __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub()); + __ z_br(klass); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); return; diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 4e8fdf275e4..647915ef4fa 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2377,7 +2377,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ z_cli(Address(Rcache, bc_offset), code); // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register method = Z_R1_scratch; const Register klass = Z_R1_scratch; __ z_brne(L_clinit_barrier_slow); @@ -2427,8 +2428,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ z_cli(Address(cache, code_offset), code); // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = index; __ z_brne(L_clinit_barrier_slow); diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 5a4a5b1809e..bbd43c1a0e8 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.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 @@ -1043,26 +1043,24 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; - Register method = rbx; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; + Register method = rbx; - { // Bypass the barrier for non-static methods - Register flags = rscratch1; - __ load_unsigned_short(flags, Address(method, Method::access_flags_offset())); - __ testl(flags, JVM_ACC_STATIC); - __ jcc(Assembler::zero, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + Register flags = rscratch1; + __ load_unsigned_short(flags, Address(method, Method::access_flags_offset())); + __ testl(flags, JVM_ACC_STATIC); + __ jcc(Assembler::zero, L_skip_barrier); // non-static - Register klass = rscratch1; - __ load_method_holder(klass, method); - __ clinit_barrier(klass, &L_skip_barrier /*L_fast_path*/); + Register klass = rscratch1; + __ load_method_holder(klass, method); + __ clinit_barrier(klass, &L_skip_barrier /*L_fast_path*/); - __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path + __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm); @@ -1904,7 +1902,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, int vep_offset = ((intptr_t)__ pc()) - start; - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; Register klass = r10; __ mov_metadata(klass, method->method_holder()); // InstanceKlass* @@ -3602,4 +3601,3 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { } #endif // INCLUDE_JFR - diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 42392b84833..db7749ec482 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.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 @@ -2216,7 +2216,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ cmpl(temp, code); // have we resolved this bytecode? // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register method = temp; const Register klass = temp; @@ -2264,8 +2265,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ cmpl(temp, code); // have we resolved this bytecode? // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = temp; __ jcc(Assembler::notEqual, L_clinit_barrier_slow); From e45f5656bc90421c9acb0cbf87164162039ddf81 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 20 Jan 2026 07:10:46 +0000 Subject: [PATCH 069/126] 8373650: Test "javax/swing/JMenuItem/6458123/ManualBug6458123.java" fails because the check icons are not aligned properly as expected Reviewed-by: tr, dnguyen --- .../plaf/windows/WindowsIconFactory.java | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 915a361a3a1..91c2cbcd61d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.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 @@ -905,10 +905,46 @@ public final class WindowsIconFactory implements Serializable XPStyle xp = XPStyle.getXP(); if (xp != null) { Skin skin = xp.getSkin(c, part); - if (icon == null || icon.getIconHeight() <= 16) { - skin.paintSkin(g, x + OFFSET, y + OFFSET, state); + if (WindowsGraphicsUtils.isLeftToRight(c)) { + if (icon == null || icon.getIconHeight() <= 16) { + skin.paintSkin(g, x + OFFSET, y + OFFSET, state); + } else { + skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state); + } } else { - skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state); + if (icon == null) { + skin.paintSkin(g, x + 4 * OFFSET, y + OFFSET, state); + } else { + int ycoord = (icon.getIconHeight() <= 16) + ? y + OFFSET + : (y + icon.getIconHeight() / 2); + if (icon.getIconWidth() <= 8) { + skin.paintSkin(g, x + OFFSET, ycoord, state); + } else if (icon.getIconWidth() <= 16) { + if (menuItem.getText().isEmpty()) { + skin.paintSkin(g, + (menuItem.getAccelerator() != null) + ? (x + 2 * OFFSET) : (x + 3 * OFFSET), + ycoord, state); + } else { + skin.paintSkin(g, + (type == JRadioButtonMenuItem.class) + ? (x + 4 * OFFSET) : (x + 3 * OFFSET), + ycoord, state); + } + } else { + if (menuItem.getText().isEmpty() + || menuItem.getAccelerator() != null) { + skin.paintSkin(g, + (type == JRadioButtonMenuItem.class) + ? (x + 3 * OFFSET) : (x + 4 * OFFSET), + ycoord, state); + } else { + skin.paintSkin(g, x + 7 * OFFSET, + ycoord, state); + } + } + } } } } From d9db4fb36e4f90546dc3fc19b5923b8be6a2f518 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 08:01:54 +0000 Subject: [PATCH 070/126] 8373894: G1: Count evacuation-failed garbage collections in gc cpu usage Reviewed-by: iwalulya, kbarrett --- src/hotspot/share/gc/g1/g1Policy.cpp | 11 ++++------- src/hotspot/share/gc/g1/g1Policy.hpp | 5 ++--- .../jtreg/gc/stress/TestMultiThreadStressRSet.java | 6 +++--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 6eef6cbfa87..2847a25c5b4 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -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 @@ -943,7 +943,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar phase_times()->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSToYoungGenCards)); } - record_pause(this_pause, start_time_sec, end_time_sec, allocation_failure); + record_pause(this_pause, start_time_sec, end_time_sec); if (G1GCPauseTypeHelper::is_last_young_pause(this_pause)) { assert(!G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause), @@ -1389,16 +1389,13 @@ void G1Policy::update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_t void G1Policy::record_pause(G1GCPauseType gc_type, double start, - double end, - bool allocation_failure) { + double end) { // Manage the MMU tracker. For some reason it ignores Full GCs. if (gc_type != G1GCPauseType::FullGC) { _mmu_tracker->add_pause(start, end); } - if (!allocation_failure) { - update_gc_pause_time_ratios(gc_type, start, end); - } + update_gc_pause_time_ratios(gc_type, start, end); update_time_to_mixed_tracking(gc_type, start, end); diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 72fdc6deb5b..cf12a7a8027 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -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 @@ -275,8 +275,7 @@ private: // Record the given STW pause with the given start and end times (in s). void record_pause(G1GCPauseType gc_type, double start, - double end, - bool allocation_failure = false); + double end); void update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_sec, double end_sec); diff --git a/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java b/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java index a323ebd945a..ffd5203dfb8 100644 --- a/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java +++ b/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java @@ -44,15 +44,15 @@ import jdk.test.lib.Utils; * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=1 -Xlog:gc + * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=1 -Xlog:gc -XX:-UseGCOverheadLimit * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 gc.stress.TestMultiThreadStressRSet 10 4 * * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc + * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc -XX:-UseGCOverheadLimit * -Xmx1G -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=1000 gc.stress.TestMultiThreadStressRSet 60 16 * * @run main/othervm/timeout=1200 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc + * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc -XX:-UseGCOverheadLimit * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 gc.stress.TestMultiThreadStressRSet 600 32 */ public class TestMultiThreadStressRSet { From c5f288e2ae2ebe6ee4a0d39d91348f746bd0e353 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Tue, 20 Jan 2026 09:30:12 +0000 Subject: [PATCH 071/126] 8373253: Re-work InjectGCWorkerCreationFailure for future changes Reviewed-by: stefank, tschatzl, iwalulya, sjohanss --- src/hotspot/share/gc/shared/workerThread.cpp | 16 +++++++++++++++- src/hotspot/share/gc/shared/workerThread.hpp | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 7a9404a195a..e4831d25d26 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -96,8 +96,22 @@ void WorkerThreads::initialize_workers() { } } +bool WorkerThreads::allow_inject_creation_failure() const { + if (!is_init_completed()) { + // Never allow creation failures during VM init + return false; + } + + if (_created_workers == 0) { + // Never allow creation failures of the first worker, it will cause the VM to exit + return false; + } + + return true; +} + WorkerThread* WorkerThreads::create_worker(uint name_suffix) { - if (is_init_completed() && InjectGCWorkerCreationFailure) { + if (InjectGCWorkerCreationFailure && allow_inject_creation_failure()) { return nullptr; } diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp index a1f7282abe4..003ce8a2959 100644 --- a/src/hotspot/share/gc/shared/workerThread.hpp +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -104,6 +104,7 @@ public: WorkerThreads(const char* name, uint max_workers); void initialize_workers(); + bool allow_inject_creation_failure() const; uint max_workers() const { return _max_workers; } uint created_workers() const { return _created_workers; } From afbb3a041545ea11ee1514d329c1a6cc4cb969d2 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 10:31:22 +0000 Subject: [PATCH 072/126] 8375620: G1: Convert G1CardTableClaimTable to use Atomic Reviewed-by: kbarrett, shade --- src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp | 8 ++++---- src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp | 5 +++-- .../share/gc/g1/g1CardTableClaimTable.inline.hpp | 11 +++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp b/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp index e0cadbdd907..d8cabaa00a4 100644 --- a/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp +++ b/src/hotspot/share/gc/g1/g1CardTableClaimTable.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 @@ -44,20 +44,20 @@ G1CardTableClaimTable::~G1CardTableClaimTable() { void G1CardTableClaimTable::initialize(uint max_reserved_regions) { assert(_card_claims == nullptr, "Must not be initialized twice"); - _card_claims = NEW_C_HEAP_ARRAY(uint, max_reserved_regions, mtGC); + _card_claims = NEW_C_HEAP_ARRAY(Atomic, max_reserved_regions, mtGC); _max_reserved_regions = max_reserved_regions; reset_all_to_unclaimed(); } void G1CardTableClaimTable::reset_all_to_unclaimed() { for (uint i = 0; i < _max_reserved_regions; i++) { - _card_claims[i] = 0; + _card_claims[i].store_relaxed(0); } } void G1CardTableClaimTable::reset_all_to_claimed() { for (uint i = 0; i < _max_reserved_regions; i++) { - _card_claims[i] = (uint)G1HeapRegion::CardsPerRegion; + _card_claims[i].store_relaxed((uint)G1HeapRegion::CardsPerRegion); } } diff --git a/src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp b/src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp index 4f524b83f97..822ef45c722 100644 --- a/src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp +++ b/src/hotspot/share/gc/g1/g1CardTableClaimTable.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 @@ -27,6 +27,7 @@ #include "gc/g1/g1CardTable.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class G1HeapRegionClosure; @@ -45,7 +46,7 @@ class G1CardTableClaimTable : public CHeapObj { // Card table iteration claim values for every heap region, from 0 (completely unclaimed) // to (>=) G1HeapRegion::CardsPerRegion (completely claimed). - uint volatile* _card_claims; + Atomic* _card_claims; uint _cards_per_chunk; // For conversion between card index and chunk index. diff --git a/src/hotspot/share/gc/g1/g1CardTableClaimTable.inline.hpp b/src/hotspot/share/gc/g1/g1CardTableClaimTable.inline.hpp index d682f0d17ae..35b2484982c 100644 --- a/src/hotspot/share/gc/g1/g1CardTableClaimTable.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardTableClaimTable.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,26 +29,25 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" -#include "runtime/atomicAccess.hpp" bool G1CardTableClaimTable::has_unclaimed_cards(uint region) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - return AtomicAccess::load(&_card_claims[region]) < G1HeapRegion::CardsPerRegion; + return _card_claims[region].load_relaxed() < G1HeapRegion::CardsPerRegion; } void G1CardTableClaimTable::reset_to_unclaimed(uint region) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - AtomicAccess::store(&_card_claims[region], 0u); + _card_claims[region].store_relaxed(0u); } uint G1CardTableClaimTable::claim_cards(uint region, uint increment) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - return AtomicAccess::fetch_then_add(&_card_claims[region], increment, memory_order_relaxed); + return _card_claims[region].fetch_then_add(increment, memory_order_relaxed); } uint G1CardTableClaimTable::claim_chunk(uint region) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - return AtomicAccess::fetch_then_add(&_card_claims[region], cards_per_chunk(), memory_order_relaxed); + return _card_claims[region].fetch_then_add(cards_per_chunk(), memory_order_relaxed); } uint G1CardTableClaimTable::claim_all_cards(uint region) { From 8c615190e69ee6e521990595fc23197f38ad6f14 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 10:34:00 +0000 Subject: [PATCH 073/126] 8375624: G1: Convert G1JavaThreadsListClaimer to use Atomic Reviewed-by: kbarrett, shade --- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 5 +++-- src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index aff7166d391..a0104d04f4f 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.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 @@ -54,6 +54,7 @@ #include "memory/allocation.hpp" #include "memory/iterator.hpp" #include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/threadSMR.hpp" #include "utilities/bitMap.hpp" @@ -124,7 +125,7 @@ class G1JavaThreadsListClaimer : public StackObj { ThreadsListHandle _list; uint _claim_step; - volatile uint _cur_claim; + Atomic _cur_claim; // Attempts to claim _claim_step JavaThreads, returning an array of claimed // JavaThread* with count elements. Returns null (and a zero count) if there diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index abd61e72d57..577450b3be9 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.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 @@ -41,7 +41,6 @@ #include "gc/shared/markBitMap.inline.hpp" #include "gc/shared/taskqueue.inline.hpp" #include "oops/stackChunkOop.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/threadSMR.inline.hpp" #include "utilities/bitMap.inline.hpp" @@ -53,10 +52,10 @@ inline bool G1STWIsAliveClosure::do_object_b(oop p) { inline JavaThread* const* G1JavaThreadsListClaimer::claim(uint& count) { count = 0; - if (AtomicAccess::load(&_cur_claim) >= _list.length()) { + if (_cur_claim.load_relaxed() >= _list.length()) { return nullptr; } - uint claim = AtomicAccess::fetch_then_add(&_cur_claim, _claim_step); + uint claim = _cur_claim.fetch_then_add(_claim_step); if (claim >= _list.length()) { return nullptr; } From fe102918dd4f33ba030c4c4301a676ac8497fd90 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 10:34:16 +0000 Subject: [PATCH 074/126] 8375630: G1: Convert G1ConcurrentMark to use Atomic Reviewed-by: kbarrett, shade --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 27 ++++++++++---------- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 9 ++++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 456d543fa10..1077939f953 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -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 @@ -67,7 +67,6 @@ #include "nmt/memTracker.hpp" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" @@ -148,25 +147,25 @@ bool G1CMMarkStack::initialize() { } G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::ChunkAllocator::allocate_new_chunk() { - if (_size >= _max_capacity) { + if (_size.load_relaxed() >= _max_capacity) { return nullptr; } - size_t cur_idx = AtomicAccess::fetch_then_add(&_size, 1u); + size_t cur_idx = _size.fetch_then_add(1u); if (cur_idx >= _max_capacity) { return nullptr; } size_t bucket = get_bucket(cur_idx); - if (AtomicAccess::load_acquire(&_buckets[bucket]) == nullptr) { + if (_buckets[bucket].load_acquire() == nullptr) { if (!_should_grow) { // Prefer to restart the CM. return nullptr; } MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); - if (AtomicAccess::load_acquire(&_buckets[bucket]) == nullptr) { + if (_buckets[bucket].load_acquire() == nullptr) { size_t desired_capacity = bucket_size(bucket) * 2; if (!try_expand_to(desired_capacity)) { return nullptr; @@ -175,7 +174,7 @@ G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::ChunkAllocator::allocate_new_ } size_t bucket_idx = get_bucket_index(cur_idx); - TaskQueueEntryChunk* result = ::new (&_buckets[bucket][bucket_idx]) TaskQueueEntryChunk; + TaskQueueEntryChunk* result = ::new (&_buckets[bucket].load_relaxed()[bucket_idx]) TaskQueueEntryChunk; result->next = nullptr; return result; } @@ -197,10 +196,10 @@ bool G1CMMarkStack::ChunkAllocator::initialize(size_t initial_capacity, size_t m _max_capacity = max_capacity; _num_buckets = get_bucket(_max_capacity) + 1; - _buckets = NEW_C_HEAP_ARRAY(TaskQueueEntryChunk*, _num_buckets, mtGC); + _buckets = NEW_C_HEAP_ARRAY(Atomic, _num_buckets, mtGC); for (size_t i = 0; i < _num_buckets; i++) { - _buckets[i] = nullptr; + _buckets[i].store_relaxed(nullptr); } size_t new_capacity = bucket_size(0); @@ -240,9 +239,9 @@ G1CMMarkStack::ChunkAllocator::~ChunkAllocator() { } for (size_t i = 0; i < _num_buckets; i++) { - if (_buckets[i] != nullptr) { - MmapArrayAllocator::free(_buckets[i], bucket_size(i)); - _buckets[i] = nullptr; + if (_buckets[i].load_relaxed() != nullptr) { + MmapArrayAllocator::free(_buckets[i].load_relaxed(), bucket_size(i)); + _buckets[i].store_relaxed(nullptr); } } @@ -259,7 +258,7 @@ bool G1CMMarkStack::ChunkAllocator::reserve(size_t new_capacity) { // and the new capacity (new_capacity). This step ensures that there are no gaps in the // array and that the capacity accurately reflects the reserved memory. for (; i <= highest_bucket; i++) { - if (AtomicAccess::load_acquire(&_buckets[i]) != nullptr) { + if (_buckets[i].load_acquire() != nullptr) { continue; // Skip over already allocated buckets. } @@ -279,7 +278,7 @@ bool G1CMMarkStack::ChunkAllocator::reserve(size_t new_capacity) { return false; } _capacity += bucket_capacity; - AtomicAccess::release_store(&_buckets[i], bucket_base); + _buckets[i].release_store(bucket_base); } return true; } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 752082ce629..1a0cfcd8caa 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.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 @@ -37,6 +37,7 @@ #include "gc/shared/workerThread.hpp" #include "gc/shared/workerUtils.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/compilerWarnings.hpp" #include "utilities/numberSeq.hpp" @@ -172,9 +173,9 @@ private: size_t _capacity; size_t _num_buckets; bool _should_grow; - TaskQueueEntryChunk* volatile* _buckets; + Atomic* _buckets; char _pad0[DEFAULT_PADDING_SIZE]; - volatile size_t _size; + Atomic _size; char _pad4[DEFAULT_PADDING_SIZE - sizeof(size_t)]; size_t bucket_size(size_t bucket) { @@ -212,7 +213,7 @@ private: bool initialize(size_t initial_capacity, size_t max_capacity); void reset() { - _size = 0; + _size.store_relaxed(0); _should_grow = false; } From 3cc713fa296dfb59bbc03f2cfd4fc7d8f4b44be2 Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Tue, 20 Jan 2026 11:40:19 +0000 Subject: [PATCH 075/126] 8374945: Avoid fstat in os::open Reviewed-by: dholmes, jsjolen, redestad --- src/hotspot/os/linux/os_linux.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 48529c6ce17..7190845a8ba 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4963,9 +4963,14 @@ int os::open(const char *path, int oflag, int mode) { oflag |= O_CLOEXEC; int fd = ::open(path, oflag, mode); - if (fd == -1) return -1; + // No further checking is needed if open() returned an error or + // access mode is not read only. + if (fd == -1 || (oflag & O_ACCMODE) != O_RDONLY) { + return fd; + } - //If the open succeeded, the file might still be a directory + // If the open succeeded and is read only, the file might be a directory + // which the JVM doesn't allow to be read. { struct stat buf; int ret = ::fstat(fd, &buf); From 037040129e82958bd023e0b24d962627e8653710 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 13:22:25 +0000 Subject: [PATCH 076/126] 8375643: G1: Convert G1RegionMarkStatsCache to use Atomic Reviewed-by: shade, kbarrett --- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 8 +++---- src/hotspot/share/gc/g1/g1FullCollector.hpp | 4 ++-- .../share/gc/g1/g1RegionMarkStatsCache.hpp | 24 ++++++++++++------- .../gc/g1/g1RegionMarkStatsCache.inline.hpp | 12 ++++------ 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 1a0cfcd8caa..7ea9151c6f1 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -557,14 +557,14 @@ public: // mark_in_bitmap call. Updates various statistics data. void add_to_liveness(uint worker_id, oop const obj, size_t size); // Did the last marking find a live object between bottom and TAMS? - bool contains_live_object(uint region) const { return _region_mark_stats[region]._live_words != 0; } + bool contains_live_object(uint region) const { return _region_mark_stats[region].live_words() != 0; } // Live bytes in the given region as determined by concurrent marking, i.e. the amount of // live bytes between bottom and TAMS. - size_t live_bytes(uint region) const { return _region_mark_stats[region]._live_words * HeapWordSize; } + size_t live_bytes(uint region) const { return _region_mark_stats[region].live_words() * HeapWordSize; } // Set live bytes for concurrent marking. - void set_live_bytes(uint region, size_t live_bytes) { _region_mark_stats[region]._live_words = live_bytes / HeapWordSize; } + void set_live_bytes(uint region, size_t live_bytes) { _region_mark_stats[region]._live_words.store_relaxed(live_bytes / HeapWordSize); } // Approximate number of incoming references found during marking. - size_t incoming_refs(uint region) const { return _region_mark_stats[region]._incoming_refs; } + size_t incoming_refs(uint region) const { return _region_mark_stats[region].incoming_refs(); } // Update the TAMS for the given region to the current top. inline void update_top_at_mark_start(G1HeapRegion* r); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index ed8225fc004..1fb3af17032 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -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 @@ -122,7 +122,7 @@ public: ReferenceProcessor* reference_processor(); size_t live_words(uint region_index) const { assert(region_index < _heap->max_num_regions(), "sanity"); - return _live_stats[region_index]._live_words; + return _live_stats[region_index].live_words(); } void before_marking_update_attribute_table(G1HeapRegion* hr); diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index 3c1a6ed4667..4dcdd33846e 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.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 "oops/oop.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/pair.hpp" @@ -40,20 +41,23 @@ // * the number of incoming references found during marking. This is an approximate // value because we do not mark through all objects. struct G1RegionMarkStats { - size_t _live_words; - size_t _incoming_refs; + Atomic _live_words; + Atomic _incoming_refs; // Clear all members. void clear() { - _live_words = 0; - _incoming_refs = 0; + _live_words.store_relaxed(0); + _incoming_refs.store_relaxed(0); } // Clear all members after a marking overflow. Only needs to clear the number of // incoming references as all objects will be rescanned, while the live words are // gathered whenever a thread can mark an object, which is synchronized. void clear_during_overflow() { - _incoming_refs = 0; + _incoming_refs.store_relaxed(0); } + + size_t live_words() const { return _live_words.load_relaxed(); } + size_t incoming_refs() const { return _incoming_refs.load_relaxed(); } }; // Per-marking thread cache for the region mark statistics. @@ -112,12 +116,16 @@ public: void add_live_words(oop obj); void add_live_words(uint region_idx, size_t live_words) { G1RegionMarkStatsCacheEntry* const cur = find_for_add(region_idx); - cur->_stats._live_words += live_words; + // This method is only ever called single-threaded, so we do not need atomic + // update here. + cur->_stats._live_words.store_relaxed(cur->_stats.live_words() + live_words); } void inc_incoming_refs(uint region_idx) { G1RegionMarkStatsCacheEntry* const cur = find_for_add(region_idx); - cur->_stats._incoming_refs++; + // This method is only ever called single-threaded, so we do not need atomic + // update here. + cur->_stats._incoming_refs.store_relaxed(cur->_stats.incoming_refs() + 1u); } void reset(uint region_idx) { diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.hpp index 6b0ebb34e0d..71cd33e71ae 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.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,8 +27,6 @@ #include "gc/g1/g1RegionMarkStatsCache.hpp" -#include "runtime/atomicAccess.hpp" - inline G1RegionMarkStatsCache::G1RegionMarkStatsCacheEntry* G1RegionMarkStatsCache::find_for_add(uint region_idx) { uint const cache_idx = hash(region_idx); @@ -46,12 +44,12 @@ inline G1RegionMarkStatsCache::G1RegionMarkStatsCacheEntry* G1RegionMarkStatsCac inline void G1RegionMarkStatsCache::evict(uint idx) { G1RegionMarkStatsCacheEntry* cur = &_cache[idx]; - if (cur->_stats._live_words != 0) { - AtomicAccess::add(&_target[cur->_region_idx]._live_words, cur->_stats._live_words); + if (cur->_stats.live_words() != 0) { + _target[cur->_region_idx]._live_words.add_then_fetch(cur->_stats.live_words()); } - if (cur->_stats._incoming_refs != 0) { - AtomicAccess::add(&_target[cur->_region_idx]._incoming_refs, cur->_stats._incoming_refs); + if (cur->_stats.incoming_refs() != 0) { + _target[cur->_region_idx]._incoming_refs.add_then_fetch(cur->_stats.incoming_refs()); } cur->clear(); From 5ba91fed345b078a67ad6bead1d8893bd9289f58 Mon Sep 17 00:00:00 2001 From: Christian Heilmann Date: Tue, 20 Jan 2026 15:00:14 +0000 Subject: [PATCH 077/126] 8297191: [macos] Printing a page range with starting page > 1 results in missing pages Reviewed-by: aivanov, prr --- .../classes/sun/lwawt/macosx/CPrinterJob.java | 24 +++++-------------- .../native/libawt_lwawt/awt/CPrinterJob.m | 8 +++---- .../native/libawt_lwawt/awt/PrinterView.h | 6 ++--- .../native/libawt_lwawt/awt/PrinterView.m | 13 +++++----- .../java/awt/print/PrinterJob/PageRanges.java | 4 ++-- 5 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java index 979eeb36239..508b1a843ef 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.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 @@ -355,20 +355,9 @@ public final class CPrinterJob extends RasterPrinterJob { validateDestination(destinationAttr); } - /* Get the range of pages we are to print. If the - * last page to print is unknown, then we print to - * the end of the document. Note that firstPage - * and lastPage are 0 based page indices. - */ - + // Note that firstPage is 0 based page index. int firstPage = getFirstPage(); - int lastPage = getLastPage(); - if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES) { - int totalPages = mDocument.getNumberOfPages(); - if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) { - lastPage = mDocument.getNumberOfPages() - 1; - } - } + int totalPages = mDocument.getNumberOfPages(); try { synchronized (this) { @@ -393,7 +382,7 @@ public final class CPrinterJob extends RasterPrinterJob { try { // Fire off the print rendering loop on the AppKit thread, and don't have // it wait and block this thread. - if (printLoop(false, firstPage, lastPage)) { + if (printLoop(false, firstPage, totalPages)) { // Start a secondary loop on EDT until printing operation is finished or cancelled printingLoop.enter(); } @@ -407,7 +396,7 @@ public final class CPrinterJob extends RasterPrinterJob { onEventThread = false; try { - printLoop(true, firstPage, lastPage); + printLoop(true, firstPage, totalPages); } catch (Exception e) { e.printStackTrace(); } @@ -417,7 +406,6 @@ public final class CPrinterJob extends RasterPrinterJob { } if (++loopi < prMembers.length) { firstPage = prMembers[loopi][0]-1; - lastPage = prMembers[loopi][1] -1; } } while (loopi < prMembers.length); } finally { @@ -693,7 +681,7 @@ public final class CPrinterJob extends RasterPrinterJob { } } - private native boolean printLoop(boolean waitUntilDone, int firstPage, int lastPage) throws PrinterException; + private native boolean printLoop(boolean waitUntilDone, int firstPage, int totalPages) throws PrinterException; private PageFormat getPageFormat(int pageIndex) { // This is called from the native side. diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m index 9cc0a18564f..555a2746f43 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m @@ -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 @@ -656,7 +656,7 @@ JNI_COCOA_EXIT(env); * Signature: ()V */ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterJob_printLoop - (JNIEnv *env, jobject jthis, jboolean blocks, jint firstPage, jint lastPage) + (JNIEnv *env, jobject jthis, jboolean blocks, jint firstPage, jint totalPages) { AWT_ASSERT_NOT_APPKIT_THREAD; @@ -672,14 +672,14 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterJob_printLoop JNI_COCOA_ENTER(env); // Get the first page's PageFormat for setting things up (This introduces // and is a facet of the same problem in Radar 2818593/2708932). - jobject page = (*env)->CallObjectMethod(env, jthis, jm_getPageFormat, 0); // AWT_THREADING Safe (!appKit) + jobject page = (*env)->CallObjectMethod(env, jthis, jm_getPageFormat, firstPage); // AWT_THREADING Safe (!appKit) CHECK_EXCEPTION(); if (page != NULL) { jobject pageFormatArea = (*env)->CallObjectMethod(env, jthis, jm_getPageFormatArea, page); // AWT_THREADING Safe (!appKit) CHECK_EXCEPTION(); PrinterView* printerView = [[PrinterView alloc] initWithFrame:JavaToNSRect(env, pageFormatArea) withEnv:env withPrinterJob:jthis]; - [printerView setFirstPage:firstPage lastPage:lastPage]; + [printerView setTotalPages:totalPages]; GET_NSPRINTINFO_METHOD_RETURN(NO) NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr((*env)->CallLongMethod(env, jthis, sjm_getNSPrintInfo)); // AWT_THREADING Safe (known object) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h index 43472bee920..95a8055cdb0 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 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 @@ -32,12 +32,12 @@ jobject fCurPainter; jobject fCurPeekGraphics; - jint fFirstPage, fLastPage; + jint fTotalPages; } - (id)initWithFrame:(NSRect)aRect withEnv:(JNIEnv*)env withPrinterJob:(jobject)printerJob; -- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage; +- (void)setTotalPages:(jint)totalPages; - (void)releaseReferences:(JNIEnv*)env; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m index d19948d9f0f..f219e8082b4 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m @@ -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 @@ -72,9 +72,8 @@ static jclass sjc_PAbortEx = NULL; } } -- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage { - fFirstPage = firstPage; - fLastPage = lastPage; +- (void)setTotalPages:(jint)totalPages { + fTotalPages = totalPages; } - (void)drawRect:(NSRect)aRect @@ -156,15 +155,15 @@ static jclass sjc_PAbortEx = NULL; return NO; } - aRange->location = fFirstPage + 1; + aRange->location = 1; - if (fLastPage == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES) + if (fTotalPages == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES) { aRange->length = NSIntegerMax; } else { - aRange->length = (fLastPage + 1) - fFirstPage; + aRange->length = fTotalPages; } return YES; diff --git a/test/jdk/java/awt/print/PrinterJob/PageRanges.java b/test/jdk/java/awt/print/PrinterJob/PageRanges.java index e80330bae6c..aea60516f78 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageRanges.java +++ b/test/jdk/java/awt/print/PrinterJob/PageRanges.java @@ -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 @@ -23,7 +23,7 @@ /* * @test - * @bug 6575331 + * @bug 6575331 8297191 * @key printer * @summary The specified pages should be printed. * @library /java/awt/regtesthelpers From 21dc41f744edd138e77970d4e25e3a7eda41621f Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Tue, 20 Jan 2026 16:16:38 +0000 Subject: [PATCH 078/126] 8314323: Implement JEP 527: TLS 1.3 Hybrid Key Exchange Co-authored-by: Jamil Nimeh Co-authored-by: Weijun Wang Reviewed-by: wetmore, mullan --- .../classes/sun/security/ssl/DHasKEM.java | 254 ++++++++++ .../classes/sun/security/ssl/Hybrid.java | 474 ++++++++++++++++++ .../sun/security/ssl/HybridProvider.java | 130 +++++ .../sun/security/ssl/KAKeyDerivation.java | 127 ++++- .../sun/security/ssl/KEMKeyExchange.java | 223 ++++++++ .../sun/security/ssl/KeyShareExtension.java | 113 +++-- .../classes/sun/security/ssl/NamedGroup.java | 131 ++++- .../sun/security/ssl/SSLKeyExchange.java | 6 +- .../classes/sun/security/ssl/ServerHello.java | 51 +- .../classes/sun/security/x509/X509Key.java | 4 + .../net/ssl/SSLParameters/NamedGroups.java | 57 ++- .../javax/net/ssl/TLSCommon/NamedGroup.java | 8 +- .../net/ssl/TLSv13/ClientHelloKeyShares.java | 15 +- .../javax/net/ssl/TLSv13/HRRKeyShares.java | 25 +- .../security/pkcs11/tls/fips/FipsModeTLS.java | 8 +- .../ssl/CipherSuite/DisabledCurve.java | 49 +- .../NamedGroupsWithCipherSuite.java | 76 ++- .../ssl/CipherSuite/RestrictNamedGroup.java | 7 +- .../ssl/CipherSuite/SupportedGroups.java | 32 +- .../bench/java/security/SSLHandshake.java | 25 +- .../bench/javax/crypto/full/KEMBench.java | 110 +++- .../crypto/full/KeyPairGeneratorBench.java | 34 +- 22 files changed, 1839 insertions(+), 120 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/ssl/DHasKEM.java create mode 100644 src/java.base/share/classes/sun/security/ssl/Hybrid.java create mode 100644 src/java.base/share/classes/sun/security/ssl/HybridProvider.java create mode 100644 src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java diff --git a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java new file mode 100644 index 00000000000..763013f280c --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 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. 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 sun.security.ssl; + +import sun.security.util.ArrayUtil; +import sun.security.util.CurveDB; +import sun.security.util.ECUtil; +import sun.security.util.NamedCurve; + +import javax.crypto.DecapsulateException; +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.XECKey; +import java.security.interfaces.XECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.NamedParameterSpec; +import java.security.spec.XECPublicKeySpec; +import java.util.Arrays; + +/** + * The DHasKEM class presents a KEM abstraction layer over traditional + * DH-based key exchange, which can be used for either straight + * ECDH/XDH or TLS hybrid key exchanges. + * + * This class can be alongside standard full post-quantum KEMs + * when hybrid implementations are required. + */ +public class DHasKEM implements KEMSpi { + + @Override + public EncapsulatorSpi engineNewEncapsulator( + PublicKey publicKey, AlgorithmParameterSpec spec, + SecureRandom secureRandom) throws InvalidKeyException { + return new Handler(publicKey, null, secureRandom); + } + + @Override + public DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, + AlgorithmParameterSpec spec) throws InvalidKeyException { + return new Handler(null, privateKey, null); + } + + private static final class Handler + implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi { + private final PublicKey pkR; + private final PrivateKey skR; + private final SecureRandom sr; + private final Params params; + + Handler(PublicKey pk, PrivateKey sk, SecureRandom sr) + throws InvalidKeyException { + this.pkR = pk; + this.skR = sk; + this.sr = sr; + this.params = paramsFromKey(pk == null ? sk : pk); + } + + @Override + public KEM.Encapsulated engineEncapsulate(int from, int to, + String algorithm) { + KeyPair kpE = params.generateKeyPair(sr); + PrivateKey skE = kpE.getPrivate(); + PublicKey pkE = kpE.getPublic(); + byte[] pkEm = params.SerializePublicKey(pkE); + try { + SecretKey dh = params.DH(algorithm, skE, pkR); + return new KEM.Encapsulated( + sub(dh, from, to), + pkEm, null); + } catch (Exception e) { + throw new ProviderException("internal error", e); + } + } + + @Override + public int engineSecretSize() { + return params.secretLen; + } + + @Override + public int engineEncapsulationSize() { + return params.publicKeyLen; + } + + @Override + public SecretKey engineDecapsulate(byte[] encapsulation, int from, + int to, String algorithm) throws DecapsulateException { + if (encapsulation.length != params.publicKeyLen) { + throw new DecapsulateException("incorrect encapsulation size"); + } + try { + PublicKey pkE = params.DeserializePublicKey(encapsulation); + SecretKey dh = params.DH(algorithm, skR, pkE); + return sub(dh, from, to); + } catch (IOException | InvalidKeyException e) { + throw new DecapsulateException("Cannot decapsulate", e); + } catch (Exception e) { + throw new ProviderException("internal error", e); + } + } + + private SecretKey sub(SecretKey key, int from, int to) { + if (from == 0 && to == params.secretLen) { + return key; + } + + // Key slicing should never happen. Otherwise, there might be + // a programming error. + throw new AssertionError( + "Unexpected key slicing: from=" + from + ", to=" + to); + } + + // This KEM is designed to be able to represent every ECDH and XDH + private Params paramsFromKey(Key k) throws InvalidKeyException { + if (k instanceof ECKey eckey) { + if (ECUtil.equals(eckey.getParams(), CurveDB.P_256)) { + return Params.P256; + } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_384)) { + return Params.P384; + } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_521)) { + return Params.P521; + } + } else if (k instanceof XECKey xkey + && xkey.getParams() instanceof NamedParameterSpec ns) { + if (ns.getName().equalsIgnoreCase( + NamedParameterSpec.X25519.getName())) { + return Params.X25519; + } else if (ns.getName().equalsIgnoreCase( + NamedParameterSpec.X448.getName())) { + return Params.X448; + } + } + throw new InvalidKeyException("Unsupported key"); + } + } + + private enum Params { + + P256(32, 2 * 32 + 1, + "ECDH", "EC", CurveDB.P_256), + + P384(48, 2 * 48 + 1, + "ECDH", "EC", CurveDB.P_384), + + P521(66, 2 * 66 + 1, + "ECDH", "EC", CurveDB.P_521), + + X25519(32, 32, + "XDH", "XDH", NamedParameterSpec.X25519), + + X448(56, 56, + "XDH", "XDH", NamedParameterSpec.X448); + + private final int secretLen; + private final int publicKeyLen; + private final String kaAlgorithm; + private final String keyAlgorithm; + private final AlgorithmParameterSpec spec; + + Params(int secretLen, int publicKeyLen, String kaAlgorithm, + String keyAlgorithm, AlgorithmParameterSpec spec) { + this.spec = spec; + this.secretLen = secretLen; + this.publicKeyLen = publicKeyLen; + this.kaAlgorithm = kaAlgorithm; + this.keyAlgorithm = keyAlgorithm; + } + + private boolean isEC() { + return this == P256 || this == P384 || this == P521; + } + + private KeyPair generateKeyPair(SecureRandom sr) { + try { + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithm); + g.initialize(spec, sr); + return g.generateKeyPair(); + } catch (Exception e) { + throw new ProviderException("internal error", e); + } + } + + private byte[] SerializePublicKey(PublicKey k) { + if (isEC()) { + ECPoint w = ((ECPublicKey) k).getW(); + return ECUtil.encodePoint(w, ((NamedCurve) spec).getCurve()); + } else { + byte[] uArray = ((XECPublicKey) k).getU().toByteArray(); + ArrayUtil.reverse(uArray); + return Arrays.copyOf(uArray, publicKeyLen); + } + } + + private PublicKey DeserializePublicKey(byte[] data) throws + IOException, NoSuchAlgorithmException, + InvalidKeySpecException { + KeySpec keySpec; + if (isEC()) { + NamedCurve curve = (NamedCurve) this.spec; + keySpec = new ECPublicKeySpec( + ECUtil.decodePoint(data, curve.getCurve()), curve); + } else { + data = data.clone(); + ArrayUtil.reverse(data); + keySpec = new XECPublicKeySpec( + this.spec, new BigInteger(1, data)); + } + return KeyFactory.getInstance(keyAlgorithm). + generatePublic(keySpec); + } + + private SecretKey DH(String alg, PrivateKey skE, PublicKey pkR) + throws NoSuchAlgorithmException, InvalidKeyException { + KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); + ka.init(skE); + ka.doPhase(pkR, true); + return ka.generateSecret(alg); + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/Hybrid.java b/src/java.base/share/classes/sun/security/ssl/Hybrid.java new file mode 100644 index 00000000000..e3e2cfa0b23 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/Hybrid.java @@ -0,0 +1,474 @@ +/* + * Copyright (c) 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. 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 sun.security.ssl; + +import sun.security.util.ArrayUtil; +import sun.security.util.CurveDB; +import sun.security.util.ECUtil; +import sun.security.util.RawKeySpec; +import sun.security.x509.X509Key; + +import javax.crypto.DecapsulateException; +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.SecretKey; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyFactorySpi; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyPairGeneratorSpi; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.*; +import java.util.Arrays; +import java.util.Locale; + +// The Hybrid class wraps two underlying algorithms (left and right sides) +// in a single TLS hybrid named group. +// It implements: +// - Hybrid KeyPair generation +// - Hybrid KeyFactory for decoding concatenated hybrid public keys +// - Hybrid KEM implementation for performing encapsulation and +// decapsulation over two underlying algorithms (traditional +// algorithm and post-quantum KEM algorithm) + +public class Hybrid { + + public static final NamedParameterSpec X25519_MLKEM768 = + new NamedParameterSpec("X25519MLKEM768"); + + public static final NamedParameterSpec SECP256R1_MLKEM768 = + new NamedParameterSpec("SecP256r1MLKEM768"); + + public static final NamedParameterSpec SECP384R1_MLKEM1024 = + new NamedParameterSpec("SecP384r1MLKEM1024"); + + private static AlgorithmParameterSpec getSpec(String name) { + if (name.startsWith("secp")) { + return new ECGenParameterSpec(name); + } else { + return new NamedParameterSpec(name); + } + } + + private static KeyPairGenerator getKeyPairGenerator(String name) throws + NoSuchAlgorithmException { + if (name.startsWith("secp")) { + name = "EC"; + } + return KeyPairGenerator.getInstance(name); + } + + private static KeyFactory getKeyFactory(String name) throws + NoSuchAlgorithmException { + if (name.startsWith("secp")) { + name = "EC"; + } + return KeyFactory.getInstance(name); + } + + /** + * Returns a KEM instance for each side of the hybrid algorithm. + * For traditional key exchange algorithms, we use the DH-based KEM + * implementation provided by DHasKEM class. + * For ML-KEM post-quantum algorithms, we obtain a KEM instance + * with "ML-KEM". This is done to work with 3rd-party providers that + * only have "ML-KEM" KEM algorithm. + */ + private static KEM getKEM(String name) throws NoSuchAlgorithmException { + if (name.startsWith("secp") || name.equals("X25519")) { + return KEM.getInstance("DH", HybridProvider.PROVIDER); + } else { + return KEM.getInstance("ML-KEM"); + } + } + + public static class KeyPairGeneratorImpl extends KeyPairGeneratorSpi { + private final KeyPairGenerator left; + private final KeyPairGenerator right; + private final AlgorithmParameterSpec leftSpec; + private final AlgorithmParameterSpec rightSpec; + + public KeyPairGeneratorImpl(String leftAlg, String rightAlg) + throws NoSuchAlgorithmException { + left = getKeyPairGenerator(leftAlg); + right = getKeyPairGenerator(rightAlg); + leftSpec = getSpec(leftAlg); + rightSpec = getSpec(rightAlg); + } + + @Override + public void initialize(AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException { + left.initialize(leftSpec, random); + right.initialize(rightSpec, random); + } + + @Override + public void initialize(int keysize, SecureRandom random) { + // NO-OP (do nothing) + } + + @Override + public KeyPair generateKeyPair() { + var kp1 = left.generateKeyPair(); + var kp2 = right.generateKeyPair(); + return new KeyPair( + new PublicKeyImpl("Hybrid", kp1.getPublic(), + kp2.getPublic()), + new PrivateKeyImpl("Hybrid", kp1.getPrivate(), + kp2.getPrivate())); + } + } + + public static class KeyFactoryImpl extends KeyFactorySpi { + private final KeyFactory left; + private final KeyFactory right; + private final int leftlen; + private final String leftname; + private final String rightname; + + public KeyFactoryImpl(String left, String right) + throws NoSuchAlgorithmException { + this.left = getKeyFactory(left); + this.right = getKeyFactory(right); + this.leftlen = leftPublicLength(left); + this.leftname = left; + this.rightname = right; + } + + @Override + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec must not be null"); + } + + if (keySpec instanceof RawKeySpec rks) { + byte[] key = rks.getKeyArr(); + if (key == null) { + throw new InvalidKeySpecException( + "RawkeySpec contains null key data"); + } + if (key.length <= leftlen) { + throw new InvalidKeySpecException( + "Hybrid key length " + key.length + + " is too short and its left key length is " + + leftlen); + } + + byte[] leftKeyBytes = Arrays.copyOfRange(key, 0, leftlen); + byte[] rightKeyBytes = Arrays.copyOfRange(key, leftlen, + key.length); + PublicKey leftKey, rightKey; + + try { + if (leftname.startsWith("secp")) { + var curve = CurveDB.lookup(leftname); + var ecSpec = new ECPublicKeySpec( + ECUtil.decodePoint(leftKeyBytes, + curve.getCurve()), curve); + leftKey = left.generatePublic(ecSpec); + } else if (leftname.startsWith("ML-KEM")) { + leftKey = left.generatePublic(new RawKeySpec( + leftKeyBytes)); + } else { + throw new InvalidKeySpecException("Unsupported left" + + " algorithm" + leftname); + } + + if (rightname.equals("X25519")) { + ArrayUtil.reverse(rightKeyBytes); + var xecSpec = new XECPublicKeySpec( + new NamedParameterSpec(rightname), + new BigInteger(1, rightKeyBytes)); + rightKey = right.generatePublic(xecSpec); + } else if (rightname.startsWith("ML-KEM")) { + rightKey = right.generatePublic(new RawKeySpec( + rightKeyBytes)); + } else { + throw new InvalidKeySpecException("Unsupported right" + + " algorithm: " + rightname); + } + + return new PublicKeyImpl("Hybrid", leftKey, rightKey); + } catch (Exception e) { + throw new InvalidKeySpecException("Failed to decode " + + "hybrid key", e); + } + } + + throw new InvalidKeySpecException( + "KeySpec type:" + + keySpec.getClass().getName() + " not supported"); + } + + private static int leftPublicLength(String name) { + return switch (name.toLowerCase(Locale.ROOT)) { + case "secp256r1" -> 65; + case "secp384r1" -> 97; + case "ml-kem-768" -> 1184; + default -> throw new IllegalArgumentException( + "Unknown named group: " + name); + }; + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws + InvalidKeySpecException { + throw new UnsupportedOperationException(); + } + + @Override + protected T engineGetKeySpec(Key key, + Class keySpec) throws InvalidKeySpecException { + throw new UnsupportedOperationException(); + } + + @Override + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + throw new UnsupportedOperationException(); + } + } + + public static class KEMImpl implements KEMSpi { + private final KEM left; + private final KEM right; + + public KEMImpl(String left, String right) + throws NoSuchAlgorithmException { + this.left = getKEM(left); + this.right = getKEM(right); + } + + @Override + public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey, + AlgorithmParameterSpec spec, SecureRandom secureRandom) throws + InvalidAlgorithmParameterException, InvalidKeyException { + if (publicKey instanceof PublicKeyImpl pk) { + return new Handler(left.newEncapsulator(pk.left, secureRandom), + right.newEncapsulator(pk.right, secureRandom), + null, null); + } + throw new InvalidKeyException(); + } + + @Override + public DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, + AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (privateKey instanceof PrivateKeyImpl pk) { + return new Handler(null, null, left.newDecapsulator(pk.left), + right.newDecapsulator(pk.right)); + } + throw new InvalidKeyException(); + } + } + + private static byte[] concat(byte[]... inputs) { + int outLen = 0; + for (byte[] in : inputs) { + outLen += in.length; + } + byte[] out = new byte[outLen]; + int pos = 0; + for (byte[] in : inputs) { + System.arraycopy(in, 0, out, pos, in.length); + pos += in.length; + } + return out; + } + + private record Handler(KEM.Encapsulator le, KEM.Encapsulator re, + KEM.Decapsulator ld, KEM.Decapsulator rd) + implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi { + @Override + public KEM.Encapsulated engineEncapsulate(int from, int to, + String algorithm) { + int expectedSecretSize = engineSecretSize(); + if (!(from == 0 && to == expectedSecretSize)) { + throw new IllegalArgumentException( + "Invalid range for encapsulation: from = " + from + + " to = " + to + ", expected total secret size = " + + expectedSecretSize); + } + + var left = le.encapsulate(); + var right = re.encapsulate(); + return new KEM.Encapsulated( + new SecretKeyImpl(left.key(), right.key()), + concat(left.encapsulation(), right.encapsulation()), + null); + } + + @Override + public int engineSecretSize() { + if (le != null) { + return le.secretSize() + re.secretSize(); + } else { + return ld.secretSize() + rd.secretSize(); + } + } + + @Override + public int engineEncapsulationSize() { + if (le != null) { + return le.encapsulationSize() + re.encapsulationSize(); + } else { + return ld.encapsulationSize() + rd.encapsulationSize(); + } + } + + @Override + public SecretKey engineDecapsulate(byte[] encapsulation, int from, + int to, String algorithm) throws DecapsulateException { + int expectedEncSize = engineEncapsulationSize(); + if (encapsulation.length != expectedEncSize) { + throw new IllegalArgumentException( + "Invalid key encapsulation message length: " + + encapsulation.length + + ", expected = " + expectedEncSize); + } + + int expectedSecretSize = engineSecretSize(); + if (!(from == 0 && to == expectedSecretSize)) { + throw new IllegalArgumentException( + "Invalid range for decapsulation: from = " + from + + " to = " + to + ", expected total secret size = " + + expectedSecretSize); + } + + var left = Arrays.copyOf(encapsulation, ld.encapsulationSize()); + var right = Arrays.copyOfRange(encapsulation, + ld.encapsulationSize(), encapsulation.length); + return new SecretKeyImpl( + ld.decapsulate(left), + rd.decapsulate(right) + ); + } + } + + // Package-private + record SecretKeyImpl(SecretKey k1, SecretKey k2) + implements SecretKey { + @Override + public String getAlgorithm() { + return "Generic"; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + } + + /** + * Hybrid public key combines two underlying public keys (left and right). + * Public keys can be transmitted/encoded because the hybrid protocol + * requires the public component to be sent. + */ + // Package-private + record PublicKeyImpl(String algorithm, PublicKey left, + PublicKey right) implements PublicKey { + @Override + public String getAlgorithm() { + return algorithm; + } + + // getFormat() returns "RAW" as hybrid key uses RAW concatenation + // of underlying encodings. + @Override + public String getFormat() { + return "RAW"; + } + + // getEncoded() returns the concatenation of the encoded bytes of the + // left and right public keys. + @Override + public byte[] getEncoded() { + return concat(onlyKey(left), onlyKey(right)); + } + + static byte[] onlyKey(PublicKey key) { + if (key instanceof X509Key xk) { + return xk.getKeyAsBytes(); + } + + // Fallback for 3rd-party providers + if (!"X.509".equalsIgnoreCase(key.getFormat())) { + throw new ProviderException("Invalid public key encoding " + + "format"); + } + var xk = new X509Key(); + try { + xk.decode(key.getEncoded()); + } catch (InvalidKeyException e) { + throw new ProviderException("Invalid public key encoding", e); + } + return xk.getKeyAsBytes(); + } + } + + /** + * Hybrid private key combines two underlying private keys (left and right). + * It is for internal use only. The private keys should never be exported. + */ + private record PrivateKeyImpl(String algorithm, PrivateKey left, + PrivateKey right) implements PrivateKey { + + @Override + public String getAlgorithm() { + return algorithm; + } + + // getFormat() returns null because there is no standard + // format for a hybrid private key. + @Override + public String getFormat() { + return null; + } + + // getEncoded() returns an empty byte array because there is no + // standard encoding format for a hybrid private key. + @Override + public byte[] getEncoded() { + return null; + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/HybridProvider.java b/src/java.base/share/classes/sun/security/ssl/HybridProvider.java new file mode 100644 index 00000000000..c77d6f66273 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/HybridProvider.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 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. 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 sun.security.ssl; + +import java.security.Provider; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; + +import static sun.security.util.SecurityConstants.PROVIDER_VER; + +// This is an internal provider used in the JSSE code for DH-as-KEM +// and Hybrid KEM support. It doesn't actually get installed in the +// system's list of security providers that is searched at runtime. +// JSSE loads this provider internally. +// It registers Hybrid KeyPairGenerator, KeyFactory, and KEM +// implementations for hybrid named groups as Provider services. + +public class HybridProvider { + + public static final Provider PROVIDER = new ProviderImpl(); + + private static final class ProviderImpl extends Provider { + @java.io.Serial + private static final long serialVersionUID = 0L; + + ProviderImpl() { + super("HybridAndDHAsKEM", PROVIDER_VER, + "Hybrid and DHAsKEM provider"); + put("KEM.DH", DHasKEM.class.getName()); + + // Hybrid KeyPairGenerator/KeyFactory/KEM + + // The order of shares in the concatenation for group name + // X25519MLKEM768 has been reversed as per the current + // draft RFC. + var attrs = Map.of("name", "X25519MLKEM768", "left", "ML-KEM-768", + "right", "X25519"); + putService(new HybridService(this, "KeyPairGenerator", + "X25519MLKEM768", + "sun.security.ssl.Hybrid$KeyPairGeneratorImpl", + null, attrs)); + putService(new HybridService(this, "KEM", + "X25519MLKEM768", + "sun.security.ssl.Hybrid$KEMImpl", + null, attrs)); + putService(new HybridService(this, "KeyFactory", + "X25519MLKEM768", + "sun.security.ssl.Hybrid$KeyFactoryImpl", + null, attrs)); + + attrs = Map.of("name", "SecP256r1MLKEM768", "left", "secp256r1", + "right", "ML-KEM-768"); + putService(new HybridService(this, "KeyPairGenerator", + "SecP256r1MLKEM768", + "sun.security.ssl.Hybrid$KeyPairGeneratorImpl", + null, attrs)); + putService(new HybridService(this, "KEM", + "SecP256r1MLKEM768", + "sun.security.ssl.Hybrid$KEMImpl", + null, attrs)); + putService(new HybridService(this, "KeyFactory", + "SecP256r1MLKEM768", + "sun.security.ssl.Hybrid$KeyFactoryImpl", + null, attrs)); + + attrs = Map.of("name", "SecP384r1MLKEM1024", "left", "secp384r1", + "right", "ML-KEM-1024"); + putService(new HybridService(this, "KeyPairGenerator", + "SecP384r1MLKEM1024", + "sun.security.ssl.Hybrid$KeyPairGeneratorImpl", + null, attrs)); + putService(new HybridService(this, "KEM", + "SecP384r1MLKEM1024", + "sun.security.ssl.Hybrid$KEMImpl", + null, attrs)); + putService(new HybridService(this, "KeyFactory", + "SecP384r1MLKEM1024", + "sun.security.ssl.Hybrid$KeyFactoryImpl", + null, attrs)); + } + } + + private static class HybridService extends Provider.Service { + + HybridService(Provider p, String type, String algo, String cn, + List aliases, Map attrs) { + super(p, type, algo, cn, aliases, attrs); + } + + @Override + public Object newInstance(Object ctrParamObj) + throws NoSuchAlgorithmException { + String type = getType(); + return switch (type) { + case "KeyPairGenerator" -> new Hybrid.KeyPairGeneratorImpl( + getAttribute("left"), getAttribute("right")); + case "KeyFactory" -> new Hybrid.KeyFactoryImpl( + getAttribute("left"), getAttribute("right")); + case "KEM" -> new Hybrid.KEMImpl( + getAttribute("left"), getAttribute("right")); + default -> throw new NoSuchAlgorithmException( + "Unexpected value: " + type); + }; + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index 623f83f547a..39e82b50435 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -24,7 +24,10 @@ */ package sun.security.ssl; +import sun.security.util.RawKeySpec; + import javax.crypto.KDF; +import javax.crypto.KEM; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import javax.crypto.spec.HKDFParameterSpec; @@ -32,9 +35,11 @@ import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.KeyFactory; import java.security.PrivateKey; +import java.security.Provider; import java.security.PublicKey; -import java.security.spec.AlgorithmParameterSpec; +import java.security.SecureRandom; import sun.security.util.KeyUtil; /** @@ -46,15 +51,32 @@ public class KAKeyDerivation implements SSLKeyDerivation { private final HandshakeContext context; private final PrivateKey localPrivateKey; private final PublicKey peerPublicKey; + private final byte[] keyshare; + private final Provider provider; + // Constructor called by Key Agreement KAKeyDerivation(String algorithmName, HandshakeContext context, PrivateKey localPrivateKey, PublicKey peerPublicKey) { + this(algorithmName, null, context, localPrivateKey, + peerPublicKey, null); + } + + // When the constructor called by KEM: store the client's public key or the + // encapsulated message in keyshare. + KAKeyDerivation(String algorithmName, + NamedGroup namedGroup, + HandshakeContext context, + PrivateKey localPrivateKey, + PublicKey peerPublicKey, + byte[] keyshare) { this.algorithmName = algorithmName; this.context = context; this.localPrivateKey = localPrivateKey; this.peerPublicKey = peerPublicKey; + this.keyshare = keyshare; + this.provider = (namedGroup != null) ? namedGroup.getProvider() : null; } @Override @@ -94,22 +116,15 @@ public class KAKeyDerivation implements SSLKeyDerivation { } } - /** - * Handle the TLSv1.3 objects, which use the HKDF algorithms. - */ - private SecretKey t13DeriveKey(String type) - throws IOException { - SecretKey sharedSecret = null; + private SecretKey deriveHandshakeSecret(String label, + SecretKey sharedSecret) + throws GeneralSecurityException, IOException { SecretKey earlySecret = null; SecretKey saltSecret = null; - try { - KeyAgreement ka = KeyAgreement.getInstance(algorithmName); - ka.init(localPrivateKey); - ka.doPhase(peerPublicKey, true); - sharedSecret = ka.generateSecret("TlsPremasterSecret"); - CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg; - SSLKeyDerivation kd = context.handshakeKeyDerivation; + CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg; + SSLKeyDerivation kd = context.handshakeKeyDerivation; + try { if (kd == null) { // No PSK is in use. // If PSK is not in use, Early Secret will still be // HKDF-Extract(0, 0). @@ -129,12 +144,90 @@ public class KAKeyDerivation implements SSLKeyDerivation { // the handshake secret key derivation (below) as it may not // work with the "sharedSecret" obj. KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm); - return hkdf.deriveKey(type, HKDFParameterSpec.ofExtract() - .addSalt(saltSecret).addIKM(sharedSecret).extractOnly()); + var spec = HKDFParameterSpec.ofExtract().addSalt(saltSecret); + if (sharedSecret instanceof Hybrid.SecretKeyImpl hsk) { + spec = spec.addIKM(hsk.k1()).addIKM(hsk.k2()); + } else { + spec = spec.addIKM(sharedSecret); + } + + return hkdf.deriveKey(label, spec.extractOnly()); + } finally { + KeyUtil.destroySecretKeys(earlySecret, saltSecret); + } + } + /** + * This method is called by the server to perform KEM encapsulation. + * It uses the client's public key (sent by the client as a keyshare) + * to encapsulate a shared secret and returns the encapsulated message. + * + * Package-private, used from KeyShareExtension.SHKeyShareProducer:: + * produce(). + */ + KEM.Encapsulated encapsulate(String algorithm, SecureRandom random) + throws IOException { + SecretKey sharedSecret = null; + + if (keyshare == null) { + throw new IOException("No keyshare available for KEM " + + "encapsulation"); + } + + try { + KeyFactory kf = (provider != null) ? + KeyFactory.getInstance(algorithmName, provider) : + KeyFactory.getInstance(algorithmName); + var pk = kf.generatePublic(new RawKeySpec(keyshare)); + + KEM kem = (provider != null) ? + KEM.getInstance(algorithmName, provider) : + KEM.getInstance(algorithmName); + KEM.Encapsulator e = kem.newEncapsulator(pk, random); + KEM.Encapsulated enc = e.encapsulate(); + sharedSecret = enc.key(); + + SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret); + + return new KEM.Encapsulated(derived, enc.encapsulation(), null); } catch (GeneralSecurityException gse) { throw new SSLHandshakeException("Could not generate secret", gse); } finally { - KeyUtil.destroySecretKeys(sharedSecret, earlySecret, saltSecret); + KeyUtil.destroySecretKeys(sharedSecret); + } + } + + /** + * Handle the TLSv1.3 objects, which use the HKDF algorithms. + */ + private SecretKey t13DeriveKey(String type) + throws IOException { + SecretKey sharedSecret = null; + + try { + if (keyshare != null) { + // Using KEM: called by the client after receiving the KEM + // ciphertext (keyshare) from the server in ServerHello. + // The client decapsulates it using its private key. + KEM kem = (provider != null) + ? KEM.getInstance(algorithmName, provider) + : KEM.getInstance(algorithmName); + var decapsulator = kem.newDecapsulator(localPrivateKey); + sharedSecret = decapsulator.decapsulate( + keyshare, 0, decapsulator.secretSize(), + "TlsPremasterSecret"); + } else { + // Using traditional DH-style Key Agreement + KeyAgreement ka = KeyAgreement.getInstance(algorithmName); + ka.init(localPrivateKey); + ka.doPhase(peerPublicKey, true); + sharedSecret = ka.generateSecret("TlsPremasterSecret"); + } + + return deriveHandshakeSecret(type, sharedSecret); + } catch (GeneralSecurityException gse) { + throw new SSLHandshakeException("Could not generate secret", gse); + } finally { + KeyUtil.destroySecretKeys(sharedSecret); } } } diff --git a/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java new file mode 100644 index 00000000000..fb8de6cb104 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 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. 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 sun.security.ssl; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.NamedParameterSpec; +import javax.crypto.SecretKey; + +import sun.security.ssl.NamedGroup.NamedGroupSpec; +import sun.security.x509.X509Key; + +/** + * Specifics for single or hybrid Key exchanges based on KEM + */ +final class KEMKeyExchange { + + static final SSLKeyAgreementGenerator kemKAGenerator + = new KEMKAGenerator(); + + static final class KEMCredentials implements NamedGroupCredentials { + + final NamedGroup namedGroup; + // Unlike other credentials, we directly store the key share + // value here, no need to convert to a key + private final byte[] keyshare; + + KEMCredentials(byte[] keyshare, NamedGroup namedGroup) { + this.keyshare = keyshare; + this.namedGroup = namedGroup; + } + + // For KEM, server performs encapsulation and the resulting + // encapsulated message becomes the key_share value sent to + // the client. It is not a public key, so no PublicKey object + // to return. + @Override + public PublicKey getPublicKey() { + throw new UnsupportedOperationException( + "KEMCredentials stores raw keyshare, not a PublicKey"); + } + + public byte[] getKeyShare() { + return keyshare; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + /** + * Instantiates a KEMCredentials object + */ + static KEMCredentials valueOf(NamedGroup namedGroup, + byte[] encodedPoint) { + + if (namedGroup.spec != NamedGroupSpec.NAMED_GROUP_KEM) { + throw new RuntimeException( + "Credentials decoding: Not KEM named group"); + } + + if (encodedPoint == null || encodedPoint.length == 0) { + return null; + } + + return new KEMCredentials(encodedPoint, namedGroup); + } + } + + private static class KEMPossession implements SSLPossession { + private final NamedGroup namedGroup; + + public KEMPossession(NamedGroup ng) { + this.namedGroup = ng; + } + public NamedGroup getNamedGroup() { + return namedGroup; + } + } + + static final class KEMReceiverPossession extends KEMPossession { + + private final PrivateKey privateKey; + private final PublicKey publicKey; + + KEMReceiverPossession(NamedGroup namedGroup, SecureRandom random) { + super(namedGroup); + String algName = null; + try { + // For KEM: This receiver side (client) generates a key pair. + algName = ((NamedParameterSpec)namedGroup.keAlgParamSpec). + getName(); + Provider provider = namedGroup.getProvider(); + KeyPairGenerator kpg = (provider != null) ? + KeyPairGenerator.getInstance(algName, provider) : + KeyPairGenerator.getInstance(algName); + + kpg.initialize(namedGroup.keAlgParamSpec, random); + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + publicKey = kp.getPublic(); + } catch (GeneralSecurityException e) { + throw new RuntimeException( + "Could not generate keypair for algorithm: " + + algName, e); + } + } + + @Override + public byte[] encode() { + if (publicKey instanceof X509Key xk) { + return xk.getKeyAsBytes(); + } else if (publicKey instanceof Hybrid.PublicKeyImpl hk) { + return hk.getEncoded(); + } + throw new ProviderException("Unsupported key type: " + publicKey); + } + + // Package-private + PublicKey getPublicKey() { + return publicKey; + } + + // Package-private + PrivateKey getPrivateKey() { + return privateKey; + } + } + + static final class KEMSenderPossession extends KEMPossession { + + private SecretKey key; + private final SecureRandom random; + + KEMSenderPossession(NamedGroup namedGroup, SecureRandom random) { + super(namedGroup); + this.random = random; + } + + // Package-private + SecureRandom getRandom() { + return random; + } + + // Package-private + SecretKey getKey() { + return key; + } + + // Package-private + void setKey(SecretKey key) { + this.key = key; + } + + @Override + public byte[] encode() { + throw new UnsupportedOperationException("encode() not supported"); + } + } + + private static final class KEMKAGenerator + implements SSLKeyAgreementGenerator { + + // Prevent instantiation of this class. + private KEMKAGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + for (SSLPossession poss : context.handshakePossessions) { + if (poss instanceof KEMReceiverPossession kposs) { + NamedGroup ng = kposs.getNamedGroup(); + for (SSLCredentials cred : context.handshakeCredentials) { + if (cred instanceof KEMCredentials kcred && + ng.equals(kcred.namedGroup)) { + String name = ((NamedParameterSpec) + ng.keAlgParamSpec).getName(); + return new KAKeyDerivation(name, ng, context, + kposs.getPrivateKey(), null, + kcred.getKeyShare()); + } + } + } + } + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No suitable KEM key agreement " + + "parameters negotiated"); + return null; + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java index 8d785f7515a..0d2cbb8f529 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java @@ -27,8 +27,11 @@ package sun.security.ssl; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.AlgorithmConstraints; import java.security.CryptoPrimitive; import java.security.GeneralSecurityException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.NamedParameterSpec; import java.text.MessageFormat; import java.util.*; import javax.net.ssl.SSLProtocolException; @@ -297,7 +300,9 @@ final class KeyShareExtension { // update the context chc.handshakePossessions.add(pos); // May need more possession types in the future. - if (pos instanceof NamedGroupPossession) { + if (pos instanceof NamedGroupPossession || + pos instanceof + KEMKeyExchange.KEMReceiverPossession) { return pos.encode(); } } @@ -358,24 +363,16 @@ final class KeyShareExtension { try { SSLCredentials kaCred = ng.decodeCredentials(entry.keyExchange); - if (shc.algorithmConstraints != null && - kaCred instanceof - NamedGroupCredentials namedGroupCredentials) { - if (!shc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroupCredentials.getPublicKey())) { - if (SSLLogger.isOn() && - SSLLogger.isOn("ssl,handshake")) { - SSLLogger.warning( + + if (!isCredentialPermitted(shc.algorithmConstraints, + kaCred)) { + if (SSLLogger.isOn() && + SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( "key share entry of " + ng + " does not " + - " comply with algorithm constraints"); - } - - kaCred = null; + "comply with algorithm constraints"); } - } - - if (kaCred != null) { + } else { credentials.add(kaCred); } } catch (GeneralSecurityException ex) { @@ -513,7 +510,8 @@ final class KeyShareExtension { @Override public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { - // The producing happens in client side only. + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; // In response to key_share request only @@ -571,7 +569,9 @@ final class KeyShareExtension { SSLPossession[] poses = ke.createPossessions(shc); for (SSLPossession pos : poses) { - if (!(pos instanceof NamedGroupPossession)) { + if (!(pos instanceof NamedGroupPossession || + pos instanceof + KEMKeyExchange.KEMSenderPossession)) { // May need more possession types in the future. continue; } @@ -579,7 +579,34 @@ final class KeyShareExtension { // update the context shc.handshakeKeyExchange = ke; shc.handshakePossessions.add(pos); - keyShare = new KeyShareEntry(ng.id, pos.encode()); + + // For KEM, perform encapsulation using the client’s public + // key (KEMCredentials). The resulting encapsulated message + // becomes the key_share value sent to the client. The + // shared secret derived from encapsulation is stored in + // the KEMSenderPossession for later use in the TLS key + // schedule. + + // SSLKeyExchange.createPossessions() returns at most one + // key-agreement possession or one KEMSenderPossession + // per handshake. + if (pos instanceof KEMKeyExchange.KEMSenderPossession xp) { + if (cd instanceof KEMKeyExchange.KEMCredentials kcred + && ng.equals(kcred.namedGroup)) { + String name = ((NamedParameterSpec) + ng.keAlgParamSpec).getName(); + KAKeyDerivation handshakeKD = new KAKeyDerivation( + name, ng, shc, null, null, + kcred.getKeyShare()); + var encaped = handshakeKD.encapsulate( + "TlsHandshakeSecret", xp.getRandom()); + xp.setKey(encaped.key()); + keyShare = new KeyShareEntry(ng.id, + encaped.encapsulation()); + } + } else { + keyShare = new KeyShareEntry(ng.id, pos.encode()); + } break; } @@ -663,19 +690,13 @@ final class KeyShareExtension { try { SSLCredentials kaCred = ng.decodeCredentials(keyShare.keyExchange); - if (chc.algorithmConstraints != null && - kaCred instanceof - NamedGroupCredentials namedGroupCredentials) { - if (!chc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroupCredentials.getPublicKey())) { - chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, - "key share entry of " + ng + " does not " + - " comply with algorithm constraints"); - } - } - if (kaCred != null) { + if (!isCredentialPermitted(chc.algorithmConstraints, + kaCred)) { + chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "key share entry of " + ng + " does not " + + "comply with algorithm constraints"); + } else { credentials = kaCred; } } catch (GeneralSecurityException ex) { @@ -696,6 +717,34 @@ final class KeyShareExtension { } } + private static boolean isCredentialPermitted( + AlgorithmConstraints constraints, + SSLCredentials cred) { + + if (constraints == null) return true; + if (cred == null) return false; + + if (cred instanceof NamedGroupCredentials namedGroupCred) { + if (namedGroupCred instanceof KEMKeyExchange.KEMCredentials + kemCred) { + AlgorithmParameterSpec paramSpec = kemCred.getNamedGroup(). + keAlgParamSpec; + String algName = (paramSpec instanceof NamedParameterSpec nps) ? + nps.getName() : null; + return algName != null && constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + algName, + null); + } else { + return constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCred.getPublicKey()); + } + } + + return true; + } + /** * The absence processing if the extension is not present in * the ServerHello handshake message. diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java index 877236ebfad..abf973727f3 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -214,6 +214,39 @@ enum NamedGroup { ProtocolVersion.PROTOCOLS_TO_13, PredefinedDHParameterSpecs.ffdheParams.get(8192)), + ML_KEM_512(0x0200, "MLKEM512", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + null), + + ML_KEM_768(0x0201, "MLKEM768", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + null), + + ML_KEM_1024(0x0202, "MLKEM1024", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + null), + + X25519MLKEM768(0x11ec, "X25519MLKEM768", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + Hybrid.X25519_MLKEM768, + HybridProvider.PROVIDER), + + SECP256R1MLKEM768(0x11eb, "SecP256r1MLKEM768", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + Hybrid.SECP256R1_MLKEM768, + HybridProvider.PROVIDER), + + SECP384R1MLKEM1024(0x11ed, "SecP384r1MLKEM1024", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + Hybrid.SECP384R1_MLKEM1024, + HybridProvider.PROVIDER), + // Elliptic Curves (RFC 4492) // // arbitrary prime and characteristic-2 curves @@ -234,22 +267,33 @@ enum NamedGroup { final AlgorithmParameterSpec keAlgParamSpec; final AlgorithmParameters keAlgParams; final boolean isAvailable; + final Provider defaultProvider; // performance optimization private static final Set KEY_AGREEMENT_PRIMITIVE_SET = Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT)); - // Constructor used for all NamedGroup types NamedGroup(int id, String name, NamedGroupSpec namedGroupSpec, ProtocolVersion[] supportedProtocols, AlgorithmParameterSpec keAlgParamSpec) { + this(id, name, namedGroupSpec, supportedProtocols, keAlgParamSpec, + null); + } + + // Constructor used for all NamedGroup types + NamedGroup(int id, String name, + NamedGroupSpec namedGroupSpec, + ProtocolVersion[] supportedProtocols, + AlgorithmParameterSpec keAlgParamSpec, + Provider defaultProvider) { this.id = id; this.name = name; this.spec = namedGroupSpec; this.algorithm = namedGroupSpec.algorithm; this.supportedProtocols = supportedProtocols; this.keAlgParamSpec = keAlgParamSpec; + this.defaultProvider = defaultProvider; // Check if it is a supported named group. AlgorithmParameters algParams = null; @@ -266,16 +310,28 @@ enum NamedGroup { // Check the specific algorithm parameters. if (mediator) { try { - algParams = - AlgorithmParameters.getInstance(namedGroupSpec.algorithm); - algParams.init(keAlgParamSpec); + // Skip AlgorithmParameters for KEMs (not supported) + // Check KEM's availability via KeyFactory + if (namedGroupSpec == NamedGroupSpec.NAMED_GROUP_KEM) { + if (defaultProvider == null) { + KeyFactory.getInstance(name); + } else { + KeyFactory.getInstance(name, defaultProvider); + } + } else { + // ECDHE or others: use AlgorithmParameters as before + algParams = AlgorithmParameters.getInstance( + namedGroupSpec.algorithm); + algParams.init(keAlgParamSpec); + } } catch (InvalidParameterSpecException | NoSuchAlgorithmException exp) { if (namedGroupSpec != NamedGroupSpec.NAMED_GROUP_XDH) { mediator = false; if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( - "No AlgorithmParameters for " + name, exp); + "No AlgorithmParameters or KeyFactory for " + name, + exp); } } else { // Please remove the following code if the XDH/X25519/X448 @@ -307,6 +363,10 @@ enum NamedGroup { this.keAlgParams = mediator ? algParams : null; } + Provider getProvider() { + return defaultProvider; + } + // // The next set of methods search & retrieve NamedGroups. // @@ -545,6 +605,10 @@ enum NamedGroup { return spec.decodeCredentials(this, encoded); } + SSLPossession createPossession(boolean isClient, SecureRandom random) { + return spec.createPossession(this, isClient, random); + } + SSLPossession createPossession(SecureRandom random) { return spec.createPossession(this, random); } @@ -566,6 +630,11 @@ enum NamedGroup { SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException; + + default SSLPossession createPossession(NamedGroup ng, boolean isClient, + SecureRandom random) { + return createPossession(ng, random); + } } enum NamedGroupSpec implements NamedGroupScheme { @@ -578,6 +647,10 @@ enum NamedGroup { // Finite Field Groups (XDH) NAMED_GROUP_XDH("XDH", XDHScheme.instance), + // Post-Quantum Cryptography (PQC) KEM groups + // Currently used for hybrid named groups + NAMED_GROUP_KEM("KEM", KEMScheme.instance), + // arbitrary prime and curves (ECDHE) NAMED_GROUP_ARBITRARY("EC", null), @@ -634,6 +707,15 @@ enum NamedGroup { return null; } + public SSLPossession createPossession( + NamedGroup ng, boolean isClient, SecureRandom random) { + if (scheme != null) { + return scheme.createPossession(ng, isClient, random); + } + + return null; + } + @Override public SSLPossession createPossession( NamedGroup ng, SecureRandom random) { @@ -739,6 +821,42 @@ enum NamedGroup { } } + private static class KEMScheme implements NamedGroupScheme { + private static final KEMScheme instance = new KEMScheme(); + + @Override + public byte[] encodePossessionPublicKey(NamedGroupPossession poss) { + return poss.encode(); + } + + @Override + public SSLCredentials decodeCredentials(NamedGroup ng, + byte[] encoded) throws IOException, GeneralSecurityException { + return KEMKeyExchange.KEMCredentials.valueOf(ng, encoded); + } + + @Override + public SSLPossession createPossession(NamedGroup ng, + SecureRandom random) { + // Must call createPossession with isClient + throw new UnsupportedOperationException(); + } + + @Override + public SSLPossession createPossession( + NamedGroup ng, boolean isClient, SecureRandom random) { + return isClient + ? new KEMKeyExchange.KEMReceiverPossession(ng, random) + : new KEMKeyExchange.KEMSenderPossession(ng, random); + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext hc) throws IOException { + return KEMKeyExchange.kemKAGenerator.createKeyDerivation(hc); + } + } + static final class SupportedGroups { // the supported named groups, non-null immutable list static final String[] namedGroups; @@ -784,6 +902,9 @@ enum NamedGroup { } else { // default groups NamedGroup[] groups = new NamedGroup[] { + // Hybrid key agreement + X25519MLKEM768, + // Primary XDH (RFC 7748) curves X25519, diff --git a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java index 22a44590ce3..263308f0659 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -570,7 +570,9 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator, @Override public SSLPossession createPossession(HandshakeContext hc) { - return namedGroup.createPossession(hc.sslContext.getSecureRandom()); + return namedGroup.createPossession( + hc instanceof ClientHandshakeContext, + hc.sslContext.getSecureRandom()); } @Override diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 76c266a628a..0567c861e18 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -565,6 +565,34 @@ final class ServerHello { clientHello); shc.serverHelloRandom = shm.serverRandom; + // For key derivation, we will either use the traditional Key + // Agreement (KA) model or the Key Encapsulation Mechanism (KEM) + // model, depending on what key exchange group is used. + // + // For KA flows, the server first receives the client's share, + // then generates its key share, and finally comes here. + // However, this is changed for KEM: the server + // must perform both actions — derive the secret and generate + // the key encapsulation message at the same time during + // encapsulation in SHKeyShareProducer. + // + // Traditional Key Agreement (KA): + // - Both peers generate a key share and exchange it. + // - Each peer computes a shared secret sometime after + // receiving the other's key share. + // + // Key Encapsulation Mechanism (KEM): + // The client publishes a public key via a KeyShareExtension, + // which the server uses to: + // + // - generate the shared secret + // - encapsulate the message which is sent to the client in + // another KeyShareExtension + // + // The derived shared secret must be stored in a + // KEMSenderPossession so it can be retrieved for handshake + // traffic secret derivation later. + // Produce extensions for ServerHello handshake message. SSLExtension[] serverHelloExtensions = shc.sslConfig.getEnabledExtensions( @@ -590,9 +618,26 @@ final class ServerHello { "Not negotiated key shares"); } - SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc); - SecretKey handshakeSecret = handshakeKD.deriveKey( - "TlsHandshakeSecret"); + SecretKey handshakeSecret = null; + + // For KEM, the shared secret has already been generated and + // stored in the server’s possession (KEMSenderPossession) + // during encapsulation in SHKeyShareProducer. + // + // Only one key share is selected by the server, so at most one + // possession will contain the pre-derived shared secret. + for (var pos : shc.handshakePossessions) { + if (pos instanceof KEMKeyExchange.KEMSenderPossession xp) { + handshakeSecret = xp.getKey(); + break; + } + } + + if (handshakeSecret == null) { + SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc); + handshakeSecret = handshakeKD.deriveKey( + "TlsHandshakeSecret"); + } SSLTrafficKeyDerivation kdg = SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); diff --git a/src/java.base/share/classes/sun/security/x509/X509Key.java b/src/java.base/share/classes/sun/security/x509/X509Key.java index c83e06f651e..1cfe3f9d95d 100644 --- a/src/java.base/share/classes/sun/security/x509/X509Key.java +++ b/src/java.base/share/classes/sun/security/x509/X509Key.java @@ -104,6 +104,10 @@ public class X509Key implements PublicKey, DerEncoder { return (BitArray)bitStringKey.clone(); } + public byte[] getKeyAsBytes() { + return bitStringKey.toByteArray(); + } + /** * Construct X.509 subject public key from a DER value. If * the runtime environment is configured with a specific class for diff --git a/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java index 25f73606b96..786b907b79a 100644 --- a/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java +++ b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +27,7 @@ /* * @test - * @bug 8281236 + * @bug 8281236 8314323 * @summary Check TLS connection behaviors for named groups configuration * @library /javax/net/ssl/templates * @run main/othervm NamedGroups @@ -136,6 +137,60 @@ public class NamedGroups extends SSLSocketTemplate { "secp256r1" }, true); + + runTest(new String[] { + "X25519MLKEM768" + }, + new String[] { + "X25519MLKEM768" + }, + false); + + runTest(new String[] { + "SecP256r1MLKEM768" + }, + new String[] { + "SecP256r1MLKEM768" + }, + false); + + runTest(new String[] { + "SecP384r1MLKEM1024" + }, + new String[] { + "SecP384r1MLKEM1024" + }, + false); + + runTest(new String[] { + "X25519MLKEM768" + }, + new String[] { + "SecP256r1MLKEM768" + }, + true); + + runTest(new String[] { + "X25519MLKEM768" + }, + new String[0], + true); + + runTest(new String[] { + "SecP256r1MLKEM768" + }, + null, + true); + + runTest(new String[] { + "X25519MLKEM768", + "x25519" + }, + new String[] { + "X25519MLKEM768", + "x25519" + }, + false); } private static void runTest(String[] serverNamedGroups, diff --git a/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java b/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java index ec89fe0d5b5..432a2bd1b0d 100644 --- a/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java +++ b/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -37,7 +37,11 @@ public enum NamedGroup { FFDHE3072("ffdhe3072"), FFDHE4096("ffdhe4096"), FFDHE6144("ffdhe6144"), - FFDHE8192("ffdhe8192"); + FFDHE8192("ffdhe8192"), + + X25519MLKEM768("X25519MLKEM768"), + SECP256R1MLKEM768("SecP256r1MLKEM768"), + SECP384R1MLKEM1024("SecP384r1MLKEM1024"); public final String name; diff --git a/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java b/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java index efb9895b33c..ed26cf90a8c 100644 --- a/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java +++ b/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java @@ -26,16 +26,21 @@ /* * @test - * @bug 8247630 + * @bug 8247630 8314323 * @summary Use two key share entries - * @run main/othervm ClientHelloKeyShares 29 23 + * @run main/othervm ClientHelloKeyShares 4588 29 * @run main/othervm -Djdk.tls.namedGroups=secp384r1,secp521r1,x448,ffdhe2048 ClientHelloKeyShares 24 30 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,sect163r1,x25519 ClientHelloKeyShares 29 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,sect163r1,secp256r1 ClientHelloKeyShares 23 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,sect163r1,ffdhe2048,ffdhe3072,ffdhe4096 ClientHelloKeyShares 256 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,ffdhe2048,x25519,secp256r1 ClientHelloKeyShares 256 29 * @run main/othervm -Djdk.tls.namedGroups=secp256r1,secp384r1,ffdhe2048,x25519 ClientHelloKeyShares 23 256 - */ + * @run main/othervm -Djdk.tls.namedGroups=X25519MLKEM768 ClientHelloKeyShares 4588 + * @run main/othervm -Djdk.tls.namedGroups=x25519,X25519MLKEM768 ClientHelloKeyShares 29 4588 + * @run main/othervm -Djdk.tls.namedGroups=SecP256r1MLKEM768,x25519 ClientHelloKeyShares 4587 29 + * @run main/othervm -Djdk.tls.namedGroups=SecP384r1MLKEM1024,secp256r1 ClientHelloKeyShares 4589 23 + * @run main/othervm -Djdk.tls.namedGroups=X25519MLKEM768,SecP256r1MLKEM768,X25519,secp256r1 ClientHelloKeyShares 4588 29 +*/ import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.*; @@ -62,10 +67,6 @@ public class ClientHelloKeyShares { private static final int HELLO_EXT_SUPP_VERS = 43; private static final int HELLO_EXT_KEY_SHARE = 51; private static final int TLS_PROT_VER_13 = 0x0304; - private static final int NG_SECP256R1 = 0x0017; - private static final int NG_SECP384R1 = 0x0018; - private static final int NG_X25519 = 0x001D; - private static final int NG_X448 = 0x001E; public static void main(String args[]) throws Exception { if (debug) { diff --git a/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java b/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java index 560faf87049..bd14e465e65 100644 --- a/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java +++ b/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java @@ -26,10 +26,12 @@ /* * @test - * @bug 8247630 + * @bug 8247630 8314323 * @summary Use two key share entries * @library /test/lib - * @run main/othervm -Djdk.tls.namedGroups=x25519,secp256r1,secp384r1 HRRKeyShares + * @run main/othervm + * -Djdk.tls.namedGroups=x25519,secp256r1,secp384r1,X25519MLKEM768,SecP256r1MLKEM768,SecP384r1MLKEM1024 + * HRRKeyShares */ import java.io.ByteArrayOutputStream; @@ -72,6 +74,10 @@ public class HRRKeyShares { private static final int NG_SECP384R1 = 0x0018; private static final int NG_X25519 = 0x001D; private static final int NG_X448 = 0x001E; + private static final int NG_X25519_MLKEM768 = 0x11EC; + private static final int NG_SECP256R1_MLKEM768 = 0x11EB; + private static final int NG_SECP384R1_MLKEM1024 = 0x11ED; + private static final int NG_GC512A = 0x0026; private static final int COMP_NONE = 0; private static final int ALERT_TYPE_FATAL = 2; @@ -238,6 +244,18 @@ public class HRRKeyShares { System.out.println("Test 4: Bad HRR using known / unasserted x448"); hrrKeyShareTest(NG_X448, false); System.out.println(); + + System.out.println("Test 5: Good HRR exchange using X25519MLKEM768"); + hrrKeyShareTest(NG_X25519_MLKEM768, true); + System.out.println(); + + System.out.println("Test 6: Good HRR exchange using SecP256r1MLKEM768"); + hrrKeyShareTest(NG_SECP256R1_MLKEM768, true); + System.out.println(); + + System.out.println("Test 7: Good HRR exchange using SecP384r1MLKEM1024"); + hrrKeyShareTest(NG_SECP384R1_MLKEM1024, true); + System.out.println(); } private static void logResult(String str, SSLEngineResult result) { @@ -348,7 +366,8 @@ public class HRRKeyShares { try { // Now we're expecting to reissue the ClientHello, this time - // with a secp384r1 share. + // with a key share for the HRR requested named + // group (hrrNamedGroup). cTOs.compact(); clientResult = engine.wrap(clientOut, cTOs); logResult("client wrap: ", clientResult); diff --git a/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java b/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java index 8799f2305bf..a54bb501f78 100644 --- a/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java +++ b/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java @@ -34,8 +34,12 @@ * -Djdk.tls.useExtendedMasterSecret=false * -Djdk.tls.client.enableSessionTicketExtension=false FipsModeTLS * @comment SunPKCS11 does not support (TLS1.2) SunTlsExtendedMasterSecret yet. - * Stateless resumption doesn't currently work with NSS-FIPS, see JDK-8368669 - * @run main/othervm/timeout=120 -Djdk.tls.client.protocols=TLSv1.3 FipsModeTLS + * Stateless resumption doesn't currently work with NSS-FIPS, see JDK-8368669. + * NSS-FIPS does not support ML-KEM, so configures the list of named groups. + * @run main/othervm/timeout=120 + * -Djdk.tls.client.protocols=TLSv1.3 + * -Djdk.tls.namedGroups=x25519,secp256r1,secp384r1,secp521r1,x448,ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192 + * FipsModeTLS */ import java.io.File; diff --git a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java index 26304c5df95..a13f8570f14 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java +++ b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -23,12 +23,24 @@ /* * @test - * @bug 8246330 + * @bug 8246330 8314323 * @library /javax/net/ssl/templates /test/lib * @run main/othervm -Djdk.tls.namedGroups="secp384r1" DisabledCurve DISABLE_NONE PASS * @run main/othervm -Djdk.tls.namedGroups="secp384r1" DisabledCurve secp384r1 FAIL + * @run main/othervm -Djdk.tls.namedGroups="X25519MLKEM768" + DisabledCurve DISABLE_NONE PASS + * @run main/othervm -Djdk.tls.namedGroups="X25519MLKEM768" + DisabledCurve X25519MLKEM768 FAIL + * @run main/othervm -Djdk.tls.namedGroups="SecP256r1MLKEM768" + DisabledCurve DISABLE_NONE PASS + * @run main/othervm -Djdk.tls.namedGroups="SecP256r1MLKEM768" + DisabledCurve SecP256r1MLKEM768 FAIL + * @run main/othervm -Djdk.tls.namedGroups="SecP384r1MLKEM1024" + DisabledCurve DISABLE_NONE PASS + * @run main/othervm -Djdk.tls.namedGroups="SecP384r1MLKEM1024" + DisabledCurve SecP384r1MLKEM1024 FAIL */ import java.security.Security; import java.util.Arrays; @@ -45,8 +57,10 @@ public class DisabledCurve extends SSLSocketTemplate { private static final String[][][] protocols = { { { "TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1" }, { "TLSv1.2" } }, { { "TLSv1.2" }, { "TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1" } }, - { { "TLSv1.2" }, { "TLSv1.2" } }, { { "TLSv1.1" }, { "TLSv1.1" } }, - { { "TLSv1" }, { "TLSv1" } } }; + { { "TLSv1.2" }, { "TLSv1.2" } }, + { { "TLSv1.1" }, { "TLSv1.1" } }, + { { "TLSv1" }, { "TLSv1" } }, + { { "TLSv1.3" }, { "TLSv1.3" } } }; @Override protected SSLContext createClientSSLContext() throws Exception { @@ -94,17 +108,36 @@ public class DisabledCurve extends SSLSocketTemplate { String expected = args[1]; String disabledName = ("DISABLE_NONE".equals(args[0]) ? "" : args[0]); boolean disabled = false; - if (disabledName.equals("")) { + + if (disabledName.isEmpty()) { Security.setProperty("jdk.disabled.namedCurves", ""); + Security.setProperty("jdk.certpath.disabledAlgorithms", ""); } else { disabled = true; - Security.setProperty("jdk.certpath.disabledAlgorithms", "secp384r1"); + Security.setProperty("jdk.certpath.disabledAlgorithms", disabledName); + if (!disabledName.contains("MLKEM")) { + Security.setProperty("jdk.disabled.namedCurves", disabledName); + } else { + Security.setProperty("jdk.disabled.namedCurves", ""); + } } // Re-enable TLSv1 and TLSv1.1 since test depends on it. SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLSv1.1"); + String namedGroups = System.getProperty("jdk.tls.namedGroups", ""); + boolean hybridGroup = namedGroups.contains("MLKEM"); + for (index = 0; index < protocols.length; index++) { + if (hybridGroup) { + String[] clientProtos = protocols[index][0]; + String[] serverProtos = protocols[index][1]; + + if (!(isTLS13(clientProtos) && isTLS13(serverProtos))) { + continue; + } + } + try { (new DisabledCurve()).run(); if (expected.equals("FAIL")) { @@ -123,4 +156,8 @@ public class DisabledCurve extends SSLSocketTemplate { } } + + private static boolean isTLS13(String[] protocols) { + return protocols.length == 1 && "TLSv1.3".equals(protocols[0]); + } } diff --git a/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java index 5732f42982c..9080f549683 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java +++ b/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -21,6 +21,8 @@ * questions. */ +import java.util.Arrays; +import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; @@ -29,7 +31,7 @@ import jdk.test.lib.security.SecurityUtils; /* * @test - * @bug 8224650 8242929 + * @bug 8224650 8242929 8314323 * @library /javax/net/ssl/templates * /javax/net/ssl/TLSCommon * /test/lib @@ -44,17 +46,20 @@ import jdk.test.lib.security.SecurityUtils; * @run main/othervm NamedGroupsWithCipherSuite ffdhe4096 * @run main/othervm NamedGroupsWithCipherSuite ffdhe6144 * @run main/othervm NamedGroupsWithCipherSuite ffdhe8192 + * @run main/othervm NamedGroupsWithCipherSuite X25519MLKEM768 + * @run main/othervm NamedGroupsWithCipherSuite SecP256r1MLKEM768 + * @run main/othervm NamedGroupsWithCipherSuite SecP384r1MLKEM1024 */ public class NamedGroupsWithCipherSuite extends SSLSocketTemplate { - private static final Protocol[] PROTOCOLS = new Protocol[] { + private static final List PROTOCOLS = List.of( Protocol.TLSV1_3, Protocol.TLSV1_2, Protocol.TLSV1_1, Protocol.TLSV1 - }; + ); - private static final CipherSuite[] CIPHER_SUITES = new CipherSuite[] { + private static final List CIPHER_SUITES = List.of( CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_AES_256_GCM_SHA384, CipherSuite.TLS_CHACHA20_POLY1305_SHA256, @@ -75,7 +80,23 @@ public class NamedGroupsWithCipherSuite extends SSLSocketTemplate { CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - }; + ); + + private static final List HYBRID_NAMEDGROUPS = List.of( + "X25519MLKEM768", + "SecP256r1MLKEM768", + "SecP384r1MLKEM1024" + ); + + private static final List HYBRID_PROTOCOL = List.of( + Protocol.TLSV1_3 + ); + + private static final List HYBRID_CIPHER_SUITES = List.of( + CipherSuite.TLS_AES_128_GCM_SHA256, + CipherSuite.TLS_AES_256_GCM_SHA384, + CipherSuite.TLS_CHACHA20_POLY1305_SHA256 + ); private String protocol; private String cipher; @@ -151,48 +172,59 @@ public class NamedGroupsWithCipherSuite extends SSLSocketTemplate { // Re-enable TLSv1 and TLSv1.1 since test depends on it. SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLSv1.1"); - for (Protocol protocol : PROTOCOLS) { - for (CipherSuite cipherSuite : CIPHER_SUITES) { - // Named group converted to lower case just - // to satisfy Test condition + boolean hybridGroup = HYBRID_NAMEDGROUPS.contains(namedGroup); + List protocolList = hybridGroup ? + HYBRID_PROTOCOL : PROTOCOLS; + List cipherList = hybridGroup ? + HYBRID_CIPHER_SUITES : CIPHER_SUITES; + + // non-Hybrid named group converted to lower case just + // to satisfy Test condition + String normalizedGroup = hybridGroup ? + namedGroup : namedGroup.toLowerCase(); + + for (Protocol protocol : protocolList) { + for (CipherSuite cipherSuite : cipherList) { if (cipherSuite.supportedByProtocol(protocol) - && groupSupportdByCipher(namedGroup.toLowerCase(), - cipherSuite)) { + && groupSupportedByCipher(normalizedGroup, + cipherSuite)) { System.out.printf("Protocol: %s, cipher suite: %s%n", protocol, cipherSuite); - // Named group converted to lower case just - // to satisfy Test condition new NamedGroupsWithCipherSuite(protocol, - cipherSuite, namedGroup.toLowerCase()).run(); + cipherSuite, normalizedGroup).run(); } } } } - private static boolean groupSupportdByCipher(String group, + private static boolean groupSupportedByCipher(String group, CipherSuite cipherSuite) { + if (HYBRID_NAMEDGROUPS.contains(group)) { + return cipherSuite.keyExAlgorithm == null; + } + return (group.startsWith("x") - && xdhGroupSupportdByCipher(cipherSuite)) + && xdhGroupSupportedByCipher(cipherSuite)) || (group.startsWith("secp") - && ecdhGroupSupportdByCipher(cipherSuite)) + && ecdhGroupSupportedByCipher(cipherSuite)) || (group.startsWith("ffdhe") - && ffdhGroupSupportdByCipher(cipherSuite)); + && ffdhGroupSupportedByCipher(cipherSuite)); } - private static boolean xdhGroupSupportdByCipher( + private static boolean xdhGroupSupportedByCipher( CipherSuite cipherSuite) { return cipherSuite.keyExAlgorithm == null || cipherSuite.keyExAlgorithm == KeyExAlgorithm.ECDHE_RSA; } - private static boolean ecdhGroupSupportdByCipher( + private static boolean ecdhGroupSupportedByCipher( CipherSuite cipherSuite) { return cipherSuite.keyExAlgorithm == null || cipherSuite.keyExAlgorithm == KeyExAlgorithm.ECDHE_RSA || cipherSuite.keyExAlgorithm == KeyExAlgorithm.ECDHE_ECDSA; } - private static boolean ffdhGroupSupportdByCipher( + private static boolean ffdhGroupSupportedByCipher( CipherSuite cipherSuite) { return cipherSuite.keyExAlgorithm == null || cipherSuite.keyExAlgorithm == KeyExAlgorithm.DHE_DSS diff --git a/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java b/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java index c4c343bf84e..4ff0c6e6e15 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java +++ b/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8226374 8242929 + * @bug 8226374 8242929 8314323 * @library /javax/net/ssl/templates * @summary Restrict signature algorithms and named groups * @run main/othervm RestrictNamedGroup x25519 @@ -36,6 +36,9 @@ * @run main/othervm RestrictNamedGroup ffdhe4096 * @run main/othervm RestrictNamedGroup ffdhe6144 * @run main/othervm RestrictNamedGroup ffdhe8192 + * @run main/othervm RestrictNamedGroup X25519MLKEM768 + * @run main/othervm RestrictNamedGroup SecP256r1MLKEM768 + * @run main/othervm RestrictNamedGroup SecP384r1MLKEM1024 */ import java.security.Security; diff --git a/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java b/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java index 88b0bf2489a..8cf6ee2b5e6 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java +++ b/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8171279 + * @bug 8171279 8314323 * @library /javax/net/ssl/templates * @summary Test TLS connection with each individual supported group * @run main/othervm SupportedGroups x25519 @@ -36,6 +36,9 @@ * @run main/othervm SupportedGroups ffdhe4096 * @run main/othervm SupportedGroups ffdhe6144 * @run main/othervm SupportedGroups ffdhe8192 + * @run main/othervm SupportedGroups X25519MLKEM768 + * @run main/othervm SupportedGroups SecP256r1MLKEM768 + * @run main/othervm SupportedGroups SecP384r1MLKEM1024 */ import java.net.InetAddress; import java.util.Arrays; @@ -45,15 +48,24 @@ import javax.net.ssl.SSLServerSocket; public class SupportedGroups extends SSLSocketTemplate { private static volatile int index; - private static final String[][][] protocols = { + private static final String[][][] protocolsForClassic = { {{"TLSv1.3"}, {"TLSv1.3"}}, {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}}, {{"TLSv1.2"}, {"TLSv1.3", "TLSv1.2"}}, {{"TLSv1.2"}, {"TLSv1.2"}} }; - public SupportedGroups() { + private static final String[][][] protocolsForHybrid = { + {{"TLSv1.3"}, {"TLSv1.3"}}, + {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.3"}}, + {{"TLSv1.3"}, {"TLSv1.3", "TLSv1.2"}} + }; + + private final String[][][] protocols; + + public SupportedGroups(String[][][] protocols) { this.serverAddress = InetAddress.getLoopbackAddress(); + this.protocols = protocols; } // Servers are configured before clients, increment test case after. @@ -85,8 +97,18 @@ public class SupportedGroups extends SSLSocketTemplate { public static void main(String[] args) throws Exception { System.setProperty("jdk.tls.namedGroups", args[0]); + boolean hybridGroup = hybridNamedGroup(args[0]); + String[][][] protocols = hybridGroup ? + protocolsForHybrid : protocolsForClassic; + for (index = 0; index < protocols.length; index++) { - (new SupportedGroups()).run(); + (new SupportedGroups(protocols)).run(); } } + + private static boolean hybridNamedGroup(String namedGroup) { + return namedGroup.equals("X25519MLKEM768") || + namedGroup.equals("SecP256r1MLKEM768") || + namedGroup.equals("SecP384r1MLKEM1024"); + } } diff --git a/test/micro/org/openjdk/bench/java/security/SSLHandshake.java b/test/micro/org/openjdk/bench/java/security/SSLHandshake.java index d8773781b58..b46704a01de 100644 --- a/test/micro/org/openjdk/bench/java/security/SSLHandshake.java +++ b/test/micro/org/openjdk/bench/java/security/SSLHandshake.java @@ -44,6 +44,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManagerFactory; @@ -75,8 +76,15 @@ public class SSLHandshake { @Param({"true", "false"}) boolean resume; - @Param({"TLSv1.2", "TLS"}) - String tlsVersion; + @Param({ + "TLSv1.2-secp256r1", + "TLSv1.3-x25519", "TLSv1.3-secp256r1", "TLSv1.3-secp384r1", + "TLSv1.3-X25519MLKEM768", "TLSv1.3-SecP256r1MLKEM768", "TLSv1.3-SecP384r1MLKEM1024" + }) + String versionAndGroup; + + private String tlsVersion; + private String namedGroup; private static SSLContext getServerContext() { try { @@ -96,6 +104,10 @@ public class SSLHandshake { @Setup(Level.Trial) public void init() throws Exception { + String[] components = versionAndGroup.split("-", 2); + tlsVersion = components[0]; + namedGroup = components[1]; + KeyStore ts = TestCertificates.getTrustStore(); TrustManagerFactory tmf = TrustManagerFactory.getInstance( @@ -195,5 +207,14 @@ public class SSLHandshake { */ clientEngine = sslClientCtx.createSSLEngine("client", 80); clientEngine.setUseClientMode(true); + + // Set the key exchange named group in client and server engines + SSLParameters clientParams = clientEngine.getSSLParameters(); + clientParams.setNamedGroups(new String[]{namedGroup}); + clientEngine.setSSLParameters(clientParams); + + SSLParameters serverParams = serverEngine.getSSLParameters(); + serverParams.setNamedGroups(new String[]{namedGroup}); + serverEngine.setSSLParameters(serverParams); } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java index 3386039c62e..dc6d2060f5e 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java @@ -23,34 +23,69 @@ package org.openjdk.bench.javax.crypto.full; import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.OperationsPerInvocation; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.infra.Blackhole; import javax.crypto.DecapsulateException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; import javax.crypto.KEM; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.spec.ECGenParameterSpec; -public class KEMBench extends CryptoBase { +public abstract class KEMBench extends CryptoBase { public static final int SET_SIZE = 128; - @Param({"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024" }) + @Param({}) private String algorithm; + @Param({""}) // Used when the KeyPairGenerator Alg != KEM Alg + private String kpgSpec; + private KeyPair[] keys; private byte[][] messages; private KEM kem; @Setup - public void setup() throws NoSuchAlgorithmException, InvalidKeyException { - kem = (prov == null) ? KEM.getInstance(algorithm) : KEM.getInstance(algorithm, prov); - KeyPairGenerator generator = (prov == null) ? KeyPairGenerator.getInstance(algorithm) : KeyPairGenerator.getInstance(algorithm, prov); + public void setup() throws NoSuchAlgorithmException, InvalidKeyException, + InvalidAlgorithmParameterException { + String kpgAlg; + String kpgParams; + kem = (prov == null) ? KEM.getInstance(algorithm) : + KEM.getInstance(algorithm, prov); + + // By default use the same provider for KEM and KPG + Provider kpgProv = prov; + if (kpgSpec.isEmpty()) { + kpgAlg = algorithm; + kpgParams = ""; + } else { + // The key pair generation spec is broken down from a colon- + // delimited string spec into 3 fields: + // [0] - the provider name + // [1] - the algorithm name + // [2] - the parameters (i.e. the name of the curve) + String[] kpgTok = kpgSpec.split(":"); + kpgProv = Security.getProvider(kpgTok[0]); + kpgAlg = kpgTok[1]; + kpgParams = kpgTok[2]; + } + KeyPairGenerator generator = (kpgProv == null) ? + KeyPairGenerator.getInstance(kpgAlg) : + KeyPairGenerator.getInstance(kpgAlg, kpgProv); + if (kpgParams != null && !kpgParams.isEmpty()) { + generator.initialize(new ECGenParameterSpec(kpgParams)); + } keys = new KeyPair[SET_SIZE]; for (int i = 0; i < keys.length; i++) { keys[i] = generator.generateKeyPair(); @@ -63,20 +98,79 @@ public class KEMBench extends CryptoBase { } } + private static Provider getInternalJce() { + try { + Class dhClazz = Class.forName("sun.security.ssl.HybridProvider"); + return (Provider) dhClazz.getField("PROVIDER").get(null); + } catch (ReflectiveOperationException exc) { + throw new RuntimeException(exc); + } + } + @Benchmark @OperationsPerInvocation(SET_SIZE) public void encapsulate(Blackhole bh) throws InvalidKeyException { for (KeyPair kp : keys) { - bh.consume(kem.newEncapsulator(kp.getPublic()).encapsulate().encapsulation()); + bh.consume(kem.newEncapsulator(kp.getPublic()).encapsulate(). + encapsulation()); } } @Benchmark @OperationsPerInvocation(SET_SIZE) - public void decapsulate(Blackhole bh) throws InvalidKeyException, DecapsulateException { + public void decapsulate(Blackhole bh) throws InvalidKeyException, + DecapsulateException { for (int i = 0; i < messages.length; i++) { - bh.consume(kem.newDecapsulator(keys[i].getPrivate()).decapsulate(messages[i])); + bh.consume(kem.newDecapsulator(keys[i].getPrivate()). + decapsulate(messages[i])); } } + public static class MLKEM extends KEMBench { + @Param({"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024" }) + private String algorithm; + + @Param({""}) // ML-KEM uses the same alg for KPG and KEM + private String kpgSpec; + } + + @Fork(value = 5, jvmArgs = {"-XX:+AlwaysPreTouch", "--add-opens", + "java.base/sun.security.ssl=ALL-UNNAMED"}) + public static class JSSE_DHasKEM extends KEMBench { + @Setup + public void init() { + try { + prov = getInternalJce(); + super.setup(); + } catch (GeneralSecurityException gse) { + throw new RuntimeException(gse); + } + } + + @Param({"DH"}) + private String algorithm; + + @Param({"SunEC:XDH:x25519", "SunEC:EC:secp256r1", "SunEC:EC:secp384r1"}) + private String kpgSpec; + } + + @Fork(value = 5, jvmArgs = {"-XX:+AlwaysPreTouch", "--add-opens", + "java.base/sun.security.ssl=ALL-UNNAMED"}) + public static class JSSE_Hybrid extends KEMBench { + @Setup + public void init() { + try { + prov = getInternalJce(); + super.setup(); + } catch (GeneralSecurityException gse) { + throw new RuntimeException(gse); + } + } + + @Param({"X25519MLKEM768", "SecP256r1MLKEM768", "SecP384r1MLKEM1024"}) + private String algorithm; + + @Param({""}) // ML-KEM uses the same alg for KPG and KEM + private String kpgSpec; + } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java index 58daff28d88..5a3c72fb263 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java @@ -22,13 +22,16 @@ */ package org.openjdk.bench.javax.crypto.full; +import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; +import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.Provider; public class KeyPairGeneratorBench extends CryptoBase { @@ -45,11 +48,21 @@ public class KeyPairGeneratorBench extends CryptoBase { setupProvider(); generator = (prov == null) ? KeyPairGenerator.getInstance(algorithm) : KeyPairGenerator.getInstance(algorithm, prov); - if (keyLength > 0) { // not all key pair generators allow the use of key length + // not all key pair generators allow the use of key length + if (keyLength > 0) { generator.initialize(keyLength); } } + private static Provider getInternalJce() { + try { + Class dhClazz = Class.forName("sun.security.ssl.HybridProvider"); + return (Provider) dhClazz.getField("PROVIDER").get(null); + } catch (ReflectiveOperationException exc) { + throw new RuntimeException(exc); + } + } + @Benchmark public KeyPair generateKeyPair() { return generator.generateKeyPair(); @@ -118,4 +131,23 @@ public class KeyPairGeneratorBench extends CryptoBase { private int keyLength; } + @Fork(value = 5, jvmArgs = {"-XX:+AlwaysPreTouch", "--add-opens", + "java.base/sun.security.ssl=ALL-UNNAMED"}) + public static class JSSE_Hybrid extends KeyPairGeneratorBench { + @Setup + public void init() { + try { + prov = getInternalJce(); + super.setup(); + } catch (GeneralSecurityException gse) { + throw new RuntimeException(gse); + } + } + + @Param({"X25519MLKEM768", "SecP256r1MLKEM768", "SecP384r1MLKEM1024"}) + private String algorithm; + + @Param({"0"}) // Hybrid KPGs don't need key lengths + private int keyLength; + } } From b2b4729ba2dbbb7cecb177612bd08927ccb085f2 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 20 Jan 2026 16:28:23 +0000 Subject: [PATCH 079/126] 8375015: CompletionAPITest::testDocumentation failed - AssertionFailedError: expected: but was: Reviewed-by: jlahoda --- test/langtools/jdk/jshell/CompletionAPITest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/langtools/jdk/jshell/CompletionAPITest.java b/test/langtools/jdk/jshell/CompletionAPITest.java index ad5dea90a95..932482ce6dc 100644 --- a/test/langtools/jdk/jshell/CompletionAPITest.java +++ b/test/langtools/jdk/jshell/CompletionAPITest.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 8366691 + * @bug 8366691 8375015 * @summary Test JShell Completion API * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -67,7 +67,7 @@ import org.junit.jupiter.api.Test; public class CompletionAPITest extends KullaTesting { - private static final long TIMEOUT = 2_000; + private static final long TIMEOUT = 20_000; @Test public void testAPI() { @@ -144,7 +144,7 @@ public class CompletionAPITest extends KullaTesting { } @Test - public void testDocumentation() { + public void testDocumentation() throws Exception { waitIndexingFinished(); Path classes = prepareZip(); @@ -171,6 +171,7 @@ public class CompletionAPITest extends KullaTesting { while (clazz.get().get() != null && (System.currentTimeMillis() - start) < TIMEOUT) { System.gc(); + Thread.sleep(100); } assertNull(clazz.get().get()); From 72bf0bb6f6eaf61b3800d885733e23b7b42bf9c9 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Tue, 20 Jan 2026 16:49:02 +0000 Subject: [PATCH 080/126] 8353115: GenShen: mixed evacuation candidate regions need accurate live_data Reviewed-by: wkemper --- .../heuristics/shenandoahHeuristics.hpp | 7 +++++++ .../heuristics/shenandoahOldHeuristics.cpp | 15 +++++++++++++++ .../gc/shenandoah/shenandoahHeapRegion.cpp | 2 ++ .../gc/shenandoah/shenandoahHeapRegion.hpp | 11 +++++++++++ .../shenandoah/shenandoahHeapRegion.inline.hpp | 17 +++++++++++++++++ 5 files changed, 52 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index fb8cfb36353..e1139765022 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -129,6 +129,13 @@ protected: #endif } + inline void update_livedata(size_t live) { + _region_union._live_data = live; +#ifdef ASSERT + _union_tag = is_live_data; +#endif + } + inline ShenandoahHeapRegion* get_region() const { assert(_union_tag != is_uninitialized, "Cannot fetch region from uninitialized RegionData"); return _region; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 029a4dd98fb..f2c6e427ea8 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -89,6 +89,17 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll return false; } + // Between consecutive mixed-evacuation cycles, the live data within each candidate region may change due to + // promotions and old-gen evacuations. Re-sort the candidate regions in order to first evacuate regions that have + // the smallest amount of live data. These are easiest to evacuate with least effort. Doing these first allows + // us to more quickly replenish free memory with empty regions. + for (uint i = _next_old_collection_candidate; i < _last_old_collection_candidate; i++) { + ShenandoahHeapRegion* r = _region_data[i].get_region(); + _region_data[i].update_livedata(r->get_mixed_candidate_live_data_bytes()); + } + QuickSort::sort(_region_data + _next_old_collection_candidate, unprocessed_old_collection_candidates(), + compare_by_live); + _first_pinned_candidate = NOT_FOUND; uint included_old_regions = 0; @@ -414,6 +425,8 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { ShenandoahHeapRegion* r = candidates[i].get_region(); size_t region_garbage = r->garbage(); size_t region_free = r->free(); + + r->capture_mixed_candidate_garbage(); candidates_garbage += region_garbage; unfragmented += region_free; } @@ -456,6 +469,8 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { r->index(), ShenandoahHeapRegion::region_state_to_string(r->state())); const size_t region_garbage = r->garbage(); const size_t region_free = r->free(); + + r->capture_mixed_candidate_garbage(); candidates_garbage += region_garbage; unfragmented += region_free; defrag_count++; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index 3cd5cdd2ec3..e794a86e473 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -75,6 +75,7 @@ ShenandoahHeapRegion::ShenandoahHeapRegion(HeapWord* start, size_t index, bool c _plab_allocs(0), _live_data(0), _critical_pins(0), + _mixed_candidate_garbage_words(0), _update_watermark(start), _age(0), #ifdef SHENANDOAH_CENSUS_NOISE @@ -565,6 +566,7 @@ void ShenandoahHeapRegion::recycle_internal() { assert(_recycling.is_set() && is_trash(), "Wrong state"); ShenandoahHeap* heap = ShenandoahHeap::heap(); + _mixed_candidate_garbage_words = 0; set_top(bottom()); clear_live_data(); reset_alloc_metadata(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp index cf0dc5476d0..9da2816e2c9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp @@ -43,6 +43,7 @@ class ShenandoahHeapRegion { friend class VMStructs; friend class ShenandoahHeapRegionStateConstant; private: + /* Region state is described by a state machine. Transitions are guarded by heap lock, which allows changing the state of several regions atomically. @@ -259,6 +260,8 @@ private: volatile size_t _live_data; volatile size_t _critical_pins; + size_t _mixed_candidate_garbage_words; + HeapWord* volatile _update_watermark; uint _age; @@ -398,6 +401,14 @@ public: // above TAMS. inline size_t get_live_data_words() const; + inline size_t get_mixed_candidate_live_data_bytes() const; + inline size_t get_mixed_candidate_live_data_words() const; + + inline void capture_mixed_candidate_garbage(); + + // Returns garbage by calculating difference between used and get_live_data_words. The value returned is only + // meaningful immediately following completion of marking. If there have been subsequent allocations in this region, + // use a different approach to determine garbage, such as (used() - get_mixed_candidate_live_data_bytes()) inline size_t garbage() const; void print_on(outputStream* st) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp index b9304ee9daa..be982433885 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp @@ -163,6 +163,23 @@ inline size_t ShenandoahHeapRegion::get_live_data_bytes() const { return get_live_data_words() * HeapWordSize; } +inline size_t ShenandoahHeapRegion::get_mixed_candidate_live_data_bytes() const { + shenandoah_assert_heaplocked_or_safepoint(); + assert(used() >= _mixed_candidate_garbage_words * HeapWordSize, "used must exceed garbage"); + return used() - _mixed_candidate_garbage_words * HeapWordSize; +} + +inline size_t ShenandoahHeapRegion::get_mixed_candidate_live_data_words() const { + shenandoah_assert_heaplocked_or_safepoint(); + assert(used() >= _mixed_candidate_garbage_words * HeapWordSize, "used must exceed garbage"); + return used() / HeapWordSize - _mixed_candidate_garbage_words; +} + +inline void ShenandoahHeapRegion::capture_mixed_candidate_garbage() { + shenandoah_assert_heaplocked_or_safepoint(); + _mixed_candidate_garbage_words = garbage() / HeapWordSize; +} + inline bool ShenandoahHeapRegion::has_live() const { return get_live_data_words() != 0; } From 5f8cb30fc0296a2b487edf9dee63e810f4861e8e Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 18:16:39 +0000 Subject: [PATCH 081/126] 8375626: G1: Convert G1CollectionSetChooser to use Atomic Reviewed-by: kbarrett, shade --- .../share/gc/g1/g1CollectionSetChooser.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp index 954ca40a77f..d9496410c12 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp @@ -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 @@ -27,7 +27,7 @@ #include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/shared/space.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/quickSort.hpp" // Determine collection set candidates (from marking): For all regions determine @@ -50,7 +50,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { G1HeapRegion** _data; - uint volatile _cur_claim_idx; + Atomic _cur_claim_idx; static int compare_region_gc_efficiency(G1HeapRegion** rr1, G1HeapRegion** rr2) { G1HeapRegion* r1 = *rr1; @@ -105,7 +105,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { // Claim a new chunk, returning its bounds [from, to[. void claim_chunk(uint& from, uint& to) { - uint result = AtomicAccess::add(&_cur_claim_idx, _chunk_size); + uint result = _cur_claim_idx.add_then_fetch(_chunk_size); assert(_max_size > result - 1, "Array too small, is %u should be %u with chunk size %u.", _max_size, result, _chunk_size); @@ -121,14 +121,15 @@ class G1BuildCandidateRegionsTask : public WorkerTask { } void sort_by_gc_efficiency() { - if (_cur_claim_idx == 0) { + uint length = _cur_claim_idx.load_relaxed(); + if (length == 0) { return; } - for (uint i = _cur_claim_idx; i < _max_size; i++) { + for (uint i = length; i < _max_size; i++) { assert(_data[i] == nullptr, "must be"); } - qsort(_data, _cur_claim_idx, sizeof(_data[0]), (_sort_Fn)compare_region_gc_efficiency); - for (uint i = _cur_claim_idx; i < _max_size; i++) { + qsort(_data, length, sizeof(_data[0]), (_sort_Fn)compare_region_gc_efficiency); + for (uint i = length; i < _max_size; i++) { assert(_data[i] == nullptr, "must be"); } } From 42439eb60c4488711f182d0d6ee5165b4972b99d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 20 Jan 2026 18:30:42 +0000 Subject: [PATCH 082/126] 8374889: C2 VectorAPI: must handle impossible combination of signed cast from float Reviewed-by: dlong, qamai --- src/hotspot/share/opto/graphKit.cpp | 23 +++-- src/hotspot/share/opto/graphKit.hpp | 2 + src/hotspot/share/opto/parse1.cpp | 3 +- src/hotspot/share/opto/vectorIntrinsics.cpp | 18 +++- .../vectorapi/TestCastShapeBadOpc.java | 91 +++++++++++++++++++ 5 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 71ae8fe44a7..bc8ebaf1869 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1489,8 +1489,7 @@ Node* GraphKit::must_be_not_null(Node* value, bool do_replace_in_map) { } Node *if_f = _gvn.transform(new IfFalseNode(iff)); Node *frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); - Node* halt = _gvn.transform(new HaltNode(if_f, frame, "unexpected null in intrinsic")); - C->root()->add_req(halt); + halt(if_f, frame, "unexpected null in intrinsic"); Node *if_t = _gvn.transform(new IfTrueNode(iff)); set_control(if_t); return cast_not_null(value, do_replace_in_map); @@ -2073,6 +2072,12 @@ void GraphKit::increment_counter(Node* counter_addr) { store_to_memory(ctrl, counter_addr, incr, T_LONG, MemNode::unordered); } +void GraphKit::halt(Node* ctrl, Node* frameptr, const char* reason, bool generate_code_in_product) { + Node* halt = new HaltNode(ctrl, frameptr, reason + PRODUCT_ONLY(COMMA generate_code_in_product)); + halt = _gvn.transform(halt); + root()->add_req(halt); +} //------------------------------uncommon_trap---------------------------------- // Bail out to the interpreter in mid-method. Implemented by calling the @@ -2195,11 +2200,15 @@ Node* GraphKit::uncommon_trap(int trap_request, // The debug info is the only real input to this call. // Halt-and-catch fire here. The above call should never return! - HaltNode* halt = new HaltNode(control(), frameptr(), "uncommon trap returned which should never happen" - PRODUCT_ONLY(COMMA /*reachable*/false)); - _gvn.set_type_bottom(halt); - root()->add_req(halt); - + // We only emit code for the HaltNode in debug, which is enough for + // verifying correctness. In product, we don't want to emit it so + // that we can save on code space. HaltNode often get folded because + // the compiler can prove that the unreachable path is dead. But we + // cannot generally expect that for uncommon traps, which are often + // reachable and occasionally taken. + halt(control(), frameptr(), + "uncommon trap returned which should never happen", + false /* don't emit code in product */); stop_and_kill_map(); return call; } diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 56e9a949e6f..0537d31ae36 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -709,6 +709,8 @@ class GraphKit : public Phase { void increment_counter(address counter_addr); // increment a debug counter void increment_counter(Node* counter_addr); // increment a debug counter + void halt(Node* ctrl, Node* frameptr, const char* reason, bool generate_code_in_product = true); + // Bail out to the interpreter right now // The optional klass is the one causing the trap. // The optional reason is debug information written to the compile log. diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 7aa96d2ace3..6122f7e7bfc 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1229,8 +1229,7 @@ void Parse::do_method_entry() { Node* not_subtype_ctrl = gen_subtype_check(receiver_obj, holder_klass); assert(!stopped(), "not a subtype"); - Node* halt = _gvn.transform(new HaltNode(not_subtype_ctrl, frameptr(), "failed receiver subtype check")); - C->root()->add_req(halt); + halt(not_subtype_ctrl, frameptr(), "failed receiver subtype check"); } } #endif // ASSERT diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index b48b5f2cd05..6dcf4615b10 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -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 @@ -25,6 +25,7 @@ #include "ci/ciSymbols.hpp" #include "classfile/vmSymbols.hpp" #include "opto/library_call.hpp" +#include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/vectornode.hpp" #include "prims/vectorSupport.hpp" @@ -2330,6 +2331,21 @@ bool LibraryCallKit::inline_vector_convert() { Node* op = opd1; if (is_cast) { assert(!is_mask || num_elem_from == num_elem_to, "vector mask cast needs the same elem num"); + + // Make sure the precondition of VectorCastNode::opcode holds: we can only have + // unsigned casts for integral types (excluding long). VectorAPI code is not + // expected to violate this at runtime, but we may compile unreachable code + // where such impossible combinations arise. + if (is_ucast && (!is_integral_type(elem_bt_from) || elem_bt_from == T_LONG)) { + // Halt-and-catch fire here. This condition should never happen at runtime. + stringStream ss; + ss.print("impossible combination: unsigned vector cast from %s", type2name(elem_bt_from)); + halt(control(), frameptr(), ss.as_string(C->comp_arena())); + stop_and_kill_map(); + log_if_needed(" ** impossible combination: unsigned cast from %s", type2name(elem_bt_from)); + return true; + } + int cast_vopc = VectorCastNode::opcode(-1, elem_bt_from, !is_ucast); // Make sure that vector cast is implemented to particular type/size combination if it is diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java new file mode 100644 index 00000000000..4c20c84bc50 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java @@ -0,0 +1,91 @@ +/* + * 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=all-flags + * @bug 8374889 + * @summary Test case that can compile unexpected code paths in VectorAPI cast intrinsification. + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation -Xbatch + * -XX:StressSeed=1462975402 + * -XX:+StressIncrementalInlining + * -XX:CompileCommand=compileonly,${test.main.class}::test2 + * ${test.main.class} + */ + +/* + * @test id=no-stress-seed + * @bug 8374889 + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation -Xbatch + * -XX:+StressIncrementalInlining + * -XX:CompileCommand=compileonly,${test.main.class}::test2 + * ${test.main.class} + */ + +/* + * @test id=vanilla + * @bug 8374889 + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.vectorapi; + +import jdk.incubator.vector.*; + +public class TestCastShapeBadOpc { + public static void main(String[] args) { + for (int i = 0; i < 100_000; ++i) { + test1(); + test2(); + } + } + + // This code does not trigger the bug itself, but seems to be important for profiling, + // so that test2 fails. + public static Object test1() { + LongVector v0 = LongVector.broadcast(LongVector.SPECIES_512, -15L); + var v1 = (ByteVector)v0.convertShape(VectorOperators.Conversion.ofReinterpret(long.class, byte.class), ByteVector.SPECIES_128, 0); + var v2 = (ByteVector)v1.castShape(ByteVector.SPECIES_256, 0); + return v2; + } + + public static Object test2() { + var v0 = ShortVector.broadcast(ShortVector.SPECIES_64, (short)7729); + var v1 = (FloatVector)v0.reinterpretShape(FloatVector.SPECIES_64, 0); + // The castShape below should take the "C" path in AbstractVector::convert0, but sometimes + // we also compile the "Z" case because of profiling. This means we attempt to create + // a vector cast from float -> long, but unfortunately with a UCAST (float -> long is signed). + // This triggered an assert in VectorCastNode::opcode. + var v2 = (LongVector)v1.castShape(LongVector.SPECIES_256, 0); + return v2; + } +} From aaca0a2c1f3de06a1349ae9084e9e9dbec991421 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 20 Jan 2026 21:54:56 +0000 Subject: [PATCH 083/126] 8375742: Test java/lang/invoke/MethodHandleProxies/Driver.java does not run Unnamed.java Reviewed-by: jvernee --- test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java | 6 +++--- test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java b/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java index 6acd4fb30e1..0e9c708e8e9 100644 --- a/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java +++ b/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 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 @@ -21,12 +21,12 @@ * questions. */ -/** +/* * @test * @bug 8280377 * @build m1/* m2/* Unnamed * @run testng/othervm m1/p1.Main - * @run testng/othervm Unnamed + * @run main/othervm Unnamed * @summary Test MethodHandleProxies::asInterfaceInstance with a default * method with varargs */ diff --git a/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java b/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java index f42071f0427..f60f36ca9de 100644 --- a/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java +++ b/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 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 @@ -39,7 +39,7 @@ public class Unnamed { // verify that the caller has no access to the proxy created on an // inaccessible interface - Method m = intf.getMethod("test", Object[].class); - assertFalse(m.canAccess(null)); + Method m = intf.getMethod("test"); + assertFalse(m.canAccess(t)); } } From 4fd7595f1b607588d9854471a701c2992c6bec60 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Tue, 20 Jan 2026 22:45:39 +0000 Subject: [PATCH 084/126] 8374905: Clarify ZonedDateTime#toString() documentation regarding omitted zero seconds Reviewed-by: rriggs, bpb --- src/java.base/share/classes/java/time/ZonedDateTime.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/time/ZonedDateTime.java b/src/java.base/share/classes/java/time/ZonedDateTime.java index 57dc98d5c68..b1ffe7b87d6 100644 --- a/src/java.base/share/classes/java/time/ZonedDateTime.java +++ b/src/java.base/share/classes/java/time/ZonedDateTime.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 @@ -2207,7 +2207,10 @@ public final class ZonedDateTime * Outputs this date-time as a {@code String}, such as * {@code 2007-12-03T10:15:30+01:00[Europe/Paris]}. *

- * The format consists of the {@code LocalDateTime} followed by the {@code ZoneOffset}. + * The format consists of the output of {@link LocalDateTime#toString()}, + * followed by the output of {@link ZoneOffset#toString()}. + * If the time has zero seconds and/or nanoseconds, they are + * omitted to produce the shortest representation. * If the {@code ZoneId} is not the same as the offset, then the ID is output. * The output is compatible with ISO-8601 if the offset and ID are the same, * and the seconds in the offset are zero. From ca3e6236a28794156cc2acf697755229c47735a8 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 20 Jan 2026 23:48:42 +0000 Subject: [PATCH 085/126] 8375657: RISC-V: Need to check size in SharedRuntime::is_wide_vector Reviewed-by: fjiang, fyang --- src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 44a6f6c0dc0..8c343f6ab2b 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -213,7 +213,7 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm) { // Is vector's size (in bytes) bigger than a size saved by default? // riscv does not ovlerlay the floating-point registers on vector registers like aarch64. bool SharedRuntime::is_wide_vector(int size) { - return UseRVV; + return UseRVV && size > 0; } // --------------------------------------------------------------------------- From a2e749572e03dd394d123b701e163e3837472dd0 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 21 Jan 2026 03:12:18 +0000 Subject: [PATCH 086/126] 8375063: Update Libpng to 1.6.54 Reviewed-by: serb, prr --- src/java.desktop/share/legal/libpng.md | 10 +- .../native/libsplashscreen/libpng/CHANGES | 27 + .../native/libsplashscreen/libpng/LICENSE | 4 +- .../native/libsplashscreen/libpng/README | 2 +- .../share/native/libsplashscreen/libpng/png.c | 21 +- .../share/native/libsplashscreen/libpng/png.h | 1121 ++++++++++------- .../native/libsplashscreen/libpng/pngconf.h | 4 +- .../native/libsplashscreen/libpng/pngerror.c | 17 +- .../native/libsplashscreen/libpng/pngget.c | 13 +- .../libsplashscreen/libpng/pnglibconf.h | 4 +- .../native/libsplashscreen/libpng/pngmem.c | 19 +- .../native/libsplashscreen/libpng/pngpriv.h | 921 ++++++++------ .../native/libsplashscreen/libpng/pngread.c | 188 +-- .../native/libsplashscreen/libpng/pngrtran.c | 5 +- .../native/libsplashscreen/libpng/pngrutil.c | 6 +- .../native/libsplashscreen/libpng/pngtrans.c | 4 +- 16 files changed, 1401 insertions(+), 965 deletions(-) diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index 8899491c6c0..80d12248ec4 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.51 +## libpng v1.6.54 ### libpng License

@@ -9,8 +9,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
 PNG Reference Library License version 2
 ---------------------------------------
 
-Copyright (C) 1995-2025 The PNG Reference Library Authors.
-Copyright (C) 2018-2025 Cosmin Truta
+Copyright (C) 1995-2026 The PNG Reference Library Authors.
+Copyright (C) 2018-2026 Cosmin Truta
 Copyright (C) 1998-2018 Glenn Randers-Pehrson
 Copyright (C) 1996-1997 Andreas Dilger
 Copyright (C) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -158,6 +158,7 @@ This is the list of PNG Reference Library ("libpng") Contributing
 Authors, for copyright and licensing purposes.
 
  * Adam Richter
+ * Alexander Smorkalov
  * Andreas Dilger
  * Chris Blume
  * Cosmin Truta
@@ -179,6 +180,7 @@ Authors, for copyright and licensing purposes.
  * Mike Klein
  * Pascal Massimino
  * Paul Schmidt
+ * Petr Simecek
  * Philippe Antoine
  * Qiang Zhou
  * Sam Bushell
@@ -209,6 +211,8 @@ Authors, for copyright and licensing purposes.
     - ZhangLixia (张利霞)
  * Samsung Group
     - Filip Wasil
+ * SpacemiT Hangzhou Technology, Co.
+    - Liang Junzhao (梁俊钊)
 
 The build projects, the build scripts, the test scripts, and other
 files in the "projects", "scripts" and "tests" directories, have
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
index 2478fd0fc08..3bb1baecd23 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
@@ -6304,6 +6304,33 @@ Version 1.6.51 [November 21, 2025]
   Added GitHub Actions workflows for automated testing.
   Performed various refactorings and cleanups.
 
+Version 1.6.52 [December 3, 2025]
+  Fixed CVE-2025-66293 (high severity):
+    Out-of-bounds read in `png_image_read_composite`.
+    (Reported by flyfish101 .)
+  Fixed the Paeth filter handling in the RISC-V RVV implementation.
+    (Reported by Filip Wasil; fixed by Liang Junzhao.)
+  Improved the performance of the RISC-V RVV implementation.
+    (Contributed by Liang Junzhao.)
+  Added allocation failure fuzzing to oss-fuzz.
+    (Contributed by Philippe Antoine.)
+
+Version 1.6.53 [December 5, 2025]
+  Fixed a build failure on RISC-V RVV caused by a misspelled intrinsic.
+    (Contributed by Alexander Smorkalov.)
+  Fixed a build failure with CMake 4.1 or newer, on Windows, when using
+    Visual C++ without MASM installed.
+
+Version 1.6.54 [January 12, 2026]
+  Fixed CVE-2026-22695 (medium severity):
+    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.)
+
+
 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
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
index ea6df986cb6..1b765ae9f96 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
@@ -4,8 +4,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
 PNG Reference Library License version 2
 ---------------------------------------
 
- * Copyright (c) 1995-2025 The PNG Reference Library Authors.
- * Copyright (c) 2018-2025 Cosmin Truta.
+ * Copyright (c) 1995-2026 The PNG Reference Library Authors.
+ * Copyright (c) 2018-2026 Cosmin Truta.
  * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
  * Copyright (c) 1996-1997 Andreas Dilger.
  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README
index 5ea329ee3da..63d1376edf7 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.51
+README for libpng version 1.6.54
 ================================
 
 See the note about version numbers near the top of `png.h`.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
index 7d85e7c8d5f..5636b4a754e 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.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.
@@ -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_51 Your_png_h_is_not_version_1_6_51;
+typedef png_libpng_version_1_6_54 Your_png_h_is_not_version_1_6_54;
 
 /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
  * corresponding macro definitions.  This causes a compile time failure if
@@ -130,7 +130,8 @@ png_sig_cmp(png_const_bytep sig, size_t start, size_t num_to_check)
 #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
 /* Function to allocate memory for zlib */
 PNG_FUNCTION(voidpf /* PRIVATE */,
-png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)
+png_zalloc,(voidpf png_ptr, uInt items, uInt size),
+    PNG_ALLOCATED)
 {
    png_alloc_size_t num_bytes = size;
 
@@ -286,7 +287,8 @@ png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
 PNG_FUNCTION(png_structp /* PRIVATE */,
 png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
     png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
+    png_malloc_ptr malloc_fn, png_free_ptr free_fn),
+    PNG_ALLOCATED)
 {
    png_struct create_struct;
 #  ifdef PNG_SETJMP_SUPPORTED
@@ -390,7 +392,8 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
 
 /* Allocate the memory for an info_struct for the application. */
 PNG_FUNCTION(png_infop,PNGAPI
-png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
+png_create_info_struct,(png_const_structrp png_ptr),
+    PNG_ALLOCATED)
 {
    png_inforp info_ptr;
 
@@ -846,8 +849,8 @@ png_get_copyright(png_const_structrp png_ptr)
    return PNG_STRING_COPYRIGHT
 #else
    return PNG_STRING_NEWLINE \
-      "libpng version 1.6.51" PNG_STRING_NEWLINE \
-      "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \
+      "libpng version 1.6.54" 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 \
       "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
@@ -2286,8 +2289,8 @@ PNG_FP_End:
 int
 png_check_fp_string(png_const_charp string, size_t size)
 {
-   int        state=0;
-   size_t char_index=0;
+   int state = 0;
+   size_t char_index = 0;
 
    if (png_check_fp_number(string, size, &state, &char_index) != 0 &&
       (char_index == size || string[char_index] == 0))
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
index d39ff73552c..ab8876a9626 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
@@ -29,9 +29,9 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.51
+ * libpng version 1.6.54
  *
- * 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.
@@ -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.51, November 2025:
+ *   libpng versions 1.6.36, December 2018, through 1.6.54, January 2026:
  *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
@@ -55,8 +55,8 @@
  * PNG Reference Library License version 2
  * ---------------------------------------
  *
- *  * Copyright (c) 1995-2025 The PNG Reference Library Authors.
- *  * Copyright (c) 2018-2025 Cosmin Truta.
+ *  * Copyright (c) 1995-2026 The PNG Reference Library Authors.
+ *  * Copyright (c) 2018-2026 Cosmin Truta.
  *  * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
  *  * Copyright (c) 1996-1997 Andreas Dilger.
  *  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -267,7 +267,7 @@
  *    ...
  *    1.5.30                  15    10530  15.so.15.30[.0]
  *    ...
- *    1.6.51                  16    10651  16.so.16.51[.0]
+ *    1.6.54                  16    10654  16.so.16.54[.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.51"
+#define PNG_LIBPNG_VER_STRING "1.6.54"
 #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 51
+#define PNG_LIBPNG_VER_RELEASE 54
 
 /* 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 10651 /* 1.6.51 */
+#define PNG_LIBPNG_VER 10654 /* 1.6.54 */
 
 /* 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_51;
+typedef char *png_libpng_version_1_6_54;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
@@ -814,17 +814,22 @@ typedef png_row_info * * png_row_infopp;
  * modify the buffer it is passed. The 'read' function, on the other hand, is
  * expected to return the read data in the buffer.
  */
-typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp));
-typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t));
-typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp));
-typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32,
-    int));
-typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32,
-    int));
+typedef PNG_CALLBACK(void, *png_error_ptr,
+   (png_structp, png_const_charp));
+typedef PNG_CALLBACK(void, *png_rw_ptr,
+   (png_structp, png_bytep, size_t));
+typedef PNG_CALLBACK(void, *png_flush_ptr,
+   (png_structp));
+typedef PNG_CALLBACK(void, *png_read_status_ptr,
+   (png_structp, png_uint_32, int));
+typedef PNG_CALLBACK(void, *png_write_status_ptr,
+   (png_structp, png_uint_32, int));
 
 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop));
-typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
+typedef PNG_CALLBACK(void, *png_progressive_info_ptr,
+   (png_structp, png_infop));
+typedef PNG_CALLBACK(void, *png_progressive_end_ptr,
+   (png_structp, png_infop));
 
 /* The following callback receives png_uint_32 row_number, int pass for the
  * png_bytep data of the row.  When transforming an interlaced image the
@@ -836,19 +841,19 @@ typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
  * find the output pixel (x,y) given an interlaced sub-image pixel
  * (row,col,pass).  (See below for these macros.)
  */
-typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep,
-    png_uint_32, int));
+typedef PNG_CALLBACK(void, *png_progressive_row_ptr,
+   (png_structp, png_bytep, png_uint_32, int));
 #endif
 
 #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
     defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
-typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop,
-    png_bytep));
+typedef PNG_CALLBACK(void, *png_user_transform_ptr,
+   (png_structp, png_row_infop, png_bytep));
 #endif
 
 #ifdef PNG_USER_CHUNKS_SUPPORTED
-typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp,
-    png_unknown_chunkp));
+typedef PNG_CALLBACK(int, *png_user_chunk_ptr,
+   (png_structp, png_unknown_chunkp));
 #endif
 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
 /* not used anywhere */
@@ -906,9 +911,10 @@ PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), (jmp_buf, int), typedef);
  * ignores the first argument) should be completely compatible with the
  * following.
  */
-typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp,
-    png_alloc_size_t));
-typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
+typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr,
+   (png_structp, png_alloc_size_t));
+typedef PNG_CALLBACK(void, *png_free_ptr,
+   (png_structp, png_voidp));
 
 /* Section 4: exported functions
  * Here are the function definitions most commonly used.  This is not
@@ -940,20 +946,22 @@ typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
  */
 
 /* Returns the version number of the library */
-PNG_EXPORT(1, png_uint_32, png_access_version_number, (void));
+PNG_EXPORT(1, png_uint_32, png_access_version_number,
+   (void));
 
 /* Tell lib we have already handled the first  magic bytes.
  * Handling more than 8 bytes from the beginning of the file is an error.
  */
-PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes));
+PNG_EXPORT(2, void, png_set_sig_bytes,
+   (png_structrp png_ptr, int num_bytes));
 
 /* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
  * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
  * signature, and non-zero otherwise.  Having num_to_check == 0 or
  * start > 7 will always fail (i.e. return non-zero).
  */
-PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start,
-    size_t num_to_check));
+PNG_EXPORT(3, int, png_sig_cmp,
+   (png_const_bytep sig, size_t start, size_t num_to_check));
 
 /* Simple signature checking function.  This is the same as calling
  * png_check_sig(sig, n) := (png_sig_cmp(sig, 0, n) == 0).
@@ -962,21 +970,21 @@ PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start,
 
 /* Allocate and initialize png_ptr struct for reading, and any other memory. */
 PNG_EXPORTA(4, png_structp, png_create_read_struct,
-    (png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn),
-    PNG_ALLOCATED);
+   (png_const_charp user_png_ver,
+    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn),
+   PNG_ALLOCATED);
 
 /* Allocate and initialize png_ptr struct for writing, and any other memory */
 PNG_EXPORTA(5, png_structp, png_create_write_struct,
-    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn),
-    PNG_ALLOCATED);
+   (png_const_charp user_png_ver,
+    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn),
+   PNG_ALLOCATED);
 
 PNG_EXPORT(6, size_t, png_get_compression_buffer_size,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 
-PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
-    size_t size));
+PNG_EXPORT(7, void, png_set_compression_buffer_size,
+   (png_structrp png_ptr, size_t size));
 
 /* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp
  * match up.
@@ -989,8 +997,8 @@ PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
  * allocated by the library - the call will return NULL on a mismatch
  * indicating an ABI mismatch.
  */
-PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr,
-    png_longjmp_ptr longjmp_fn, size_t jmp_buf_size));
+PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn,
+   (png_structrp png_ptr, png_longjmp_ptr longjmp_fn, size_t jmp_buf_size));
 #  define png_jmpbuf(png_ptr) \
       (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf))))
 #else
@@ -1002,67 +1010,77 @@ PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr,
  * will use it; otherwise it will call PNG_ABORT().  This function was
  * added in libpng-1.5.0.
  */
-PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val),
-    PNG_NORETURN);
+PNG_EXPORTA(9, void, png_longjmp,
+   (png_const_structrp png_ptr, int val),
+   PNG_NORETURN);
 
 #ifdef PNG_READ_SUPPORTED
 /* Reset the compression stream */
-PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED);
+PNG_EXPORTA(10, int, png_reset_zstream,
+   (png_structrp png_ptr),
+   PNG_DEPRECATED);
 #endif
 
 /* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
 #ifdef PNG_USER_MEM_SUPPORTED
 PNG_EXPORTA(11, png_structp, png_create_read_struct_2,
-    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn,
+   (png_const_charp user_png_ver,
+    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn,
     png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
-    PNG_ALLOCATED);
+   PNG_ALLOCATED);
 PNG_EXPORTA(12, png_structp, png_create_write_struct_2,
-    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn,
+   (png_const_charp user_png_ver,
+    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn,
     png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
-    PNG_ALLOCATED);
+   PNG_ALLOCATED);
 #endif
 
 /* Write the PNG file signature. */
-PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr));
+PNG_EXPORT(13, void, png_write_sig,
+   (png_structrp png_ptr));
 
 /* Write a PNG chunk - size, type, (optional) data, CRC. */
-PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep
-    chunk_name, png_const_bytep data, size_t length));
+PNG_EXPORT(14, void, png_write_chunk,
+   (png_structrp png_ptr,
+    png_const_bytep chunk_name, png_const_bytep data, size_t length));
 
 /* Write the start of a PNG chunk - length and chunk name. */
-PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr,
+PNG_EXPORT(15, void, png_write_chunk_start,
+   (png_structrp png_ptr,
     png_const_bytep chunk_name, png_uint_32 length));
 
 /* Write the data of a PNG chunk started with png_write_chunk_start(). */
-PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr,
+PNG_EXPORT(16, void, png_write_chunk_data,
+   (png_structrp png_ptr,
     png_const_bytep data, size_t length));
 
 /* Finish a chunk started with png_write_chunk_start() (includes CRC). */
-PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr));
+PNG_EXPORT(17, void, png_write_chunk_end,
+   (png_structrp png_ptr));
 
 /* Allocate and initialize the info structure */
-PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr),
-    PNG_ALLOCATED);
+PNG_EXPORTA(18, png_infop, png_create_info_struct,
+   (png_const_structrp png_ptr),
+   PNG_ALLOCATED);
 
 /* DEPRECATED: this function allowed init structures to be created using the
  * default allocation method (typically malloc).  Use is deprecated in 1.6.0 and
  * the API will be removed in the future.
  */
-PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr,
-    size_t png_info_struct_size), PNG_DEPRECATED);
+PNG_EXPORTA(19, void, png_info_init_3,
+   (png_infopp info_ptr, size_t png_info_struct_size),
+   PNG_DEPRECATED);
 
 /* Writes all the PNG information before the image. */
 PNG_EXPORT(20, void, png_write_info_before_PLTE,
-    (png_structrp png_ptr, png_const_inforp info_ptr));
+   (png_structrp png_ptr, png_const_inforp info_ptr));
 PNG_EXPORT(21, void, png_write_info,
-    (png_structrp png_ptr, png_const_inforp info_ptr));
+   (png_structrp png_ptr, png_const_inforp info_ptr));
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 /* Read the information before the actual image data. */
 PNG_EXPORT(22, void, png_read_info,
-    (png_structrp png_ptr, png_inforp info_ptr));
+   (png_structrp png_ptr, png_inforp info_ptr));
 #endif
 
 #ifdef PNG_TIME_RFC1123_SUPPORTED
@@ -1072,45 +1090,54 @@ PNG_EXPORT(22, void, png_read_info,
     */
 #if PNG_LIBPNG_VER < 10700
 /* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */
-PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr,
-    png_const_timep ptime),PNG_DEPRECATED);
+PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123,
+   (png_structrp png_ptr, png_const_timep ptime),
+   PNG_DEPRECATED);
 #endif
-PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29],
-    png_const_timep ptime));
+PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer,
+   (char out[29], png_const_timep ptime));
 #endif
 
 #ifdef PNG_CONVERT_tIME_SUPPORTED
 /* Convert from a struct tm to png_time */
-PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime,
-    const struct tm * ttime));
+PNG_EXPORT(24, void, png_convert_from_struct_tm,
+   (png_timep ptime, const struct tm * ttime));
 
 /* Convert from time_t to png_time.  Uses gmtime() */
-PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime));
+PNG_EXPORT(25, void, png_convert_from_time_t,
+   (png_timep ptime, time_t ttime));
 #endif /* CONVERT_tIME */
 
 #ifdef PNG_READ_EXPAND_SUPPORTED
 /* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
-PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr));
-PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr));
-PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr));
-PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr));
+PNG_EXPORT(26, void, png_set_expand,
+   (png_structrp png_ptr));
+PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8,
+   (png_structrp png_ptr));
+PNG_EXPORT(28, void, png_set_palette_to_rgb,
+   (png_structrp png_ptr));
+PNG_EXPORT(29, void, png_set_tRNS_to_alpha,
+   (png_structrp png_ptr));
 #endif
 
 #ifdef PNG_READ_EXPAND_16_SUPPORTED
 /* Expand to 16-bit channels, forces conversion of palette to RGB and expansion
  * of a tRNS chunk if present.
  */
-PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr));
+PNG_EXPORT(221, void, png_set_expand_16,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
 /* Use blue, green, red order for pixels. */
-PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr));
+PNG_EXPORT(30, void, png_set_bgr,
+   (png_structrp png_ptr));
 #endif
 
 #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
 /* Expand the grayscale to 24-bit RGB if necessary. */
-PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr));
+PNG_EXPORT(31, void, png_set_gray_to_rgb,
+   (png_structrp png_ptr));
 #endif
 
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
@@ -1120,18 +1147,20 @@ PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr));
 #define PNG_ERROR_ACTION_ERROR 3
 #define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/
 
-PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr,
+PNG_FP_EXPORT(32, void, png_set_rgb_to_gray,
+   (png_structrp png_ptr,
     int error_action, double red, double green))
-PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr,
+PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed,
+   (png_structrp png_ptr,
     int error_action, png_fixed_point red, png_fixed_point green))
 
-PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp
-    png_ptr));
+PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status,
+   (png_const_structrp png_ptr));
 #endif
 
 #ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
-PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
-    png_colorp palette));
+PNG_EXPORT(35, void, png_build_grayscale_palette,
+   (int bit_depth, png_colorp palette));
 #endif
 
 #ifdef PNG_READ_ALPHA_MODE_SUPPORTED
@@ -1176,10 +1205,10 @@ PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
 #define PNG_ALPHA_OPTIMIZED     2 /* 'PNG' for opaque pixels, else 'STANDARD' */
 #define PNG_ALPHA_BROKEN        3 /* the alpha channel is gamma encoded */
 
-PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode,
-    double output_gamma))
-PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr,
-    int mode, png_fixed_point output_gamma))
+PNG_FP_EXPORT(227, void, png_set_alpha_mode,
+   (png_structrp png_ptr, int mode, double output_gamma))
+PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed,
+   (png_structrp png_ptr, int mode, png_fixed_point output_gamma))
 #endif
 
 #if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED)
@@ -1269,51 +1298,57 @@ PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr,
  */
 
 #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
-PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr));
+PNG_EXPORT(36, void, png_set_strip_alpha,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
     defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
-PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr));
+PNG_EXPORT(37, void, png_set_swap_alpha,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
     defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
-PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr));
+PNG_EXPORT(38, void, png_set_invert_alpha,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
 /* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
-PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler,
-    int flags));
+PNG_EXPORT(39, void, png_set_filler,
+   (png_structrp png_ptr, png_uint_32 filler, int flags));
 /* The values of the PNG_FILLER_ defines should NOT be changed */
 #  define PNG_FILLER_BEFORE 0
 #  define PNG_FILLER_AFTER 1
 /* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
-PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr,
-    png_uint_32 filler, int flags));
+PNG_EXPORT(40, void, png_set_add_alpha,
+   (png_structrp png_ptr, png_uint_32 filler, int flags));
 #endif /* READ_FILLER || WRITE_FILLER */
 
 #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
 /* Swap bytes in 16-bit depth files. */
-PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr));
+PNG_EXPORT(41, void, png_set_swap,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
 /* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
-PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr));
+PNG_EXPORT(42, void, png_set_packing,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
     defined(PNG_WRITE_PACKSWAP_SUPPORTED)
 /* Swap packing order of pixels in bytes. */
-PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr));
+PNG_EXPORT(43, void, png_set_packswap,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
 /* Converts files to legal bit depths. */
-PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p
-    true_bits));
+PNG_EXPORT(44, void, png_set_shift,
+   (png_structrp png_ptr, png_const_color_8p true_bits));
 #endif
 
 #if defined(PNG_READ_INTERLACING_SUPPORTED) || \
@@ -1324,12 +1359,14 @@ PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p
  * necessary to call png_read_row or png_read_rows png_get_image_height
  * times for each pass.
 */
-PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr));
+PNG_EXPORT(45, int, png_set_interlace_handling,
+   (png_structrp png_ptr));
 #endif
 
 #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
 /* Invert monochrome files */
-PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr));
+PNG_EXPORT(46, void, png_set_invert_mono,
+   (png_structrp png_ptr));
 #endif
 
 #ifdef PNG_READ_BACKGROUND_SUPPORTED
@@ -1338,10 +1375,12 @@ PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr));
  * read.  Doing so will result in unexpected behavior and possible warnings or
  * errors if the PNG file contains a bKGD chunk.
  */
-PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr,
+PNG_FP_EXPORT(47, void, png_set_background,
+   (png_structrp png_ptr,
     png_const_color_16p background_color, int background_gamma_code,
     int need_expand, double background_gamma))
-PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr,
+PNG_FIXED_EXPORT(215, void, png_set_background_fixed,
+   (png_structrp png_ptr,
     png_const_color_16p background_color, int background_gamma_code,
     int need_expand, png_fixed_point background_gamma))
 #endif
@@ -1354,20 +1393,23 @@ PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr,
 
 #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
 /* Scale a 16-bit depth file down to 8-bit, accurately. */
-PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr));
+PNG_EXPORT(229, void, png_set_scale_16,
+   (png_structrp png_ptr));
 #endif
 
 #ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
 #define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */
 /* Strip the second byte of information from a 16-bit depth file. */
-PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr));
+PNG_EXPORT(48, void, png_set_strip_16,
+   (png_structrp png_ptr));
 #endif
 
 #ifdef PNG_READ_QUANTIZE_SUPPORTED
 /* Turn on quantizing, and reduce the palette to the number of colors
  * available.
  */
-PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr,
+PNG_EXPORT(49, void, png_set_quantize,
+   (png_structrp png_ptr,
     png_colorp palette, int num_palette, int maximum_colors,
     png_const_uint_16p histogram, int full_quantize));
 #endif
@@ -1389,82 +1431,92 @@ PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr,
  * API (floating point or fixed.)  Notice, however, that the 'file_gamma' value
  * is the inverse of a 'screen gamma' value.
  */
-PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr,
+PNG_FP_EXPORT(50, void, png_set_gamma,
+   (png_structrp png_ptr,
     double screen_gamma, double override_file_gamma))
-PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr,
+PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed,
+   (png_structrp png_ptr,
     png_fixed_point screen_gamma, png_fixed_point override_file_gamma))
 #endif
 
 #ifdef PNG_WRITE_FLUSH_SUPPORTED
 /* Set how many lines between output flushes - 0 for no flushing */
-PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows));
+PNG_EXPORT(51, void, png_set_flush,
+   (png_structrp png_ptr, int nrows));
 /* Flush the current PNG output buffer */
-PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr));
+PNG_EXPORT(52, void, png_write_flush,
+   (png_structrp png_ptr));
 #endif
 
 /* Optional update palette with requested transformations */
-PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr));
+PNG_EXPORT(53, void, png_start_read_image,
+   (png_structrp png_ptr));
 
 /* Optional call to update the users info structure */
-PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr,
-    png_inforp info_ptr));
+PNG_EXPORT(54, void, png_read_update_info,
+   (png_structrp png_ptr, png_inforp info_ptr));
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 /* Read one or more rows of image data. */
-PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row,
+PNG_EXPORT(55, void, png_read_rows,
+   (png_structrp png_ptr, png_bytepp row,
     png_bytepp display_row, png_uint_32 num_rows));
 #endif
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 /* Read a row of data. */
-PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row,
-    png_bytep display_row));
+PNG_EXPORT(56, void, png_read_row,
+   (png_structrp png_ptr, png_bytep row, png_bytep display_row));
 #endif
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 /* Read the whole image into memory at once. */
-PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image));
+PNG_EXPORT(57, void, png_read_image,
+   (png_structrp png_ptr, png_bytepp image));
 #endif
 
 /* Write a row of image data */
-PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr,
-    png_const_bytep row));
+PNG_EXPORT(58, void, png_write_row,
+   (png_structrp png_ptr, png_const_bytep row));
 
 /* Write a few rows of image data: (*row) is not written; however, the type
  * is declared as writeable to maintain compatibility with previous versions
  * of libpng and to allow the 'display_row' array from read_rows to be passed
  * unchanged to write_rows.
  */
-PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row,
-    png_uint_32 num_rows));
+PNG_EXPORT(59, void, png_write_rows,
+   (png_structrp png_ptr, png_bytepp row, png_uint_32 num_rows));
 
 /* Write the image data */
-PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image));
+PNG_EXPORT(60, void, png_write_image,
+   (png_structrp png_ptr, png_bytepp image));
 
 /* Write the end of the PNG file. */
-PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr,
-    png_inforp info_ptr));
+PNG_EXPORT(61, void, png_write_end,
+   (png_structrp png_ptr, png_inforp info_ptr));
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
 /* Read the end of the PNG file. */
-PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr));
+PNG_EXPORT(62, void, png_read_end,
+   (png_structrp png_ptr, png_inforp info_ptr));
 #endif
 
 /* Free any memory associated with the png_info_struct */
-PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr,
-    png_infopp info_ptr_ptr));
+PNG_EXPORT(63, void, png_destroy_info_struct,
+   (png_const_structrp png_ptr, png_infopp info_ptr_ptr));
 
 /* Free any memory associated with the png_struct and the png_info_structs */
-PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr,
+PNG_EXPORT(64, void, png_destroy_read_struct,
+   (png_structpp png_ptr_ptr,
     png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
 
 /* Free any memory associated with the png_struct and the png_info_structs */
-PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr,
-    png_infopp info_ptr_ptr));
+PNG_EXPORT(65, void, png_destroy_write_struct,
+   (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr));
 
 /* Set the libpng method of handling chunk CRC errors */
-PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
-    int ancil_action));
+PNG_EXPORT(66, void, png_set_crc_action,
+   (png_structrp png_ptr, int crit_action, int ancil_action));
 
 /* Values for png_set_crc_action() say how to handle CRC errors in
  * ancillary and critical chunks, and whether to use the data contained
@@ -1494,8 +1546,8 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
 /* Set the filtering method(s) used by libpng.  Currently, the only valid
  * value for "method" is 0.
  */
-PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
-    int filters));
+PNG_EXPORT(67, void, png_set_filter,
+   (png_structrp png_ptr, int method, int filters));
 #endif /* WRITE */
 
 /* Flags for png_set_filter() to say which filters to use.  The flags
@@ -1524,11 +1576,14 @@ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
 
 #ifdef PNG_WRITE_SUPPORTED
 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
-PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr,
-    int heuristic_method, int num_weights, png_const_doublep filter_weights,
+PNG_FP_EXPORT(68, void, png_set_filter_heuristics,
+   (png_structrp png_ptr,
+    int heuristic_method, int num_weights,
+    png_const_doublep filter_weights,
     png_const_doublep filter_costs))
 PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
-    (png_structrp png_ptr, int heuristic_method, int num_weights,
+   (png_structrp png_ptr,
+    int heuristic_method, int num_weights,
     png_const_fixed_point_p filter_weights,
     png_const_fixed_point_p filter_costs))
 #endif /* WRITE_WEIGHTED_FILTER */
@@ -1547,44 +1602,44 @@ PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
  * these values may not correspond directly to the zlib compression levels.
  */
 #ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
-PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr,
-    int level));
+PNG_EXPORT(69, void, png_set_compression_level,
+   (png_structrp png_ptr, int level));
 
-PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr,
-    int mem_level));
+PNG_EXPORT(70, void, png_set_compression_mem_level,
+   (png_structrp png_ptr, int mem_level));
 
-PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr,
-    int strategy));
+PNG_EXPORT(71, void, png_set_compression_strategy,
+   (png_structrp png_ptr, int strategy));
 
 /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
  * smaller value of window_bits if it can do so safely.
  */
-PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr,
-    int window_bits));
+PNG_EXPORT(72, void, png_set_compression_window_bits,
+   (png_structrp png_ptr, int window_bits));
 
-PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr,
-    int method));
+PNG_EXPORT(73, void, png_set_compression_method,
+   (png_structrp png_ptr, int method));
 #endif /* WRITE_CUSTOMIZE_COMPRESSION */
 
 #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
 /* Also set zlib parameters for compressing non-IDAT chunks */
-PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr,
-    int level));
+PNG_EXPORT(222, void, png_set_text_compression_level,
+   (png_structrp png_ptr, int level));
 
-PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr,
-    int mem_level));
+PNG_EXPORT(223, void, png_set_text_compression_mem_level,
+   (png_structrp png_ptr, int mem_level));
 
-PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr,
-    int strategy));
+PNG_EXPORT(224, void, png_set_text_compression_strategy,
+   (png_structrp png_ptr, int strategy));
 
 /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
  * smaller value of window_bits if it can do so safely.
  */
 PNG_EXPORT(225, void, png_set_text_compression_window_bits,
-    (png_structrp png_ptr, int window_bits));
+   (png_structrp png_ptr, int window_bits));
 
-PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
-    int method));
+PNG_EXPORT(226, void, png_set_text_compression_method,
+   (png_structrp png_ptr, int method));
 #endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
 #endif /* WRITE */
 
@@ -1599,7 +1654,8 @@ PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
 
 #ifdef PNG_STDIO_SUPPORTED
 /* Initialize the input/output for the PNG file to the default functions. */
-PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, FILE *fp));
+PNG_EXPORT(74, void, png_init_io,
+   (png_structrp png_ptr, FILE *fp));
 #endif
 
 /* Replace the (error and abort), and warning functions with user
@@ -1610,11 +1666,13 @@ PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, FILE *fp));
  * default function will be used.
  */
 
-PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr,
+PNG_EXPORT(75, void, png_set_error_fn,
+   (png_structrp png_ptr,
     png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn));
 
 /* Return the user pointer associated with the error functions */
-PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr));
+PNG_EXPORT(76, png_voidp, png_get_error_ptr,
+   (png_const_structrp png_ptr));
 
 /* Replace the default data output functions with a user supplied one(s).
  * If buffered output is not used, then output_flush_fn can be set to NULL.
@@ -1626,47 +1684,54 @@ PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr));
  * default flush function, which uses the standard *FILE structure, will
  * be used.
  */
-PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr,
+PNG_EXPORT(77, void, png_set_write_fn,
+   (png_structrp png_ptr,
+    png_voidp io_ptr,
     png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
 
 /* Replace the default data input function with a user supplied one. */
-PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr,
-    png_rw_ptr read_data_fn));
+PNG_EXPORT(78, void, png_set_read_fn,
+   (png_structrp png_ptr,
+    png_voidp io_ptr, png_rw_ptr read_data_fn));
 
 /* Return the user pointer associated with the I/O functions */
-PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr));
+PNG_EXPORT(79, png_voidp, png_get_io_ptr,
+   (png_const_structrp png_ptr));
 
-PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr,
-    png_read_status_ptr read_row_fn));
+PNG_EXPORT(80, void, png_set_read_status_fn,
+   (png_structrp png_ptr, png_read_status_ptr read_row_fn));
 
-PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr,
-    png_write_status_ptr write_row_fn));
+PNG_EXPORT(81, void, png_set_write_status_fn,
+   (png_structrp png_ptr, png_write_status_ptr write_row_fn));
 
 #ifdef PNG_USER_MEM_SUPPORTED
 /* Replace the default memory allocation functions with user supplied one(s). */
-PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr,
-    png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+PNG_EXPORT(82, void, png_set_mem_fn,
+   (png_structrp png_ptr,
+    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn));
 /* Return the user pointer associated with the memory functions */
-PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr));
+PNG_EXPORT(83, png_voidp, png_get_mem_ptr,
+   (png_const_structrp png_ptr));
 #endif
 
 #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
-PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr,
-    png_user_transform_ptr read_user_transform_fn));
+PNG_EXPORT(84, void, png_set_read_user_transform_fn,
+   (png_structrp png_ptr, png_user_transform_ptr read_user_transform_fn));
 #endif
 
 #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
-PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr,
-    png_user_transform_ptr write_user_transform_fn));
+PNG_EXPORT(85, void, png_set_write_user_transform_fn,
+   (png_structrp png_ptr, png_user_transform_ptr write_user_transform_fn));
 #endif
 
 #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
-PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr,
-    png_voidp user_transform_ptr, int user_transform_depth,
-    int user_transform_channels));
+PNG_EXPORT(86, void, png_set_user_transform_info,
+   (png_structrp png_ptr,
+    png_voidp user_transform_ptr,
+    int user_transform_depth, int user_transform_channels));
 /* Return the user pointer associated with the user transform functions */
 PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 #endif
 
 #ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
@@ -1681,8 +1746,10 @@ PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr,
  * find the output pixel (x,y) given an interlaced sub-image pixel
  * (row,col,pass).  (See below for these macros.)
  */
-PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp));
-PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
+PNG_EXPORT(217, png_uint_32, png_get_current_row_number,
+   (png_const_structrp));
+PNG_EXPORT(218, png_byte, png_get_current_pass_number,
+   (png_const_structrp));
 #endif
 
 #ifdef PNG_READ_USER_CHUNKS_SUPPORTED
@@ -1705,28 +1772,32 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
  * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about
  * how this behavior will change in libpng 1.7
  */
-PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr,
+PNG_EXPORT(88, void, png_set_read_user_chunk_fn,
+   (png_structrp png_ptr,
     png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
 #endif
 
 #ifdef PNG_USER_CHUNKS_SUPPORTED
-PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr));
+PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr,
+   (png_const_structrp png_ptr));
 #endif
 
 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
 /* Sets the function callbacks for the push reader, and a pointer to a
  * user-defined structure available to the callback functions.
  */
-PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr,
+PNG_EXPORT(90, void, png_set_progressive_read_fn,
+   (png_structrp png_ptr,
     png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
     png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn));
 
 /* Returns the user pointer associated with the push read functions */
 PNG_EXPORT(91, png_voidp, png_get_progressive_ptr,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 
 /* Function to be called when data becomes available */
-PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr,
+PNG_EXPORT(92, void, png_process_data,
+   (png_structrp png_ptr,
     png_inforp info_ptr, png_bytep buffer, size_t buffer_size));
 
 /* A function which may be called *only* within png_process_data to stop the
@@ -1736,7 +1807,8 @@ PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr,
  * 'save' is set to true the routine will first save all the pending data and
  * will always return 0.
  */
-PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save));
+PNG_EXPORT(219, size_t, png_process_data_pause,
+   (png_structrp, int save));
 
 /* A function which may be called *only* outside (after) a call to
  * png_process_data.  It returns the number of bytes of data to skip in the
@@ -1744,45 +1816,53 @@ PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save));
  * application must skip than number of bytes of input data and pass the
  * following data to the next call to png_process_data.
  */
-PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp));
+PNG_EXPORT(220, png_uint_32, png_process_data_skip,
+   (png_structrp));
 
 /* Function that combines rows.  'new_row' is a flag that should come from
  * the callback and be non-NULL if anything needs to be done; the library
  * stores its own version of the new data internally and ignores the passed
  * in value.
  */
-PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr,
+PNG_EXPORT(93, void, png_progressive_combine_row,
+   (png_const_structrp png_ptr,
     png_bytep old_row, png_const_bytep new_row));
 #endif /* PROGRESSIVE_READ */
 
-PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED);
+PNG_EXPORTA(94, png_voidp, png_malloc,
+   (png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED);
 /* Added at libpng version 1.4.0 */
-PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED);
+PNG_EXPORTA(95, png_voidp, png_calloc,
+   (png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED);
 
 /* Added at libpng version 1.2.4 */
-PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED);
+PNG_EXPORTA(96, png_voidp, png_malloc_warn,
+   (png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED);
 
 /* Frees a pointer allocated by png_malloc() */
-PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr));
+PNG_EXPORT(97, void, png_free,
+   (png_const_structrp png_ptr, png_voidp ptr));
 
 /* Free data that was allocated internally */
-PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 free_me, int num));
+PNG_EXPORT(98, void, png_free_data,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_32 free_me, int num));
 
 /* Reassign the responsibility for freeing existing data, whether allocated
  * by libpng or by the application; this works on the png_info structure passed
  * in, without changing the state for other png_info structures.
  */
-PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int freer, png_uint_32 mask));
+PNG_EXPORT(99, void, png_data_freer,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    int freer, png_uint_32 mask));
 
 /* Assignments for png_data_freer */
 #define PNG_DESTROY_WILL_FREE_DATA 1
-#define PNG_SET_WILL_FREE_DATA 1
-#define PNG_USER_WILL_FREE_DATA 2
+#define PNG_SET_WILL_FREE_DATA     1
+#define PNG_USER_WILL_FREE_DATA    2
 /* Flags for png_ptr->free_me and info_ptr->free_me */
 #define PNG_FREE_HIST 0x0008U
 #define PNG_FREE_ICCP 0x0010U
@@ -1802,36 +1882,42 @@ PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr,
 #define PNG_FREE_MUL  0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
 
 #ifdef PNG_USER_MEM_SUPPORTED
-PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr,
-    png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED);
-PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr,
-    png_voidp ptr), PNG_DEPRECATED);
+PNG_EXPORTA(100, png_voidp, png_malloc_default,
+   (png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED PNG_DEPRECATED);
+PNG_EXPORTA(101, void, png_free_default,
+   (png_const_structrp png_ptr, png_voidp ptr),
+   PNG_DEPRECATED);
 #endif
 
 #ifdef PNG_ERROR_TEXT_SUPPORTED
 /* Fatal error in PNG image of libpng - can't continue */
-PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr,
-    png_const_charp error_message), PNG_NORETURN);
+PNG_EXPORTA(102, void, png_error,
+   (png_const_structrp png_ptr, png_const_charp error_message),
+   PNG_NORETURN);
 
 /* The same, but the chunk name is prepended to the error string. */
-PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr,
-    png_const_charp error_message), PNG_NORETURN);
+PNG_EXPORTA(103, void, png_chunk_error,
+   (png_const_structrp png_ptr, png_const_charp error_message),
+   PNG_NORETURN);
 
 #else
 /* Fatal error in PNG image of libpng - can't continue */
-PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN);
+PNG_EXPORTA(104, void, png_err,
+   (png_const_structrp png_ptr),
+   PNG_NORETURN);
 #  define png_error(s1,s2) png_err(s1)
 #  define png_chunk_error(s1,s2) png_err(s1)
 #endif
 
 #ifdef PNG_WARNINGS_SUPPORTED
 /* Non-fatal error in libpng.  Can continue, but may have a problem. */
-PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
+PNG_EXPORT(105, void, png_warning,
+   (png_const_structrp png_ptr, png_const_charp warning_message));
 
 /* Non-fatal error in libpng, chunk name is prepended to message. */
-PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
+PNG_EXPORT(106, void, png_chunk_warning,
+   (png_const_structrp png_ptr, png_const_charp warning_message));
 #else
 #  define png_warning(s1,s2) ((void)(s1))
 #  define png_chunk_warning(s1,s2) ((void)(s1))
@@ -1840,17 +1926,17 @@ PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr,
 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
 /* Benign error in libpng.  Can continue, but may have a problem.
  * User can choose whether to handle as a fatal error or as a warning. */
-PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
+PNG_EXPORT(107, void, png_benign_error,
+   (png_const_structrp png_ptr, png_const_charp warning_message));
 
 #ifdef PNG_READ_SUPPORTED
 /* Same, chunk name is prepended to message (only during read) */
-PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr,
-    png_const_charp warning_message));
+PNG_EXPORT(108, void, png_chunk_benign_error,
+   (png_const_structrp png_ptr, png_const_charp warning_message));
 #endif
 
 PNG_EXPORT(109, void, png_set_benign_errors,
-    (png_structrp png_ptr, int allowed));
+   (png_structrp png_ptr, int allowed));
 #else
 #  ifdef PNG_ALLOW_BENIGN_ERRORS
 #    define png_benign_error png_warning
@@ -1874,169 +1960,181 @@ PNG_EXPORT(109, void, png_set_benign_errors,
  * png_info_struct.
  */
 /* Returns "flag" if chunk data is valid in info_ptr. */
-PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 flag));
+PNG_EXPORT(110, png_uint_32, png_get_valid,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag));
 
 /* Returns number of bytes needed to hold a transformed row. */
-PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(111, size_t, png_get_rowbytes,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 #ifdef PNG_INFO_IMAGE_SUPPORTED
 /* Returns row_pointers, which is an array of pointers to scanlines that was
  * returned from png_read_png().
  */
-PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(112, png_bytepp, png_get_rows,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Set row_pointers, which is an array of pointers to scanlines for use
  * by png_write_png().
  */
-PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytepp row_pointers));
+PNG_EXPORT(113, void, png_set_rows,
+   (png_const_structrp png_ptr, png_inforp info_ptr, png_bytepp row_pointers));
 #endif
 
 /* Returns number of color channels in image. */
-PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(114, png_byte, png_get_channels,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 #ifdef PNG_EASY_ACCESS_SUPPORTED
 /* Returns image width in pixels. */
-PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(115, png_uint_32, png_get_image_width,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns image height in pixels. */
-PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(116, png_uint_32, png_get_image_height,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns image bit_depth. */
-PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(117, png_byte, png_get_bit_depth,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns image color_type. */
-PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(118, png_byte, png_get_color_type,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns image filter_type. */
-PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(119, png_byte, png_get_filter_type,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns image interlace_type. */
-PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(120, png_byte, png_get_interlace_type,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns image compression_type. */
-PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(121, png_byte, png_get_compression_type,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns image resolution in pixels per meter, from pHYs chunk data. */
 PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 /* Returns pixel aspect ratio, computed from pHYs chunk data.  */
 PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+   (png_const_structrp png_ptr, png_const_inforp info_ptr))
 PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+   (png_const_structrp png_ptr, png_const_inforp info_ptr))
 
 /* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
 PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 PNG_EXPORT(128, png_int_32, png_get_x_offset_microns,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 PNG_EXPORT(129, png_int_32, png_get_y_offset_microns,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 #endif /* EASY_ACCESS */
 
 #ifdef PNG_READ_SUPPORTED
 /* Returns pointer to signature string read from PNG header */
-PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr));
+PNG_EXPORT(130, png_const_bytep, png_get_signature,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 #endif
 
 #ifdef PNG_bKGD_SUPPORTED
-PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_color_16p *background));
+PNG_EXPORT(131, png_uint_32, png_get_bKGD,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_color_16p *background));
 #endif
 
 #ifdef PNG_bKGD_SUPPORTED
-PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_color_16p background));
+PNG_EXPORT(132, void, png_set_bKGD,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_color_16p background));
 #endif
 
 #ifdef PNG_cHRM_SUPPORTED
-PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x,
-    double *red_y, double *green_x, double *green_y, double *blue_x,
-    double *blue_y))
-PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z,
-    double *green_X, double *green_Y, double *green_Z, double *blue_X,
-    double *blue_Y, double *blue_Z))
+PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    double *white_x, double *white_y,
+    double *red_x, double *red_y,
+    double *green_x, double *green_y,
+    double *blue_x, double *blue_y))
+PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    double *red_X, double *red_Y, double *red_Z,
+    double *green_X, double *green_Y, double *green_Z,
+    double *blue_X, double *blue_Y, double *blue_Z))
 PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
     png_fixed_point *int_white_x, png_fixed_point *int_white_y,
     png_fixed_point *int_red_x, png_fixed_point *int_red_y,
     png_fixed_point *int_green_x, png_fixed_point *int_green_y,
     png_fixed_point *int_blue_x, png_fixed_point *int_blue_y))
 PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
     png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
-    png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
-    png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
+    png_fixed_point *int_red_Z,
+    png_fixed_point *int_green_X, png_fixed_point *int_green_Y,
+    png_fixed_point *int_green_Z,
     png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
     png_fixed_point *int_blue_Z))
 #endif
 
 #ifdef PNG_cHRM_SUPPORTED
-PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr,
-    png_inforp info_ptr,
-    double white_x, double white_y, double red_x, double red_y, double green_x,
-    double green_y, double blue_x, double blue_y))
-PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr,
-    png_inforp info_ptr, double red_X, double red_Y, double red_Z,
-    double green_X, double green_Y, double green_Z, double blue_X,
-    double blue_Y, double blue_Z))
-PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_fixed_point int_white_x,
-    png_fixed_point int_white_y, png_fixed_point int_red_x,
-    png_fixed_point int_red_y, png_fixed_point int_green_x,
-    png_fixed_point int_green_y, png_fixed_point int_blue_x,
-    png_fixed_point int_blue_y))
-PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y,
-    png_fixed_point int_red_Z, png_fixed_point int_green_X,
-    png_fixed_point int_green_Y, png_fixed_point int_green_Z,
+PNG_FP_EXPORT(135, void, png_set_cHRM,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    double white_x, double white_y,
+    double red_x, double red_y,
+    double green_x, double green_y,
+    double blue_x, double blue_y))
+PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    double red_X, double red_Y, double red_Z,
+    double green_X, double green_Y, double green_Z,
+    double blue_X, double blue_Y, double blue_Z))
+PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_fixed_point int_white_x, png_fixed_point int_white_y,
+    png_fixed_point int_red_x, png_fixed_point int_red_y,
+    png_fixed_point int_green_x, png_fixed_point int_green_y,
+    png_fixed_point int_blue_x, png_fixed_point int_blue_y))
+PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_fixed_point int_red_X, png_fixed_point int_red_Y,
+    png_fixed_point int_red_Z,
+    png_fixed_point int_green_X, png_fixed_point int_green_Y,
+    png_fixed_point int_green_Z,
     png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
     png_fixed_point int_blue_Z))
 #endif
 
 #ifdef PNG_cICP_SUPPORTED
-PNG_EXPORT(250, png_uint_32, png_get_cICP, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_bytep colour_primaries,
-    png_bytep transfer_function, png_bytep matrix_coefficients,
-    png_bytep video_full_range_flag));
+PNG_EXPORT(250, png_uint_32, png_get_cICP,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_bytep colour_primaries, png_bytep transfer_function,
+    png_bytep matrix_coefficients, png_bytep video_full_range_flag));
 #endif
 
 #ifdef PNG_cICP_SUPPORTED
-PNG_EXPORT(251, void, png_set_cICP, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_byte colour_primaries,
-    png_byte transfer_function, png_byte matrix_coefficients,
-    png_byte video_full_range_flag));
+PNG_EXPORT(251, void, png_set_cICP,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_byte colour_primaries, png_byte transfer_function,
+    png_byte matrix_coefficients, png_byte video_full_range_flag));
 #endif
 
 #ifdef PNG_cLLI_SUPPORTED
-PNG_FP_EXPORT(252, png_uint_32, png_get_cLLI, (png_const_structrp png_ptr,
-         png_const_inforp info_ptr, double *maximum_content_light_level,
-         double *maximum_frame_average_light_level))
+PNG_FP_EXPORT(252, png_uint_32, png_get_cLLI,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    double *maximum_content_light_level,
+    double *maximum_frame_average_light_level))
 PNG_FIXED_EXPORT(253, png_uint_32, png_get_cLLI_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
     /* The values below are in cd/m2 (nits) and are scaled by 10,000; not
      * 100,000 as in the case of png_fixed_point.
      */
@@ -2045,11 +2143,12 @@ PNG_FIXED_EXPORT(253, png_uint_32, png_get_cLLI_fixed,
 #endif
 
 #ifdef PNG_cLLI_SUPPORTED
-PNG_FP_EXPORT(254, void, png_set_cLLI, (png_const_structrp png_ptr,
-         png_inforp info_ptr, double maximum_content_light_level,
-         double maximum_frame_average_light_level))
-PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr,
+PNG_FP_EXPORT(254, void, png_set_cLLI,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    double maximum_content_light_level,
+    double maximum_frame_average_light_level))
+PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
     /* The values below are in cd/m2 (nits) and are scaled by 10,000; not
      * 100,000 as in the case of png_fixed_point.
      */
@@ -2058,64 +2157,73 @@ PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed, (png_const_structrp png_ptr,
 #endif
 
 #ifdef PNG_eXIf_SUPPORTED
-PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytep *exif));
-PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytep exif));
+PNG_EXPORT(246, png_uint_32, png_get_eXIf,
+   (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *exif));
+PNG_EXPORT(247, void, png_set_eXIf,
+   (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep exif));
 
-PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif));
-PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif));
+PNG_EXPORT(248, png_uint_32, png_get_eXIf_1,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *num_exif, png_bytep *exif));
+PNG_EXPORT(249, void, png_set_eXIf_1,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_32 num_exif, png_bytep exif));
 #endif
 
 #ifdef PNG_gAMA_SUPPORTED
-PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, double *file_gamma))
+PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    double *file_gamma))
 PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
     png_fixed_point *int_file_gamma))
 #endif
 
 #ifdef PNG_gAMA_SUPPORTED
-PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr,
-    png_inforp info_ptr, double file_gamma))
-PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_fixed_point int_file_gamma))
+PNG_FP_EXPORT(139, void, png_set_gAMA,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    double file_gamma))
+PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_fixed_point int_file_gamma))
 #endif
 
 #ifdef PNG_hIST_SUPPORTED
-PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_16p *hist));
-PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_uint_16p hist));
+PNG_EXPORT(141, png_uint_32, png_get_hIST,
+   (png_const_structrp png_ptr, png_inforp info_ptr, png_uint_16p *hist));
+PNG_EXPORT(142, void, png_set_hIST,
+   (png_const_structrp png_ptr, png_inforp info_ptr, png_const_uint_16p hist));
 #endif
 
-PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height,
-    int *bit_depth, int *color_type, int *interlace_method,
-    int *compression_method, int *filter_method));
+PNG_EXPORT(143, png_uint_32, png_get_IHDR,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *width, png_uint_32 *height,
+    int *bit_depth, int *color_type,
+    int *interlace_method, int *compression_method, int *filter_method));
 
-PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth,
-    int color_type, int interlace_method, int compression_method,
-    int filter_method));
+PNG_EXPORT(144, void, png_set_IHDR,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_32 width, png_uint_32 height,
+    int bit_depth, int color_type,
+    int interlace_method, int compression_method, int filter_method));
 
 #ifdef PNG_mDCV_SUPPORTED
-PNG_FP_EXPORT(256, png_uint_32, png_get_mDCV, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr,
+PNG_FP_EXPORT(256, png_uint_32, png_get_mDCV,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
     /* The chromaticities of the mastering display.  As cHRM, but independent of
      * the encoding endpoints in cHRM, or cICP, or iCCP.  These values will
      * always be in the range 0 to 1.3107.
      */
-    double *white_x, double *white_y, double *red_x, double *red_y,
-    double *green_x, double *green_y, double *blue_x, double *blue_y,
+    double *white_x, double *white_y,
+    double *red_x, double *red_y,
+    double *green_x, double *green_y,
+    double *blue_x, double *blue_y,
     /* Mastering display luminance in cd/m2 (nits). */
     double *mastering_display_maximum_luminance,
     double *mastering_display_minimum_luminance))
 
 PNG_FIXED_EXPORT(257, png_uint_32, png_get_mDCV_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
     png_fixed_point *int_white_x, png_fixed_point *int_white_y,
     png_fixed_point *int_red_x, png_fixed_point *int_red_y,
     png_fixed_point *int_green_x, png_fixed_point *int_green_y,
@@ -2128,19 +2236,21 @@ PNG_FIXED_EXPORT(257, png_uint_32, png_get_mDCV_fixed,
 #endif
 
 #ifdef PNG_mDCV_SUPPORTED
-PNG_FP_EXPORT(258, void, png_set_mDCV, (png_const_structrp png_ptr,
-    png_inforp info_ptr,
+PNG_FP_EXPORT(258, void, png_set_mDCV,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
     /* The chromaticities of the mastering display.  As cHRM, but independent of
      * the encoding endpoints in cHRM, or cICP, or iCCP.
      */
-    double white_x, double white_y, double red_x, double red_y, double green_x,
-    double green_y, double blue_x, double blue_y,
+    double white_x, double white_y,
+    double red_x, double red_y,
+    double green_x, double green_y,
+    double blue_x, double blue_y,
     /* Mastering display luminance in cd/m2 (nits). */
     double mastering_display_maximum_luminance,
     double mastering_display_minimum_luminance))
 
-PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed, (png_const_structrp png_ptr,
-    png_inforp info_ptr,
+PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
     /* The admissible range of these values is not the full range of a PNG
      * fixed point value.  Negative values cannot be encoded and the maximum
      * value is about 1.3 */
@@ -2156,95 +2266,107 @@ PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed, (png_const_structrp png_ptr,
 #endif
 
 #ifdef PNG_oFFs_SUPPORTED
-PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr,
-   png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y,
-   int *unit_type));
+PNG_EXPORT(145, png_uint_32, png_get_oFFs,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type));
 #endif
 
 #ifdef PNG_oFFs_SUPPORTED
-PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y,
-    int unit_type));
+PNG_EXPORT(146, void, png_set_oFFs,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_int_32 offset_x, png_int_32 offset_y, int unit_type));
 #endif
 
 #ifdef PNG_pCAL_SUPPORTED
-PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_charp *purpose, png_int_32 *X0,
-    png_int_32 *X1, int *type, int *nparams, png_charp *units,
-    png_charpp *params));
+PNG_EXPORT(147, png_uint_32, png_get_pCAL,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_charp *purpose, png_int_32 *X0, png_int_32 *X1,
+    int *type, int *nparams, png_charp *units, png_charpp *params));
 #endif
 
 #ifdef PNG_pCAL_SUPPORTED
-PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1,
+PNG_EXPORT(148, void, png_set_pCAL,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_charp purpose, png_int_32 X0, png_int_32 X1,
     int type, int nparams, png_const_charp units, png_charpp params));
 #endif
 
 #ifdef PNG_pHYs_SUPPORTED
-PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
-    int *unit_type));
+PNG_EXPORT(149, png_uint_32, png_get_pHYs,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
 #endif
 
 #ifdef PNG_pHYs_SUPPORTED
-PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type));
+PNG_EXPORT(150, void, png_set_pHYs,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_uint_32 res_x, png_uint_32 res_y, int unit_type));
 #endif
 
-PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr,
-   png_inforp info_ptr, png_colorp *palette, int *num_palette));
+PNG_EXPORT(151, png_uint_32, png_get_PLTE,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_colorp *palette, int *num_palette));
 
-PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr,
-    png_inforp info_ptr, png_const_colorp palette, int num_palette));
+PNG_EXPORT(152, void, png_set_PLTE,
+   (png_structrp png_ptr, png_inforp info_ptr,
+    png_const_colorp palette, int num_palette));
 
 #ifdef PNG_sBIT_SUPPORTED
-PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_color_8p *sig_bit));
+PNG_EXPORT(153, png_uint_32, png_get_sBIT,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_color_8p *sig_bit));
 #endif
 
 #ifdef PNG_sBIT_SUPPORTED
-PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_color_8p sig_bit));
+PNG_EXPORT(154, void, png_set_sBIT,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_color_8p sig_bit));
 #endif
 
 #ifdef PNG_sRGB_SUPPORTED
-PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, int *file_srgb_intent));
+PNG_EXPORT(155, png_uint_32, png_get_sRGB,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *file_srgb_intent));
 #endif
 
 #ifdef PNG_sRGB_SUPPORTED
-PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int srgb_intent));
-PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int srgb_intent));
+PNG_EXPORT(156, void, png_set_sRGB,
+   (png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent));
+PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM,
+   (png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent));
 #endif
 
 #ifdef PNG_iCCP_SUPPORTED
-PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_charpp name, int *compression_type,
+PNG_EXPORT(158, png_uint_32, png_get_iCCP,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_charpp name, int *compression_type,
     png_bytepp profile, png_uint_32 *proflen));
 #endif
 
 #ifdef PNG_iCCP_SUPPORTED
-PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_charp name, int compression_type,
+PNG_EXPORT(159, void, png_set_iCCP,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_charp name, int compression_type,
     png_const_bytep profile, png_uint_32 proflen));
 #endif
 
 #ifdef PNG_sPLT_SUPPORTED
-PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_sPLT_tpp entries));
+PNG_EXPORT(160, int, png_get_sPLT,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_sPLT_tpp entries));
 #endif
 
 #ifdef PNG_sPLT_SUPPORTED
-PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_sPLT_tp entries, int nentries));
+PNG_EXPORT(161, void, png_set_sPLT,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_sPLT_tp entries, int nentries));
 #endif
 
 #ifdef PNG_TEXT_SUPPORTED
 /* png_get_text also returns the number of text chunks in *num_text */
-PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_textp *text_ptr, int *num_text));
+PNG_EXPORT(162, int, png_get_text,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_textp *text_ptr, int *num_text));
 #endif
 
 /* Note while png_set_text() will accept a structure whose text,
@@ -2255,35 +2377,41 @@ PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr,
  */
 
 #ifdef PNG_TEXT_SUPPORTED
-PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_textp text_ptr, int num_text));
+PNG_EXPORT(163, void, png_set_text,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_textp text_ptr, int num_text));
 #endif
 
 #ifdef PNG_tIME_SUPPORTED
-PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_timep *mod_time));
+PNG_EXPORT(164, png_uint_32, png_get_tIME,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_timep *mod_time));
 #endif
 
 #ifdef PNG_tIME_SUPPORTED
-PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_timep mod_time));
+PNG_EXPORT(165, void, png_set_tIME,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_timep mod_time));
 #endif
 
 #ifdef PNG_tRNS_SUPPORTED
-PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans,
+PNG_EXPORT(166, png_uint_32, png_get_tRNS,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_bytep *trans_alpha, int *num_trans,
     png_color_16p *trans_color));
 #endif
 
 #ifdef PNG_tRNS_SUPPORTED
-PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr,
-    png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans,
+PNG_EXPORT(167, void, png_set_tRNS,
+   (png_structrp png_ptr, png_inforp info_ptr,
+    png_const_bytep trans_alpha, int num_trans,
     png_const_color_16p trans_color));
 #endif
 
 #ifdef PNG_sCAL_SUPPORTED
-PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, int *unit, double *width, double *height))
+PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *unit, double *width, double *height))
 #if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \
    defined(PNG_FLOATING_POINT_SUPPORTED)
 /* NOTE: this API is currently implemented using floating point arithmetic,
@@ -2292,21 +2420,22 @@ PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr,
  * is highly recommended that png_get_sCAL_s be used instead.
  */
 PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
-    png_fixed_point *width, png_fixed_point *height))
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *unit, png_fixed_point *width, png_fixed_point *height))
 #endif
 PNG_EXPORT(169, png_uint_32, png_get_sCAL_s,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
-    png_charpp swidth, png_charpp sheight));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    int *unit, png_charpp swidth, png_charpp sheight));
 
-PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int unit, double width, double height))
-PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr,
-   png_inforp info_ptr, int unit, png_fixed_point width,
-   png_fixed_point height))
-PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int unit,
-    png_const_charp swidth, png_const_charp sheight));
+PNG_FP_EXPORT(170, void, png_set_sCAL,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    int unit, double width, double height))
+PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    int unit, png_fixed_point width, png_fixed_point height))
+PNG_EXPORT(171, void, png_set_sCAL_s,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    int unit, png_const_charp swidth, png_const_charp sheight));
 #endif /* sCAL */
 
 #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
@@ -2409,7 +2538,8 @@ PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
  *    be processed by libpng.
  */
 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
+PNG_EXPORT(172, void, png_set_keep_unknown_chunks,
+   (png_structrp png_ptr,
     int keep, png_const_bytep chunk_list, int num_chunks));
 #endif /* HANDLE_AS_UNKNOWN */
 
@@ -2417,14 +2547,14 @@ PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
  * the result is therefore true (non-zero) if special handling is required,
  * false for the default handling.
  */
-PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr,
-    png_const_bytep chunk_name));
+PNG_EXPORT(173, int, png_handle_as_unknown,
+   (png_const_structrp png_ptr, png_const_bytep chunk_name));
 #endif /* SET_UNKNOWN_CHUNKS */
 
 #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
-PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_unknown_chunkp unknowns,
-    int num_unknowns));
+PNG_EXPORT(174, void, png_set_unknown_chunks,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_const_unknown_chunkp unknowns, int num_unknowns));
    /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added
     * unknowns to the location currently stored in the png_struct.  This is
     * invariably the wrong value on write.  To fix this call the following API
@@ -2435,43 +2565,47 @@ PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr,
     */
 
 PNG_EXPORT(175, void, png_set_unknown_chunk_location,
-    (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location));
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    int chunk, int location));
 
-PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr,
-    png_inforp info_ptr, png_unknown_chunkpp entries));
+PNG_EXPORT(176, int, png_get_unknown_chunks,
+   (png_const_structrp png_ptr, png_inforp info_ptr,
+    png_unknown_chunkpp entries));
 #endif
 
 /* Png_free_data() will turn off the "valid" flag for anything it frees.
  * If you need to turn it off for a chunk that your application has freed,
  * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK);
  */
-PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr,
-    png_inforp info_ptr, int mask));
+PNG_EXPORT(177, void, png_set_invalid,
+   (png_const_structrp png_ptr, png_inforp info_ptr, int mask));
 
 #ifdef PNG_INFO_IMAGE_SUPPORTED
 /* The "params" pointer is currently not used and is for future expansion. */
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr,
+PNG_EXPORT(178, void, png_read_png,
+   (png_structrp png_ptr, png_inforp info_ptr,
     int transforms, png_voidp params));
 #endif
 #ifdef PNG_WRITE_SUPPORTED
-PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr,
+PNG_EXPORT(179, void, png_write_png,
+   (png_structrp png_ptr, png_inforp info_ptr,
     int transforms, png_voidp params));
 #endif
 #endif
 
 PNG_EXPORT(180, png_const_charp, png_get_copyright,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 PNG_EXPORT(181, png_const_charp, png_get_header_ver,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 PNG_EXPORT(182, png_const_charp, png_get_header_version,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 PNG_EXPORT(183, png_const_charp, png_get_libpng_ver,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 
 #ifdef PNG_MNG_FEATURES_SUPPORTED
-PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr,
-    png_uint_32 mng_features_permitted));
+PNG_EXPORT(184, png_uint_32, png_permit_mng_features,
+   (png_structrp png_ptr, png_uint_32 mng_features_permitted));
 #endif
 
 /* For use in png_set_keep_unknown, added to version 1.2.6 */
@@ -2485,71 +2619,74 @@ PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr,
  * messages before passing them to the error or warning handler.
  */
 #ifdef PNG_ERROR_NUMBERS_SUPPORTED
-PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr,
-    png_uint_32 strip_mode));
+PNG_EXPORT(185, void, png_set_strip_error_numbers,
+   (png_structrp png_ptr, png_uint_32 strip_mode));
 #endif
 
 /* Added in libpng-1.2.6 */
 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
-PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr,
+PNG_EXPORT(186, void, png_set_user_limits,
+   (png_structrp png_ptr,
     png_uint_32 user_width_max, png_uint_32 user_height_max));
 PNG_EXPORT(187, png_uint_32, png_get_user_width_max,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 PNG_EXPORT(188, png_uint_32, png_get_user_height_max,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 /* Added in libpng-1.4.0 */
-PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr,
-    png_uint_32 user_chunk_cache_max));
+PNG_EXPORT(189, void, png_set_chunk_cache_max,
+   (png_structrp png_ptr, png_uint_32 user_chunk_cache_max));
 PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 /* Added in libpng-1.4.1 */
-PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr,
-    png_alloc_size_t user_chunk_cache_max));
+PNG_EXPORT(191, void, png_set_chunk_malloc_max,
+   (png_structrp png_ptr, png_alloc_size_t user_chunk_cache_max));
 PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 #endif
 
 #if defined(PNG_INCH_CONVERSIONS_SUPPORTED)
 PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+   (png_const_structrp png_ptr, png_const_inforp info_ptr));
 
 PNG_FP_EXPORT(196, float, png_get_x_offset_inches,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+   (png_const_structrp png_ptr, png_const_inforp info_ptr))
 #ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
 PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+   (png_const_structrp png_ptr, png_const_inforp info_ptr))
 #endif
 
-PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr))
+PNG_FP_EXPORT(197, float, png_get_y_offset_inches,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr))
 #ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
 PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed,
-    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+   (png_const_structrp png_ptr, png_const_inforp info_ptr))
 #endif
 
 #  ifdef PNG_pHYs_SUPPORTED
-PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr,
-    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
-    int *unit_type));
+PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi,
+   (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
 #  endif /* pHYs */
 #endif  /* INCH_CONVERSIONS */
 
 /* Added in libpng-1.4.0 */
 #ifdef PNG_IO_STATE_SUPPORTED
-PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr));
+PNG_EXPORT(199, png_uint_32, png_get_io_state,
+   (png_const_structrp png_ptr));
 
 /* Removed from libpng 1.6; use png_get_io_chunk_type. */
-PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr),
-    PNG_DEPRECATED)
+PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name,
+   (png_structrp png_ptr),
+   PNG_DEPRECATED)
 
 PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
-    (png_const_structrp png_ptr));
+   (png_const_structrp png_ptr));
 
 /* The flags returned by png_get_io_state() are the following: */
 #  define PNG_IO_NONE        0x0000   /* no I/O at this moment */
@@ -2674,21 +2811,26 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
 #endif /* READ_COMPOSITE_NODIV */
 
 #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
-PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf));
-PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf));
-PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf));
+PNG_EXPORT(201, png_uint_32, png_get_uint_32,
+   (png_const_bytep buf));
+PNG_EXPORT(202, png_uint_16, png_get_uint_16,
+   (png_const_bytep buf));
+PNG_EXPORT(203, png_int_32, png_get_int_32,
+   (png_const_bytep buf));
 #endif
 
-PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr,
-    png_const_bytep buf));
+PNG_EXPORT(204, png_uint_32, png_get_uint_31,
+   (png_const_structrp png_ptr, png_const_bytep buf));
 /* No png_get_int_16 -- may be added if there's a real need for it. */
 
 /* Place a 32-bit number into a buffer in PNG byte order (big-endian). */
 #ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
-PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i));
+PNG_EXPORT(205, void, png_save_uint_32,
+   (png_bytep buf, png_uint_32 i));
 #endif
 #ifdef PNG_SAVE_INT_32_SUPPORTED
-PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
+PNG_EXPORT(206, void, png_save_int_32,
+   (png_bytep buf, png_int_32 i));
 #endif
 
 /* Place a 16-bit number into a buffer in PNG byte order.
@@ -2696,7 +2838,8 @@ PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
  * just to avoid potential problems on pre-ANSI C compilers.
  */
 #ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
-PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
+PNG_EXPORT(207, void, png_save_uint_16,
+   (png_bytep buf, unsigned int i));
 /* No png_save_int_16 -- may be added if there's a real need for it. */
 #endif
 
@@ -2743,10 +2886,10 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
 
 #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
 PNG_EXPORT(242, void, png_set_check_for_invalid_index,
-    (png_structrp png_ptr, int allowed));
+   (png_structrp png_ptr, int allowed));
 #  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
-PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr,
-    png_const_infop info_ptr));
+PNG_EXPORT(243, int, png_get_palette_max,
+   (png_const_structp png_ptr, png_const_infop info_ptr));
 #  endif
 #endif /* CHECK_FOR_INVALID_INDEX */
 
@@ -3110,24 +3253,25 @@ typedef struct
  * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.)
  */
 #ifdef PNG_STDIO_SUPPORTED
-PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image,
-   const char *file_name));
+PNG_EXPORT(234, int, png_image_begin_read_from_file,
+   (png_imagep image, const char *file_name));
    /* The named file is opened for read and the image header is filled in
     * from the PNG header in the file.
     */
 
-PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image,
-   FILE *file));
+PNG_EXPORT(235, int, png_image_begin_read_from_stdio,
+   (png_imagep image, FILE *file));
    /* The PNG header is read from the stdio FILE object. */
 #endif /* STDIO */
 
-PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
-   png_const_voidp memory, size_t size));
+PNG_EXPORT(236, int, png_image_begin_read_from_memory,
+   (png_imagep image, png_const_voidp memory, size_t size));
    /* The PNG header is read from the given memory buffer. */
 
-PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
-   png_const_colorp background, void *buffer, png_int_32 row_stride,
-   void *colormap));
+PNG_EXPORT(237, int, png_image_finish_read,
+   (png_imagep image,
+    png_const_colorp background, void *buffer, png_int_32 row_stride,
+    void *colormap));
    /* Finish reading the image into the supplied buffer and clean up the
     * png_image structure.
     *
@@ -3160,7 +3304,8 @@ PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
     * written to the colormap; this may be less than the original value.
     */
 
-PNG_EXPORT(238, void, png_image_free, (png_imagep image));
+PNG_EXPORT(238, void, png_image_free,
+   (png_imagep image));
    /* Free any data allocated by libpng in image->opaque, setting the pointer to
     * NULL.  May be called at any time after the structure is initialized.
     */
@@ -3184,14 +3329,16 @@ PNG_EXPORT(238, void, png_image_free, (png_imagep image));
  * colormap_entries: set to the number of entries in the color-map (0 to 256)
  */
 #ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
-PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image,
-   const char *file, int convert_to_8bit, const void *buffer,
-   png_int_32 row_stride, const void *colormap));
+PNG_EXPORT(239, int, png_image_write_to_file,
+   (png_imagep image,
+    const char *file, int convert_to_8bit, const void *buffer,
+    png_int_32 row_stride, const void *colormap));
    /* Write the image to the named file. */
 
-PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
-   int convert_to_8_bit, const void *buffer, png_int_32 row_stride,
-   const void *colormap));
+PNG_EXPORT(240, int, png_image_write_to_stdio,
+   (png_imagep image,
+    FILE *file, int convert_to_8_bit, const void *buffer,
+    png_int_32 row_stride, const void *colormap));
    /* Write the image to the given FILE object. */
 #endif /* SIMPLIFIED_WRITE_STDIO */
 
@@ -3216,9 +3363,11 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
  * notices) you need to use one of the other APIs.
  */
 
-PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
-   png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit,
-   const void *buffer, png_int_32 row_stride, const void *colormap));
+PNG_EXPORT(245, int, png_image_write_to_memory,
+   (png_imagep image,
+    void *memory, png_alloc_size_t * PNG_RESTRICT memory_bytes,
+    int convert_to_8_bit,
+    const void *buffer, png_int_32 row_stride, const void *colormap));
    /* Write the image to the given memory buffer.  The function both writes the
     * whole PNG data stream to *memory and updates *memory_bytes with the count
     * of bytes written.
@@ -3394,7 +3543,7 @@ PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option,
  * one to use is one more than this.)
  */
 #ifdef PNG_EXPORT_LAST_ORDINAL
-  PNG_EXPORT_LAST_ORDINAL(259);
+   PNG_EXPORT_LAST_ORDINAL(259);
 #endif
 
 #ifdef __cplusplus
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
index 4bc5f7bb468..959c604edbc 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
@@ -29,9 +29,9 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.51
+ * libpng version 1.6.54
  *
- * Copyright (c) 2018-2025 Cosmin Truta
+ * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
  * Copyright (c) 1996-1997 Andreas Dilger
  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
index 44c86ebfef9..324d1951a52 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
@@ -78,7 +78,8 @@ png_error,(png_const_structrp png_ptr, png_const_charp error_message),
 }
 #else
 PNG_FUNCTION(void,PNGAPI
-png_err,(png_const_structrp png_ptr),PNG_NORETURN)
+png_err,(png_const_structrp png_ptr),
+    PNG_NORETURN)
 {
    /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed
     * erroneously as '\0', instead of the empty string "".  This was
@@ -405,8 +406,8 @@ static const char png_digit[16] = {
 };
 
 static void /* PRIVATE */
-png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
-    error_message)
+png_format_buffer(png_const_structrp png_ptr, png_charp buffer,
+    png_const_charp error_message)
 {
    png_uint_32 chunk_name = png_ptr->chunk_name;
    int iout = 0, ishift = 24;
@@ -485,8 +486,8 @@ png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
 #ifdef PNG_READ_SUPPORTED
 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
 void PNGAPI
-png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp
-    error_message)
+png_chunk_benign_error(png_const_structrp png_ptr,
+    png_const_charp error_message)
 {
    if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
       png_chunk_warning(png_ptr, error_message);
@@ -543,7 +544,8 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
 #ifdef PNG_ERROR_TEXT_SUPPORTED
 #ifdef PNG_FLOATING_POINT_SUPPORTED
 PNG_FUNCTION(void,
-png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)
+png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),
+    PNG_NORETURN)
 {
 #  define fixed_message "fixed point overflow in "
 #  define fixed_message_ln ((sizeof fixed_message)-1)
@@ -696,7 +698,8 @@ png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
 }
 
 PNG_FUNCTION(void,PNGAPI
-png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
+png_longjmp,(png_const_structrp png_ptr, int val),
+    PNG_NORETURN)
 {
 #ifdef PNG_SETJMP_SUPPORTED
    if (png_ptr != NULL && png_ptr->longjmp_fn != NULL &&
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
index ed2e7f886f5..a5bdcd1b524 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
@@ -151,8 +151,8 @@ png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
 }
 
 png_uint_32 PNGAPI
-png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
-   info_ptr)
+png_get_x_pixels_per_meter(png_const_structrp png_ptr,
+    png_const_inforp info_ptr)
 {
 #ifdef PNG_pHYs_SUPPORTED
    png_debug(1, "in png_get_x_pixels_per_meter");
@@ -172,8 +172,8 @@ png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
 }
 
 png_uint_32 PNGAPI
-png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
-    info_ptr)
+png_get_y_pixels_per_meter(png_const_structrp png_ptr,
+    png_const_inforp info_ptr)
 {
 #ifdef PNG_pHYs_SUPPORTED
    png_debug(1, "in png_get_y_pixels_per_meter");
@@ -215,8 +215,8 @@ png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr)
 
 #ifdef PNG_FLOATING_POINT_SUPPORTED
 float PNGAPI
-png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp
-   info_ptr)
+png_get_pixel_aspect_ratio(png_const_structrp png_ptr,
+    png_const_inforp info_ptr)
 {
 #ifdef PNG_READ_pHYs_SUPPORTED
    png_debug(1, "in png_get_pixel_aspect_ratio");
@@ -766,7 +766,6 @@ png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
    }
 
    return 0;
-
 }
 #endif
 
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
index 4cfae474751..b413b510acf 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
@@ -31,9 +31,9 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  */
-/* libpng version 1.6.51 */
+/* libpng version 1.6.54 */
 
-/* Copyright (c) 2018-2025 Cosmin Truta */
+/* Copyright (c) 2018-2026 Cosmin Truta */
 /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
 
 /* This code is released under the libpng license. */
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c
index 12b71bcbc02..8ec703616ec 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c
@@ -75,7 +75,8 @@ png_destroy_png_struct(png_structrp png_ptr)
  * have the ability to do that.
  */
 PNG_FUNCTION(png_voidp,PNGAPI
-png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),
+    PNG_ALLOCATED)
 {
    png_voidp ret;
 
@@ -147,7 +148,8 @@ png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
 
 PNG_FUNCTION(png_voidp /* PRIVATE */,
 png_malloc_array,(png_const_structrp png_ptr, int nelements,
-    size_t element_size),PNG_ALLOCATED)
+    size_t element_size),
+    PNG_ALLOCATED)
 {
    if (nelements <= 0 || element_size == 0)
       png_error(png_ptr, "internal error: array alloc");
@@ -157,7 +159,8 @@ png_malloc_array,(png_const_structrp png_ptr, int nelements,
 
 PNG_FUNCTION(png_voidp /* PRIVATE */,
 png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
-    int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
+    int old_elements, int add_elements, size_t element_size),
+    PNG_ALLOCATED)
 {
    /* These are internal errors: */
    if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
@@ -196,7 +199,8 @@ png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
  * function png_malloc_default is also provided.
  */
 PNG_FUNCTION(png_voidp,PNGAPI
-png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),
+    PNG_ALLOCATED)
 {
    png_voidp ret;
 
@@ -270,7 +274,8 @@ png_free(png_const_structrp png_ptr, png_voidp ptr)
 }
 
 PNG_FUNCTION(void,PNGAPI
-png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
+png_free_default,(png_const_structrp png_ptr, png_voidp ptr),
+    PNG_DEPRECATED)
 {
    if (png_ptr == NULL || ptr == NULL)
       return;
@@ -284,8 +289,8 @@ png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
  * of allocating and freeing memory.
  */
 void PNGAPI
-png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr
-  malloc_fn, png_free_ptr free_fn)
+png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr,
+    png_malloc_ptr malloc_fn, png_free_ptr free_fn)
 {
    if (png_ptr != NULL)
    {
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
index dcd005efb34..ee91f58d4ba 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
@@ -1104,15 +1104,17 @@ extern "C" {
  */
 /* Zlib support */
 #define PNG_UNEXPECTED_ZLIB_RETURN (-7)
-PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
+PNG_INTERNAL_FUNCTION(void, png_zstream_error,
+   (png_structrp png_ptr, int ret),
    PNG_EMPTY);
    /* Used by the zlib handling functions to ensure that z_stream::msg is always
     * set before they return.
     */
 
 #ifdef PNG_WRITE_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
-   png_compression_bufferp *list),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_free_buffer_list,
+   (png_structrp png_ptr, png_compression_bufferp *list),
+   PNG_EMPTY);
    /* Free the buffer list used by the compressed write code. */
 #endif
 
@@ -1124,22 +1126,25 @@ PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
    defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \
    (defined(PNG_sCAL_SUPPORTED) && \
    defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr,
-   double fp, png_const_charp text),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_fixed_point, png_fixed,
+   (png_const_structrp png_ptr, double fp, png_const_charp text),
+   PNG_EMPTY);
 #endif
 
 #if defined(PNG_FLOATING_POINT_SUPPORTED) && \
    !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
    (defined(PNG_cLLI_SUPPORTED) || defined(PNG_mDCV_SUPPORTED))
-PNG_INTERNAL_FUNCTION(png_uint_32,png_fixed_ITU,(png_const_structrp png_ptr,
-   double fp, png_const_charp text),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_uint_32, png_fixed_ITU,
+   (png_const_structrp png_ptr, double fp, png_const_charp text),
+   PNG_EMPTY);
 #endif
 
 /* Check the user version string for compatibility, returns false if the version
  * numbers aren't compatible.
  */
-PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
-   png_const_charp user_png_ver),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_user_version_check,
+   (png_structrp png_ptr, png_const_charp user_png_ver),
+   PNG_EMPTY);
 
 #ifdef PNG_READ_SUPPORTED /* should only be used on read */
 /* Security: read limits on the largest allocations while reading a PNG.  This
@@ -1164,24 +1169,28 @@ PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
  * does, however, call the application provided allocator and that could call
  * png_error (although that would be a bug in the application implementation.)
  */
-PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr,
-   png_alloc_size_t size),PNG_ALLOCATED);
+PNG_INTERNAL_FUNCTION(png_voidp, png_malloc_base,
+   (png_const_structrp png_ptr, png_alloc_size_t size),
+   PNG_ALLOCATED);
 
 #if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
    defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
 /* Internal array allocator, outputs no error or warning messages on failure,
  * just returns NULL.
  */
-PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr,
-   int nelements, size_t element_size),PNG_ALLOCATED);
+PNG_INTERNAL_FUNCTION(png_voidp, png_malloc_array,
+   (png_const_structrp png_ptr, int nelements, size_t element_size),
+   PNG_ALLOCATED);
 
 /* The same but an existing array is extended by add_elements.  This function
  * also memsets the new elements to 0 and copies the old elements.  The old
  * array is not freed or altered.
  */
-PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr,
-   png_const_voidp array, int old_elements, int add_elements,
-   size_t element_size),PNG_ALLOCATED);
+PNG_INTERNAL_FUNCTION(png_voidp, png_realloc_array,
+   (png_const_structrp png_ptr,
+    png_const_voidp array, int old_elements, int add_elements,
+    size_t element_size),
+   PNG_ALLOCATED);
 #endif /* text, sPLT or unknown chunks */
 
 /* Magic to create a struct when there is no struct to call the user supplied
@@ -1190,84 +1199,106 @@ PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr,
  * restriction so libpng has to assume that the 'free' handler, at least, might
  * call png_error.
  */
-PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct,
-   (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
-    png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn,
-    png_free_ptr free_fn),PNG_ALLOCATED);
+PNG_INTERNAL_FUNCTION(png_structp, png_create_png_struct,
+   (png_const_charp user_png_ver,
+    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn,
+    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
+   PNG_ALLOCATED);
 
 /* Free memory from internal libpng struct */
-PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr),
+PNG_INTERNAL_FUNCTION(void, png_destroy_png_struct,
+   (png_structrp png_ptr),
    PNG_EMPTY);
 
 /* Free an allocated jmp_buf (always succeeds) */
-PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_free_jmpbuf,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 
 /* Function to allocate memory for zlib.  PNGAPI is disallowed. */
-PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size),
+PNG_INTERNAL_FUNCTION(voidpf, png_zalloc,
+   (voidpf png_ptr, uInt items, uInt size),
    PNG_ALLOCATED);
 
 /* Function to free memory for zlib.  PNGAPI is disallowed. */
-PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_zfree,
+   (voidpf png_ptr, voidpf ptr),
+   PNG_EMPTY);
 
 /* Next four functions are used internally as callbacks.  PNGCBAPI is required
  * but not PNG_EXPORT.  PNGAPI added at libpng version 1.2.3, changed to
  * PNGCBAPI at 1.5.0
  */
 
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr,
-    png_bytep data, size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_default_read_data,
+   (png_structp png_ptr, png_bytep data, size_t length),
+   PNG_EMPTY);
 
 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr,
-    png_bytep buffer, size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_push_fill_buffer,
+   (png_structp png_ptr, png_bytep buffer, size_t length),
+   PNG_EMPTY);
 #endif
 
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr,
-    png_bytep data, size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_default_write_data,
+   (png_structp png_ptr, png_bytep data, size_t length),
+   PNG_EMPTY);
 
 #ifdef PNG_WRITE_FLUSH_SUPPORTED
 #  ifdef PNG_STDIO_SUPPORTED
-PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr),
+PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_default_flush,
+   (png_structp png_ptr),
    PNG_EMPTY);
 #  endif
 #endif
 
 /* Reset the CRC variable */
-PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_reset_crc,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 
 /* Write the "data" buffer to whatever output you are using */
-PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr,
-    png_const_bytep data, size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_data,
+   (png_structrp png_ptr, png_const_bytep data, size_t length),
+   PNG_EMPTY);
 
 /* Read and check the PNG file signature */
-PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr,
-   png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_sig,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
 
 /* Read the chunk header (length + type name) */
-PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr),
+PNG_INTERNAL_FUNCTION(png_uint_32, png_read_chunk_header,
+   (png_structrp png_ptr),
    PNG_EMPTY);
 
 /* Read data from whatever input you are using into the "data" buffer */
-PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data,
-    size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_data,
+   (png_structrp png_ptr, png_bytep data, size_t length),
+   PNG_EMPTY);
 
 /* Read bytes into buf, and update png_ptr->crc */
-PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,
-    png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_crc_read,
+   (png_structrp png_ptr, png_bytep buf, png_uint_32 length),
+   PNG_EMPTY);
 
 /* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
-PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,
-   png_uint_32 skip),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_crc_finish,
+   (png_structrp png_ptr, png_uint_32 skip),
+   PNG_EMPTY);
 
 /* Calculate the CRC over a section of data.  Note that we are only
  * passing a maximum of 64K on systems that have this as a memory limit,
  * since this is the maximum buffer size we can specify.
  */
-PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,
-   png_const_bytep ptr, size_t length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_calculate_crc,
+   (png_structrp png_ptr, png_const_bytep ptr, size_t length),
+   PNG_EMPTY);
 
 #ifdef PNG_WRITE_FLUSH_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_flush,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 #endif
 
 /* Write various chunks */
@@ -1275,68 +1306,86 @@ PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
 /* Write the IHDR chunk, and update the png_struct with the necessary
  * information.
  */
-PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
-   png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
-   int compression_method, int filter_method, int interlace_method),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
-   png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);
-
-PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
-   png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
+PNG_INTERNAL_FUNCTION(void, png_write_IHDR,
+   (png_structrp png_ptr,
+    png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
+    int compression_method, int filter_method, int interlace_method),
    PNG_EMPTY);
 
-PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_PLTE,
+   (png_structrp png_ptr,
+    png_const_colorp palette, png_uint_32 num_pal),
+   PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void, png_compress_IDAT,
+   (png_structrp png_ptr,
+    png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
+   PNG_EMPTY);
+
+PNG_INTERNAL_FUNCTION(void, png_write_IEND,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 
 #ifdef PNG_WRITE_gAMA_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr,
-    png_fixed_point file_gamma),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_gAMA_fixed,
+   (png_structrp png_ptr, png_fixed_point file_gamma),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_sBIT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr,
-    png_const_color_8p sbit, int color_type),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_sBIT,
+   (png_structrp png_ptr, png_const_color_8p sbit, int color_type),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_cHRM_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr,
-    const png_xy *xy), PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_cHRM_fixed,
+   (png_structrp png_ptr, const png_xy *xy),
+   PNG_EMPTY);
    /* The xy value must have been previously validated */
 #endif
 
 #ifdef PNG_WRITE_cICP_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_cICP,(png_structrp png_ptr,
+PNG_INTERNAL_FUNCTION(void, png_write_cICP,
+   (png_structrp png_ptr,
     png_byte colour_primaries, png_byte transfer_function,
-    png_byte matrix_coefficients, png_byte video_full_range_flag), PNG_EMPTY);
+    png_byte matrix_coefficients, png_byte video_full_range_flag),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_cLLI_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_cLLI_fixed,(png_structrp png_ptr,
-   png_uint_32 maxCLL, png_uint_32 maxFALL), PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_cLLI_fixed,
+   (png_structrp png_ptr, png_uint_32 maxCLL, png_uint_32 maxFALL),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_mDCV_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_mDCV_fixed,(png_structrp png_ptr,
-   png_uint_16 red_x, png_uint_16 red_y,
-   png_uint_16 green_x, png_uint_16 green_y,
-   png_uint_16 blue_x, png_uint_16 blue_y,
-   png_uint_16 white_x, png_uint_16 white_y,
-   png_uint_32 maxDL, png_uint_32 minDL), PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_mDCV_fixed,
+   (png_structrp png_ptr,
+    png_uint_16 red_x, png_uint_16 red_y,
+    png_uint_16 green_x, png_uint_16 green_y,
+    png_uint_16 blue_x, png_uint_16 blue_y,
+    png_uint_16 white_x, png_uint_16 white_y,
+    png_uint_32 maxDL, png_uint_32 minDL),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_sRGB_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr,
-    int intent),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_sRGB,
+   (png_structrp png_ptr, int intent),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_eXIf_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr,
-    png_bytep exif, int num_exif),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_eXIf,
+   (png_structrp png_ptr, png_bytep exif, int num_exif),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
-   png_const_charp name, png_const_bytep profile, png_uint_32 proflen),
+PNG_INTERNAL_FUNCTION(void, png_write_iCCP,
+   (png_structrp png_ptr,
+    png_const_charp name, png_const_bytep profile, png_uint_32 proflen),
    PNG_EMPTY);
    /* Writes a previously 'set' profile.  The profile argument is **not**
     * compressed.
@@ -1344,82 +1393,106 @@ PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
 #endif
 
 #ifdef PNG_WRITE_sPLT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr,
-    png_const_sPLT_tp palette),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_sPLT,
+   (png_structrp png_ptr, png_const_sPLT_tp palette),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_tRNS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr,
+PNG_INTERNAL_FUNCTION(void, png_write_tRNS,
+   (png_structrp png_ptr,
     png_const_bytep trans, png_const_color_16p values, int number,
-    int color_type),PNG_EMPTY);
+    int color_type),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_bKGD_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr,
-    png_const_color_16p values, int color_type),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_bKGD,
+   (png_structrp png_ptr, png_const_color_16p values, int color_type),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_hIST_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr,
-    png_const_uint_16p hist, int num_hist),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_hIST,
+   (png_structrp png_ptr, png_const_uint_16p hist, int num_hist),
+   PNG_EMPTY);
 #endif
 
 /* Chunks that have keywords */
 #ifdef PNG_WRITE_tEXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
-   png_const_charp key, png_const_charp text, size_t text_len),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_tEXt,
+   (png_structrp png_ptr,
+    png_const_charp key, png_const_charp text, size_t text_len),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_zTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp
-    key, png_const_charp text, int compression),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_zTXt,
+   (png_structrp png_ptr,
+    png_const_charp key, png_const_charp text, int compression),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_iTXt_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr,
+PNG_INTERNAL_FUNCTION(void, png_write_iTXt,
+   (png_structrp png_ptr,
     int compression, png_const_charp key, png_const_charp lang,
-    png_const_charp lang_key, png_const_charp text),PNG_EMPTY);
+    png_const_charp lang_key, png_const_charp text),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_TEXT_SUPPORTED  /* Added at version 1.0.14 and 1.2.4 */
-PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr,
-    png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_set_text_2,
+   (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_textp text_ptr, int num_text),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_oFFs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr,
-    png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_oFFs,
+   (png_structrp png_ptr,
+    png_int_32 x_offset, png_int_32 y_offset, int unit_type),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_pCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr,
-    png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,
-    png_const_charp units, png_charpp params),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_pCAL,
+   (png_structrp png_ptr,
+    png_charp purpose, png_int_32 X0, png_int_32 X1,
+    int type, int nparams, png_const_charp units, png_charpp params),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_pHYs_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr,
+PNG_INTERNAL_FUNCTION(void, png_write_pHYs,
+   (png_structrp png_ptr,
     png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
-    int unit_type),PNG_EMPTY);
+    int unit_type),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_tIME_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr,
-    png_const_timep mod_time),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_tIME,
+   (png_structrp png_ptr, png_const_timep mod_time),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_WRITE_sCAL_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
-    int unit, png_const_charp width, png_const_charp height),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_sCAL_s,
+   (png_structrp png_ptr,
+    int unit, png_const_charp width, png_const_charp height),
+   PNG_EMPTY);
 #endif
 
 /* Called when finished processing a row of data */
-PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr),
-    PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_finish_row,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 
 /* Internal use only.   Called before first row of data */
-PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),
-    PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_start_row,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 
 /* Combine a row of data, dealing with alpha, etc. if requested.  'row' is an
  * array of png_ptr->width pixels.  If the image is not interlaced or this
@@ -1447,8 +1520,9 @@ PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),
 #ifndef PNG_USE_COMPILE_TIME_MASKS
 #  define PNG_USE_COMPILE_TIME_MASKS 1
 #endif
-PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,
-    png_bytep row, int display),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_combine_row,
+   (png_const_structrp png_ptr, png_bytep row, int display),
+   PNG_EMPTY);
 
 #ifdef PNG_READ_INTERLACING_SUPPORTED
 /* Expand an interlaced row: the 'row_info' describes the pass data that has
@@ -1457,170 +1531,230 @@ PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,
  * the pixels are *replicated* to the intervening space.  This is essential for
  * the correct operation of png_combine_row, above.
  */
-PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info,
-    png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_read_interlace,
+   (png_row_infop row_info,
+    png_bytep row, int pass, png_uint_32 transformations),
+   PNG_EMPTY);
 #endif
 
 /* GRR TO DO (2.0 or whenever):  simplify other internal calling interfaces */
 
 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
 /* Grab pixels out of a row for an interlaced pass */
-PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info,
-    png_bytep row, int pass),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_write_interlace,
+   (png_row_infop row_info, png_bytep row, int pass),
+   PNG_EMPTY);
 #endif
 
 /* Unfilter a row: check the filter value before calling this, there is no point
  * calling it for PNG_FILTER_VALUE_NONE.
  */
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row,
+   (png_structrp pp, png_row_infop row_info,
+    png_bytep row, png_const_bytep prev_row, int filter),
+   PNG_EMPTY);
 
 #if PNG_ARM_NEON_OPT > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info,
-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_neon,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_neon,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_neon,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_neon,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_neon,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_neon,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_neon,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
 #endif
 
 #if PNG_MIPS_MSA_IMPLEMENTATION == 1
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_msa,(png_row_infop row_info,
-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_msa,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_msa,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_msa,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_msa,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_msa,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_msa,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_msa,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_msa,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
 #endif
 
 #if PNG_MIPS_MMI_IMPLEMENTATION > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_mmi,(png_row_infop row_info,
-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_mmi,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_mmi,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_mmi,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_mmi,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_mmi,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_mmi,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_mmi,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_mmi,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_mmi,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_mmi,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_mmi,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_mmi,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_mmi,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
 #endif
 
 #if PNG_POWERPC_VSX_OPT > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_vsx,(png_row_infop row_info,
-    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_vsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_vsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_vsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_vsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_vsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_vsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_vsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_vsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
 #endif
 
 #if PNG_INTEL_SSE_IMPLEMENTATION > 0
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_sse2,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_sse2,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_sse2,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_sse2,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_sse2,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_sse2,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
 #endif
 
 #if PNG_LOONGARCH_LSX_IMPLEMENTATION == 1
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_lsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_lsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_lsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_lsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_lsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_lsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_lsx,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_lsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_lsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_lsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_lsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_lsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_lsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_lsx,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
 #endif
 
 #if PNG_RISCV_RVV_IMPLEMENTATION == 1
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_rvv,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_rvv,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_rvv,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_rvv,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_rvv,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_rvv,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_rvv,(png_row_infop
-    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_rvv,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_rvv,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_rvv,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_rvv,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_rvv,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_rvv,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_rvv,
+   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
+   PNG_EMPTY);
 #endif
 
 /* Choose the best filter to use and filter the row data */
-PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
-    png_row_infop row_info),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_write_find_filter,
+   (png_structrp png_ptr, png_row_infop row_info),
+   PNG_EMPTY);
 
 #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
-   png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_IDAT_data,
+   (png_structrp png_ptr, png_bytep output, png_alloc_size_t avail_out),
+   PNG_EMPTY);
    /* Read 'avail_out' bytes of data from the IDAT stream.  If the output buffer
     * is NULL the function checks, instead, for the end of the stream.  In this
     * case a benign error will be issued if the stream end is not found or if
     * extra data has to be consumed.
     */
-PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),
+PNG_INTERNAL_FUNCTION(void, png_read_finish_IDAT,
+   (png_structrp png_ptr),
    PNG_EMPTY);
    /* This cleans up when the IDAT LZ stream does not end when the last image
     * byte is read; there is still some pending input.
     */
 
-PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),
+PNG_INTERNAL_FUNCTION(void, png_read_finish_row,
+   (png_structrp png_ptr),
    PNG_EMPTY);
    /* Finish a row while reading, dealing with interlacing passes, etc. */
 #endif /* SEQUENTIAL_READ */
 
 /* Initialize the row buffers, etc. */
-PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_start_row,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 
 #if ZLIB_VERNUM >= 0x1240
-PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush),
-      PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_zlib_inflate,
+   (png_structrp png_ptr, int flush),
+   PNG_EMPTY);
 #  define PNG_INFLATE(pp, flush) png_zlib_inflate(pp, flush)
 #else /* Zlib < 1.2.4 */
 #  define PNG_INFLATE(pp, flush) inflate(&(pp)->zstream, flush)
@@ -1628,38 +1762,44 @@ PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush),
 
 #ifdef PNG_READ_TRANSFORMS_SUPPORTED
 /* Optional call to update the users info structure */
-PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_transform_info,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
 #endif
 
 /* Shared transform functions, defined in pngtran.c */
 #if defined(PNG_WRITE_FILLER_SUPPORTED) || \
     defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info,
-    png_bytep row, int at_start),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_strip_channel,
+   (png_row_infop row_info, png_bytep row, int at_start),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_16BIT_SUPPORTED
 #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_swap,
+   (png_row_infop row_info, png_bytep row),
+   PNG_EMPTY);
 #endif
 #endif
 
 #if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
     defined(PNG_WRITE_PACKSWAP_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_packswap,
+   (png_row_infop row_info, png_bytep row),
+   PNG_EMPTY);
 #endif
 
 #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_invert,
+   (png_row_infop row_info, png_bytep row),
+   PNG_EMPTY);
 #endif
 
 #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info,
-    png_bytep row),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_bgr,
+   (png_row_infop row_info, png_bytep row),
+   PNG_EMPTY);
 #endif
 
 /* The following decodes the appropriate chunks, and does error correction,
@@ -1680,25 +1820,27 @@ typedef enum
    handled_ok          /* known, supported and handled without error */
 } png_handle_result_code;
 
-PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_unknown,
-    (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep),
-    PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_handle_result_code, png_handle_unknown,
+   (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep),
+   PNG_EMPTY);
    /* This is the function that gets called for unknown chunks.  The 'keep'
     * argument is either non-zero for a known chunk that has been set to be
     * handled as unknown or zero for an unknown chunk.  By default the function
     * just skips the chunk or errors out if it is critical.
     */
 
-PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_chunk,
-    (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_handle_result_code, png_handle_chunk,
+   (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),
+   PNG_EMPTY);
    /* This handles the current chunk png_ptr->chunk_name with unread
     * data[length] and returns one of the above result codes.
     */
 
 #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
     defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
-PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
-    (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_chunk_unknown_handling,
+   (png_const_structrp png_ptr, png_uint_32 chunk_name),
+   PNG_EMPTY);
    /* Exactly as the API png_handle_as_unknown() except that the argument is a
     * 32-bit chunk name, not a string.
     */
@@ -1706,93 +1848,122 @@ PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
 
 /* Handle the transformations for reading and writing */
 #ifdef PNG_READ_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr,
-   png_row_infop row_info),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_read_transformations,
+   (png_structrp png_ptr, png_row_infop row_info),
+   PNG_EMPTY);
 #endif
 #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr,
-   png_row_infop row_info),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_write_transformations,
+   (png_structrp png_ptr, png_row_infop row_info),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_READ_TRANSFORMS_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr),
-    PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_init_read_transformations,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 #endif
 
 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr),
-    PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr,
-    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,
-    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),
-    PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr,
-   png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,
-   png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr,
-    png_bytep row),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,
-    png_inforp info_ptr),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr),
-    PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_read_chunk,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_read_sig,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_check_crc,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_save_buffer,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_restore_buffer,
+   (png_structrp png_ptr, png_bytep buffer, size_t buffer_length),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_read_IDAT,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_process_IDAT_data,
+   (png_structrp png_ptr, png_bytep buffer, size_t buffer_length),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_process_row,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_have_info,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_have_end,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_have_row,
+   (png_structrp png_ptr, png_bytep row),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_push_read_end,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_process_some_data,
+   (png_structrp png_ptr, png_inforp info_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_read_push_finish_row,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
 #endif /* PROGRESSIVE_READ */
 
 #ifdef PNG_iCCP_SUPPORTED
 /* Routines for checking parts of an ICC profile. */
 #ifdef PNG_READ_iCCP_SUPPORTED
-PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,
-   png_const_charp name, png_uint_32 profile_length), PNG_EMPTY);
-#endif /* READ_iCCP */
-PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,
-   png_const_charp name, png_uint_32 profile_length,
-   png_const_bytep profile /* first 132 bytes only */, int color_type),
+PNG_INTERNAL_FUNCTION(int, png_icc_check_length,
+   (png_const_structrp png_ptr,
+    png_const_charp name, png_uint_32 profile_length),
+   PNG_EMPTY);
+#endif /* READ_iCCP */
+PNG_INTERNAL_FUNCTION(int, png_icc_check_header,
+   (png_const_structrp png_ptr,
+    png_const_charp name, png_uint_32 profile_length,
+    png_const_bytep profile /* first 132 bytes only */, int color_type),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_icc_check_tag_table,
+   (png_const_structrp png_ptr,
+    png_const_charp name, png_uint_32 profile_length,
+    png_const_bytep profile /* header plus whole tag table */),
    PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr,
-   png_const_charp name, png_uint_32 profile_length,
-   png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY);
 #endif /* iCCP */
 
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients, (png_structrp png_ptr),
+PNG_INTERNAL_FUNCTION(void, png_set_rgb_coefficients,
+   (png_structrp png_ptr),
    PNG_EMPTY);
    /* Set the rgb_to_gray coefficients from the cHRM Y values (if unset) */
 #endif /* READ_RGB_TO_GRAY */
 
 /* Added at libpng version 1.4.0 */
-PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,
-    png_uint_32 width, png_uint_32 height, int bit_depth,
-    int color_type, int interlace_type, int compression_type,
-    int filter_type),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_check_IHDR,
+   (png_const_structrp png_ptr,
+    png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
+    int interlace_type, int compression_type, int filter_type),
+   PNG_EMPTY);
 
 /* Added at libpng version 1.5.10 */
 #if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
     defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes,
-   (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_do_check_palette_indexes,
+   (png_structrp png_ptr, png_row_infop row_info),
+   PNG_EMPTY);
 #endif
 
 #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
-PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr,
-   png_const_charp name),PNG_NORETURN);
+PNG_INTERNAL_FUNCTION(void, png_fixed_error,
+   (png_const_structrp png_ptr, png_const_charp name),
+   PNG_NORETURN);
 #endif
 
 /* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite
  * the end.  Always leaves the buffer nul terminated.  Never errors out (and
  * there is no error code.)
  */
-PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize,
-   size_t pos, png_const_charp string),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(size_t, png_safecat,
+   (png_charp buffer, size_t bufsize, size_t pos, png_const_charp string),
+   PNG_EMPTY);
 
 /* Various internal functions to handle formatted warning messages, currently
  * only implemented for warnings.
@@ -1803,8 +1974,9 @@ PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize,
  * Returns the pointer to the start of the formatted string.  This utility only
  * does unsigned values.
  */
-PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start,
-   png_charp end, int format, png_alloc_size_t number),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_charp, png_format_number,
+   (png_const_charp start, png_charp end, int format, png_alloc_size_t number),
+   PNG_EMPTY);
 
 /* Convenience macro that takes an array: */
 #define PNG_FORMAT_NUMBER(buffer,format,number) \
@@ -1836,23 +2008,26 @@ PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start,
 typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][
    PNG_WARNING_PARAMETER_SIZE];
 
-PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p,
-   int number, png_const_charp string),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_warning_parameter,
+   (png_warning_parameters p, int number, png_const_charp string),
+   PNG_EMPTY);
    /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters,
     * including the trailing '\0'.
     */
-PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned,
+PNG_INTERNAL_FUNCTION(void, png_warning_parameter_unsigned,
    (png_warning_parameters p, int number, int format, png_alloc_size_t value),
    PNG_EMPTY);
    /* Use png_alloc_size_t because it is an unsigned type as big as any we
     * need to output.  Use the following for a signed value.
     */
-PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed,
+PNG_INTERNAL_FUNCTION(void, png_warning_parameter_signed,
    (png_warning_parameters p, int number, int format, png_int_32 value),
    PNG_EMPTY);
 
-PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr,
-   png_warning_parameters p, png_const_charp message),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_formatted_warning,
+   (png_const_structrp png_ptr,
+    png_warning_parameters p, png_const_charp message),
+   PNG_EMPTY);
    /* 'message' follows the X/Open approach of using @1, @2 to insert
     * parameters previously supplied using the above functions.  Errors in
     * specifying the parameters will simply result in garbage substitutions.
@@ -1874,14 +2049,16 @@ PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr,
  * If benign errors aren't supported they end up as the corresponding base call
  * (png_warning or png_error.)
  */
-PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr,
-   png_const_charp message),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_app_warning,
+   (png_const_structrp png_ptr, png_const_charp message),
+   PNG_EMPTY);
    /* The application provided invalid parameters to an API function or called
     * an API function at the wrong time, libpng can completely recover.
     */
 
-PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr,
-   png_const_charp message),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_app_error,
+   (png_const_structrp png_ptr, png_const_charp message),
+   PNG_EMPTY);
    /* As above but libpng will ignore the call, or attempt some other partial
     * recovery from the error.
     */
@@ -1890,8 +2067,9 @@ PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr,
 #  define png_app_error(pp,s) png_error(pp,s)
 #endif
 
-PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,
-   png_const_charp message, int error),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_chunk_report,
+   (png_const_structrp png_ptr, png_const_charp message, int error),
+   PNG_EMPTY);
    /* Report a recoverable issue in chunk data.  On read this is used to report
     * a problem found while reading a particular chunk and the
     * png_chunk_benign_error or png_chunk_warning function is used as
@@ -1917,14 +2095,17 @@ PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,
 #define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/)
 
 #ifdef PNG_FLOATING_POINT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr,
-   png_charp ascii, size_t size, double fp, unsigned int precision),
+PNG_INTERNAL_FUNCTION(void, png_ascii_from_fp,
+   (png_const_structrp png_ptr,
+    png_charp ascii, size_t size, double fp, unsigned int precision),
    PNG_EMPTY);
 #endif /* FLOATING_POINT */
 
 #ifdef PNG_FIXED_POINT_SUPPORTED
-PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
-   png_charp ascii, size_t size, png_fixed_point fp),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_ascii_from_fixed,
+   (png_const_structrp png_ptr,
+    png_charp ascii, size_t size, png_fixed_point fp),
+   PNG_EMPTY);
 #endif /* FIXED_POINT */
 #endif /* sCAL */
 
@@ -2016,8 +2197,9 @@ PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
  * that omits the last character (i.e. set the size to the index of
  * the problem character.)  This has not been tested within libpng.
  */
-PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,
-   size_t size, int *statep, size_t *whereami),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_check_fp_number,
+   (png_const_charp string, size_t size, int *statep, size_t *whereami),
+   PNG_EMPTY);
 
 /* This is the same but it checks a complete string and returns true
  * only if it just contains a floating point number.  As of 1.5.4 this
@@ -2025,8 +2207,9 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,
  * it was valid (otherwise it returns 0.)  This can be used for testing
  * for negative or zero values using the sticky flag.
  */
-PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
-   size_t size),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_check_fp_string,
+   (png_const_charp string, size_t size),
+   PNG_EMPTY);
 #endif /* pCAL || sCAL */
 
 #if defined(PNG_READ_GAMMA_SUPPORTED) ||\
@@ -2039,14 +2222,17 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
  * for overflow, true (1) if no overflow, in which case *res
  * holds the result.
  */
-PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a,
-   png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_muldiv,
+   (png_fixed_point_p res, png_fixed_point a,
+    png_int_32 multiplied_by, png_int_32 divided_by),
+   PNG_EMPTY);
 
 /* Calculate a reciprocal - used for gamma values.  This returns
  * 0 if the argument is 0 in order to maintain an undefined value;
  * there are no warnings.
  */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
+PNG_INTERNAL_FUNCTION(png_fixed_point, png_reciprocal,
+   (png_fixed_point a),
    PNG_EMPTY);
 #endif
 
@@ -2055,11 +2241,13 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
  * values.  Accuracy is suitable for gamma calculations but this is
  * not exact - use png_muldiv for that.  Only required at present on read.
  */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a,
-   png_fixed_point b),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_fixed_point, png_reciprocal2,
+   (png_fixed_point a, png_fixed_point b),
+   PNG_EMPTY);
 
 /* Return true if the gamma value is significantly different from 1.0 */
-PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
+PNG_INTERNAL_FUNCTION(int, png_gamma_significant,
+   (png_fixed_point gamma_value),
    PNG_EMPTY);
 
 /* PNGv3: 'resolve' the file gamma according to the new PNGv3 rules for colour
@@ -2070,8 +2258,9 @@ PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
  * transforms.  For this reason a gamma specified by png_set_gamma always takes
  * precedence.
  */
-PNG_INTERNAL_FUNCTION(png_fixed_point,png_resolve_file_gamma,
-   (png_const_structrp png_ptr),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_fixed_point, png_resolve_file_gamma,
+   (png_const_structrp png_ptr),
+   PNG_EMPTY);
 
 /* Internal fixed point gamma correction.  These APIs are called as
  * required to convert single values - they don't need to be fast,
@@ -2080,37 +2269,45 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_resolve_file_gamma,
  * While the input is an 'unsigned' value it must actually be the
  * correct bit value - 0..255 or 0..65535 as required.
  */
-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr,
-   unsigned int value, png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value,
-   png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value,
-   png_fixed_point gamma_value),PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr),
+PNG_INTERNAL_FUNCTION(png_uint_16, png_gamma_correct,
+   (png_structrp png_ptr, unsigned int value, png_fixed_point gamma_value),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_uint_16, png_gamma_16bit_correct,
+   (unsigned int value, png_fixed_point gamma_value),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_byte, png_gamma_8bit_correct,
+   (unsigned int value, png_fixed_point gamma_value),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_destroy_gamma_table,
+   (png_structrp png_ptr),
+   PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_build_gamma_table,
+   (png_structrp png_ptr, int bit_depth),
    PNG_EMPTY);
-PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr,
-   int bit_depth),PNG_EMPTY);
 #endif /* READ_GAMMA */
 
 #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
 /* Set the RGB coefficients if not already set by png_set_rgb_to_gray */
-PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients,(png_structrp png_ptr),
+PNG_INTERNAL_FUNCTION(void, png_set_rgb_coefficients,
+   (png_structrp png_ptr),
    PNG_EMPTY);
 #endif
 
 #if defined(PNG_cHRM_SUPPORTED) || defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
-PNG_INTERNAL_FUNCTION(int,png_XYZ_from_xy,(png_XYZ *XYZ, const png_xy *xy),
+PNG_INTERNAL_FUNCTION(int, png_XYZ_from_xy,
+   (png_XYZ *XYZ, const png_xy *xy),
    PNG_EMPTY);
 #endif /* cHRM || READ_RGB_TO_GRAY */
 
 #ifdef PNG_COLORSPACE_SUPPORTED
-PNG_INTERNAL_FUNCTION(int,png_xy_from_XYZ,(png_xy *xy, const png_XYZ *XYZ),
+PNG_INTERNAL_FUNCTION(int, png_xy_from_XYZ,
+   (png_xy *xy, const png_XYZ *XYZ),
    PNG_EMPTY);
 #endif
 
 /* SIMPLIFIED READ/WRITE SUPPORT */
 #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
-   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
+    defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
 /* The internal structure that png_image::opaque points to. */
 typedef struct png_control
 {
@@ -2138,28 +2335,34 @@ typedef struct png_control
  * errors that might occur.  Returns true on success, false on failure (either
  * of the function or as a result of a png_error.)
  */
-PNG_INTERNAL_CALLBACK(void,png_safe_error,(png_structp png_ptr,
-   png_const_charp error_message),PNG_NORETURN);
+PNG_INTERNAL_CALLBACK(void, png_safe_error,
+   (png_structp png_ptr, png_const_charp error_message),
+   PNG_NORETURN);
 
 #ifdef PNG_WARNINGS_SUPPORTED
-PNG_INTERNAL_CALLBACK(void,png_safe_warning,(png_structp png_ptr,
-   png_const_charp warning_message),PNG_EMPTY);
+PNG_INTERNAL_CALLBACK(void, png_safe_warning,
+   (png_structp png_ptr, png_const_charp warning_message),
+   PNG_EMPTY);
 #else
 #  define png_safe_warning 0/*dummy argument*/
 #endif
 
-PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image,
-   int (*function)(png_voidp), png_voidp arg),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_safe_execute,
+   (png_imagep image, int (*function)(png_voidp), png_voidp arg),
+   PNG_EMPTY);
 
 /* Utility to log an error; this also cleans up the png_image; the function
  * always returns 0 (false).
  */
-PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image,
-   png_const_charp error_message),PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(int, png_image_error,
+   (png_imagep image, png_const_charp error_message),
+   PNG_EMPTY);
 
 #ifndef PNG_SIMPLIFIED_READ_SUPPORTED
 /* png_image_free is used by the write code but not exported */
-PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, png_image_free,
+   (png_imagep image),
+   PNG_EMPTY);
 #endif /* !SIMPLIFIED_READ */
 
 #endif /* SIMPLIFIED READ/WRITE */
@@ -2170,8 +2373,9 @@ PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
  * the generic code is used.
  */
 #ifdef PNG_FILTER_OPTIMIZATIONS
-PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr,
-   unsigned int bpp), PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS,
+   (png_structp png_ptr, unsigned int bpp),
+   PNG_EMPTY);
    /* Just declare the optimization that will be used */
 #else
    /* List *all* the possible optimizations here - this branch is required if
@@ -2180,37 +2384,44 @@ PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr,
     */
 #  if PNG_ARM_NEON_OPT > 0
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+   (png_structp png_ptr, unsigned int bpp),
+   PNG_EMPTY);
 #endif
 
 #if PNG_MIPS_MSA_IMPLEMENTATION == 1
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_mips,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+   (png_structp png_ptr, unsigned int bpp),
+   PNG_EMPTY);
 #endif
 
 #  if PNG_MIPS_MMI_IMPLEMENTATION > 0
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_mips,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+   (png_structp png_ptr, unsigned int bpp),
+   PNG_EMPTY);
 #  endif
 
 #  if PNG_INTEL_SSE_IMPLEMENTATION > 0
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_sse2,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+   (png_structp png_ptr, unsigned int bpp),
+   PNG_EMPTY);
 #  endif
 #endif
 
 #if PNG_LOONGARCH_LSX_OPT > 0
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_lsx,
-    (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+   (png_structp png_ptr, unsigned int bpp),
+   PNG_EMPTY);
 #endif
 
 #  if PNG_RISCV_RVV_IMPLEMENTATION == 1
 PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_rvv,
-   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
+   (png_structp png_ptr, unsigned int bpp),
+   PNG_EMPTY);
 #endif
 
-PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr,
-   png_const_charp key, png_bytep new_key), PNG_EMPTY);
+PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword,
+   (png_structrp png_ptr, png_const_charp key, png_bytep new_key),
+   PNG_EMPTY);
 
 #if PNG_ARM_NEON_IMPLEMENTATION == 1
 PNG_INTERNAL_FUNCTION(void,
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
index b53668a09ce..79fd9ad6a82 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.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.
@@ -52,7 +52,8 @@
 /* Create a PNG structure for reading, and allocate any memory needed. */
 PNG_FUNCTION(png_structp,PNGAPI
 png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
-    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
+    png_error_ptr error_fn, png_error_ptr warn_fn),
+    PNG_ALLOCATED)
 {
 #ifndef PNG_USER_MEM_SUPPORTED
    png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
@@ -68,7 +69,8 @@ png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
 PNG_FUNCTION(png_structp,PNGAPI
 png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
     png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
-    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
+    png_malloc_ptr malloc_fn, png_free_ptr free_fn),
+    PNG_ALLOCATED)
 {
    png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
        error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
@@ -548,7 +550,6 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
 
    if (png_ptr->read_row_fn != NULL)
       (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
-
 }
 #endif /* SEQUENTIAL_READ */
 
@@ -896,7 +897,7 @@ png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn)
 #ifdef PNG_INFO_IMAGE_SUPPORTED
 void PNGAPI
 png_read_png(png_structrp png_ptr, png_inforp info_ptr,
-    int transforms, voidp params)
+    int transforms, png_voidp params)
 {
    png_debug(1, "in png_read_png");
 
@@ -1133,19 +1134,20 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
 
 typedef struct
 {
-   /* Arguments: */
+   /* Arguments */
    png_imagep image;
-   png_voidp  buffer;
+   png_voidp buffer;
    png_int_32 row_stride;
-   png_voidp  colormap;
+   png_voidp colormap;
    png_const_colorp background;
-   /* Local variables: */
-   png_voidp       local_row;
-   png_voidp       first_row;
-   ptrdiff_t       row_bytes;           /* step between rows */
-   int             file_encoding;       /* E_ values above */
-   png_fixed_point gamma_to_linear;     /* For P_FILE, reciprocal of gamma */
-   int             colormap_processing; /* PNG_CMAP_ values above */
+
+   /* Instance variables */
+   png_voidp local_row;
+   png_voidp first_row;
+   ptrdiff_t row_step;              /* step between rows */
+   int file_encoding;               /* E_ values above */
+   png_fixed_point gamma_to_linear; /* For P_FILE, reciprocal of gamma */
+   int colormap_processing;         /* PNG_CMAP_ values above */
 } png_image_read_control;
 
 /* Do all the *safe* initialization - 'safe' means that png_error won't be
@@ -2866,17 +2868,17 @@ png_image_read_and_map(png_voidp argument)
    }
 
    {
-      png_uint_32  height = image->height;
-      png_uint_32  width = image->width;
-      int          proc = display->colormap_processing;
-      png_bytep    first_row = png_voidcast(png_bytep, display->first_row);
-      ptrdiff_t    step_row = display->row_bytes;
+      png_uint_32 height = image->height;
+      png_uint_32 width = image->width;
+      int proc = display->colormap_processing;
+      png_bytep first_row = png_voidcast(png_bytep, display->first_row);
+      ptrdiff_t row_step = display->row_step;
       int pass;
 
       for (pass = 0; pass < passes; ++pass)
       {
-         unsigned int     startx, stepx, stepy;
-         png_uint_32      y;
+         unsigned int startx, stepx, stepy;
+         png_uint_32 y;
 
          if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
          {
@@ -2900,7 +2902,7 @@ png_image_read_and_map(png_voidp argument)
          for (; ylocal_row);
-            png_bytep outrow = first_row + y * step_row;
+            png_bytep outrow = first_row + y * row_step;
             png_const_bytep end_row = outrow + width;
 
             /* Read read the libpng data into the temporary buffer. */
@@ -3109,20 +3111,20 @@ png_image_read_colormapped(png_voidp argument)
     */
    {
       png_voidp first_row = display->buffer;
-      ptrdiff_t row_bytes = display->row_stride;
+      ptrdiff_t row_step = display->row_stride;
 
-      /* The following expression is designed to work correctly whether it gives
-       * a signed or an unsigned result.
+      /* The following adjustment is to ensure that calculations are correct,
+       * regardless whether row_step is positive or negative.
        */
-      if (row_bytes < 0)
+      if (row_step < 0)
       {
          char *ptr = png_voidcast(char*, first_row);
-         ptr += (image->height-1) * (-row_bytes);
+         ptr += (image->height-1) * (-row_step);
          first_row = png_voidcast(png_voidp, ptr);
       }
 
       display->first_row = first_row;
-      display->row_bytes = row_bytes;
+      display->row_step = row_step;
    }
 
    if (passes == 0)
@@ -3140,17 +3142,17 @@ png_image_read_colormapped(png_voidp argument)
 
    else
    {
-      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
+      ptrdiff_t row_step = display->row_step;
 
       while (--passes >= 0)
       {
-         png_uint_32      y = image->height;
-         png_bytep        row = png_voidcast(png_bytep, display->first_row);
+         png_uint_32 y = image->height;
+         png_bytep row = png_voidcast(png_bytep, display->first_row);
 
          for (; y > 0; --y)
          {
             png_read_row(png_ptr, row, NULL);
-            row += row_bytes;
+            row += row_step;
          }
       }
 
@@ -3166,9 +3168,11 @@ png_image_read_direct_scaled(png_voidp argument)
        argument);
    png_imagep image = display->image;
    png_structrp png_ptr = image->opaque->png_ptr;
+   png_inforp info_ptr = image->opaque->info_ptr;
    png_bytep local_row = png_voidcast(png_bytep, display->local_row);
    png_bytep first_row = png_voidcast(png_bytep, display->first_row);
-   ptrdiff_t row_bytes = display->row_bytes;
+   ptrdiff_t row_step = display->row_step;
+   size_t row_bytes = png_get_rowbytes(png_ptr, info_ptr);
    int passes;
 
    /* Handle interlacing. */
@@ -3197,9 +3201,14 @@ png_image_read_direct_scaled(png_voidp argument)
          /* Read into local_row (gets transformed 8-bit data). */
          png_read_row(png_ptr, local_row, NULL);
 
-         /* Copy from local_row to user buffer. */
-         memcpy(output_row, local_row, (size_t)row_bytes);
-         output_row += row_bytes;
+         /* Copy from local_row to user buffer.
+          * Use row_bytes (i.e. the actual size in bytes of the row data) for
+          * copying into output_row. Use row_step for advancing output_row,
+          * to respect the caller's stride for padding or negative (bottom-up)
+          * layouts.
+          */
+         memcpy(output_row, local_row, row_bytes);
+         output_row += row_step;
       }
    }
 
@@ -3231,17 +3240,18 @@ png_image_read_composite(png_voidp argument)
    }
 
    {
-      png_uint_32  height = image->height;
-      png_uint_32  width = image->width;
-      ptrdiff_t    step_row = display->row_bytes;
+      png_uint_32 height = image->height;
+      png_uint_32 width = image->width;
+      ptrdiff_t row_step = display->row_step;
       unsigned int channels =
           (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
+      int optimize_alpha = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;
       int pass;
 
       for (pass = 0; pass < passes; ++pass)
       {
-         unsigned int     startx, stepx, stepy;
-         png_uint_32      y;
+         unsigned int startx, stepx, stepy;
+         png_uint_32 y;
 
          if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
          {
@@ -3273,7 +3283,7 @@ png_image_read_composite(png_voidp argument)
             png_read_row(png_ptr, inrow, NULL);
 
             outrow = png_voidcast(png_bytep, display->first_row);
-            outrow += y * step_row;
+            outrow += y * row_step;
             end_row = outrow + width * channels;
 
             /* Now do the composition on each pixel in this row. */
@@ -3292,20 +3302,44 @@ png_image_read_composite(png_voidp argument)
 
                      if (alpha < 255) /* else just use component */
                      {
-                        /* This is PNG_OPTIMIZED_ALPHA, the component value
-                         * is a linear 8-bit value.  Combine this with the
-                         * current outrow[c] value which is sRGB encoded.
-                         * Arithmetic here is 16-bits to preserve the output
-                         * values correctly.
-                         */
-                        component *= 257*255; /* =65535 */
-                        component += (255-alpha)*png_sRGB_table[outrow[c]];
+                        if (optimize_alpha != 0)
+                        {
+                           /* This is PNG_OPTIMIZED_ALPHA, the component value
+                            * is a linear 8-bit value.  Combine this with the
+                            * current outrow[c] value which is sRGB encoded.
+                            * Arithmetic here is 16-bits to preserve the output
+                            * values correctly.
+                            */
+                           component *= 257*255; /* =65535 */
+                           component += (255-alpha)*png_sRGB_table[outrow[c]];
 
-                        /* So 'component' is scaled by 255*65535 and is
-                         * therefore appropriate for the sRGB to linear
-                         * conversion table.
-                         */
-                        component = PNG_sRGB_FROM_LINEAR(component);
+                           /* Clamp to the valid range to defend against
+                            * unforeseen cases where the data might be sRGB
+                            * instead of linear premultiplied.
+                            * (Belt-and-suspenders for CVE-2025-66293.)
+                            */
+                           if (component > 255*65535)
+                              component = 255*65535;
+
+                           /* So 'component' is scaled by 255*65535 and is
+                            * therefore appropriate for the sRGB-to-linear
+                            * conversion table.
+                            */
+                           component = PNG_sRGB_FROM_LINEAR(component);
+                        }
+                        else
+                        {
+                           /* Compositing was already done on the palette
+                            * entries.  The data is sRGB premultiplied on black.
+                            * Composite with the background in sRGB space.
+                            * This is not gamma-correct, but matches what was
+                            * done to the palette.
+                            */
+                           png_uint_32 background = outrow[c];
+                           component += ((255-alpha) * background + 127) / 255;
+                           if (component > 255)
+                              component = 255;
+                        }
                      }
 
                      outrow[c] = (png_byte)component;
@@ -3394,12 +3428,12 @@ png_image_read_background(png_voidp argument)
           */
          {
             png_bytep first_row = png_voidcast(png_bytep, display->first_row);
-            ptrdiff_t step_row = display->row_bytes;
+            ptrdiff_t row_step = display->row_step;
 
             for (pass = 0; pass < passes; ++pass)
             {
-               unsigned int     startx, stepx, stepy;
-               png_uint_32      y;
+               unsigned int startx, stepx, stepy;
+               png_uint_32 y;
 
                if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
                {
@@ -3426,7 +3460,7 @@ png_image_read_background(png_voidp argument)
                   {
                      png_bytep inrow = png_voidcast(png_bytep,
                          display->local_row);
-                     png_bytep outrow = first_row + y * step_row;
+                     png_bytep outrow = first_row + y * row_step;
                      png_const_bytep end_row = outrow + width;
 
                      /* Read the row, which is packed: */
@@ -3471,7 +3505,7 @@ png_image_read_background(png_voidp argument)
                   {
                      png_bytep inrow = png_voidcast(png_bytep,
                          display->local_row);
-                     png_bytep outrow = first_row + y * step_row;
+                     png_bytep outrow = first_row + y * row_step;
                      png_const_bytep end_row = outrow + width;
 
                      /* Read the row, which is packed: */
@@ -3517,9 +3551,9 @@ png_image_read_background(png_voidp argument)
             png_uint_16p first_row = png_voidcast(png_uint_16p,
                 display->first_row);
             /* The division by two is safe because the caller passed in a
-             * stride which was multiplied by 2 (below) to get row_bytes.
+             * stride which was multiplied by 2 (below) to get row_step.
              */
-            ptrdiff_t    step_row = display->row_bytes / 2;
+            ptrdiff_t row_step = display->row_step / 2;
             unsigned int preserve_alpha = (image->format &
                 PNG_FORMAT_FLAG_ALPHA) != 0;
             unsigned int outchannels = 1U+preserve_alpha;
@@ -3533,8 +3567,8 @@ png_image_read_background(png_voidp argument)
 
             for (pass = 0; pass < passes; ++pass)
             {
-               unsigned int     startx, stepx, stepy;
-               png_uint_32      y;
+               unsigned int startx, stepx, stepy;
+               png_uint_32 y;
 
                /* The 'x' start and step are adjusted to output components here.
                 */
@@ -3561,7 +3595,7 @@ png_image_read_background(png_voidp argument)
                for (; ybuffer;
-      ptrdiff_t row_bytes = display->row_stride;
+      ptrdiff_t row_step = display->row_stride;
 
       if (linear != 0)
-         row_bytes *= 2;
+         row_step *= 2;
 
-      /* The following expression is designed to work correctly whether it gives
-       * a signed or an unsigned result.
+      /* The following adjustment is to ensure that calculations are correct,
+       * regardless whether row_step is positive or negative.
        */
-      if (row_bytes < 0)
+      if (row_step < 0)
       {
          char *ptr = png_voidcast(char*, first_row);
-         ptr += (image->height-1) * (-row_bytes);
+         ptr += (image->height - 1) * (-row_step);
          first_row = png_voidcast(png_voidp, ptr);
       }
 
       display->first_row = first_row;
-      display->row_bytes = row_bytes;
+      display->row_step = row_step;
    }
 
    if (do_local_compose != 0)
@@ -4063,17 +4097,17 @@ png_image_read_direct(png_voidp argument)
 
    else
    {
-      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
+      ptrdiff_t row_step = display->row_step;
 
       while (--passes >= 0)
       {
-         png_uint_32      y = image->height;
-         png_bytep        row = png_voidcast(png_bytep, display->first_row);
+         png_uint_32 y = image->height;
+         png_bytep row = png_voidcast(png_bytep, display->first_row);
 
          for (; y > 0; --y)
          {
             png_read_row(png_ptr, row, NULL);
-            row += row_bytes;
+            row += row_step;
          }
       }
 
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
index a19615f49fe..7680fe64828 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
@@ -1149,8 +1149,8 @@ png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,
 #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
     defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
 void PNGAPI
-png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
-    read_user_transform_fn)
+png_set_read_user_transform_fn(png_structrp png_ptr,
+    png_user_transform_ptr read_user_transform_fn)
 {
    png_debug(1, "in png_set_read_user_transform_fn");
 
@@ -1872,6 +1872,7 @@ png_init_read_transformations(png_structrp png_ptr)
              * transformations elsewhere.
              */
             png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
+            png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
          } /* color_type == PNG_COLOR_TYPE_PALETTE */
 
          /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
index 07d53cb2c76..01bb0c8bedc 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
@@ -2415,7 +2415,7 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 static png_handle_result_code /* PRIVATE */
 png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
-   png_text  text_info;
+   png_text text_info;
    png_bytep buffer;
    png_charp key;
    png_charp text;
@@ -2488,8 +2488,8 @@ static png_handle_result_code /* PRIVATE */
 png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
 {
    png_const_charp errmsg = NULL;
-   png_bytep       buffer;
-   png_uint_32     keyword_length;
+   png_bytep buffer;
+   png_uint_32 keyword_length;
 
    png_debug(1, "in png_handle_zTXt");
 
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
index 2350057e70e..b9f6cb5d437 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
@@ -831,8 +831,8 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
     defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
 #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
 void PNGAPI
-png_set_user_transform_info(png_structrp png_ptr, png_voidp
-   user_transform_ptr, int user_transform_depth, int user_transform_channels)
+png_set_user_transform_info(png_structrp png_ptr, png_voidp user_transform_ptr,
+    int user_transform_depth, int user_transform_channels)
 {
    png_debug(1, "in png_set_user_transform_info");
 

From 599ed0bb5fd62e26c71651bc02f198cd27636cfb Mon Sep 17 00:00:00 2001
From: SendaoYan 
Date: Wed, 21 Jan 2026 03:39:02 +0000
Subject: [PATCH 087/126] 8375485: Tests in vmTestbase/nsk are failing due to
 missing class unloading after 8373945

Reviewed-by: lmesnik, cjplummer
---
 .../jvmti/scenarios/events/EM02/em02t003.java   | 17 ++++++++++++++++-
 .../vmTestbase/nsk/share/ClassUnloader.java     | 17 ++++++++++++++++-
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java
index d4cb4a8569a..a23732dc554 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2018, 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
@@ -105,6 +105,21 @@ public class em02t003 extends DebugeeClass {
                 return Consts.TEST_FAILED;
             }
 
+            while (thrd.isAlive()) {
+                logger.display("Thread state: " + thrd.getState()
+                    + " - waiting for completion.");
+                try {
+                    // small delay to avoid produce huge amount of output
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                }
+            }
+            try {
+                // give some time wait thread to exit completely
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+            }
+
             logger.display("MethodCompiling:: Provoke unloading compiled method - "
                                 + "\n\ttrying to unload class...");
             thrd = null;
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java
index 94b086f5757..5c7d2b8d640 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java
@@ -239,6 +239,9 @@ public class ClassUnloader {
      *
      * @see WhiteBox.getWhiteBox().fullGC()
      */
+
+    public static final int MAX_UNLOAD_ATTEMPS = 10;
+
     public boolean unloadClass() {
 
         // free references to class and class loader to be able for collecting by GC
@@ -247,14 +250,26 @@ public class ClassUnloader {
 
         // force class unloading by triggering full GC
         WhiteBox.getWhiteBox().fullGC();
+        int count = 0;
+        while (count++ < MAX_UNLOAD_ATTEMPS && !isClassLoaderReclaimed()) {
+            System.out.println("ClassUnloader: waiting for class loader reclaiming... " + count);
+            WhiteBox.getWhiteBox().fullGC();
+            try {
+                // small delay to give more changes to process objects
+                // inside VM like jvmti deferred queue
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+            }
+        }
 
         // force GC to unload marked class loader and its classes
         if (isClassLoaderReclaimed()) {
-            Runtime.getRuntime().gc();
+            System.out.println("ClassUnloader: class loader has been reclaimed.");
             return true;
         }
 
         // class loader has not been reclaimed
+        System.out.println("ClassUnloader: class loader is still reachable.");
         return false;
     }
 }

From a448f0b9f46de35ef26994e8540b9ae242372e8d Mon Sep 17 00:00:00 2001
From: SendaoYan 
Date: Wed, 21 Jan 2026 03:39:26 +0000
Subject: [PATCH 088/126] 8375668: Compiler warning
 implicit-const-int-float-conversion by clang23

Reviewed-by: dholmes, cnorrbin
---
 src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
index 8aafbf49c63..d083a9985c2 100644
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
@@ -188,7 +188,7 @@ class CachedMetric : public CHeapObj{
     volatile jlong _next_check_counter;
   public:
     CachedMetric() {
-      _metric = value_unlimited;
+      _metric = static_cast(value_unlimited);
       _next_check_counter = min_jlong;
     }
     bool should_check_metric() {

From 34d6e5e07b8ee43ee7f913dd47fa7c897f52e6c0 Mon Sep 17 00:00:00 2001
From: Kim Barrett 
Date: Wed, 21 Jan 2026 05:56:19 +0000
Subject: [PATCH 089/126] 8375737: Fix -Wzero-as-null-pointer-constant warnings
 in arm32 code

Reviewed-by: dholmes
---
 src/hotspot/cpu/arm/frame_arm.cpp         | 6 +++---
 src/hotspot/cpu/arm/nativeInst_arm_32.cpp | 6 +++---
 src/hotspot/cpu/arm/nativeInst_arm_32.hpp | 4 ++--
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/hotspot/cpu/arm/frame_arm.cpp b/src/hotspot/cpu/arm/frame_arm.cpp
index 7a23296a3d4..f791fae7bd7 100644
--- a/src/hotspot/cpu/arm/frame_arm.cpp
+++ b/src/hotspot/cpu/arm/frame_arm.cpp
@@ -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
@@ -356,10 +356,10 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const {
 bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
   assert(is_interpreted_frame(), "Not an interpreted frame");
   // These are reasonable sanity checks
-  if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) {
+  if (fp() == nullptr || (intptr_t(fp()) & (wordSize-1)) != 0) {
     return false;
   }
-  if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) {
+  if (sp() == nullptr || (intptr_t(sp()) & (wordSize-1)) != 0) {
     return false;
   }
   if (fp() + interpreter_frame_initial_sp_offset < sp()) {
diff --git a/src/hotspot/cpu/arm/nativeInst_arm_32.cpp b/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
index 232294b246a..df780ac31a6 100644
--- a/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
+++ b/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
@@ -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
@@ -172,7 +172,7 @@ void NativeMovConstReg::set_data(intptr_t x, address pc) {
 
     address addr = oop_addr != nullptr ? (address)oop_addr : (address)metadata_addr;
 
-    if(pc == 0) {
+    if (pc == nullptr) {
       offset = addr - instruction_address() - 8;
     } else {
       offset = addr - pc - 8;
@@ -228,7 +228,7 @@ void NativeMovConstReg::set_data(intptr_t x, address pc) {
 
 void NativeMovConstReg::set_pc_relative_offset(address addr, address pc) {
   int offset;
-  if (pc == 0) {
+  if (pc == nullptr) {
     offset = addr - instruction_address() - 8;
   } else {
     offset = addr - pc - 8;
diff --git a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp b/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
index 82385bf0244..2b52db89285 100644
--- a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
+++ b/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
@@ -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
@@ -371,7 +371,7 @@ class NativeMovConstReg: public NativeInstruction {
  public:
 
   intptr_t data() const;
-  void set_data(intptr_t x, address pc = 0);
+  void set_data(intptr_t x, address pc = nullptr);
   bool is_pc_relative() {
     return !is_movw();
   }

From b5727d27622e1e321733f8d0e606b366984104be Mon Sep 17 00:00:00 2001
From: Kim Barrett 
Date: Wed, 21 Jan 2026 06:04:09 +0000
Subject: [PATCH 090/126] 8375738: Fix -Wzero-as-null-pointer-constant warnings
 in MacOSX/bsd code

Reviewed-by: erikj, dholmes
---
 make/hotspot/lib/CompileGtest.gmk           | 5 +++--
 src/hotspot/os/bsd/memMapPrinter_macosx.cpp | 2 +-
 src/hotspot/os/bsd/os_bsd.cpp               | 4 ++--
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk
index 60912992134..327014b1e9d 100644
--- a/make/hotspot/lib/CompileGtest.gmk
+++ b/make/hotspot/lib/CompileGtest.gmk
@@ -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
@@ -61,7 +61,8 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \
     INCLUDE_FILES := gtest-all.cc gmock-all.cc, \
     DISABLED_WARNINGS_gcc := format-nonliteral maybe-uninitialized undef \
         unused-result zero-as-null-pointer-constant, \
-    DISABLED_WARNINGS_clang := format-nonliteral undef unused-result, \
+    DISABLED_WARNINGS_clang := format-nonliteral undef unused-result \
+        zero-as-null-pointer-constant, \
     DISABLED_WARNINGS_microsoft := 4530, \
     DEFAULT_CFLAGS := false, \
     CFLAGS := $(JVM_CFLAGS) \
diff --git a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp
index 6fd08d63e85..30e258c9d2c 100644
--- a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp
+++ b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp
@@ -132,7 +132,7 @@ public:
   static const char* tagToStr(uint32_t user_tag) {
     switch (user_tag) {
       case 0:
-        return 0;
+        return nullptr;
       X1(MALLOC, malloc);
       X1(MALLOC_SMALL, malloc_small);
       X1(MALLOC_LARGE, malloc_large);
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 667810bd6ca..0e21c2d1785 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -628,7 +628,7 @@ static void *thread_native_entry(Thread *thread) {
   log_info(os, thread)("Thread finished (tid: %zu, pthread id: %zu).",
     os::current_thread_id(), (uintx) pthread_self());
 
-  return 0;
+  return nullptr;
 }
 
 bool os::create_thread(Thread* thread, ThreadType thr_type,
@@ -1420,7 +1420,7 @@ int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *pa
 #elif defined(__APPLE__)
   for (uint32_t i = 1; i < _dyld_image_count(); i++) {
     // Value for top_address is returned as 0 since we don't have any information about module size
-    if (callback(_dyld_get_image_name(i), (address)_dyld_get_image_header(i), (address)0, param)) {
+    if (callback(_dyld_get_image_name(i), (address)_dyld_get_image_header(i), nullptr, param)) {
       return 1;
     }
   }

From 560a92a6327221c90596bcd17a87722e4910472a Mon Sep 17 00:00:00 2001
From: Jie Fu 
Date: Wed, 21 Jan 2026 06:33:54 +0000
Subject: [PATCH 091/126] 8375787: compiler/vectorapi/TestCastShapeBadOpc.java
 fails with release VMs

Reviewed-by: syan, lmesnik, fyang, epeter
---
 test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java
index 4c20c84bc50..743c243cb58 100644
--- a/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java
+++ b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java
@@ -30,6 +30,7 @@
  * @run main/othervm
  *      -XX:+IgnoreUnrecognizedVMOptions
  *      -XX:-TieredCompilation -Xbatch
+ *      -XX:+UnlockDiagnosticVMOptions
  *      -XX:StressSeed=1462975402
  *      -XX:+StressIncrementalInlining
  *      -XX:CompileCommand=compileonly,${test.main.class}::test2
@@ -44,6 +45,7 @@
  * @run main/othervm
  *      -XX:+IgnoreUnrecognizedVMOptions
  *      -XX:-TieredCompilation -Xbatch
+ *      -XX:+UnlockDiagnosticVMOptions
  *      -XX:+StressIncrementalInlining
  *      -XX:CompileCommand=compileonly,${test.main.class}::test2
  *      ${test.main.class}

From 4f87fb53ee5c6071fa57dfe9452eca9fe7b460ee Mon Sep 17 00:00:00 2001
From: Thomas Schatzl 
Date: Wed, 21 Jan 2026 09:01:00 +0000
Subject: [PATCH 092/126] 8375622: G1: Convert G1CodeRootSet to use Atomic

Reviewed-by: shade, sjohanss
---
 src/hotspot/share/gc/g1/g1CodeRootSet.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp
index 60ad3a2af32..ca4487876b9 100644
--- a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp
+++ b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
  * 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 @@
 #include "gc/g1/g1HeapRegion.hpp"
 #include "memory/allocation.hpp"
 #include "oops/oop.inline.hpp"
-#include "runtime/atomicAccess.hpp"
+#include "runtime/atomic.hpp"
 #include "utilities/concurrentHashTable.inline.hpp"
 #include "utilities/concurrentHashTableTasks.inline.hpp"
 
@@ -60,7 +60,7 @@ class G1CodeRootSetHashTable : public CHeapObj {
   HashTable _table;
   HashTableScanTask _table_scanner;
 
-  size_t volatile _num_entries;
+  Atomic _num_entries;
 
   bool is_empty() const { return number_of_entries() == 0; }
 
@@ -120,7 +120,7 @@ public:
     bool grow_hint = false;
     bool inserted = _table.insert(Thread::current(), lookup, method, &grow_hint);
     if (inserted) {
-      AtomicAccess::inc(&_num_entries);
+      _num_entries.add_then_fetch(1u);
     }
     if (grow_hint) {
       _table.grow(Thread::current());
@@ -131,7 +131,7 @@ public:
     HashTableLookUp lookup(method);
     bool removed = _table.remove(Thread::current(), lookup);
     if (removed) {
-      AtomicAccess::dec(&_num_entries);
+      _num_entries.sub_then_fetch(1u);
     }
     return removed;
   }
@@ -182,7 +182,7 @@ public:
     guarantee(succeeded, "unable to clean table");
 
     if (num_deleted != 0) {
-      size_t current_size = AtomicAccess::sub(&_num_entries, num_deleted);
+      size_t current_size = _num_entries.sub_then_fetch(num_deleted);
       shrink_to_match(current_size);
     }
   }
@@ -226,7 +226,7 @@ public:
 
   size_t mem_size() { return sizeof(*this) + _table.get_mem_size(Thread::current()); }
 
-  size_t number_of_entries() const { return AtomicAccess::load(&_num_entries); }
+  size_t number_of_entries() const { return _num_entries.load_relaxed(); }
 };
 
 uintx G1CodeRootSetHashTable::HashTableLookUp::get_hash() const {

From b1340305c8f5ea53b45b8bd3bd2ebe8f74864d40 Mon Sep 17 00:00:00 2001
From: Ivan Walulya 
Date: Wed, 21 Jan 2026 09:51:01 +0000
Subject: [PATCH 093/126] 8238686: G1 may waste lots of space or fail to
 uncommit when observing MinHeapFreeRatio during sizing after full gc

Reviewed-by: tschatzl, sjohanss
---
 src/hotspot/share/gc/g1/g1Arguments.cpp | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp
index 58e76cdd43a..ffb06a7d822 100644
--- a/src/hotspot/share/gc/g1/g1Arguments.cpp
+++ b/src/hotspot/share/gc/g1/g1Arguments.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) 2017, Red Hat, Inc. and/or its affiliates.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -209,6 +209,17 @@ void G1Arguments::initialize() {
     FLAG_SET_DEFAULT(GCTimeRatio, 24);
   }
 
+  // Do not interfere with GC-Pressure driven heap resizing unless the user
+  // explicitly sets otherwise. G1 heap sizing should be free to grow or shrink
+  // the heap based on GC pressure, rather than being forced to satisfy
+  // MinHeapFreeRatio or MaxHeapFreeRatio defaults that the user did not set.
+  if (FLAG_IS_DEFAULT(MinHeapFreeRatio)) {
+    FLAG_SET_DEFAULT(MinHeapFreeRatio, 0);
+  }
+  if (FLAG_IS_DEFAULT(MaxHeapFreeRatio)) {
+    FLAG_SET_DEFAULT(MaxHeapFreeRatio, 100);
+  }
+
   // Below, we might need to calculate the pause time interval based on
   // the pause target. When we do so we are going to give G1 maximum
   // flexibility and allow it to do pauses when it needs to. So, we'll

From 5c7c2f093b83a017970d9d05c258b4c0910bfc2c Mon Sep 17 00:00:00 2001
From: Francesco Andreuzzi 
Date: Wed, 21 Jan 2026 10:42:05 +0000
Subject: [PATCH 094/126] 8375717: Outdated link in jdk.jfr.internal.JVM
 javadoc

Reviewed-by: egahlin
---
 src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java
index 841221c57d9..24c92e81a4c 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java
@@ -71,7 +71,7 @@ public final class JVM {
     /**
      * Begin recording events
      *
-     * Requires that JFR has been started with {@link #createNativeJFR()}
+     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
      */
     public static native void beginRecording();
 
@@ -83,7 +83,7 @@ public final class JVM {
     /**
      * End recording events, which includes flushing data in thread buffers
      *
-     * Requires that JFR has been started with {@link #createNativeJFR()}
+     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
      *
      */
     public static native void endRecording();
@@ -144,7 +144,7 @@ public final class JVM {
     /**
      * Return unique identifier for stack trace.
      *
-     * Requires that JFR has been started with {@link #createNativeJFR()}
+     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
      *
      * @param skipCount number of frames to skip, or 0 if no frames should be
      *                  skipped
@@ -295,7 +295,7 @@ public final class JVM {
     /**
      * Sets the file where data should be written.
      *
-     * Requires that JFR has been started with {@link #createNativeJFR()}
+     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
      *
      * 
      * Recording  Previous  Current  Action
@@ -380,7 +380,7 @@ public final class JVM {
      * chunk, data should be written after GMT offset and size of metadata event
      * should be adjusted
      *
-     * Requires that JFR has been started with {@link #createNativeJFR()}
+     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
      *
      * @param bytes binary representation of metadata descriptor
      */
@@ -409,7 +409,7 @@ public final class JVM {
     /**
      * Destroys native part of JFR. If already destroy, call is ignored.
      *
-     * Requires that JFR has been started with {@link #createNativeJFR()}
+     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
      *
      * @return if an instance was actually destroyed.
      *

From 983ae96f60c935aa52f482d21ae6a0d947679541 Mon Sep 17 00:00:00 2001
From: Jatin Bhateja 
Date: Wed, 21 Jan 2026 11:20:18 +0000
Subject: [PATCH 095/126] 8375498: [VectorAPI] Dump primary vector IR details
 with -XX:+TraceNewVectors

Reviewed-by: epeter
---
 src/hotspot/share/opto/vectorIntrinsics.cpp | 81 ++++++++++++---------
 1 file changed, 45 insertions(+), 36 deletions(-)

diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp
index 6dcf4615b10..65d54e076b6 100644
--- a/src/hotspot/share/opto/vectorIntrinsics.cpp
+++ b/src/hotspot/share/opto/vectorIntrinsics.cpp
@@ -74,6 +74,11 @@ static bool is_vector_mask(ciKlass* klass) {
   return klass->is_subclass_of(ciEnv::current()->vector_VectorMask_klass());
 }
 
+static Node* trace_vector(Node* operation) {
+  VectorNode::trace_new_vector(operation, "VectorAPI");
+  return operation;
+}
+
 bool LibraryCallKit::arch_supports_vector_rotate(int opc, int num_elem, BasicType elem_bt,
                                                  VectorMaskUseType mask_use_type, bool has_scalar_args) {
   bool is_supported = true;
@@ -458,7 +463,7 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) {
     }
     default: fatal("unsupported arity: %d", n);
   }
-
+  trace_vector(operation);
   if (is_masked_op && mask != nullptr) {
     if (use_predicate) {
       operation->add_req(mask);
@@ -466,7 +471,7 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) {
     } else {
       operation->add_flag(Node::Flag_is_predicated_using_blend);
       operation = gvn().transform(operation);
-      operation = new VectorBlendNode(opd1, operation, mask);
+      operation = trace_vector(new VectorBlendNode(opd1, operation, mask));
     }
   }
   operation = gvn().transform(operation);
@@ -627,7 +632,7 @@ bool LibraryCallKit::inline_vector_mask_operation() {
     mask_vec = gvn().transform(VectorStoreMaskNode::make(gvn(), mask_vec, elem_bt, num_elem));
   }
   const Type* maskoper_ty = mopc == Op_VectorMaskToLong ? (const Type*)TypeLong::LONG : (const Type*)TypeInt::INT;
-  Node* maskoper = gvn().transform(VectorMaskOpNode::make(mask_vec, maskoper_ty, mopc));
+  Node* maskoper = gvn().transform(trace_vector(VectorMaskOpNode::make(mask_vec, maskoper_ty, mopc)));
   if (mopc != Op_VectorMaskToLong) {
     maskoper = ConvI2L(maskoper);
   }
@@ -710,10 +715,10 @@ bool LibraryCallKit::inline_vector_frombits_coerced() {
   if (opc == Op_VectorLongToMask) {
     const TypeVect* vt = TypeVect::makemask(elem_bt, num_elem);
     if (Matcher::mask_op_prefers_predicate(opc, vt)) {
-      broadcast = gvn().transform(new VectorLongToMaskNode(elem, vt));
+      broadcast = gvn().transform(trace_vector(new VectorLongToMaskNode(elem, vt)));
     } else {
       const TypeVect* mvt = TypeVect::make(T_BOOLEAN, num_elem);
-      broadcast = gvn().transform(new VectorLongToMaskNode(elem, mvt));
+      broadcast = gvn().transform(trace_vector(new VectorLongToMaskNode(elem, mvt)));
       broadcast = gvn().transform(new VectorLoadMaskNode(broadcast, vt));
     }
   } else {
@@ -741,7 +746,7 @@ bool LibraryCallKit::inline_vector_frombits_coerced() {
       }
       default: fatal("%s", type2name(elem_bt));
     }
-    broadcast = VectorNode::scalar2vector(elem, num_elem, elem_bt, is_mask);
+    broadcast = trace_vector(VectorNode::scalar2vector(elem, num_elem, elem_bt, is_mask));
     broadcast = gvn().transform(broadcast);
   }
 
@@ -927,22 +932,22 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) {
     if (is_mask) {
       val = gvn().transform(VectorStoreMaskNode::make(gvn(), val, elem_bt, num_elem));
     }
-    Node* vstore = gvn().transform(StoreVectorNode::make(0, control(), memory(addr), addr, addr_type, val, store_num_elem));
+    Node* vstore = gvn().transform(trace_vector(StoreVectorNode::make(0, control(), memory(addr), addr, addr_type, val, store_num_elem)));
     set_memory(vstore, addr_type);
   } else {
     // When using byte array, we need to load as byte then reinterpret the value. Otherwise, do a simple vector load.
     Node* vload = nullptr;
     if (mismatched_ms) {
-      vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt));
+      vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt)));
       const TypeVect* to_vect_type = TypeVect::make(elem_bt, num_elem);
       vload = gvn().transform(new VectorReinterpretNode(vload, vload->bottom_type()->is_vect(), to_vect_type));
     } else {
       // Special handle for masks
       if (is_mask) {
-        vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, T_BOOLEAN));
+        vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, T_BOOLEAN)));
         vload = gvn().transform(new VectorLoadMaskNode(vload, TypeVect::makemask(elem_bt, num_elem)));
       } else {
-        vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, elem_bt));
+        vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, elem_bt)));
       }
     }
     Node* box = box_vector(vload, vbox_type, elem_bt, num_elem);
@@ -1140,7 +1145,7 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) {
       const TypeVect* to_mask_type = TypeVect::makemask(mem_elem_bt, mem_num_elem);
       mask = gvn().transform(new VectorReinterpretNode(mask, from_mask_type, to_mask_type));
     }
-    Node* vstore = gvn().transform(new StoreVectorMaskedNode(control(), memory(addr), addr, val, addr_type, mask));
+    Node* vstore = gvn().transform(trace_vector(new StoreVectorMaskedNode(control(), memory(addr), addr, val, addr_type, mask)));
     set_memory(vstore, addr_type);
   } else {
     Node* vload = nullptr;
@@ -1155,13 +1160,13 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) {
     if (supports_predicate) {
       // Generate masked load vector node if predicate feature is supported.
       const TypeVect* vt = TypeVect::make(mem_elem_bt, mem_num_elem);
-      vload = gvn().transform(new LoadVectorMaskedNode(control(), memory(addr), addr, addr_type, vt, mask));
+      vload = gvn().transform(trace_vector(new LoadVectorMaskedNode(control(), memory(addr), addr, addr_type, vt, mask)));
     } else {
       // Use the vector blend to implement the masked load vector. The biased elements are zeros.
       Node* zero = gvn().transform(gvn().zerocon(mem_elem_bt));
       zero = gvn().transform(VectorNode::scalar2vector(zero, mem_num_elem, mem_elem_bt));
-      vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt));
-      vload = gvn().transform(new VectorBlendNode(zero, vload, mask));
+      vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt)));
+      vload = gvn().transform(trace_vector(new VectorBlendNode(zero, vload, mask)));
     }
 
     if (mismatched_ms) {
@@ -1365,17 +1370,17 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) {
 
     Node* vstore = nullptr;
     if (mask != nullptr) {
-      vstore = gvn().transform(new StoreVectorScatterMaskedNode(control(), memory(addr), addr, addr_type, val, indexes, mask));
+      vstore = gvn().transform(trace_vector(new StoreVectorScatterMaskedNode(control(), memory(addr), addr, addr_type, val, indexes, mask)));
     } else {
-      vstore = gvn().transform(new StoreVectorScatterNode(control(), memory(addr), addr, addr_type, val, indexes));
+      vstore = gvn().transform(trace_vector(new StoreVectorScatterNode(control(), memory(addr), addr, addr_type, val, indexes)));
     }
     set_memory(vstore, addr_type);
   } else {
     Node* vload = nullptr;
     if (mask != nullptr) {
-      vload = gvn().transform(new LoadVectorGatherMaskedNode(control(), memory(addr), addr, addr_type, vector_type, indexes, mask));
+      vload = gvn().transform(trace_vector(new LoadVectorGatherMaskedNode(control(), memory(addr), addr, addr_type, vector_type, indexes, mask)));
     } else {
-      vload = gvn().transform(new LoadVectorGatherNode(control(), memory(addr), addr, addr_type, vector_type, indexes));
+      vload = gvn().transform(trace_vector(new LoadVectorGatherNode(control(), memory(addr), addr, addr_type, vector_type, indexes)));
     }
     Node* box = box_vector(vload, vbox_type, elem_bt, num_elem);
     set_result(box);
@@ -1493,7 +1498,7 @@ bool LibraryCallKit::inline_vector_reduction() {
 
   // Make an unordered Reduction node. This affects only AddReductionVF/VD and MulReductionVF/VD,
   // as these operations are allowed to be associative (not requiring strict order) in VectorAPI.
-  value = ReductionNode::make(opc, nullptr, init, value, elem_bt, /* requires_strict_order */ false);
+  value = trace_vector(ReductionNode::make(opc, nullptr, init, value, elem_bt, /* requires_strict_order */ false));
 
   if (mask != nullptr && use_predicate) {
     value->add_req(mask);
@@ -1585,7 +1590,7 @@ bool LibraryCallKit::inline_vector_test() {
     return false; // operand unboxing failed
   }
 
-  Node* cmp = gvn().transform(new VectorTestNode(opd1, opd2, booltest));
+  Node* cmp = gvn().transform(trace_vector(new VectorTestNode(opd1, opd2, booltest)));
   BoolTest::mask test = Matcher::vectortest_mask(booltest == BoolTest::overflow,
                                                  opd1->bottom_type()->isa_vectmask(), num_elem);
   Node* bol = gvn().transform(new BoolNode(cmp, test));
@@ -1653,7 +1658,7 @@ bool LibraryCallKit::inline_vector_blend() {
     return false; // operand unboxing failed
   }
 
-  Node* blend = gvn().transform(new VectorBlendNode(v1, v2, mask));
+  Node* blend = gvn().transform(trace_vector(new VectorBlendNode(v1, v2, mask)));
 
   Node* box = box_vector(blend, vbox_type, elem_bt, num_elem);
   set_result(box);
@@ -1748,6 +1753,7 @@ bool LibraryCallKit::inline_vector_compare() {
 
   const TypeVect* vmask_type = TypeVect::makemask(mask_bt, num_elem);
   Node* operation = new VectorMaskCmpNode(pred, v1, v2, pred_node, vmask_type);
+  trace_vector(operation);
 
   if (is_masked_op) {
     if (use_predicate) {
@@ -1887,6 +1893,7 @@ bool LibraryCallKit::inline_vector_rearrange() {
   }
 
   Node* rearrange = new VectorRearrangeNode(v1, shuffle);
+  trace_vector(rearrange);
   if (is_masked_op) {
     if (use_predicate) {
       rearrange->add_req(mask);
@@ -2034,6 +2041,7 @@ bool LibraryCallKit::inline_vector_select_from() {
 
   // and finally rearrange
   Node* rearrange = new VectorRearrangeNode(v2, shuffle);
+  trace_vector(rearrange);
   if (is_masked_op) {
     if (use_predicate) {
       // masked rearrange is supported so use that directly
@@ -2193,6 +2201,7 @@ bool LibraryCallKit::inline_vector_broadcast_int() {
   }
 
   Node* operation = VectorNode::make(opc, opd1, opd2, num_elem, elem_bt);
+  trace_vector(operation);
   if (is_masked_op && mask != nullptr) {
     if (use_predicate) {
       operation->add_req(mask);
@@ -2370,7 +2379,7 @@ bool LibraryCallKit::inline_vector_convert() {
         return false;
       }
 
-      op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_for_cast));
+      op = gvn().transform(trace_vector(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_for_cast)));
       // Now ensure that the destination gets properly resized to needed size.
       op = gvn().transform(new VectorReinterpretNode(op, op->bottom_type()->is_vect(), dst_type));
     } else if (num_elem_from > num_elem_to) {
@@ -2391,7 +2400,7 @@ bool LibraryCallKit::inline_vector_convert() {
 
       const TypeVect* resize_type = TypeVect::make(elem_bt_from, num_elem_for_resize);
       op = gvn().transform(new VectorReinterpretNode(op, src_type, resize_type));
-      op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to));
+      op = gvn().transform(trace_vector(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to)));
     } else { // num_elem_from == num_elem_to
       if (is_mask) {
         // Make sure that cast for vector mask is implemented to particular type/size combination.
@@ -2400,16 +2409,16 @@ bool LibraryCallKit::inline_vector_convert() {
                           num_elem_to, type2name(elem_bt_to), is_mask);
           return false;
         }
-        op = gvn().transform(new VectorMaskCastNode(op, dst_type));
+        op = gvn().transform(trace_vector(new VectorMaskCastNode(op, dst_type)));
       } else {
         // Since input and output number of elements match, and since we know this vector size is
         // supported, simply do a cast with no resize needed.
-        op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to));
+        op = gvn().transform(trace_vector(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to)));
       }
     }
   } else if (!Type::equals(src_type, dst_type)) {
     assert(!is_cast, "must be reinterpret");
-    op = gvn().transform(new VectorReinterpretNode(op, src_type, dst_type));
+    op = gvn().transform(trace_vector(new VectorReinterpretNode(op, src_type, dst_type)));
   }
 
   const TypeInstPtr* vbox_type_to = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass_to);
@@ -2494,7 +2503,7 @@ bool LibraryCallKit::inline_vector_insert() {
     default: fatal("%s", type2name(elem_bt)); break;
   }
 
-  Node* operation = gvn().transform(VectorInsertNode::make(opd, insert_val, idx->get_con(), gvn()));
+  Node* operation = gvn().transform(trace_vector(VectorInsertNode::make(opd, insert_val, idx->get_con(), gvn())));
 
   Node* vbox = box_vector(operation, vbox_type, elem_bt, num_elem);
   set_result(vbox);
@@ -2552,7 +2561,7 @@ bool LibraryCallKit::inline_vector_extract() {
       if (opd == nullptr) {
         return false;
       }
-      opd = gvn().transform(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem));
+      opd = gvn().transform(trace_vector(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem)));
       opd = gvn().transform(new ExtractUBNode(opd, pos));
       opd = gvn().transform(new ConvI2LNode(opd));
     } else if (arch_supports_vector(Op_VectorMaskToLong, num_elem, elem_bt, VecMaskUseLoad)) {
@@ -2562,7 +2571,7 @@ bool LibraryCallKit::inline_vector_extract() {
       }
       // VectorMaskToLongNode requires the input is either a mask or a vector with BOOLEAN type.
       if (!Matcher::mask_op_prefers_predicate(Op_VectorMaskToLong, opd->bottom_type()->is_vect())) {
-        opd = gvn().transform(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem));
+        opd = gvn().transform(trace_vector(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem)));
       }
       // ((toLong() >>> pos) & 1L
       opd = gvn().transform(new VectorMaskToLongNode(opd, TypeLong::LONG));
@@ -2680,8 +2689,8 @@ static Node* LowerSelectFromTwoVectorOperation(PhaseGVN& phase, Node* index_vec,
   vmask_type = TypeVect::makemask(elem_bt, num_elem);
   mask = phase.transform(new VectorMaskCastNode(mask, vmask_type));
 
-  Node* p1 = phase.transform(new VectorRearrangeNode(src1, wrapped_index_vec));
-  Node* p2 = phase.transform(new VectorRearrangeNode(src2, wrapped_index_vec));
+  Node* p1 = phase.transform(trace_vector(new VectorRearrangeNode(src1, wrapped_index_vec)));
+  Node* p2 = phase.transform(trace_vector(new VectorRearrangeNode(src2, wrapped_index_vec)));
 
   return new VectorBlendNode(p2, p1, mask);
 }
@@ -2799,7 +2808,7 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() {
     Node* wrap_mask = gvn().makecon(TypeInteger::make(indexRangeMask, indexRangeMask, Type::WidenMin, index_elem_bt != T_LONG ? T_INT : index_elem_bt));
     Node* wrap_mask_vec = gvn().transform(VectorNode::scalar2vector(wrap_mask, num_elem, index_elem_bt, false));
     opd1 = gvn().transform(VectorNode::make(Op_AndV, opd1, wrap_mask_vec, opd1->bottom_type()->is_vect()));
-    operation = gvn().transform(VectorNode::make(Op_SelectFromTwoVector, opd1, opd2, opd3, vt));
+    operation = gvn().transform(trace_vector(VectorNode::make(Op_SelectFromTwoVector, opd1, opd2, opd3, vt)));
   }
 
   // Wrap it up in VectorBox to keep object type information.
@@ -2884,7 +2893,7 @@ bool LibraryCallKit::inline_vector_compress_expand() {
   }
 
   const TypeVect* vt = TypeVect::make(elem_bt, num_elem, opc == Op_CompressM);
-  Node* operation = gvn().transform(VectorNode::make(opc, opd1, mask, vt));
+  Node* operation = gvn().transform(trace_vector(VectorNode::make(opc, opd1, mask, vt)));
 
   // Wrap it up in VectorBox to keep object type information.
   const TypeInstPtr* box_type = opc == Op_CompressM ? mbox_type : vbox_type;
@@ -3017,12 +3026,12 @@ bool LibraryCallKit::inline_index_vector() {
       default: fatal("%s", type2name(elem_bt));
     }
     scale = gvn().transform(VectorNode::scalar2vector(scale, num_elem, elem_bt));
-    index = gvn().transform(VectorNode::make(vmul_op, index, scale, vt));
+    index = gvn().transform(trace_vector(VectorNode::make(vmul_op, index, scale, vt)));
   }
 
   // Add "opd" if addition is needed.
   if (needs_add) {
-    index = gvn().transform(VectorNode::make(vadd_op, opd, index, vt));
+    index = gvn().transform(trace_vector(VectorNode::make(vadd_op, opd, index, vt)));
   }
   Node* vbox = box_vector(index, vbox_type, elem_bt, num_elem);
   set_result(vbox);
@@ -3139,7 +3148,7 @@ bool LibraryCallKit::inline_index_partially_in_upper_range() {
     // Compute the vector mask with "mask = iota < indexLimit".
     ConINode* pred_node = (ConINode*)gvn().makecon(TypeInt::make(BoolTest::lt));
     const TypeVect* vmask_type = TypeVect::makemask(elem_bt, num_elem);
-    mask = gvn().transform(new VectorMaskCmpNode(BoolTest::lt, iota, indexLimit, pred_node, vmask_type));
+    mask = gvn().transform(trace_vector(new VectorMaskCmpNode(BoolTest::lt, iota, indexLimit, pred_node, vmask_type)));
   }
   Node* vbox = box_vector(mask, box_type, elem_bt, num_elem);
   set_result(vbox);

From 4c9103f7b6c91b0f237859516ef72bb9ee27157e Mon Sep 17 00:00:00 2001
From: Matthias Baesken 
Date: Wed, 21 Jan 2026 14:14:33 +0000
Subject: [PATCH 096/126] 8374998: Failing os::write - remove bad file

Reviewed-by: mdoerr, lucy
---
 src/hotspot/os/posix/perfMemory_posix.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp
index 08a19270943..ce9c2a4f031 100644
--- a/src/hotspot/os/posix/perfMemory_posix.cpp
+++ b/src/hotspot/os/posix/perfMemory_posix.cpp
@@ -112,6 +112,10 @@ static void save_memory_to_file(char* addr, size_t size) {
     result = ::close(fd);
     if (result == OS_ERR) {
       warning("Could not close %s: %s\n", destfile, os::strerror(errno));
+    } else {
+      if (!successful_write) {
+        remove(destfile);
+      }
     }
   }
   FREE_C_HEAP_ARRAY(char, destfile);
@@ -949,6 +953,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
         warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
       }
       result = OS_ERR;
+      remove(filename);
       break;
     }
   }

From 3033e6f421d0f6e0aea1d976a806d7abca7c6360 Mon Sep 17 00:00:00 2001
From: Kim Barrett 
Date: Wed, 21 Jan 2026 14:55:26 +0000
Subject: [PATCH 097/126] 8375544: JfrSet::clear should not use memset

Reviewed-by: mgronlun
---
 src/hotspot/share/jfr/utilities/jfrSet.hpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp
index 3d394d10d8b..c443434800a 100644
--- a/src/hotspot/share/jfr/utilities/jfrSet.hpp
+++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp
@@ -26,6 +26,7 @@
 #define SHARE_JFR_UTILITIES_JFRSET_HPP
 
 #include "cppstdlib/new.hpp"
+#include "cppstdlib/type_traits.hpp"
 #include "jfr/utilities/jfrTypes.hpp"
 #include "memory/allocation.hpp"
 
@@ -110,7 +111,14 @@ class JfrSetStorage : public AnyObj {
   }
 
   void clear() {
-    memset(_table, 0, _table_size * sizeof(K));
+    for (unsigned i = 0; i < _table_size; ++i) {
+      if constexpr (std::is_copy_assignable_v) {
+        _table[i] = K{};
+      } else {
+        _table[i].~K();
+        ::new (&_table[i]) K{};
+      }
+    }
   }
 };
 

From 17086d31196827432477391fd2921a82868eaa05 Mon Sep 17 00:00:00 2001
From: Maurizio Cimadamore 
Date: Wed, 21 Jan 2026 16:14:35 +0000
Subject: [PATCH 098/126] 8375646: Some parser flags seem unused

Reviewed-by: jlahoda, vromero
---
 .../sun/tools/javac/parser/JavacParser.java   | 114 ++++++++----------
 1 file changed, 51 insertions(+), 63 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 d3539b53541..d658275d4dc 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
@@ -271,16 +271,11 @@ public class JavacParser implements Parser {
     /** When terms are parsed, the mode determines which is expected:
      *     mode = EXPR        : an expression
      *     mode = TYPE        : a type
-     *     mode = NOPARAMS    : no parameters allowed for type
-     *     mode = TYPEARG     : type argument
-     *     mode |= NOLAMBDA   : lambdas are not allowed
+     *     mode = NOLAMBDA    : lambdas are not allowed
      */
     protected static final int EXPR          = 1 << 0;
     protected static final int TYPE          = 1 << 1;
-    protected static final int NOPARAMS      = 1 << 2;
-    protected static final int TYPEARG       = 1 << 3;
-    protected static final int DIAMOND       = 1 << 4;
-    protected static final int NOLAMBDA      = 1 << 5;
+    protected static final int NOLAMBDA      = 1 << 2;
 
     protected void setMode(int mode) {
         this.mode = mode;
@@ -1439,12 +1434,6 @@ public class JavacParser implements Parser {
         int startMode = mode;
         List typeArgs = typeArgumentsOpt(EXPR);
         switch (token.kind) {
-        case QUES:
-            if (isMode(TYPE) && isMode(TYPEARG) && !isMode(NOPARAMS)) {
-                selectTypeMode();
-                return typeArgument();
-            } else
-                return illegal();
         case PLUSPLUS: case SUBSUB: case BANG: case TILDE: case PLUS: case SUB:
             if (typeArgs == null && isMode(EXPR)) {
                 TokenKind tk = token.kind;
@@ -1522,7 +1511,7 @@ public class JavacParser implements Parser {
             if (isMode(EXPR)) {
                 selectExprMode();
                 nextToken();
-                if (token.kind == LT) typeArgs = typeArguments(false);
+                if (token.kind == LT) typeArgs = typeArguments();
                 t = creator(pos, typeArgs);
                 typeArgs = null;
             } else return illegal();
@@ -1625,7 +1614,6 @@ public class JavacParser implements Parser {
                             return illegal();
                         }
                         int prevmode = mode;
-                        setMode(mode & ~NOPARAMS);
                         typeArgs = typeArgumentsOpt(EXPR);
                         setMode(prevmode);
                         if (isMode(EXPR)) {
@@ -1653,7 +1641,7 @@ public class JavacParser implements Parser {
                                 selectExprMode();
                                 int pos1 = token.pos;
                                 nextToken();
-                                if (token.kind == LT) typeArgs = typeArguments(false);
+                                if (token.kind == LT) typeArgs = typeArguments();
                                 t = innerCreator(pos1, typeArgs, t);
                                 typeArgs = null;
                                 break loop;
@@ -1704,7 +1692,7 @@ public class JavacParser implements Parser {
                                 nextToken();
                                 selectTypeMode();
                                 t = toP(F.at(token.pos).Select(t, ident()));
-                                t = typeArgumentsOpt(t);
+                                t = typeApplyOpt(t);
                             }
                             t = bracketsOpt(t);
                             if (token.kind != COLCOL) {
@@ -1721,7 +1709,7 @@ public class JavacParser implements Parser {
                 }
             }
             if (typeArgs != null) illegal();
-            t = typeArgumentsOpt(t);
+            t = typeApplyOpt(t);
             break;
         case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
         case DOUBLE: case BOOLEAN:
@@ -1880,7 +1868,7 @@ public class JavacParser implements Parser {
                     selectExprMode();
                     int pos2 = token.pos;
                     nextToken();
-                    if (token.kind == LT) typeArgs = typeArguments(false);
+                    if (token.kind == LT) typeArgs = typeArguments();
                     t = innerCreator(pos2, typeArgs, t);
                     typeArgs = null;
                 } else {
@@ -1900,7 +1888,7 @@ public class JavacParser implements Parser {
                     if (tyannos != null && tyannos.nonEmpty()) {
                         t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
                     }
-                    t = argumentsOpt(typeArgs, typeArgumentsOpt(t));
+                    t = argumentsOpt(typeArgs, typeApplyOpt(t));
                     typeArgs = null;
                 }
             } else if (isMode(EXPR) && token.kind == COLCOL) {
@@ -2302,7 +2290,7 @@ public class JavacParser implements Parser {
         } else {
             int pos = token.pos;
             accept(DOT);
-            typeArgs = (token.kind == LT) ? typeArguments(false) : null;
+            typeArgs = (token.kind == LT) ? typeArguments() : null;
             t = toP(F.at(pos).Select(t, ident()));
             t = argumentsOpt(typeArgs, t);
         }
@@ -2373,12 +2361,11 @@ public class JavacParser implements Parser {
 
     /**  TypeArgumentsOpt = [ TypeArguments ]
      */
-    JCExpression typeArgumentsOpt(JCExpression t) {
+    JCExpression typeApplyOpt(JCExpression t) {
         if (token.kind == LT &&
-            isMode(TYPE) &&
-            !isMode(NOPARAMS)) {
+            isMode(TYPE)) {
             selectTypeMode();
-            return typeArguments(t, false);
+            return typeApply(t);
         } else {
             return t;
         }
@@ -2389,12 +2376,11 @@ public class JavacParser implements Parser {
 
     List typeArgumentsOpt(int useMode) {
         if (token.kind == LT) {
-            if (!isMode(useMode) ||
-                isMode(NOPARAMS)) {
+            if (!isMode(useMode)) {
                 illegal();
             }
             setMode(useMode);
-            return typeArguments(false);
+            return typeArguments();
         }
         return null;
     }
@@ -2404,35 +2390,29 @@ public class JavacParser implements Parser {
      *  TypeArguments  = "<" TypeArgument {"," TypeArgument} ">"
      *  }
      */
-    List typeArguments(boolean diamondAllowed) {
+    List typeArguments() {
         if (token.kind == LT) {
             nextToken();
-            if (token.kind == GT && diamondAllowed) {
-                setMode(mode | DIAMOND);
+            ListBuffer args = new ListBuffer<>();
+            args.append(!isMode(EXPR) ? typeArgument() : parseType());
+            while (token.kind == COMMA) {
                 nextToken();
-                return List.nil();
-            } else {
-                ListBuffer args = new ListBuffer<>();
                 args.append(!isMode(EXPR) ? typeArgument() : parseType());
-                while (token.kind == COMMA) {
-                    nextToken();
-                    args.append(!isMode(EXPR) ? typeArgument() : parseType());
-                }
-                switch (token.kind) {
-
-                case GTGTGTEQ: case GTGTEQ: case GTEQ:
-                case GTGTGT: case GTGT:
-                    token = S.split();
-                    break;
-                case GT:
-                    nextToken();
-                    break;
-                default:
-                    args.append(syntaxError(token.pos, Errors.Expected2(GT, COMMA)));
-                    break;
-                }
-                return args.toList();
             }
+            switch (token.kind) {
+
+            case GTGTGTEQ: case GTGTEQ: case GTEQ:
+            case GTGTGT: case GTGT:
+                token = S.split();
+                break;
+            case GT:
+                nextToken();
+                break;
+            default:
+                args.append(syntaxError(token.pos, Errors.Expected2(GT, COMMA)));
+                break;
+            }
+            return args.toList();
         } else {
             return List.of(syntaxError(token.pos, Errors.Expected(LT)));
         }
@@ -2480,12 +2460,23 @@ public class JavacParser implements Parser {
         return result;
     }
 
-    JCTypeApply typeArguments(JCExpression t, boolean diamondAllowed) {
+    JCTypeApply typeApply(JCExpression t) {
         int pos = token.pos;
-        List args = typeArguments(diamondAllowed);
+        List args = typeArguments();
         return toP(F.at(pos).TypeApply(t, args));
     }
 
+    JCTypeApply typeApplyOrDiamond(JCExpression t) {
+        if (peekToken(GT)) {
+            int pos = token.pos;
+            accept(LT);
+            accept(GT);
+            return toP(F.at(pos).TypeApply(t, List.nil()));
+        } else {
+            return typeApply(t);
+        }
+    }
+
     /**
      * BracketsOpt = { [Annotations] "[" "]" }*
      *
@@ -2585,7 +2576,7 @@ public class JavacParser implements Parser {
         selectExprMode();
         List typeArgs = null;
         if (token.kind == LT) {
-            typeArgs = typeArguments(false);
+            typeArgs = typeArguments();
         }
         Name refName;
         ReferenceMode refMode;
@@ -2622,15 +2613,13 @@ public class JavacParser implements Parser {
 
         int prevmode = mode;
         selectTypeMode();
-        boolean diamondFound = false;
         int lastTypeargsPos = -1;
         if (token.kind == LT) {
             lastTypeargsPos = token.pos;
-            t = typeArguments(t, true);
-            diamondFound = isMode(DIAMOND);
+            t = typeApplyOrDiamond(t);
         }
         while (token.kind == DOT) {
-            if (diamondFound) {
+            if (TreeInfo.isDiamond(t)) {
                 //cannot select after a diamond
                 illegal();
             }
@@ -2645,8 +2634,7 @@ public class JavacParser implements Parser {
 
             if (token.kind == LT) {
                 lastTypeargsPos = token.pos;
-                t = typeArguments(t, true);
-                diamondFound = isMode(DIAMOND);
+                t = typeApplyOrDiamond(t);
             }
         }
         setMode(prevmode);
@@ -2657,7 +2645,7 @@ public class JavacParser implements Parser {
             }
 
             JCExpression e = arrayCreatorRest(newpos, t);
-            if (diamondFound) {
+            if (TreeInfo.isDiamond(t)) {
                 reportSyntaxError(lastTypeargsPos, Errors.CannotCreateArrayWithDiamond);
                 return toP(F.at(newpos).Erroneous(List.of(e)));
             }
@@ -2702,7 +2690,7 @@ public class JavacParser implements Parser {
 
         if (token.kind == LT) {
             int prevmode = mode;
-            t = typeArguments(t, true);
+            t = typeApplyOrDiamond(t);
             setMode(prevmode);
         }
         return classCreatorRest(newpos, encl, typeArgs, t);

From a0ac5b34a742cf18d86f3ac77110bcaa00192169 Mon Sep 17 00:00:00 2001
From: Damon Nguyen 
Date: Wed, 21 Jan 2026 18:47:39 +0000
Subject: [PATCH 099/126] 8375775: JDK 26 RDP2 L10n resource files update

Reviewed-by: naoto, jlu, liach
---
 .../com/sun/tools/javac/resources/compiler_zh_CN.properties     | 2 +-
 .../classes/com/sun/tools/javac/resources/javac_ja.properties   | 2 +-
 .../com/sun/tools/javac/resources/javac_zh_CN.properties        | 2 +-
 .../classes/jdk/tools/jlink/resources/plugins_de.properties     | 2 +-
 .../classes/jdk/tools/jlink/resources/plugins_ja.properties     | 2 +-
 .../classes/jdk/tools/jlink/resources/plugins_zh_CN.properties  | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties
index 861f371632d..900557a29da 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties
@@ -292,7 +292,7 @@ compiler.err.annotation.decl.not.allowed.here=此处不允许批注接口声明
 compiler.err.cant.inherit.from.final=无法从最终{0}进行继承
 
 # 0: symbol or name
-compiler.err.cant.ref.before.ctor.called=对 {0} 的引用只能在显式调用构造器后显示
+compiler.err.cant.ref.before.ctor.called=对 {0} 的引用只能在显式调用构造器后出现
 
 # 0: symbol or name
 compiler.err.cant.assign.initialized.before.ctor.called=对初始化字段 ''{0}'' 的分配只能在显式调用构造器后显示
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties
index 0ea1796a8e6..3ae7ab1690e 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties
@@ -60,7 +60,7 @@ javac.opt.target=指定されたJava SEリリースに適したクラス・フ
 javac.opt.release=指定されたJava SEリリースに対してコンパイルします。サポートされているリリース: \n    {0}
 javac.opt.source=指定されたJava SEリリースとソースの互換性を保持します。サポートされているリリース: \n    {0}
 javac.opt.Werror=警告が発生した場合にコンパイルを終了します
-javac.opt.arg.Werror=(,)*
+javac.opt.arg.Werror=<キー>(,<キー>)*
 javac.opt.Werror.custom=コンパイルを終了する警告のlintカテゴリを\nコンマで区切って指定します。\n指定したカテゴリを除外するには、キーの前に''-''を指定します。\nサポートされているキーを表示するには--help-lintを使用します。
 javac.opt.A=注釈プロセッサに渡されるオプション
 javac.opt.implicit=暗黙的に参照されるファイルについてクラス・ファイルを生成するかどうかを指定する
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties
index 0cbfac7e778..447d0d26239 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties
@@ -60,7 +60,7 @@ javac.opt.target=生成适合指定的 Java SE 发行版的类文件。支持的
 javac.opt.release=为指定的 Java SE 发行版编译。支持的发行版:{0}
 javac.opt.source=提供与指定的 Java SE 发行版的源兼容性。支持的发行版:{0}
 javac.opt.Werror=出现任何警告时终止编译
-javac.opt.arg.Werror=(,)*
+javac.opt.arg.Werror=<键>(,<键>)*
 javac.opt.Werror.custom=指定出现警告时应终止编译的 lint 类别,\n以逗号分隔。\n在关键字前面加上 ''-'' 可排除指定的类别。\n使用 --help-lint 可查看支持的关键字。
 javac.opt.A=传递给批注处理程序的选项
 javac.opt.implicit=指定是否为隐式引用文件生成类文件
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties
index 80d1ba6e05f..c313d6a348d 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties
@@ -170,7 +170,7 @@ plugin.opt.resources-last-sorter=\      --resources-last-sorter     Das le
 
 plugin.opt.disable-plugin=\      --disable-plugin      Deaktiviert das angegebene Plug-in
 
-plugin.opt.compress=\      --compress              Komprimiert alle Ressourcen im Ausgabeimage:\n                                        Zulässige Werte:\n                                        zip-'{0-9}', wobei "zip-0" für keine Komprimierung\n                                        und "zip-9" für die beste Komprimierung steht.\n                                        Standardwert ist "zip-6."\n                                        Veraltete Werte, die in einem zukünftigen Release entfernt werden:\n                                        0:  Keine Komprimierung. Verwenden Sie stattdessen "zip-0".\n                                        1:  Gemeinsame Verwendung konstanter Zeichenfolgen\n                                        2:  ZIP. Verwenden Sie stattdessen "zip-6".
+plugin.opt.compress=\      --compress              Komprimiert alle Ressourcen im Ausgabeimage:\n                                        Zulässige Werte:\n                                        zip-'{0-9}', wobei "zip-0" für keine Komprimierung\n                                        und "zip-9" für die beste Komprimierung steht.\n                                        Standardwert ist "zip-6".\n                                        Veraltete Werte, die in einem zukünftigen Release entfernt werden:\n                                        0:  Keine Komprimierung. Verwenden Sie stattdessen "zip-0".\n                                        1:  Gemeinsame Verwendung konstanter Zeichenfolgen\n                                        2:  ZIP. Verwenden Sie stattdessen "zip-6".
 
 plugin.opt.strip-debug=\  -G, --strip-debug                     Entfernt Debuginformationen
 
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties
index 6ed0a486132..a5dc70061f6 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties
@@ -170,7 +170,7 @@ plugin.opt.resources-last-sorter=\      --resources-last-sorter     最後
 
 plugin.opt.disable-plugin=\      --disable-plugin      指定したプラグインを無効にします
 
-plugin.opt.compress=\      --compress              出力イメージ内のすべてのリソースを圧縮します:\n                                        使用可能な値は\n                                        zip-'{0-9}'です。zip-0では圧縮は行われず、\n                                        zip-9では最適な圧縮が行われます。\n                                        デフォルトはzip-6です。\n                                        今後のリリースで削除される非推奨の値:\n                                        0:  圧縮なし。かわりにzip-0を使用。\n                                        1:  定数文字列の共有\n                                        2:  ZIP。かわりにzip-6を使用。
+plugin.opt.compress=\      --compress <圧縮>             出力イメージ内のすべてのリソースを圧縮します:\n                                        使用可能な値は\n                                        zip-'{0-9}'です。zip-0では圧縮は行われず、\n                                        zip-9では最適な圧縮が行われます。\n                                        デフォルトはzip-6です。\n                                        今後のリリースで削除される非推奨の値:\n                                        0:  圧縮なし。かわりにzip-0を使用。\n                                        1:  定数文字列の共有\n                                        2:  ZIP。かわりにzip-6を使用。
 
 plugin.opt.strip-debug=\  -G, --strip-debug                     デバッグ情報を削除します
 
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties
index a0480a31fc3..1de1ef372b6 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties
@@ -170,7 +170,7 @@ plugin.opt.resources-last-sorter=\      --resources-last-sorter     允许
 
 plugin.opt.disable-plugin=\      --disable-plugin      禁用所提及的插件
 
-plugin.opt.compress=\      --compress              在输出映像中压缩所有资源:\n                                        接受的值包括:\n                                        zip-'{0-9}',其中 zip-0 表示无压缩,\n                                        zip-9 表示最佳压缩。\n                                        默认值为 zip-6。\n                                        要在未来发行版中删除的已过时值:\n                                        0:无压缩。改为使用 zip-0。\n                                        1:常量字符串共享\n                                        2:ZIP。改为使用 zip-6。
+plugin.opt.compress=\      --compress <压缩>             在输出映像中压缩所有资源:\n                                        接受的值包括:\n                                        zip-'{0-9}',其中 zip-0 表示无压缩,\n                                        zip-9 表示最佳压缩。\n                                        默认值为 zip-6。\n                                        要在未来发行版中删除的已过时值:\n                                        0:无压缩。改为使用 zip-0。\n                                        1:常量字符串共享\n                                        2:ZIP。改为使用 zip-6。
 
 plugin.opt.strip-debug=\  -G, --strip-debug                     去除调试信息
 

From 3d919ad43a041eb60ce51e78831c77fd3b109aee Mon Sep 17 00:00:00 2001
From: Serguei Spitsyn 
Date: Thu, 22 Jan 2026 01:53:42 +0000
Subject: [PATCH 100/126] 8373366: HandshakeState should disallow suspend ops
 for disabler threads 8375362: Deadlock with unmount of suspended virtual
 thread interrupting another virtual thread

Reviewed-by: lmesnik, pchilanomate
---
 src/hotspot/share/runtime/handshake.cpp       |   6 +-
 src/hotspot/share/runtime/javaThread.cpp      |  12 +-
 src/hotspot/share/runtime/javaThread.hpp      |   8 +-
 .../share/runtime/mountUnmountDisabler.cpp    |  15 +-
 .../share/runtime/suspendResumeManager.cpp    |   3 +-
 .../ThreadStateTest2/ThreadStateTest2.java    | 144 ++++++++++++++++++
 .../ThreadStateTest2/libThreadStateTest2.cpp  | 118 ++++++++++++++
 7 files changed, 286 insertions(+), 20 deletions(-)
 create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java
 create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp

diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp
index 89b02717a7a..b54068d65d6 100644
--- a/src/hotspot/share/runtime/handshake.cpp
+++ b/src/hotspot/share/runtime/handshake.cpp
@@ -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
@@ -521,8 +521,8 @@ HandshakeOperation* HandshakeState::get_op_for_self(bool allow_suspend, bool che
   assert(_lock.owned_by_self(), "Lock must be held");
   assert(allow_suspend || !check_async_exception, "invalid case");
 #if INCLUDE_JVMTI
-  if (allow_suspend && _handshakee->is_disable_suspend()) {
-    // filter out suspend operations while JavaThread is in disable_suspend mode
+  if (allow_suspend && (_handshakee->is_disable_suspend() || _handshakee->is_vthread_transition_disabler())) {
+    // filter out suspend operations while JavaThread can not be suspended
     allow_suspend = false;
   }
 #endif
diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp
index 4ee9a9dfd79..e73347f35d8 100644
--- a/src/hotspot/share/runtime/javaThread.cpp
+++ b/src/hotspot/share/runtime/javaThread.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) 2021, Azul Systems, Inc. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -499,7 +499,7 @@ JavaThread::JavaThread(MemTag mem_tag) :
   _suspend_resume_manager(this, &_handshake._lock),
 
   _is_in_vthread_transition(false),
-  DEBUG_ONLY(_is_vthread_transition_disabler(false) COMMA)
+  JVMTI_ONLY(_is_vthread_transition_disabler(false) COMMA)
   DEBUG_ONLY(_is_disabler_at_start(false) COMMA)
 
   _popframe_preserved_args(nullptr),
@@ -1165,11 +1165,13 @@ void JavaThread::set_is_in_vthread_transition(bool val) {
   AtomicAccess::store(&_is_in_vthread_transition, val);
 }
 
-#ifdef ASSERT
+#if INCLUDE_JVMTI
 void JavaThread::set_is_vthread_transition_disabler(bool val) {
   _is_vthread_transition_disabler = val;
 }
+#endif
 
+#ifdef ASSERT
 void JavaThread::set_is_disabler_at_start(bool val) {
   _is_disabler_at_start = val;
 }
@@ -1183,7 +1185,9 @@ void JavaThread::set_is_disabler_at_start(bool val) {
 //
 bool JavaThread::java_suspend(bool register_vthread_SR) {
   // Suspending a vthread transition disabler can cause deadlocks.
-  assert(!is_vthread_transition_disabler(), "no suspend allowed for vthread transition disablers");
+  // The HandshakeState::has_operation does not allow such suspends.
+  // But the suspender thread is an exclusive transition disablers, so there can't be other disabers here.
+  JVMTI_ONLY(assert(!is_vthread_transition_disabler(), "suspender thread is an exclusive transition disabler");)
 
   guarantee(Thread::is_JavaThread_protected(/* target */ this),
             "target JavaThread is not protected in calling context.");
diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp
index d4c12887e10..1aae37c0697 100644
--- a/src/hotspot/share/runtime/javaThread.hpp
+++ b/src/hotspot/share/runtime/javaThread.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) 2021, Azul Systems, Inc. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -734,14 +734,14 @@ public:
 
 private:
   bool _is_in_vthread_transition;                    // thread is in virtual thread mount state transition
-  DEBUG_ONLY(bool _is_vthread_transition_disabler;)  // thread currently disabled vthread transitions
+  JVMTI_ONLY(bool _is_vthread_transition_disabler;)  // thread currently disabled vthread transitions
   DEBUG_ONLY(bool _is_disabler_at_start;)            // thread at process of disabling vthread transitions
 public:
   bool is_in_vthread_transition() const;
   void set_is_in_vthread_transition(bool val);
+  JVMTI_ONLY(bool is_vthread_transition_disabler() const { return _is_vthread_transition_disabler; })
+  JVMTI_ONLY(void set_is_vthread_transition_disabler(bool val);)
 #ifdef ASSERT
-  bool is_vthread_transition_disabler() const       { return _is_vthread_transition_disabler; }
-  void set_is_vthread_transition_disabler(bool val);
   bool is_disabler_at_start() const                 { return _is_disabler_at_start; }
   void set_is_disabler_at_start(bool val);
 #endif
diff --git a/src/hotspot/share/runtime/mountUnmountDisabler.cpp b/src/hotspot/share/runtime/mountUnmountDisabler.cpp
index 8635eeb2dcc..65a82d6c563 100644
--- a/src/hotspot/share/runtime/mountUnmountDisabler.cpp
+++ b/src/hotspot/share/runtime/mountUnmountDisabler.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
@@ -129,7 +129,8 @@ bool MountUnmountDisabler::is_start_transition_disabled(JavaThread* thread, oop
   int base_disable_count = notify_jvmti_events() ? 1 : 0;
   return java_lang_Thread::vthread_transition_disable_count(vthread) > 0
          || global_vthread_transition_disable_count() > base_disable_count
-         JVMTI_ONLY(|| (JvmtiVTSuspender::is_vthread_suspended(java_lang_Thread::thread_id(vthread)) || thread->is_suspended()));
+         JVMTI_ONLY(|| (!thread->is_vthread_transition_disabler() &&
+                        (JvmtiVTSuspender::is_vthread_suspended(java_lang_Thread::thread_id(vthread)) || thread->is_suspended())));
 }
 
 void MountUnmountDisabler::start_transition(JavaThread* current, oop vthread, bool is_mount, bool is_thread_end) {
@@ -294,7 +295,7 @@ MountUnmountDisabler::disable_transition_for_one() {
   // carrierThread to float up.
   // This pairs with the release barrier in end_transition().
   OrderAccess::acquire();
-  DEBUG_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);)
+  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);)
 }
 
 // disable transitions for all virtual threads
@@ -335,7 +336,7 @@ MountUnmountDisabler::disable_transition_for_all() {
   // carrierThread to float up.
   // This pairs with the release barrier in end_transition().
   OrderAccess::acquire();
-  DEBUG_ONLY(thread->set_is_vthread_transition_disabler(true);)
+  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);)
   DEBUG_ONLY(thread->set_is_disabler_at_start(false);)
 }
 
@@ -358,14 +359,12 @@ MountUnmountDisabler::enable_transition_for_one() {
   if (java_lang_Thread::vthread_transition_disable_count(_vthread()) == 0) {
     ml.notify_all();
   }
-  DEBUG_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(false);)
+  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(false);)
 }
 
 // enable transitions for all virtual threads
 void
 MountUnmountDisabler::enable_transition_for_all() {
-  JavaThread* thread = JavaThread::current();
-
   // End of the critical section. If some target was unmounted, we need a
   // release barrier before decrementing _global_vthread_transition_disable_count
   // to make sure any memory operations executed by the disabler are visible to
@@ -384,7 +383,7 @@ MountUnmountDisabler::enable_transition_for_all() {
   if (global_vthread_transition_disable_count() == base_disable_count || _is_exclusive) {
     ml.notify_all();
   }
-  DEBUG_ONLY(thread->set_is_vthread_transition_disabler(false);)
+  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(false);)
 }
 
 int MountUnmountDisabler::global_vthread_transition_disable_count() {
diff --git a/src/hotspot/share/runtime/suspendResumeManager.cpp b/src/hotspot/share/runtime/suspendResumeManager.cpp
index 067579b6386..3408d763e57 100644
--- a/src/hotspot/share/runtime/suspendResumeManager.cpp
+++ b/src/hotspot/share/runtime/suspendResumeManager.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
@@ -130,6 +130,7 @@ void SuspendResumeManager::do_owner_suspend() {
   assert(_state_lock->owned_by_self(), "Lock must be held");
   assert(!_target->has_last_Java_frame() || _target->frame_anchor()->walkable(), "should have walkable stack");
   assert(_target->thread_state() == _thread_blocked, "Caller should have transitioned to _thread_blocked");
+  JVMTI_ONLY(assert(!_target->is_vthread_transition_disabler(), "attempt to suspend a vthread transition disabler");)
 
   while (is_suspended()) {
     log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_target));
diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java
new file mode 100644
index 00000000000..ce3f2a5fe40
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java
@@ -0,0 +1,144 @@
+/*
+ * 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 8373366
+ * @summary HandshakeState should disallow suspend ops for disabler threads
+ * @requires vm.continuations
+ * @requires vm.jvmti
+ * @requires vm.compMode != "Xcomp"
+ * @modules java.base/java.lang:+open
+ * @library /test/lib
+ * @run main/othervm/native -agentlib:ThreadStateTest2 ThreadStateTest2
+ */
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import jdk.test.lib.thread.VThreadScheduler;
+
+/* Testing scenario:
+ * Several threads are involved:
+ *  - VT-0: a virtual thread which is interrupt-friendly and constantly interrupted with JVMTI InterruptThread
+ *  - VT-1: a virtual thread which state is constantly checked with JVMTI GetThreadState
+ *  - VT-2: a virtual thread: in a loop calls JVMTI InterruptThread(VT-0) and GetThreadState(VT-1)
+ *  - main: a platform thread: in a loop invokes native method testSuspendResume which suspends and resumes VT-2
+ * The JVMTI functions above install a MountUnmountDisabler for target virtual thread (VT-0 or VT-1).
+ * The goal is to catch VT-2 in an attempt to self-suspend while in a context of MountUnmountDisabler.
+ * This would mean there is a suspend point while VT-2 is in a context of MountUnmountDisabler.
+ * The InterruptThread implementation does a Java upcall to j.l.Thread::interrupt().
+ * The JavaCallWrapper constructor has such a suspend point.
+ */
+public class ThreadStateTest2 {
+    private static native void setMonitorContendedMode(boolean enable);
+    private static native void testSuspendResume(Thread vthread);
+    private static native void testInterruptThread(Thread vthread);
+    private static native int testGetThreadState(Thread vthread);
+
+    static Thread vthread0;
+    static Thread vthread1;
+    static Thread vthread2;
+    static AtomicBoolean vt2Started = new AtomicBoolean();
+    static AtomicBoolean vt2Finished = new AtomicBoolean();
+
+    static void log(String msg) { System.out.println(msg); }
+
+    // Should handle interruptions from vthread2.
+    final Runnable FOO_0 = () -> {
+        log("VT-0 started");
+        while (!vt2Finished.get()) {
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException ie) {
+                // ignore
+            }
+        }
+        log("VT-0 finished");
+    };
+
+    // A target for vthread2 to check state with JVMTI GetThreadState.
+    final Runnable FOO_1 = () -> {
+        log("VT-1 started");
+        while (!vt2Finished.get()) {
+            Thread.yield();
+        }
+        log("VT-1 finished");
+    };
+
+    // In a loop execute JVMTI functions on threads vthread0 and vthread1:
+    // InterruptThread(vthread0) and GetThreadState(vthread1).
+    final Runnable FOO_2 = () -> {
+        log("VT-2 started");
+        vt2Started.set(true);
+        for (int i = 0; i < 40; i++) {
+            testInterruptThread(vthread0);
+            int state = testGetThreadState(vthread1);
+            if (state == 2) {
+                break;
+            }
+            Thread.yield();
+        }
+        vt2Finished.set(true);
+        log("VT-2 finished");
+    };
+
+    private void runTest() throws Exception {
+        // Force creation of JvmtiThreadState on vthread start.
+        setMonitorContendedMode(true);
+
+        ExecutorService scheduler = Executors.newFixedThreadPool(2);
+        ThreadFactory factory = VThreadScheduler.virtualThreadBuilder(scheduler).factory();
+
+        vthread0 = factory.newThread(FOO_0);
+        vthread1 = factory.newThread(FOO_1);
+        vthread2 = factory.newThread(FOO_2);
+        vthread0.setName("VT-0");
+        vthread1.setName("VT-1");
+        vthread2.setName("VT-2");
+        vthread0.start();
+        vthread1.start();
+        vthread2.start();
+
+        // Give some time for vthreads to start.
+        while (!vt2Started.get()) {
+            Thread.sleep(1);
+        }
+        while (!vt2Finished.get() /* && tryCount-- > 0 */) {
+            testSuspendResume(vthread2);
+        }
+        vthread0.join();
+        vthread1.join();
+        vthread2.join();
+
+        // Let all carriers go away.
+        scheduler.shutdown();
+        Thread.sleep(20);
+    }
+
+    public static void main(String[] args) throws Exception {
+        ThreadStateTest2 obj = new ThreadStateTest2();
+        obj.runTest();
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp
new file mode 100644
index 00000000000..8464e8ebc57
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#include "jvmti_common.hpp"
+
+// set by Agent_OnLoad
+static jvmtiEnv* jvmti = nullptr;
+static jrawMonitorID agent_event_lock = nullptr;
+
+extern "C" {
+
+static void JNICALL
+MonitorContended(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread,
+                 jobject object) {
+}
+
+JNIEXPORT void JNICALL
+Java_ThreadStateTest2_testSuspendResume(JNIEnv* jni, jclass klass, jthread thread) {
+  jvmtiError err;
+  RawMonitorLocker event_locker(jvmti, jni, agent_event_lock);
+
+  LOG("\nMAIN: testSuspendResume: before suspend\n");
+  err = jvmti->SuspendThread(thread);
+  if (err == JVMTI_ERROR_THREAD_NOT_ALIVE) {
+    return;
+  }
+  check_jvmti_status(jni, err, "testSuspendResume error in JVMTI SuspendThread");
+  LOG("\nMAIN: testSuspendResume:  after suspend\n");
+
+  event_locker.wait(1);
+
+  LOG("MAIN: testSuspendResume: before resume\n");
+  err = jvmti->ResumeThread(thread);
+  check_jvmti_status(jni, err, "testSuspendResume error in JVMTI ResumeThread");
+}
+
+JNIEXPORT void JNICALL
+Java_ThreadStateTest2_setMonitorContendedMode(JNIEnv* jni, jclass klass, jboolean enable) {
+  set_event_notification_mode(jvmti, jni, enable ? JVMTI_ENABLE : JVMTI_DISABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, nullptr);
+}
+
+JNIEXPORT void JNICALL
+Java_ThreadStateTest2_testInterruptThread(JNIEnv* jni, jclass klass, jthread vthread) {
+  char* tname = get_thread_name(jvmti, jni, vthread);
+  LOG("VT-2: testInterruptThread: %s\n", tname);
+
+  jvmtiError err = jvmti->InterruptThread(vthread);
+  check_jvmti_status(jni, err, "testInterruptThread error in JVMTI InterruptThread");
+}
+
+JNIEXPORT jint JNICALL
+Java_ThreadStateTest2_testGetThreadState(JNIEnv* jni, jclass klass, jthread vthread) {
+  jint  state = get_thread_state(jvmti, jni, vthread);
+  char* tname = get_thread_name(jvmti, jni, vthread);
+
+  LOG("VT-2: testGetThreadState: %s state: %x\n", tname, state);
+  return state;
+}
+
+JNIEXPORT jint JNICALL
+Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
+  jvmtiEventCallbacks callbacks;
+  jvmtiCapabilities caps;
+  jvmtiError err;
+
+  printf("Agent_OnLoad: started\n");
+  if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
+    LOG("Agent_OnLoad: error in GetEnv");
+    return JNI_ERR;
+  }
+
+  memset(&caps, 0, sizeof(caps));
+  caps.can_suspend = 1;
+  caps.can_signal_thread = 1;
+  caps.can_support_virtual_threads = 1;
+  caps.can_generate_monitor_events = 1;
+
+  err = jvmti->AddCapabilities(&caps);
+  if (err != JVMTI_ERROR_NONE) {
+    LOG("Agent_OnLoad: error in JVMTI AddCapabilities: %d\n", err);
+  }
+  memset(&callbacks, 0, sizeof(callbacks));
+  callbacks.MonitorContendedEnter = &MonitorContended;
+  err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
+  if (err != JVMTI_ERROR_NONE) {
+    LOG("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
+  }
+  agent_event_lock = create_raw_monitor(jvmti, "agent_event_lock");
+  printf("Agent_OnLoad: finished\n");
+
+  return 0;
+}
+
+} // extern "C"

From 38a8309b3f2544fa13448f5217e4227f0e2fe171 Mon Sep 17 00:00:00 2001
From: Ivan Walulya 
Date: Thu, 22 Jan 2026 05:38:32 +0000
Subject: [PATCH 101/126] 8341630: G1: Adopt PartialArrayState to consolidate
 marking stack in concurrent marking

Co-authored-by: Stefan Johansson 
Reviewed-by: tschatzl, sjohanss
---
 src/hotspot/share/gc/g1/g1ConcurrentMark.cpp  | 107 ++++++++++++++++--
 src/hotspot/share/gc/g1/g1ConcurrentMark.hpp  |  81 +++++++------
 .../share/gc/g1/g1ConcurrentMark.inline.hpp   |  49 ++++----
 .../g1/g1ConcurrentMarkObjArrayProcessor.cpp  |  80 -------------
 .../g1/g1ConcurrentMarkObjArrayProcessor.hpp  |  59 ----------
 ...ConcurrentMarkObjArrayProcessor.inline.hpp |  38 -------
 src/hotspot/share/gc/shared/taskqueue.hpp     |   6 +-
 7 files changed, 167 insertions(+), 253 deletions(-)
 delete mode 100644 src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp
 delete mode 100644 src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp
 delete mode 100644 src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp

diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
index 1077939f953..52591f7ce5f 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
@@ -51,6 +51,9 @@
 #include "gc/shared/gcTimer.hpp"
 #include "gc/shared/gcTraceTime.inline.hpp"
 #include "gc/shared/gcVMOperations.hpp"
+#include "gc/shared/partialArraySplitter.inline.hpp"
+#include "gc/shared/partialArrayState.hpp"
+#include "gc/shared/partialArrayTaskStats.hpp"
 #include "gc/shared/referencePolicy.hpp"
 #include "gc/shared/suspendibleThreadSet.hpp"
 #include "gc/shared/taskqueue.inline.hpp"
@@ -75,6 +78,7 @@
 #include "runtime/prefetch.inline.hpp"
 #include "runtime/threads.hpp"
 #include "utilities/align.hpp"
+#include "utilities/checkedCast.hpp"
 #include "utilities/formatBuffer.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/powerOfTwo.hpp"
@@ -98,7 +102,7 @@ bool G1CMBitMapClosure::do_addr(HeapWord* const addr) {
   // We move that task's local finger along.
   _task->move_finger_to(addr);
 
-  _task->scan_task_entry(G1TaskQueueEntry::from_oop(cast_to_oop(addr)));
+  _task->process_entry(G1TaskQueueEntry(cast_to_oop(addr)), false /* stolen */);
   // we only partially drain the local queue and global stack
   _task->drain_local_queue(true);
   _task->drain_global_stack(true);
@@ -489,6 +493,7 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h,
 
   _task_queues(new G1CMTaskQueueSet(_max_num_tasks)),
   _terminator(_max_num_tasks, _task_queues),
+  _partial_array_state_manager(new PartialArrayStateManager(_max_num_tasks)),
 
   _first_overflow_barrier_sync(),
   _second_overflow_barrier_sync(),
@@ -555,6 +560,10 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h,
   reset_at_marking_complete();
 }
 
+PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const {
+  return _partial_array_state_manager;
+}
+
 void G1ConcurrentMark::reset() {
   _has_aborted = false;
 
@@ -649,7 +658,26 @@ void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurr
   }
 }
 
+#if TASKQUEUE_STATS
+void G1ConcurrentMark::print_and_reset_taskqueue_stats() {
+
+  _task_queues->print_and_reset_taskqueue_stats("G1ConcurrentMark Oop Queue");
+
+  auto get_pa_stats = [&](uint i) {
+    return _tasks[i]->partial_array_task_stats();
+  };
+
+  PartialArrayTaskStats::log_set(_max_num_tasks, get_pa_stats,
+                                 "G1ConcurrentMark Partial Array Task Stats");
+
+  for (uint i = 0; i < _max_num_tasks; ++i) {
+    get_pa_stats(i)->reset();
+  }
+}
+#endif
+
 void G1ConcurrentMark::reset_at_marking_complete() {
+  TASKQUEUE_STATS_ONLY(print_and_reset_taskqueue_stats());
   // We set the global marking state to some default values when we're
   // not doing marking.
   reset_marking_for_restart();
@@ -803,11 +831,25 @@ void G1ConcurrentMark::cleanup_for_next_mark() {
 
   clear_bitmap(_concurrent_workers, true);
 
+  reset_partial_array_state_manager();
+
   // Repeat the asserts from above.
   guarantee(cm_thread()->in_progress(), "invariant");
   guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant");
 }
 
+void G1ConcurrentMark::reset_partial_array_state_manager() {
+  for (uint i = 0; i < _max_num_tasks; ++i) {
+    _tasks[i]->unregister_partial_array_splitter();
+  }
+
+  partial_array_state_manager()->reset();
+
+  for (uint i = 0; i < _max_num_tasks; ++i) {
+    _tasks[i]->register_partial_array_splitter();
+  }
+}
+
 void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers) {
   assert_at_safepoint_on_vm_thread();
   // To avoid fragmentation the full collection requesting to clear the bitmap
@@ -1788,17 +1830,18 @@ public:
   { }
 
   void operator()(G1TaskQueueEntry task_entry) const {
-    if (task_entry.is_array_slice()) {
-      guarantee(_g1h->is_in_reserved(task_entry.slice()), "Slice " PTR_FORMAT " must be in heap.", p2i(task_entry.slice()));
+    if (task_entry.is_partial_array_state()) {
+      oop obj = task_entry.to_partial_array_state()->source();
+      guarantee(_g1h->is_in_reserved(obj), "Partial Array " PTR_FORMAT " must be in heap.", p2i(obj));
       return;
     }
-    guarantee(oopDesc::is_oop(task_entry.obj()),
+    guarantee(oopDesc::is_oop(task_entry.to_oop()),
               "Non-oop " PTR_FORMAT ", phase: %s, info: %d",
-              p2i(task_entry.obj()), _phase, _info);
-    G1HeapRegion* r = _g1h->heap_region_containing(task_entry.obj());
+              p2i(task_entry.to_oop()), _phase, _info);
+    G1HeapRegion* r = _g1h->heap_region_containing(task_entry.to_oop());
     guarantee(!(r->in_collection_set() || r->has_index_in_opt_cset()),
               "obj " PTR_FORMAT " from %s (%d) in region %u in (optional) collection set",
-              p2i(task_entry.obj()), _phase, _info, r->hrm_index());
+              p2i(task_entry.to_oop()), _phase, _info, r->hrm_index());
   }
 };
 
@@ -2054,6 +2097,17 @@ void G1CMTask::reset(G1CMBitMap* mark_bitmap) {
   _mark_stats_cache.reset();
 }
 
+void G1CMTask::register_partial_array_splitter() {
+
+  ::new (&_partial_array_splitter) PartialArraySplitter(_cm->partial_array_state_manager(),
+                                                        _cm->max_num_tasks(),
+                                                        ObjArrayMarkingStride);
+}
+
+void G1CMTask::unregister_partial_array_splitter() {
+  _partial_array_splitter.~PartialArraySplitter();
+}
+
 bool G1CMTask::should_exit_termination() {
   if (!regular_clock_call()) {
     return true;
@@ -2184,7 +2238,7 @@ bool G1CMTask::get_entries_from_global_stack() {
     if (task_entry.is_null()) {
       break;
     }
-    assert(task_entry.is_array_slice() || oopDesc::is_oop(task_entry.obj()), "Element " PTR_FORMAT " must be an array slice or oop", p2i(task_entry.obj()));
+    assert(task_entry.is_partial_array_state() || oopDesc::is_oop(task_entry.to_oop()), "Element " PTR_FORMAT " must be an array slice or oop", p2i(task_entry.to_oop()));
     bool success = _task_queue->push(task_entry);
     // We only call this when the local queue is empty or under a
     // given target limit. So, we do not expect this push to fail.
@@ -2215,7 +2269,7 @@ void G1CMTask::drain_local_queue(bool partially) {
     G1TaskQueueEntry entry;
     bool ret = _task_queue->pop_local(entry);
     while (ret) {
-      scan_task_entry(entry);
+      process_entry(entry, false /* stolen */);
       if (_task_queue->size() <= target_size || has_aborted()) {
         ret = false;
       } else {
@@ -2225,6 +2279,37 @@ void G1CMTask::drain_local_queue(bool partially) {
   }
 }
 
+size_t G1CMTask::start_partial_array_processing(oop obj) {
+  assert(should_be_sliced(obj), "Must be an array object %d and large %zu", obj->is_objArray(), obj->size());
+
+  objArrayOop obj_array = objArrayOop(obj);
+  size_t array_length = obj_array->length();
+
+  size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj_array, nullptr, array_length);
+
+  // Mark objArray klass metadata
+  if (_cm_oop_closure->do_metadata()) {
+    _cm_oop_closure->do_klass(obj_array->klass());
+  }
+
+  process_array_chunk(obj_array, 0, initial_chunk_size);
+
+  // Include object header size
+  return objArrayOopDesc::object_size(checked_cast(initial_chunk_size));
+}
+
+size_t G1CMTask::process_partial_array(const G1TaskQueueEntry& task, bool stolen) {
+  PartialArrayState* state = task.to_partial_array_state();
+  // Access state before release by claim().
+  objArrayOop obj = objArrayOop(state->source());
+
+  PartialArraySplitter::Claim claim =
+    _partial_array_splitter.claim(state, _task_queue, stolen);
+
+  process_array_chunk(obj, claim._start, claim._end);
+  return heap_word_size((claim._end - claim._start) * heapOopSize);
+}
+
 void G1CMTask::drain_global_stack(bool partially) {
   if (has_aborted()) {
     return;
@@ -2429,7 +2514,7 @@ void G1CMTask::attempt_stealing() {
   while (!has_aborted()) {
     G1TaskQueueEntry entry;
     if (_cm->try_stealing(_worker_id, entry)) {
-      scan_task_entry(entry);
+      process_entry(entry, true /* stolen */);
 
       // And since we're towards the end, let's totally drain the
       // local queue and global stack.
@@ -2758,12 +2843,12 @@ G1CMTask::G1CMTask(uint worker_id,
                    G1ConcurrentMark* cm,
                    G1CMTaskQueue* task_queue,
                    G1RegionMarkStats* mark_stats) :
-  _objArray_processor(this),
   _worker_id(worker_id),
   _g1h(G1CollectedHeap::heap()),
   _cm(cm),
   _mark_bitmap(nullptr),
   _task_queue(task_queue),
+  _partial_array_splitter(_cm->partial_array_state_manager(), _cm->max_num_tasks(), ObjArrayMarkingStride),
   _mark_stats_cache(mark_stats, G1RegionMarkStatsCache::RegionMarkStatsCacheSize),
   _calls(0),
   _time_target_ms(0.0),
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
index 7ea9151c6f1..52a1b133439 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
@@ -26,11 +26,13 @@
 #define SHARE_GC_G1_G1CONCURRENTMARK_HPP
 
 #include "gc/g1/g1ConcurrentMarkBitMap.hpp"
-#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp"
 #include "gc/g1/g1HeapRegionSet.hpp"
 #include "gc/g1/g1HeapVerifier.hpp"
 #include "gc/g1/g1RegionMarkStatsCache.hpp"
 #include "gc/shared/gcCause.hpp"
+#include "gc/shared/partialArraySplitter.hpp"
+#include "gc/shared/partialArrayState.hpp"
+#include "gc/shared/partialArrayTaskStats.hpp"
 #include "gc/shared/taskqueue.hpp"
 #include "gc/shared/taskTerminator.hpp"
 #include "gc/shared/verifyOption.hpp"
@@ -54,41 +56,7 @@ class G1RegionToSpaceMapper;
 class G1SurvivorRegions;
 class ThreadClosure;
 
-// This is a container class for either an oop or a continuation address for
-// mark stack entries. Both are pushed onto the mark stack.
-class G1TaskQueueEntry {
-private:
-  void* _holder;
-
-  static const uintptr_t ArraySliceBit = 1;
-
-  G1TaskQueueEntry(oop obj) : _holder(obj) {
-    assert(_holder != nullptr, "Not allowed to set null task queue element");
-  }
-  G1TaskQueueEntry(HeapWord* addr) : _holder((void*)((uintptr_t)addr | ArraySliceBit)) { }
-public:
-
-  G1TaskQueueEntry() : _holder(nullptr) { }
-  // Trivially copyable, for use in GenericTaskQueue.
-
-  static G1TaskQueueEntry from_slice(HeapWord* what) { return G1TaskQueueEntry(what); }
-  static G1TaskQueueEntry from_oop(oop obj) { return G1TaskQueueEntry(obj); }
-
-  oop obj() const {
-    assert(!is_array_slice(), "Trying to read array slice " PTR_FORMAT " as oop", p2i(_holder));
-    return cast_to_oop(_holder);
-  }
-
-  HeapWord* slice() const {
-    assert(is_array_slice(), "Trying to read oop " PTR_FORMAT " as array slice", p2i(_holder));
-    return (HeapWord*)((uintptr_t)_holder & ~ArraySliceBit);
-  }
-
-  bool is_oop() const { return !is_array_slice(); }
-  bool is_array_slice() const { return ((uintptr_t)_holder & ArraySliceBit) != 0; }
-  bool is_null() const { return _holder == nullptr; }
-};
-
+typedef ScannerTask G1TaskQueueEntry;
 typedef GenericTaskQueue G1CMTaskQueue;
 typedef GenericTaskQueueSet G1CMTaskQueueSet;
 
@@ -412,6 +380,8 @@ class G1ConcurrentMark : public CHeapObj {
   G1CMTaskQueueSet*       _task_queues; // Task queue set
   TaskTerminator          _terminator;  // For termination
 
+  PartialArrayStateManager* _partial_array_state_manager;
+
   // Two sync barriers that are used to synchronize tasks when an
   // overflow occurs. The algorithm is the following. All tasks enter
   // the first one to ensure that they have all stopped manipulating
@@ -489,6 +459,8 @@ class G1ConcurrentMark : public CHeapObj {
   // Prints all gathered CM-related statistics
   void print_stats();
 
+  void print_and_reset_taskqueue_stats();
+
   HeapWord*           finger()       { return _finger;   }
   bool                concurrent()   { return _concurrent; }
   uint                active_tasks() { return _num_active_tasks; }
@@ -583,6 +555,8 @@ public:
 
   uint worker_id_offset() const { return _worker_id_offset; }
 
+  uint max_num_tasks() const {return _max_num_tasks; }
+
   // Clear statistics gathered during the concurrent cycle for the given region after
   // it has been reclaimed.
   void clear_statistics(G1HeapRegion* r);
@@ -632,6 +606,8 @@ public:
   // Calculates the number of concurrent GC threads to be used in the marking phase.
   uint calc_active_marking_workers();
 
+  PartialArrayStateManager* partial_array_state_manager() const;
+
   // Resets the global marking data structures, as well as the
   // task local ones; should be called during concurrent start.
   void reset();
@@ -643,6 +619,10 @@ public:
   // to be called concurrently to the mutator. It will yield to safepoint requests.
   void cleanup_for_next_mark();
 
+  // Recycle the memory that has been requested by allocators associated with
+  // this manager.
+  void reset_partial_array_state_manager();
+
   // Clear the next marking bitmap during safepoint.
   void clear_bitmap(WorkerThreads* workers);
 
@@ -733,14 +713,13 @@ private:
     refs_reached_period           = 1024,
   };
 
-  G1CMObjArrayProcessor       _objArray_processor;
-
   uint                        _worker_id;
   G1CollectedHeap*            _g1h;
   G1ConcurrentMark*           _cm;
   G1CMBitMap*                 _mark_bitmap;
   // the task queue of this task
   G1CMTaskQueue*              _task_queue;
+  PartialArraySplitter        _partial_array_splitter;
 
   G1RegionMarkStatsCache      _mark_stats_cache;
   // Number of calls to this task
@@ -851,13 +830,24 @@ private:
   // mark bitmap scan, and so needs to be pushed onto the mark stack.
   bool is_below_finger(oop obj, HeapWord* global_finger) const;
 
-  template void process_grey_task_entry(G1TaskQueueEntry task_entry);
+  template void process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen);
+
+  static bool should_be_sliced(oop obj);
+  // Start processing the given objArrayOop by first pushing its continuations and
+  // then scanning the first chunk including the header.
+  size_t start_partial_array_processing(oop obj);
+  // Process the given continuation. Returns the number of words scanned.
+  size_t process_partial_array(const G1TaskQueueEntry& task, bool stolen);
+  // Apply the closure to the given range of elements in the objArray.
+  inline void process_array_chunk(objArrayOop obj, size_t start, size_t end);
 public:
-  // Apply the closure on the given area of the objArray. Return the number of words
-  // scanned.
-  inline size_t scan_objArray(objArrayOop obj, MemRegion mr);
   // Resets the task; should be called right at the beginning of a marking phase.
   void reset(G1CMBitMap* mark_bitmap);
+  // Register/unregister Partial Array Splitter Allocator with the PartialArrayStateManager.
+  // This allows us to discard memory arenas used for partial object array states at the end
+  // of a concurrent mark cycle.
+  void register_partial_array_splitter();
+  void unregister_partial_array_splitter();
   // Clears all the fields that correspond to a claimed region.
   void clear_region_fields();
 
@@ -913,7 +903,7 @@ public:
   inline bool deal_with_reference(T* p);
 
   // Scans an object and visits its children.
-  inline void scan_task_entry(G1TaskQueueEntry task_entry);
+  inline void process_entry(G1TaskQueueEntry task_entry, bool stolen);
 
   // Pushes an object on the local queue.
   inline void push(G1TaskQueueEntry task_entry);
@@ -958,6 +948,11 @@ public:
   Pair flush_mark_stats_cache();
   // Prints statistics associated with this task
   void print_stats();
+#if TASKQUEUE_STATS
+  PartialArrayTaskStats* partial_array_task_stats() {
+    return _partial_array_splitter.stats();
+  }
+#endif
 };
 
 // Class that's used to to print out per-region liveness
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp
index 6f71012ff7c..fe72c68d4eb 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.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
@@ -29,7 +29,6 @@
 
 #include "gc/g1/g1CollectedHeap.inline.hpp"
 #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp"
-#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp"
 #include "gc/g1/g1HeapRegion.hpp"
 #include "gc/g1/g1HeapRegionRemSet.inline.hpp"
 #include "gc/g1/g1OopClosures.inline.hpp"
@@ -39,6 +38,7 @@
 #include "gc/shared/suspendibleThreadSet.hpp"
 #include "gc/shared/taskqueue.inline.hpp"
 #include "utilities/bitMap.inline.hpp"
+#include "utilities/checkedCast.hpp"
 
 inline bool G1CMIsAliveClosure::do_object_b(oop obj) {
   // Check whether the passed in object is null. During discovery the referent
@@ -107,13 +107,15 @@ inline void G1CMMarkStack::iterate(Fn fn) const {
 #endif
 
 // It scans an object and visits its children.
-inline void G1CMTask::scan_task_entry(G1TaskQueueEntry task_entry) { process_grey_task_entry(task_entry); }
+inline void G1CMTask::process_entry(G1TaskQueueEntry task_entry, bool stolen) {
+  process_grey_task_entry(task_entry, stolen);
+}
 
 inline void G1CMTask::push(G1TaskQueueEntry task_entry) {
-  assert(task_entry.is_array_slice() || _g1h->is_in_reserved(task_entry.obj()), "invariant");
-  assert(task_entry.is_array_slice() || !_g1h->is_on_master_free_list(
-              _g1h->heap_region_containing(task_entry.obj())), "invariant");
-  assert(task_entry.is_array_slice() || _mark_bitmap->is_marked(cast_from_oop(task_entry.obj())), "invariant");
+  assert(task_entry.is_partial_array_state() || _g1h->is_in_reserved(task_entry.to_oop()), "invariant");
+  assert(task_entry.is_partial_array_state() || !_g1h->is_on_master_free_list(
+              _g1h->heap_region_containing(task_entry.to_oop())), "invariant");
+  assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())), "invariant");
 
   if (!_task_queue->push(task_entry)) {
     // The local task queue looks full. We need to push some entries
@@ -159,29 +161,34 @@ inline bool G1CMTask::is_below_finger(oop obj, HeapWord* global_finger) const {
 }
 
 template
-inline void G1CMTask::process_grey_task_entry(G1TaskQueueEntry task_entry) {
-  assert(scan || (task_entry.is_oop() && task_entry.obj()->is_typeArray()), "Skipping scan of grey non-typeArray");
-  assert(task_entry.is_array_slice() || _mark_bitmap->is_marked(cast_from_oop(task_entry.obj())),
+inline void G1CMTask::process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen) {
+  assert(scan || (!task_entry.is_partial_array_state() && task_entry.to_oop()->is_typeArray()), "Skipping scan of grey non-typeArray");
+  assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())),
          "Any stolen object should be a slice or marked");
 
   if (scan) {
-    if (task_entry.is_array_slice()) {
-      _words_scanned += _objArray_processor.process_slice(task_entry.slice());
+    if (task_entry.is_partial_array_state()) {
+      _words_scanned += process_partial_array(task_entry, stolen);
     } else {
-      oop obj = task_entry.obj();
-      if (G1CMObjArrayProcessor::should_be_sliced(obj)) {
-        _words_scanned += _objArray_processor.process_obj(obj);
+      oop obj = task_entry.to_oop();
+      if (should_be_sliced(obj)) {
+        _words_scanned += start_partial_array_processing(obj);
       } else {
-        _words_scanned += obj->oop_iterate_size(_cm_oop_closure);;
+        _words_scanned += obj->oop_iterate_size(_cm_oop_closure);
       }
     }
   }
   check_limits();
 }
 
-inline size_t G1CMTask::scan_objArray(objArrayOop obj, MemRegion mr) {
-  obj->oop_iterate(_cm_oop_closure, mr);
-  return mr.word_size();
+inline bool G1CMTask::should_be_sliced(oop obj) {
+  return obj->is_objArray() && ((objArrayOop)obj)->length() >= (int)ObjArrayMarkingStride;
+}
+
+inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t end) {
+  obj->oop_iterate_elements_range(_cm_oop_closure,
+                                  checked_cast(start),
+                                  checked_cast(end));
 }
 
 inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) {
@@ -265,7 +272,7 @@ inline bool G1CMTask::make_reference_grey(oop obj) {
   // be pushed on the stack. So, some duplicate work, but no
   // correctness problems.
   if (is_below_finger(obj, global_finger)) {
-    G1TaskQueueEntry entry = G1TaskQueueEntry::from_oop(obj);
+    G1TaskQueueEntry entry(obj);
     if (obj->is_typeArray()) {
       // Immediately process arrays of primitive types, rather
       // than pushing on the mark stack.  This keeps us from
@@ -277,7 +284,7 @@ inline bool G1CMTask::make_reference_grey(oop obj) {
       // by only doing a bookkeeping update and avoiding the
       // actual scan of the object - a typeArray contains no
       // references, and the metadata is built-in.
-      process_grey_task_entry(entry);
+      process_grey_task_entry(entry, false /* stolen */);
     } else {
       push(entry);
     }
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp
deleted file mode 100644
index 7f62e5527d5..00000000000
--- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2016, 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.
- *
- */
-
-#include "gc/g1/g1CollectedHeap.inline.hpp"
-#include "gc/g1/g1ConcurrentMark.inline.hpp"
-#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp"
-#include "gc/g1/g1HeapRegion.inline.hpp"
-#include "gc/shared/gc_globals.hpp"
-#include "memory/memRegion.hpp"
-#include "utilities/globalDefinitions.hpp"
-
-void G1CMObjArrayProcessor::push_array_slice(HeapWord* what) {
-  _task->push(G1TaskQueueEntry::from_slice(what));
-}
-
-size_t G1CMObjArrayProcessor::process_array_slice(objArrayOop obj, HeapWord* start_from, size_t remaining) {
-  size_t words_to_scan = MIN2(remaining, (size_t)ObjArrayMarkingStride);
-
-  if (remaining > ObjArrayMarkingStride) {
-    push_array_slice(start_from + ObjArrayMarkingStride);
-  }
-
-  // Then process current area.
-  MemRegion mr(start_from, words_to_scan);
-  return _task->scan_objArray(obj, mr);
-}
-
-size_t G1CMObjArrayProcessor::process_obj(oop obj) {
-  assert(should_be_sliced(obj), "Must be an array object %d and large %zu", obj->is_objArray(), obj->size());
-
-  return process_array_slice(objArrayOop(obj), cast_from_oop(obj), objArrayOop(obj)->size());
-}
-
-size_t G1CMObjArrayProcessor::process_slice(HeapWord* slice) {
-
-  // Find the start address of the objArrayOop.
-  // Shortcut the BOT access if the given address is from a humongous object. The BOT
-  // slide is fast enough for "smaller" objects in non-humongous regions, but is slower
-  // than directly using heap region table.
-  G1CollectedHeap* g1h = G1CollectedHeap::heap();
-  G1HeapRegion* r = g1h->heap_region_containing(slice);
-
-  HeapWord* const start_address = r->is_humongous() ?
-                                  r->humongous_start_region()->bottom() :
-                                  r->block_start(slice);
-
-  assert(cast_to_oop(start_address)->is_objArray(), "Address " PTR_FORMAT " does not refer to an object array ", p2i(start_address));
-  assert(start_address < slice,
-         "Object start address " PTR_FORMAT " must be smaller than decoded address " PTR_FORMAT,
-         p2i(start_address),
-         p2i(slice));
-
-  objArrayOop objArray = objArrayOop(cast_to_oop(start_address));
-
-  size_t already_scanned = pointer_delta(slice, start_address);
-  size_t remaining = objArray->size() - already_scanned;
-
-  return process_array_slice(objArray, slice, remaining);
-}
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp
deleted file mode 100644
index c2737dbbda6..00000000000
--- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
- * 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_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_HPP
-#define SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_HPP
-
-#include "oops/oopsHierarchy.hpp"
-
-class G1CMTask;
-
-// Helper class to mark through large objArrays during marking in an efficient way.
-// Instead of pushing large object arrays, we push continuations onto the
-// mark stack. These continuations are identified by having their LSB set.
-// This allows incremental processing of large objects.
-class G1CMObjArrayProcessor {
-private:
-  // Reference to the task for doing the actual work.
-  G1CMTask* _task;
-
-  // Push the continuation at the given address onto the mark stack.
-  void push_array_slice(HeapWord* addr);
-
-  // Process (apply the closure) on the given continuation of the given objArray.
-  size_t process_array_slice(objArrayOop const obj, HeapWord* start_from, size_t remaining);
-public:
-  static bool should_be_sliced(oop obj);
-
-  G1CMObjArrayProcessor(G1CMTask* task) : _task(task) {
-  }
-
-  // Process the given continuation. Returns the number of words scanned.
-  size_t process_slice(HeapWord* slice);
-  // Start processing the given objArrayOop by scanning the header and pushing its
-  // continuation.
-  size_t process_obj(oop obj);
-};
-
-#endif // SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_HPP
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp
deleted file mode 100644
index f6d47acdc01..00000000000
--- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2016, 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_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_INLINE_HPP
-#define SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_INLINE_HPP
-
-#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp"
-
-#include "gc/shared/gc_globals.hpp"
-#include "oops/oop.inline.hpp"
-#include "oops/oopsHierarchy.hpp"
-
-inline bool G1CMObjArrayProcessor::should_be_sliced(oop obj) {
-  return obj->is_objArray() && ((objArrayOop)obj)->size() >= 2 * ObjArrayMarkingStride;
-}
-
-#endif // SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_INLINE_HPP
diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp
index 4334773a4e9..5c2fe4e5178 100644
--- a/src/hotspot/share/gc/shared/taskqueue.hpp
+++ b/src/hotspot/share/gc/shared/taskqueue.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
@@ -641,6 +641,10 @@ public:
     return (raw_value() & PartialArrayTag) != 0;
   }
 
+  bool is_null() const {
+    return _p == nullptr;
+  }
+
   oop* to_oop_ptr() const {
     return static_cast(decode(OopTag));
   }

From 0f4d775085109981fbf00623d38da22655d04675 Mon Sep 17 00:00:00 2001
From: Tobias Hartmann 
Date: Thu, 22 Jan 2026 06:56:51 +0000
Subject: [PATCH 102/126] 8375534: Debug method 'pp' should support compressed
 oops

Reviewed-by: vlivanov, phubner
---
 src/hotspot/share/utilities/debug.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp
index 0e1ca1efb98..504b923237e 100644
--- a/src/hotspot/share/utilities/debug.cpp
+++ b/src/hotspot/share/utilities/debug.cpp
@@ -429,10 +429,8 @@ extern "C" DEBUGEXPORT void pp(void* p) {
     tty->print_cr("null");
     return;
   }
-  if (Universe::heap()->is_in(p)) {
-    oop obj = cast_to_oop(p);
-    obj->print();
-  } else {
+
+  if (!Universe::heap()->print_location(tty, p)) {
     // Ask NMT about this pointer.
     // GDB note: We will be using SafeFetch to access the supposed malloc header. If the address is
     // not readable, this will generate a signal. That signal will trip up the debugger: gdb will

From f3381f0ffe2207e1765558f6f49e5a0280a3f920 Mon Sep 17 00:00:00 2001
From: Thomas Schatzl 
Date: Thu, 22 Jan 2026 08:29:05 +0000
Subject: [PATCH 103/126] 8375314: Parallel: Crash iterating over unloaded
 classes for ObjectCountAfterGC event

Reviewed-by: rkennke, sjohanss, iwalulya
---
 src/hotspot/share/gc/g1/g1CollectedHeap.hpp   |   1 -
 .../share/gc/g1/g1CollectedHeap.inline.hpp    |   8 +-
 .../share/gc/parallel/psParallelCompact.cpp   |  23 ++--
 .../share/gc/parallel/psParallelCompact.hpp   |   1 +
 src/hotspot/share/gc/shared/collectedHeap.hpp |   4 +-
 .../share/gc/shared/collectedHeap.inline.hpp  |   9 +-
 .../gc/parallel/TestObjectCountAfterGC.java   | 104 ++++++++++++++++++
 7 files changed, 134 insertions(+), 16 deletions(-)
 create mode 100644 test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java

diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
index a0104d04f4f..8009df1fa6a 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
@@ -1268,7 +1268,6 @@ public:
 
   bool is_marked(oop obj) const;
 
-  inline static bool is_obj_filler(const oop obj);
   // Determine if an object is dead, given the object and also
   // the region to which the object belongs.
   inline bool is_obj_dead(const oop obj, const G1HeapRegion* hr) const;
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp
index 577450b3be9..958b171444e 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp
@@ -38,6 +38,7 @@
 #include "gc/g1/g1Policy.hpp"
 #include "gc/g1/g1RegionPinCache.inline.hpp"
 #include "gc/g1/g1RemSet.hpp"
+#include "gc/shared/collectedHeap.inline.hpp"
 #include "gc/shared/markBitMap.inline.hpp"
 #include "gc/shared/taskqueue.inline.hpp"
 #include "oops/stackChunkOop.hpp"
@@ -229,16 +230,11 @@ inline bool G1CollectedHeap::requires_barriers(stackChunkOop obj) const {
   return !heap_region_containing(obj)->is_young(); // is_in_young does an unnecessary null check
 }
 
-inline bool G1CollectedHeap::is_obj_filler(const oop obj) {
-  Klass* k = obj->klass_without_asserts();
-  return k == Universe::fillerArrayKlass() || k == vmClasses::FillerObject_klass();
-}
-
 inline bool G1CollectedHeap::is_obj_dead(const oop obj, const G1HeapRegion* hr) const {
   assert(!hr->is_free(), "looking up obj " PTR_FORMAT " in Free region %u", p2i(obj), hr->hrm_index());
   if (hr->is_in_parsable_area(obj)) {
     // This object is in the parsable part of the heap, live unless scrubbed.
-    return is_obj_filler(obj);
+    return is_filler_object(obj);
   } else {
     // From Remark until a region has been concurrently scrubbed, parts of the
     // region is not guaranteed to be parsable. Use the bitmap for liveness.
diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
index b1b07b4bc5c..bab72296d4c 100644
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.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
@@ -44,6 +44,7 @@
 #include "gc/parallel/psStringDedup.hpp"
 #include "gc/parallel/psYoungGen.hpp"
 #include "gc/shared/classUnloadingContext.hpp"
+#include "gc/shared/collectedHeap.inline.hpp"
 #include "gc/shared/fullGCForwarding.inline.hpp"
 #include "gc/shared/gcCause.hpp"
 #include "gc/shared/gcHeapSummary.hpp"
@@ -932,6 +933,17 @@ void PSParallelCompact::summary_phase(bool should_do_max_compaction)
   }
 }
 
+void PSParallelCompact::report_object_count_after_gc() {
+  GCTraceTime(Debug, gc, phases) tm("Report Object Count", &_gc_timer);
+  // The heap is compacted, all objects are iterable. However there may be
+  // filler objects in the heap which we should ignore.
+  class SkipFillerObjectClosure : public BoolObjectClosure {
+  public:
+    bool do_object_b(oop obj) override { return !CollectedHeap::is_filler_object(obj); }
+  } cl;
+  _gc_tracer.report_object_count_after_gc(&cl, &ParallelScavengeHeap::heap()->workers());
+}
+
 bool PSParallelCompact::invoke(bool clear_all_soft_refs, bool should_do_max_compaction) {
   assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
   assert(Thread::current() == (Thread*)VMThread::vm_thread(),
@@ -1027,6 +1039,8 @@ bool PSParallelCompact::invoke(bool clear_all_soft_refs, bool should_do_max_comp
 
     heap->print_heap_change(pre_gc_values);
 
+    report_object_count_after_gc();
+
     // Track memory usage and detect low memory
     MemoryService::track_memory_usage();
     heap->update_counters();
@@ -1274,10 +1288,6 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) {
     }
   }
 
-  {
-    GCTraceTime(Debug, gc, phases) tm("Report Object Count", &_gc_timer);
-    _gc_tracer.report_object_count_after_gc(is_alive_closure(), &ParallelScavengeHeap::heap()->workers());
-  }
 #if TASKQUEUE_STATS
   ParCompactionManager::print_and_reset_taskqueue_stats();
 #endif
@@ -1835,8 +1845,7 @@ void PSParallelCompact::verify_filler_in_dense_prefix() {
       oop obj = cast_to_oop(cur_addr);
       oopDesc::verify(obj);
       if (!mark_bitmap()->is_marked(cur_addr)) {
-        Klass* k = cast_to_oop(cur_addr)->klass();
-        assert(k == Universe::fillerArrayKlass() || k == vmClasses::FillerObject_klass(), "inv");
+        assert(CollectedHeap::is_filler_object(cast_to_oop(cur_addr)), "inv");
       }
       cur_addr += obj->size();
     }
diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.hpp
index 2297d720b35..4ac9395d727 100644
--- a/src/hotspot/share/gc/parallel/psParallelCompact.hpp
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.hpp
@@ -749,6 +749,7 @@ private:
   // Move objects to new locations.
   static void compact();
 
+  static void report_object_count_after_gc();
   // Add available regions to the stack and draining tasks to the task queue.
   static void prepare_region_draining_tasks(uint parallel_gc_threads);
 
diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
index 6f335b1cdf4..363ccf321b2 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.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
@@ -309,6 +309,8 @@ protected:
     fill_with_object(start, pointer_delta(end, start), zap);
   }
 
+  inline static bool is_filler_object(oop obj);
+
   virtual void fill_with_dummy_object(HeapWord* start, HeapWord* end, bool zap);
   static size_t min_dummy_object_size() {
     return oopDesc::header_size();
diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
index c9d84f54449..194c1fe0bf2 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.inline.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
@@ -27,7 +27,9 @@
 
 #include "gc/shared/collectedHeap.hpp"
 
+#include "classfile/vmClasses.hpp"
 #include "gc/shared/memAllocator.hpp"
+#include "memory/universe.hpp"
 #include "oops/oop.inline.hpp"
 #include "utilities/align.hpp"
 
@@ -50,4 +52,9 @@ inline void CollectedHeap::add_vmthread_cpu_time(jlong time) {
   _vmthread_cpu_time += time;
 }
 
+inline bool CollectedHeap::is_filler_object(oop obj) {
+  Klass* k = obj->klass_without_asserts();
+  return k == Universe::fillerArrayKlass() || k == vmClasses::FillerObject_klass();
+}
+
 #endif // SHARE_GC_SHARED_COLLECTEDHEAP_INLINE_HPP
diff --git a/test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java b/test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java
new file mode 100644
index 00000000000..e61e7518938
--- /dev/null
+++ b/test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java
@@ -0,0 +1,104 @@
+/*
+ * 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 gc.parallel;
+
+/*
+ * @test TestObjectCountAfterGC
+ * @bug 8375314
+ * @summary Verifies that the HeapInspection VM operation for the ObjectCountAfterGC JFR event does not crash the VM.
+ *          Creates a set of custom classes that are about to be unloaded to cause metaspace to uncommit pages. When
+ *          the execution of the heap inspection iterates over the heap, it will come across these unloaded classes
+ *          referencing uncommitted memory, crashing.
+ * @requires vm.gc.Parallel
+ * @requires vm.opt.final.ClassUnloading
+ * @library /test/lib
+ * @library /testlibrary/asm
+ * @modules java.base/jdk.internal.misc
+ * @run main/othervm -XX:+UseParallelGC -Xlog:gc=debug,metaspace=info -XX:StartFlightRecording:gc=all,duration=1s,filename=myrecording.jfr
+ *                   gc.parallel.TestObjectCountAfterGC
+ */
+
+import java.lang.ref.Reference;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class TestObjectCountAfterGC {
+
+    static final String className = "ClassToLoadUnload";
+
+    public static void main(String args[]) throws Exception {
+        final int KEEPALIVE_LENGTH = 100;
+
+        Object[] keepalive = new Object[KEEPALIVE_LENGTH];
+
+        for (int i = 1; i < 1000; i++) {
+            ClassLoader cl = new MyClassLoader();
+            Object o = null;
+            // Create some random kept alive objects so that the
+            // compaction regions are not totally empty and the
+            // heap inspection VM operation needs to iterate them.
+            keepalive[(i / KEEPALIVE_LENGTH) % KEEPALIVE_LENGTH] = new int[100];
+            o = cl.loadClass(className + i).newInstance();
+
+            cl = null;
+            o = null;
+        }
+
+        // There is heap inspection VM operation for the ObjectCountAfterGC event
+        // when JFR stops recording.
+
+        Reference.reachabilityFence(keepalive);
+    }
+}
+
+class MyClassLoader extends ClassLoader {
+
+    // Create a class of the given name with a default constructor.
+    public byte[] createClass(String name) {
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, name, null, "java/lang/Object", null);
+        // Add default constructor that just calls the super class constructor.
+        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
+        mv.visitCode();
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
+        mv.visitInsn(Opcodes.RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+        return cw.toByteArray();
+    }
+
+    // If the given name starts with "TestObjectCountAfterGC" create a new class on the fly,
+    // delegate otherwise.
+    public Class loadClass(String name) throws ClassNotFoundException {
+        if (!name.startsWith(TestObjectCountAfterGC.className)) {
+            return super.loadClass(name);
+        }
+        byte[] cls = createClass(name);
+        return defineClass(name, cls, 0, cls.length, null);
+    }
+  }
+

From e50bf1f2a4702ef48cf16cc4f45d034a652bf358 Mon Sep 17 00:00:00 2001
From: Thomas Schatzl 
Date: Thu, 22 Jan 2026 08:29:27 +0000
Subject: [PATCH 104/126] 8375616: G1: Convert G1BatchedTask to use Atomic

Reviewed-by: sjohanss, kbarrett
---
 src/hotspot/share/gc/g1/g1BatchedTask.cpp | 9 ++++-----
 src/hotspot/share/gc/g1/g1BatchedTask.hpp | 5 +++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/hotspot/share/gc/g1/g1BatchedTask.cpp b/src/hotspot/share/gc/g1/g1BatchedTask.cpp
index 57558301541..1f082153476 100644
--- a/src/hotspot/share/gc/g1/g1BatchedTask.cpp
+++ b/src/hotspot/share/gc/g1/g1BatchedTask.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.
  * 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 @@
 #include "gc/g1/g1BatchedTask.hpp"
 #include "gc/g1/g1CollectedHeap.inline.hpp"
 #include "gc/g1/g1GCParPhaseTimesTracker.hpp"
-#include "runtime/atomicAccess.hpp"
 #include "utilities/growableArray.hpp"
 
 void G1AbstractSubTask::record_work_item(uint worker_id, uint index, size_t count) {
@@ -40,7 +39,7 @@ const char* G1AbstractSubTask::name() const {
 }
 
 bool G1BatchedTask::try_claim_serial_task(int& task) {
-  task = AtomicAccess::fetch_then_add(&_num_serial_tasks_done, 1);
+  task = _num_serial_tasks_done.fetch_then_add(1);
   return task < _serial_tasks.length();
 }
 
@@ -96,8 +95,8 @@ void G1BatchedTask::work(uint worker_id) {
 }
 
 G1BatchedTask::~G1BatchedTask() {
-  assert(AtomicAccess::load(&_num_serial_tasks_done) >= _serial_tasks.length(),
-         "Only %d tasks of %d claimed", AtomicAccess::load(&_num_serial_tasks_done), _serial_tasks.length());
+  assert(_num_serial_tasks_done.load_relaxed() >= _serial_tasks.length(),
+         "Only %d tasks of %d claimed", _num_serial_tasks_done.load_relaxed(), _serial_tasks.length());
 
   for (G1AbstractSubTask* task : _parallel_tasks) {
     delete task;
diff --git a/src/hotspot/share/gc/g1/g1BatchedTask.hpp b/src/hotspot/share/gc/g1/g1BatchedTask.hpp
index 020fda634e4..a6d2ef923c0 100644
--- a/src/hotspot/share/gc/g1/g1BatchedTask.hpp
+++ b/src/hotspot/share/gc/g1/g1BatchedTask.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.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
 #include "gc/g1/g1GCPhaseTimes.hpp"
 #include "gc/shared/workerThread.hpp"
 #include "memory/allocation.hpp"
+#include "runtime/atomic.hpp"
 
 template 
 class GrowableArrayCHeap;
@@ -120,7 +121,7 @@ public:
 // 5) ~T()
 //
 class G1BatchedTask : public WorkerTask {
-  volatile int _num_serial_tasks_done;
+  Atomic _num_serial_tasks_done;
   G1GCPhaseTimes* _phase_times;
 
   bool try_claim_serial_task(int& task);

From 92236ead1dea813cf456855f0aa6b73c16e9dc70 Mon Sep 17 00:00:00 2001
From: Quan Anh Mai 
Date: Thu, 22 Jan 2026 08:32:01 +0000
Subject: [PATCH 105/126] 8375618: Incorrect assert in CastLLNode::Ideal

Reviewed-by: chagedorn, dlong
---
 src/hotspot/share/opto/castnode.cpp           |  6 +-
 src/hotspot/share/opto/type.cpp               | 14 +++++
 src/hotspot/share/opto/type.hpp               | 10 +++
 .../jtreg/compiler/igvn/CastLLBits.java       | 63 +++++++++++++++++++
 4 files changed, 90 insertions(+), 3 deletions(-)
 create mode 100644 test/hotspot/jtreg/compiler/igvn/CastLLBits.java

diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp
index 998b6a79903..2ebbdd7cdb3 100644
--- a/src/hotspot/share/opto/castnode.cpp
+++ b/src/hotspot/share/opto/castnode.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -392,8 +392,8 @@ Node* CastLLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
     if (t != Type::TOP && t_in != Type::TOP) {
       const TypeLong* tl = t->is_long();
       const TypeLong* t_in_l = t_in->is_long();
-      assert(tl->_lo >= t_in_l->_lo && tl->_hi <= t_in_l->_hi, "CastLL type should be narrower than or equal to the type of its input");
-      assert((tl != t_in_l) == (tl->_lo > t_in_l->_lo || tl->_hi < t_in_l->_hi), "if type differs then this nodes's type must be narrower");
+      assert(t_in_l->contains(tl), "CastLL type should be narrower than or equal to the type of its input");
+      assert((tl != t_in_l) == t_in_l->strictly_contains(tl), "if type differs then this nodes's type must be narrower");
       if (tl != t_in_l) {
         const TypeInt* ti = TypeInt::make(checked_cast(tl->_lo), checked_cast(tl->_hi), tl->_widen);
         Node* castii = phase->transform(new CastIINode(in(0), in1->in(1), ti));
diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp
index d3271e79f2f..c637737eef9 100644
--- a/src/hotspot/share/opto/type.cpp
+++ b/src/hotspot/share/opto/type.cpp
@@ -1816,6 +1816,13 @@ bool TypeInt::contains(const TypeInt* t) const {
   return TypeIntHelper::int_type_is_subset(this, t);
 }
 
+#ifdef ASSERT
+bool TypeInt::strictly_contains(const TypeInt* t) const {
+  assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation");
+  return TypeIntHelper::int_type_is_subset(this, t) && !TypeIntHelper::int_type_is_equal(this, t);
+}
+#endif // ASSERT
+
 const Type* TypeInt::xmeet(const Type* t) const {
   return TypeIntHelper::int_type_xmeet(this, t);
 }
@@ -1944,6 +1951,13 @@ bool TypeLong::contains(const TypeLong* t) const {
   return TypeIntHelper::int_type_is_subset(this, t);
 }
 
+#ifdef ASSERT
+bool TypeLong::strictly_contains(const TypeLong* t) const {
+  assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation");
+  return TypeIntHelper::int_type_is_subset(this, t) && !TypeIntHelper::int_type_is_equal(this, t);
+}
+#endif // ASSERT
+
 const Type* TypeLong::xmeet(const Type* t) const {
   return TypeIntHelper::int_type_xmeet(this, t);
 }
diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp
index 7c7ff035a54..135f37f7267 100644
--- a/src/hotspot/share/opto/type.hpp
+++ b/src/hotspot/share/opto/type.hpp
@@ -812,6 +812,11 @@ public:
   bool contains(jint i) const;
   bool contains(const TypeInt* t) const;
 
+#ifdef ASSERT
+  // Check whether t is a proper subset (i.e. a subset that is not equal to the superset) of this
+  bool strictly_contains(const TypeInt* t) const;
+#endif // ASSERT
+
   virtual bool is_finite() const;  // Has a finite value
 
   virtual const Type* xmeet(const Type* t) const;
@@ -897,6 +902,11 @@ public:
   bool contains(jlong i) const;
   bool contains(const TypeLong* t) const;
 
+#ifdef ASSERT
+  // Check whether t is a proper subset (i.e. a subset that is not equal to the superset) of this
+  bool strictly_contains(const TypeLong* t) const;
+#endif // ASSERT
+
   // Check for positive 32-bit value.
   int is_positive_int() const { return _lo >= 0 && _hi <= (jlong)max_jint; }
 
diff --git a/test/hotspot/jtreg/compiler/igvn/CastLLBits.java b/test/hotspot/jtreg/compiler/igvn/CastLLBits.java
new file mode 100644
index 00000000000..a212fd1d3b6
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/igvn/CastLLBits.java
@@ -0,0 +1,63 @@
+/*
+ * 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.igvn;
+
+/**
+ * @test
+ * @bug 8375618
+ * @summary A CastLLNode may change only a bit of its input, which triggers the incorrect assertion
+ *          that the signed range must changes
+ * @run main/othervm -XX:-TieredCompilation -Xbatch ${test.main.class}
+ * @run main ${test.main.class}
+ */
+public class CastLLBits {
+    static long instanceCount;
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 2000; i++) {
+            test();
+        }
+    }
+
+    static void test() {
+        int i, i1 = 6, i9, i10, i11, i12;
+        boolean b = false;
+        for (i = 25; i > 5; i--) {
+            i9 = 1;
+            do {
+                i1 += 0;
+                for (i10 = 1; i10 < 3; ++i10) {
+                    instanceCount = i9;
+                }
+                i12 = 3;
+                while (--i12 > 0) {
+                    i11 = (int) instanceCount;
+                    i1 = i11;
+                    if (b) {};
+                    instanceCount &= 21;
+                }
+                i9++;
+            } while (i9 < 9);
+        }
+    }
+}

From 63be87d7f38a83c5fcdf59b54c6d63e0f0ca34d6 Mon Sep 17 00:00:00 2001
From: Thomas Schatzl 
Date: Thu, 22 Jan 2026 08:35:03 +0000
Subject: [PATCH 106/126] 8375977: G1: Convert JVMCICleaningTask to use
 Atomic

Reviewed-by: kbarrett
---
 src/hotspot/share/gc/g1/g1ParallelCleaning.cpp | 7 +++----
 src/hotspot/share/gc/g1/g1ParallelCleaning.hpp | 7 +++++--
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp
index 8d5e2a3239c..e3eabff5a50 100644
--- a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp
+++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.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
@@ -24,7 +24,6 @@
 
 
 #include "gc/g1/g1ParallelCleaning.hpp"
-#include "runtime/atomicAccess.hpp"
 #if INCLUDE_JVMCI
 #include "jvmci/jvmci.hpp"
 #endif
@@ -35,11 +34,11 @@ JVMCICleaningTask::JVMCICleaningTask() :
 }
 
 bool JVMCICleaningTask::claim_cleaning_task() {
-  if (AtomicAccess::load(&_cleaning_claimed)) {
+  if (_cleaning_claimed.load_relaxed()) {
     return false;
   }
 
-  return !AtomicAccess::cmpxchg(&_cleaning_claimed, false, true);
+  return _cleaning_claimed.compare_set(false, true);
 }
 
 void JVMCICleaningTask::work(bool unloading_occurred) {
diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp
index d8725cb110d..815b0883e16 100644
--- a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp
+++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.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
@@ -26,10 +26,13 @@
 #define SHARE_GC_G1_G1PARALLELCLEANING_HPP
 
 #include "gc/shared/parallelCleaning.hpp"
+#if INCLUDE_JVMCI
+#include "runtime/atomic.hpp"
+#endif
 
 #if INCLUDE_JVMCI
 class JVMCICleaningTask : public StackObj {
-  volatile bool _cleaning_claimed;
+  Atomic _cleaning_claimed;
 
 public:
   JVMCICleaningTask();

From 03038d802cc43b7694f554978ac9de8edca8a954 Mon Sep 17 00:00:00 2001
From: Thomas Schatzl 
Date: Thu, 22 Jan 2026 08:35:32 +0000
Subject: [PATCH 107/126] 8375978: G1: Convert G1Policy to use Atomic

Reviewed-by: kbarrett
---
 src/hotspot/share/gc/g1/g1Policy.cpp |  4 ++--
 src/hotspot/share/gc/g1/g1Policy.hpp | 13 +++++--------
 2 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp
index 2847a25c5b4..8818b477aae 100644
--- a/src/hotspot/share/gc/g1/g1Policy.cpp
+++ b/src/hotspot/share/gc/g1/g1Policy.cpp
@@ -203,8 +203,8 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l
   // allocation.
   // That is "fine" - at most this will schedule a GC (hopefully only a little) too
   // early or too late.
-  AtomicAccess::store(&_young_list_desired_length, new_young_list_desired_length);
-  AtomicAccess::store(&_young_list_target_length, new_young_list_target_length);
+  _young_list_desired_length.store_relaxed(new_young_list_desired_length);
+  _young_list_target_length.store_relaxed(new_young_list_target_length);
 }
 
 // Calculates desired young gen length. It is calculated from:
diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp
index cf12a7a8027..9513c79869e 100644
--- a/src/hotspot/share/gc/g1/g1Policy.hpp
+++ b/src/hotspot/share/gc/g1/g1Policy.hpp
@@ -35,7 +35,7 @@
 #include "gc/g1/g1RemSetTrackingPolicy.hpp"
 #include "gc/g1/g1YoungGenSizer.hpp"
 #include "gc/shared/gcCause.hpp"
-#include "runtime/atomicAccess.hpp"
+#include "runtime/atomic.hpp"
 #include "utilities/pair.hpp"
 #include "utilities/ticks.hpp"
 
@@ -81,12 +81,9 @@ class G1Policy: public CHeapObj {
 
   // Desired young gen length without taking actually available free regions into
   // account.
-  volatile uint _young_list_desired_length;
+  Atomic _young_list_desired_length;
   // Actual target length given available free memory.
-  volatile uint _young_list_target_length;
-  // The max number of regions we can extend the eden by while the GC
-  // locker is active. This should be >= _young_list_target_length;
-  volatile uint _young_list_max_length;
+  Atomic _young_list_target_length;
 
   // The survivor rate groups below must be initialized after the predictor because they
   // indirectly use it through the "this" object passed to their constructor.
@@ -362,8 +359,8 @@ public:
   // This must be called at the very beginning of an evacuation pause.
   void decide_on_concurrent_start_pause();
 
-  uint young_list_desired_length() const { return AtomicAccess::load(&_young_list_desired_length); }
-  uint young_list_target_length() const { return AtomicAccess::load(&_young_list_target_length); }
+  uint young_list_desired_length() const { return _young_list_desired_length.load_relaxed(); }
+  uint young_list_target_length() const { return _young_list_target_length.load_relaxed(); }
 
   bool should_allocate_mutator_region() const;
   bool should_expand_on_mutator_allocation() const;

From 6165daf03c8582cca8e5b075560aa978b90f677c Mon Sep 17 00:00:00 2001
From: Matthias Baesken 
Date: Thu, 22 Jan 2026 08:50:11 +0000
Subject: [PATCH 108/126] 8375458: Check legal folder of JDK image for unwanted
 files

Reviewed-by: erikj
---
 test/jdk/build/CheckFiles.java | 59 +++++++++++++++++++++++++++++++++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/test/jdk/build/CheckFiles.java b/test/jdk/build/CheckFiles.java
index eb903c0a224..5a915e881f0 100644
--- a/test/jdk/build/CheckFiles.java
+++ b/test/jdk/build/CheckFiles.java
@@ -23,6 +23,7 @@
  */
 
 import java.io.IOException;
+import java.nio.file.DirectoryStream;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -36,7 +37,7 @@ import jdk.test.lib.Platform;
 
 /*
  * @test
- * @summary Check for unwanted file (types/extensions) in the jdk image
+ * @summary Check for unwanted files (types/extensions) in the jdk image
  * @library /test/lib
  * @requires !vm.debug
  * @run main CheckFiles
@@ -47,6 +48,20 @@ public class CheckFiles {
     // JTREG=JAVA_OPTIONS=-Djdk.test.build.CheckFiles.dir=/path/to/dir
     public static final String DIR_PROPERTY = "jdk.test.build.CheckFiles.dir";
 
+    private static boolean isGpl(Path myFile) {
+        if (myFile == null || !Files.exists(myFile)) {
+            return false;
+        }
+
+        try {
+            String firstLine = Files.readAllLines(myFile).stream()
+                                    .findFirst().orElse("");
+            return firstLine.contains("The GNU General Public License (GPL)");
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
     public static void main(String[] args) throws Exception {
         String jdkPathString = System.getProperty("test.jdk");
         Path jdkHome = Paths.get(jdkPathString);
@@ -148,6 +163,48 @@ public class CheckFiles {
                 throw new Error("jmods dir scan failed");
             }
         }
+
+        Path legalDir = mainDirToScan.resolve("legal");
+        Path javabaseLicenseFile = mainDirToScan.resolve("legal/java.base/LICENSE");
+        if (isGpl(javabaseLicenseFile)) { // for now check only legal dir of GPL based images; other ones might have other content
+            System.out.println("GPL info found in java.base LICENSE file");
+            ArrayList allowedEndingsLegalDir = new ArrayList<>();
+            allowedEndingsLegalDir.add(".md");
+            allowedEndingsLegalDir.add("ADDITIONAL_LICENSE_INFO");
+            allowedEndingsLegalDir.add("ASSEMBLY_EXCEPTION");
+            allowedEndingsLegalDir.add("LICENSE");
+
+            ArrayList requiredFilesInLegalSubdirs = new ArrayList<>();
+            requiredFilesInLegalSubdirs.add("LICENSE");
+            requiredFilesInLegalSubdirs.add("ADDITIONAL_LICENSE_INFO");
+            requiredFilesInLegalSubdirs.add("ASSEMBLY_EXCEPTION");
+
+            System.out.println("Legal directory to scan:" + legalDir);
+            try (DirectoryStream stream = Files.newDirectoryStream(legalDir)) {
+                for (Path subfolder : stream) {
+                    if (Files.isDirectory(subfolder)) {
+                        System.out.println("Checking legal dir subfolder for required files: " + subfolder.getFileName());
+
+                        for (String fileName : requiredFilesInLegalSubdirs) {
+                            Path filePath = subfolder.resolve(fileName);
+                            if (Files.exists(filePath)) {
+                                System.out.println("  Found " + fileName);
+                            } else {
+                                System.out.println("  Missing " + fileName);
+                                throw new Error("legal dir scan for required files failed");
+                            }
+                        }
+                    }
+                }
+            }
+
+            boolean legalDirRes = scanFiles(legalDir, allowedEndingsLegalDir);
+            if (legalDirRes) {
+                System.out.println("Legal directory scan successful.");
+            } else {
+                throw new Error("Legal dir scan failed");
+            }
+        }
     }
 
     private static boolean scanFiles(Path root, ArrayList allowedEndings) throws IOException {

From ddbd4617a6172e3054b2afade4f304f66c79816e Mon Sep 17 00:00:00 2001
From: Casper Norrbin 
Date: Thu, 22 Jan 2026 09:45:40 +0000
Subject: [PATCH 109/126] 8303470: containers/docker/TestMemoryAwareness.java
 failed with "'memory_limit_in_bytes:.*512000 k' missing from stdout/stderr"

Reviewed-by: sgehwolf, dholmes
---
 src/hotspot/os/linux/osContainer_linux.cpp | 13 +++++++------
 test/hotspot/jtreg/ProblemList.txt         |  1 -
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp
index fe1dbc17239..b46263efd99 100644
--- a/src/hotspot/os/linux/osContainer_linux.cpp
+++ b/src/hotspot/os/linux/osContainer_linux.cpp
@@ -304,12 +304,13 @@ void OSContainer::print_container_metric(outputStream* st, const char* metrics,
   constexpr int longest_value = max_length - 11; // Max length - shortest "metric: " string ("cpu_quota: ")
   char value_str[longest_value + 1] = {};
   os::snprintf_checked(value_str, longest_value, metric_fmt::fmt, value);
-  st->print("%s: %*s", metrics, max_length - static_cast(strlen(metrics)) - 2, value_str); // -2 for the ": "
-  if (unit[0] != '\0') {
-    st->print_cr(" %s", unit);
-  } else {
-    st->print_cr("");
-  }
+
+  const int pad_width = max_length - static_cast(strlen(metrics)) - 2; // -2 for the ": "
+  const char* unit_prefix = unit[0] != '\0' ? " " : "";
+
+  char line[128] = {};
+  os::snprintf_checked(line, sizeof(line), "%s: %*s%s%s", metrics, pad_width, value_str, unit_prefix, unit);
+  st->print_cr("%s", line);
 }
 
 void OSContainer::print_container_helper(outputStream* st, MetricResult& res, const char* metrics) {
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index 13e1ea30a34..84520b00056 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -113,7 +113,6 @@ runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x
 applications/jcstress/copy.java 8229852 linux-all
 
 containers/docker/TestJcmd.java 8278102 linux-all
-containers/docker/TestMemoryAwareness.java 8303470 linux-all
 containers/docker/TestJFREvents.java 8327723 linux-x64
 containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64
 

From e8eb218ca2d05736adc4b0aefa4b17e3062959b8 Mon Sep 17 00:00:00 2001
From: Liam Miller-Cushon 
Date: Thu, 22 Jan 2026 10:05:05 +0000
Subject: [PATCH 110/126] 8374643: Fix reference to implMethodKind in
 LambdaToMethod debug printf statement

Reviewed-by: vromero, liach
---
 .../sun/tools/javac/comp/LambdaToMethod.java  | 27 +++++++++-------
 .../tools/javac/resources/compiler.properties | 11 +++++++
 .../examples/LambdaDeserializationStat.java   | 31 +++++++++++++++++++
 .../lambda/SerializableObjectMethods.java     |  2 +-
 .../lambda/SerializableObjectMethods.out      |  4 +++
 5 files changed, 63 insertions(+), 12 deletions(-)
 create mode 100644 test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java
 create mode 100644 test/langtools/tools/javac/lambda/SerializableObjectMethods.out

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
index 22cb796870b..7d0c5192039 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
@@ -142,6 +142,9 @@ public class LambdaToMethod extends TreeTranslator {
     /** dump statistics about lambda code generation */
     private final boolean dumpLambdaToMethodStats;
 
+    /** dump statistics about lambda deserialization code generation */
+    private final boolean dumpLambdaDeserializationStats;
+
     /** force serializable representation, for stress testing **/
     private final boolean forceSerializable;
 
@@ -187,6 +190,7 @@ public class LambdaToMethod extends TreeTranslator {
         transTypes = TransTypes.instance(context);
         Options options = Options.instance(context);
         dumpLambdaToMethodStats = options.isSet("debug.dumpLambdaToMethodStats");
+        dumpLambdaDeserializationStats = options.isSet("debug.dumpLambdaDeserializationStats");
         attr = Attr.instance(context);
         forceSerializable = options.isSet("forceSerializable");
         boolean lineDebugInfo =
@@ -714,8 +718,9 @@ public class LambdaToMethod extends TreeTranslator {
         String implMethodName = refSym.getQualifiedName().toString();
         String implMethodSignature = typeSig(types.erasure(refSym.type));
 
+        int implMethodKind = refSym.referenceKind();
         JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType),
-                make.Literal(refSym.referenceKind()));
+                make.Literal(implMethodKind));
         ListBuffer serArgs = new ListBuffer<>();
         int i = 0;
         for (Type t : indyType.getParameterTypes()) {
@@ -743,16 +748,16 @@ public class LambdaToMethod extends TreeTranslator {
             stmts = new ListBuffer<>();
             kInfo.deserializeCases.put(implMethodName, stmts);
         }
-        /* **
-        System.err.printf("+++++++++++++++++\n");
-        System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
-        System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
-        System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
-        System.err.printf("*implMethodKind: %d\n", implMethodKind);
-        System.err.printf("*implClass: '%s'\n", implClass);
-        System.err.printf("*implMethodName: '%s'\n", implMethodName);
-        System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
-        ****/
+        if (dumpLambdaDeserializationStats) {
+            log.note(pos, Notes.LambdaDeserializationStat(
+                    functionalInterfaceClass,
+                    functionalInterfaceMethodName,
+                    functionalInterfaceMethodSignature,
+                    implMethodKind,
+                    implClass,
+                    implMethodName,
+                    implMethodSignature));
+        }
         stmts.append(stmt);
     }
 
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
index 95a7546e2b3..2ba9122c04a 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
@@ -1701,6 +1701,17 @@ compiler.note.mref.stat.1=\
     alternate metafactory = {0}\n\
     bridge method = {1}
 
+# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string
+compiler.note.lambda.deserialization.stat=\
+    Generating lambda deserialization\n\
+    functionalInterfaceClass: {0}\n\
+    functionalInterfaceMethodName: {1}\n\
+    functionalInterfaceMethodSignature:{2}\n\
+    implMethodKind: {3}\n\
+    implClass: {4}\n\
+    implMethodName: {5}\n\
+    implMethodSignature: {6}
+
 compiler.note.note=\
     Note:\u0020
 
diff --git a/test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java b/test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java
new file mode 100644
index 00000000000..4ab23ed0b3d
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2026, Google LLC and/or its affiliates. All rights reserved.
+ * 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.
+ */
+
+// key: compiler.note.lambda.deserialization.stat
+// options: --debug=dumpLambdaDeserializationStats
+
+import java.io.Serializable;
+
+class LambdaDeserializationStat {
+    Runnable r = (Runnable & Serializable) () -> {};
+}
diff --git a/test/langtools/tools/javac/lambda/SerializableObjectMethods.java b/test/langtools/tools/javac/lambda/SerializableObjectMethods.java
index 52d4cfc7bf6..b489a22c7b4 100644
--- a/test/langtools/tools/javac/lambda/SerializableObjectMethods.java
+++ b/test/langtools/tools/javac/lambda/SerializableObjectMethods.java
@@ -25,7 +25,7 @@
  * @test
  * @bug 8282080
  * @summary Check that serializable lambdas referring to j.l.Object methods work.
- * @compile SerializableObjectMethods.java
+ * @compile/ref=SerializableObjectMethods.out -XDrawDiagnostics --debug=dumpLambdaDeserializationStats SerializableObjectMethods.java
  * @run main SerializableObjectMethods
  */
 import java.io.ByteArrayInputStream;
diff --git a/test/langtools/tools/javac/lambda/SerializableObjectMethods.out b/test/langtools/tools/javac/lambda/SerializableObjectMethods.out
new file mode 100644
index 00000000000..d03cc145794
--- /dev/null
+++ b/test/langtools/tools/javac/lambda/SerializableObjectMethods.out
@@ -0,0 +1,4 @@
+SerializableObjectMethods.java:59:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I
+SerializableObjectMethods.java:60:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 9, SerializableObjectMethods$I2, hashCode, ()I
+- compiler.note.unchecked.filename: SerializableObjectMethods.java
+- compiler.note.unchecked.recompile

From 6e9256cb613c9a3594546a45975a81def2efcf46 Mon Sep 17 00:00:00 2001
From: Roland Westrelin 
Date: Thu, 22 Jan 2026 10:37:26 +0000
Subject: [PATCH 111/126] 8373343: C2: verify AddP base input only set for heap
 addresses

Reviewed-by: dlong, chagedorn, qamai
---
 .../share/gc/shared/c2/barrierSetC2.cpp       |  2 +-
 src/hotspot/share/opto/addnode.hpp            |  5 ++-
 src/hotspot/share/opto/callnode.cpp           |  4 +--
 src/hotspot/share/opto/callnode.hpp           |  2 +-
 src/hotspot/share/opto/escape.cpp             |  7 ++--
 src/hotspot/share/opto/graphKit.cpp           |  6 ++--
 src/hotspot/share/opto/library_call.cpp       | 26 +++++++-------
 src/hotspot/share/opto/macro.cpp              | 19 +++++-----
 src/hotspot/share/opto/macro.hpp              | 13 ++++---
 src/hotspot/share/opto/macroArrayCopy.cpp     | 26 ++++++--------
 src/hotspot/share/opto/memnode.cpp            | 35 ++++++++++++++-----
 src/hotspot/share/opto/memnode.hpp            |  4 +++
 src/hotspot/share/opto/parse.hpp              |  2 +-
 src/hotspot/share/opto/parse1.cpp             | 19 +++++-----
 src/hotspot/share/opto/parseHelper.cpp        |  2 +-
 src/hotspot/share/opto/subtypenode.cpp        |  4 +--
 16 files changed, 98 insertions(+), 78 deletions(-)

diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
index c4eefee5f65..53577bad1d8 100644
--- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
+++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
@@ -771,7 +771,7 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi
   //       this will require extensive changes to the loop optimization in order to
   //       prevent a degradation of the optimization.
   //       See comment in memnode.hpp, around line 227 in class LoadPNode.
-  Node* tlab_end = macro->make_load(toobig_false, mem, tlab_end_adr, 0, TypeRawPtr::BOTTOM, T_ADDRESS);
+  Node* tlab_end = macro->make_load_raw(toobig_false, mem, tlab_end_adr, 0, TypeRawPtr::BOTTOM, T_ADDRESS);
 
   // Load the TLAB top.
   Node* old_tlab_top = new LoadPNode(toobig_false, mem, tlab_top_adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, MemNode::unordered);
diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp
index 4151ab5d065..6128de00efb 100644
--- a/src/hotspot/share/opto/addnode.hpp
+++ b/src/hotspot/share/opto/addnode.hpp
@@ -226,8 +226,11 @@ public:
          Base,                  // Base oop, for GC purposes
          Address,               // Actually address, derived from base
          Offset } ;             // Offset added to address
-  AddPNode( Node *base, Node *ptr, Node *off ) : Node(nullptr,base,ptr,off) {
+  AddPNode(Node *base, Node *ptr, Node *off) : Node(nullptr,base,ptr,off) {
     init_class_id(Class_AddP);
+    assert((ptr->bottom_type() == Type::TOP) ||
+      ((base == Compile::current()->top()) == (ptr->bottom_type()->make_ptr()->isa_oopptr() == nullptr)),
+      "base input only needed for heap addresses");
   }
   virtual int Opcode() const;
   virtual Node* Identity(PhaseGVN* phase);
diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
index fac8596173f..9b3d7b38d15 100644
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -1737,11 +1737,11 @@ void AllocateNode::compute_MemBar_redundancy(ciMethod* initializer)
     _is_allocation_MemBar_redundant = true;
   }
 }
-Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem) {
+Node *AllocateNode::make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem) {
   Node* mark_node = nullptr;
   if (UseCompactObjectHeaders) {
     Node* klass_node = in(AllocateNode::KlassNode);
-    Node* proto_adr = phase->transform(new AddPNode(klass_node, klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset()))));
+    Node* proto_adr = phase->transform(new AddPNode(phase->C->top(), klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset()))));
     mark_node = LoadNode::make(*phase, control, mem, proto_adr, TypeRawPtr::BOTTOM, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
   } else {
     // For now only enable fast locking for non-array types
diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp
index 0bb064efc43..41d0c9f5aac 100644
--- a/src/hotspot/share/opto/callnode.hpp
+++ b/src/hotspot/share/opto/callnode.hpp
@@ -1101,7 +1101,7 @@ public:
   void compute_MemBar_redundancy(ciMethod* initializer);
   bool is_allocation_MemBar_redundant() { return _is_allocation_MemBar_redundant; }
 
-  Node* make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem);
+  Node* make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem);
 
   NOT_PRODUCT(virtual void dump_spec(outputStream* st) const;)
 };
diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp
index 792384b1ec1..357c91e8eb5 100644
--- a/src/hotspot/share/opto/escape.cpp
+++ b/src/hotspot/share/opto/escape.cpp
@@ -3772,9 +3772,9 @@ Node* ConnectionGraph::get_addp_base(Node *addp) {
   //       | |
   //       AddP  ( base == address )
   //
-  // case #6. Constant Pool, ThreadLocal, CastX2P or
+  // case #6. Constant Pool, ThreadLocal, CastX2P, Klass, OSR buffer buf or
   //          Raw object's field reference:
-  //      {ConP, ThreadLocal, CastX2P, raw Load}
+  //      {ConP, ThreadLocal, CastX2P, raw Load, Parm0}
   //  top   |
   //     \  |
   //     AddP  ( base == top )
@@ -3816,7 +3816,9 @@ Node* ConnectionGraph::get_addp_base(Node *addp) {
       int opcode = uncast_base->Opcode();
       assert(opcode == Op_ConP || opcode == Op_ThreadLocal ||
              opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() ||
+             (_igvn->C->is_osr_compilation() && uncast_base->is_Parm() && uncast_base->as_Parm()->_con == TypeFunc::Parms)||
              (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_rawptr() != nullptr)) ||
+             (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_klassptr() != nullptr)) ||
              is_captured_store_address(addp), "sanity");
     }
   }
@@ -4411,7 +4413,6 @@ void ConnectionGraph::split_unique_types(GrowableArray  &alloc_worklist,
   uint new_index_start = (uint) _compile->num_alias_types();
   VectorSet visited;
   ideal_nodes.clear(); // Reset for use with set_map/get_map.
-  uint unique_old = _compile->unique();
 
   //  Phase 1:  Process possible allocations from alloc_worklist.
   //  Create instance types for the CheckCastPP for allocations where possible.
diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp
index bc8ebaf1869..3d127322439 100644
--- a/src/hotspot/share/opto/graphKit.cpp
+++ b/src/hotspot/share/opto/graphKit.cpp
@@ -2743,7 +2743,7 @@ Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, No
   // will always succeed.  We could leave a dependency behind to ensure this.
 
   // First load the super-klass's check-offset
-  Node *p1 = gvn.transform(new AddPNode(superklass, superklass, gvn.MakeConX(in_bytes(Klass::super_check_offset_offset()))));
+  Node *p1 = gvn.transform(new AddPNode(C->top(), superklass, gvn.MakeConX(in_bytes(Klass::super_check_offset_offset()))));
   Node* m = C->immutable_memory();
   Node *chk_off = gvn.transform(new LoadINode(nullptr, m, p1, gvn.type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered));
   int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset());
@@ -2761,7 +2761,7 @@ Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, No
 #ifdef _LP64
   chk_off_X = gvn.transform(new ConvI2LNode(chk_off_X));
 #endif
-  Node *p2 = gvn.transform(new AddPNode(subklass,subklass,chk_off_X));
+  Node* p2 = gvn.transform(new AddPNode(C->top(), subklass, chk_off_X));
   // For some types like interfaces the following loadKlass is from a 1-word
   // cache which is mutable so can't use immutable memory.  Other
   // types load from the super-class display table which is immutable.
@@ -3598,7 +3598,7 @@ Node* GraphKit::get_layout_helper(Node* klass_node, jint& constant_value) {
     }
   }
   constant_value = Klass::_lh_neutral_value;  // put in a known value
-  Node* lhp = basic_plus_adr(klass_node, klass_node, in_bytes(Klass::layout_helper_offset()));
+  Node* lhp = basic_plus_adr(top(), klass_node, in_bytes(Klass::layout_helper_offset()));
   return make_load(nullptr, lhp, TypeInt::INT, T_INT, MemNode::unordered);
 }
 
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index 2c2a7c020b8..e481833c816 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -3000,7 +3000,7 @@ bool LibraryCallKit::inline_unsafe_allocate() {
     // Note:  The argument might still be an illegal value like
     // Serializable.class or Object[].class.   The runtime will handle it.
     // But we must make an explicit check for initialization.
-    Node* insp = basic_plus_adr(kls, in_bytes(InstanceKlass::init_state_offset()));
+    Node* insp = basic_plus_adr(top(), kls, in_bytes(InstanceKlass::init_state_offset()));
     // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler
     // can generate code to load it as unsigned byte.
     Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::acquire);
@@ -3048,7 +3048,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co
   IdealKit ideal(this);
 
   Node* thread = ideal.thread();
-  Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset()));
+  Node* jt_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_in_vthread_transition_offset()));
   Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset());
   access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED);
   access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED);
@@ -3089,7 +3089,7 @@ bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, cons
     ideal.sync_kit(this);
   } ideal.else_(); {
     Node* thread = ideal.thread();
-    Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset()));
+    Node* jt_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_in_vthread_transition_offset()));
     Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset());
 
     sync_kit(ideal);
@@ -3115,7 +3115,7 @@ bool LibraryCallKit::inline_native_notify_jvmti_sync() {
     // unconditionally update the is_disable_suspend bit in current JavaThread
     Node* thread = ideal.thread();
     Node* arg = _gvn.transform(argument(0)); // argument for notification
-    Node* addr = basic_plus_adr(thread, in_bytes(JavaThread::is_disable_suspend_offset()));
+    Node* addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_disable_suspend_offset()));
     const TypePtr *addr_type = _gvn.type(addr)->isa_ptr();
 
     sync_kit(ideal);
@@ -3689,7 +3689,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) {
   IfNode* iff_thread_not_equal_carrierThread =
     create_and_map_if(control(), test_thread_not_equal_carrierThread, PROB_FAIR, COUNT_UNKNOWN);
 
-  Node* vthread_offset = basic_plus_adr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_OFFSET_JFR));
+  Node* vthread_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_OFFSET_JFR));
 
   // False branch, is carrierThread.
   Node* thread_equal_carrierThread = _gvn.transform(new IfFalseNode(iff_thread_not_equal_carrierThread));
@@ -3714,7 +3714,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) {
   Node* tid = load_field_from_object(thread, "tid", "J");
 
   // Store the vthread tid to the jfr thread local.
-  Node* thread_id_offset = basic_plus_adr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_ID_OFFSET_JFR));
+  Node* thread_id_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_ID_OFFSET_JFR));
   Node* tid_memory = store_to_memory(control(), thread_id_offset, tid, T_LONG, MemNode::unordered, true);
 
   // Branch is_excluded to conditionalize updating the epoch .
@@ -3736,7 +3736,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) {
   Node* epoch = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(epoch_mask)));
 
   // Store the vthread epoch to the jfr thread local.
-  Node* vthread_epoch_offset = basic_plus_adr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR));
+  Node* vthread_epoch_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR));
   Node* included_memory = store_to_memory(control(), vthread_epoch_offset, epoch, T_CHAR, MemNode::unordered, true);
 
   RegionNode* excluded_rgn = new RegionNode(PATH_LIMIT);
@@ -3759,7 +3759,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) {
   set_all_memory(excluded_mem);
 
   // Store the vthread exclusion state to the jfr thread local.
-  Node* thread_local_excluded_offset = basic_plus_adr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EXCLUDED_OFFSET_JFR));
+  Node* thread_local_excluded_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EXCLUDED_OFFSET_JFR));
   store_to_memory(control(), thread_local_excluded_offset, _gvn.transform(exclusion), T_BOOLEAN, MemNode::unordered, true);
 
   // Store release
@@ -3814,7 +3814,7 @@ bool LibraryCallKit::inline_native_setCurrentThread() {
 
   // Change the _monitor_owner_id of the JavaThread
   Node* tid = load_field_from_object(arr, "tid", "J");
-  Node* monitor_owner_id_offset = basic_plus_adr(thread, in_bytes(JavaThread::monitor_owner_id_offset()));
+  Node* monitor_owner_id_offset = basic_plus_adr(top(), thread, in_bytes(JavaThread::monitor_owner_id_offset()));
   store_to_memory(control(), monitor_owner_id_offset, tid, T_LONG, MemNode::unordered, true);
 
   JFR_ONLY(extend_setCurrentThread(thread, arr);)
@@ -3956,7 +3956,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) {
 //---------------------------load_mirror_from_klass----------------------------
 // Given a klass oop, load its java mirror (a java.lang.Class oop).
 Node* LibraryCallKit::load_mirror_from_klass(Node* klass) {
-  Node* p = basic_plus_adr(klass, in_bytes(Klass::java_mirror_offset()));
+  Node* p = basic_plus_adr(top(), klass, in_bytes(Klass::java_mirror_offset()));
   Node* load = make_load(nullptr, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered);
   // mirror = ((OopHandle)mirror)->resolve();
   return access_load(load, TypeInstPtr::MIRROR, T_OBJECT, IN_NATIVE);
@@ -3996,7 +3996,7 @@ Node* LibraryCallKit::generate_klass_flags_guard(Node* kls, int modifier_mask, i
                                                  ByteSize offset, const Type* type, BasicType bt) {
   // Branch around if the given klass has the given modifier bit set.
   // Like generate_guard, adds a new path onto the region.
-  Node* modp = basic_plus_adr(kls, in_bytes(offset));
+  Node* modp = basic_plus_adr(top(), kls, in_bytes(offset));
   Node* mods = make_load(nullptr, modp, type, bt, MemNode::unordered);
   Node* mask = intcon(modifier_mask);
   Node* bits = intcon(modifier_bits);
@@ -4130,7 +4130,7 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) {
       phi->add_req(null());
     }
     // If we fall through, it's a plain class.  Get its _super.
-    p = basic_plus_adr(kls, in_bytes(Klass::super_offset()));
+    p = basic_plus_adr(top(), kls, in_bytes(Klass::super_offset()));
     kls = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), p, TypeRawPtr::BOTTOM, TypeInstKlassPtr::OBJECT_OR_NULL));
     null_ctl = top();
     kls = null_check_oop(kls, &null_ctl);
@@ -4668,7 +4668,7 @@ Node* LibraryCallKit::generate_virtual_guard(Node* obj_klass,
   int entry_offset  = in_bytes(Klass::vtable_start_offset()) +
                      vtable_index*vtableEntry::size_in_bytes() +
                      in_bytes(vtableEntry::method_offset());
-  Node* entry_addr  = basic_plus_adr(obj_klass, entry_offset);
+  Node* entry_addr  = basic_plus_adr(top(), obj_klass, entry_offset);
   Node* target_call = make_load(nullptr, entry_addr, TypePtr::NOTNULL, T_ADDRESS, MemNode::unordered);
 
   // Compare the target method with the expected method (e.g., Object.hashCode).
diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
index 4df03714376..56262d226fc 100644
--- a/src/hotspot/share/opto/macro.cpp
+++ b/src/hotspot/share/opto/macro.cpp
@@ -1198,8 +1198,8 @@ bool PhaseMacroExpand::eliminate_boxing_node(CallStaticJavaNode *boxing) {
 }
 
 
-Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) {
-  Node* adr = basic_plus_adr(base, offset);
+Node* PhaseMacroExpand::make_load_raw(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) {
+  Node* adr = basic_plus_adr(top(), base, offset);
   const TypePtr* adr_type = adr->bottom_type()->is_ptr();
   Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt, MemNode::unordered);
   transform_later(value);
@@ -1207,8 +1207,8 @@ Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset,
 }
 
 
-Node* PhaseMacroExpand::make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) {
-  Node* adr = basic_plus_adr(base, offset);
+Node* PhaseMacroExpand::make_store_raw(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) {
+  Node* adr = basic_plus_adr(top(), base, offset);
   mem = StoreNode::make(_igvn, ctl, mem, adr, nullptr, value, bt, MemNode::unordered);
   transform_later(mem);
   return mem;
@@ -1753,20 +1753,20 @@ PhaseMacroExpand::initialize_object(AllocateNode* alloc,
                                     Node* size_in_bytes) {
   InitializeNode* init = alloc->initialization();
   // Store the klass & mark bits
-  Node* mark_node = alloc->make_ideal_mark(&_igvn, object, control, rawmem);
+  Node* mark_node = alloc->make_ideal_mark(&_igvn, control, rawmem);
   if (!mark_node->is_Con()) {
     transform_later(mark_node);
   }
-  rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, TypeX_X->basic_type());
+  rawmem = make_store_raw(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, TypeX_X->basic_type());
 
   if (!UseCompactObjectHeaders) {
-    rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_METADATA);
+    rawmem = make_store_raw(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_METADATA);
   }
   int header_size = alloc->minimum_header_size();  // conservatively small
 
   // Array length
   if (length != nullptr) {         // Arrays need length field
-    rawmem = make_store(control, rawmem, object, arrayOopDesc::length_offset_in_bytes(), length, T_INT);
+    rawmem = make_store_raw(control, rawmem, object, arrayOopDesc::length_offset_in_bytes(), length, T_INT);
     // conservatively small header size:
     header_size = arrayOopDesc::base_offset_in_bytes(T_BYTE);
     if (_igvn.type(klass_node)->isa_aryklassptr()) {   // we know the exact header size in most cases:
@@ -1792,6 +1792,7 @@ PhaseMacroExpand::initialize_object(AllocateNode* alloc,
     if (!(UseTLAB && ZeroTLAB)) {
       rawmem = ClearArrayNode::clear_memory(control, rawmem, object,
                                             header_size, size_in_bytes,
+                                            true,
                                             &_igvn);
     }
   } else {
@@ -1946,7 +1947,7 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false,
       uint step_size = AllocatePrefetchStepSize;
       uint distance = AllocatePrefetchDistance;
       for ( intx i = 0; i < lines; i++ ) {
-        prefetch_adr = new AddPNode( old_eden_top, new_eden_top,
+        prefetch_adr = new AddPNode( top(), new_eden_top,
                                             _igvn.MakeConX(distance) );
         transform_later(prefetch_adr);
         prefetch = new PrefetchAllocationNode( i_o, prefetch_adr );
diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp
index c899bed567c..0f8d8fb5172 100644
--- a/src/hotspot/share/opto/macro.hpp
+++ b/src/hotspot/share/opto/macro.hpp
@@ -58,10 +58,10 @@ public:
     _igvn.register_new_node_with_optimizer(n);
     return n;
   }
-  Node* make_load( Node* ctl, Node* mem, Node* base, int offset,
-                   const Type* value_type, BasicType bt);
-  Node* make_store(Node* ctl, Node* mem, Node* base, int offset,
-                   Node* value, BasicType bt);
+  Node* make_load_raw(Node* ctl, Node* mem, Node* base, int offset,
+                      const Type* value_type, BasicType bt);
+  Node* make_store_raw(Node* ctl, Node* mem, Node* base, int offset,
+                       Node* value, BasicType bt);
 
   Node* make_leaf_call(Node* ctrl, Node* mem,
                        const TypeFunc* call_type, address call_addr,
@@ -144,11 +144,10 @@ private:
                             Node* slice_idx,
                             Node* slice_len,
                             Node* dest_size);
-  bool generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io,
+  bool generate_block_arraycopy(Node** ctrl, MergeMemNode** mem,
                                 const TypePtr* adr_type,
                                 BasicType basic_elem_type,
-                                AllocateNode* alloc,
-                                Node* src,  Node* src_offset,
+                                Node* src, Node* src_offset,
                                 Node* dest, Node* dest_offset,
                                 Node* dest_size, bool dest_uninitialized);
   MergeMemNode* generate_slow_arraycopy(ArrayCopyNode *ac,
diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp
index fb4a1842a36..0719ffc45a5 100644
--- a/src/hotspot/share/opto/macroArrayCopy.cpp
+++ b/src/hotspot/share/opto/macroArrayCopy.cpp
@@ -384,7 +384,6 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
     transform_later(slow_region);
   }
 
-  Node* original_dest = dest;
   bool  dest_needs_zeroing   = false;
   bool  acopy_to_uninitialized = false;
 
@@ -424,7 +423,6 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
     // No zeroing elimination needed here.
     alloc                  = nullptr;
     acopy_to_uninitialized = false;
-    //original_dest        = dest;
     //dest_needs_zeroing   = false;
   }
 
@@ -557,10 +555,9 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
         MergeMemNode* local_mem = MergeMemNode::make(mem);
         transform_later(local_mem);
 
-        didit = generate_block_arraycopy(&local_ctrl, &local_mem, local_io,
-                                         adr_type, basic_elem_type, alloc,
-                                         src, src_offset, dest, dest_offset,
-                                         dest_size, acopy_to_uninitialized);
+        didit = generate_block_arraycopy(&local_ctrl, &local_mem, adr_type,
+                                         basic_elem_type, src, src_offset,
+                                         dest, dest_offset, dest_size, acopy_to_uninitialized);
         if (didit) {
           // Present the results of the block-copying fast call.
           result_region->init_req(bcopy_path, local_ctrl);
@@ -641,7 +638,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
 
         // (At this point we can assume disjoint_bases, since types differ.)
         int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset());
-        Node* p1 = basic_plus_adr(dest_klass, ek_offset);
+        Node* p1 = basic_plus_adr(top(), dest_klass, ek_offset);
         Node* n1 = LoadKlassNode::make(_igvn, C->immutable_memory(), p1, TypeRawPtr::BOTTOM);
         Node* dest_elem_klass = transform_later(n1);
         Node* cv = generate_checkcast_arraycopy(&local_ctrl, &local_mem,
@@ -918,12 +915,12 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
   if (start_con >= 0 && end_con >= 0) {
     // Constant start and end.  Simple.
     mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
-                                       start_con, end_con, &_igvn);
+                                       start_con, end_con, false, &_igvn);
   } else if (start_con >= 0 && dest_size != top()) {
     // Constant start, pre-rounded end after the tail of the array.
     Node* end = dest_size;
     mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
-                                       start_con, end, &_igvn);
+                                       start_con, end, false, &_igvn);
   } else if (start_con >= 0 && slice_len != top()) {
     // Constant start, non-constant end.  End needs rounding up.
     // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8)
@@ -936,7 +933,7 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
     end = transform_later(new AddXNode(end, MakeConX(end_base)) );
     end = transform_later(new AndXNode(end, MakeConX(~end_round)) );
     mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
-                                       start_con, end, &_igvn);
+                                       start_con, end, false, &_igvn);
   } else if (start_con < 0 && dest_size != top()) {
     // Non-constant start, pre-rounded end after the tail of the array.
     // This is almost certainly a "round-to-end" operation.
@@ -970,7 +967,7 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
     }
     Node* end = dest_size; // pre-rounded
     mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
-                                       start, end, &_igvn);
+                                       start, end, false, &_igvn);
   } else {
     // Non-constant start, unrounded non-constant end.
     // (Nobody zeroes a random midsection of an array using this routine.)
@@ -981,11 +978,10 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
   merge_mem->set_memory_at(alias_idx, mem);
 }
 
-bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io,
+bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem,
                                                 const TypePtr* adr_type,
                                                 BasicType basic_elem_type,
-                                                AllocateNode* alloc,
-                                                Node* src,  Node* src_offset,
+                                                Node* src, Node* src_offset,
                                                 Node* dest, Node* dest_offset,
                                                 Node* dest_size, bool dest_uninitialized) {
   // See if there is an advantage from block transfer.
@@ -1133,7 +1129,7 @@ Node* PhaseMacroExpand::generate_checkcast_arraycopy(Node** ctrl, MergeMemNode**
   // look in each non-null element's class, at the desired klass's
   // super_check_offset, for the desired klass.
   int sco_offset = in_bytes(Klass::super_check_offset_offset());
-  Node* p3 = basic_plus_adr(dest_elem_klass, sco_offset);
+  Node* p3 = basic_plus_adr(top(), dest_elem_klass, sco_offset);
   Node* n3 = new LoadINode(nullptr, *mem /*memory(p3)*/, p3, _igvn.type(p3)->is_ptr(), TypeInt::INT, MemNode::unordered);
   Node* check_offset = ConvI2X(transform_later(n3));
   Node* check_value  = dest_elem_klass;
diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp
index 0d4fb6791a4..7f152eddd65 100644
--- a/src/hotspot/share/opto/memnode.cpp
+++ b/src/hotspot/share/opto/memnode.cpp
@@ -2563,7 +2563,13 @@ Node* LoadNode::klass_identity_common(PhaseGVN* phase) {
            ) {
           int mirror_field = in_bytes(Klass::java_mirror_offset());
           if (tkls->offset() == mirror_field) {
-            return adr2->in(AddPNode::Base);
+#ifdef ASSERT
+            const TypeKlassPtr* tkls2 = phase->type(adr2->in(AddPNode::Address))->is_klassptr();
+            assert(tkls2->offset() == 0, "not a load of java_mirror");
+#endif
+            assert(adr2->in(AddPNode::Base)->is_top(), "not an off heap load");
+            assert(adr2->in(AddPNode::Offset)->find_intptr_t_con(-1) == in_bytes(Klass::java_mirror_offset()), "incorrect offset");
+            return adr2->in(AddPNode::Address);
           }
         }
       }
@@ -4112,18 +4118,27 @@ bool ClearArrayNode::step_through(Node** np, uint instance_id, PhaseValues* phas
   return true;
 }
 
+Node* ClearArrayNode::make_address(Node* dest, Node* offset, bool raw_base, PhaseGVN* phase) {
+  Node* base = dest;
+  if (raw_base) {
+    // May be called as part of the initialization of a just allocated object
+    base = phase->C->top();
+  }
+  return phase->transform(new AddPNode(base, dest, offset));
+}
+
 //----------------------------clear_memory-------------------------------------
 // Generate code to initialize object storage to zero.
 Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
                                    intptr_t start_offset,
                                    Node* end_offset,
+                                   bool raw_base,
                                    PhaseGVN* phase) {
   intptr_t offset = start_offset;
 
   int unit = BytesPerLong;
   if ((offset % unit) != 0) {
-    Node* adr = new AddPNode(dest, dest, phase->MakeConX(offset));
-    adr = phase->transform(adr);
+    Node* adr = make_address(dest, phase->MakeConX(offset), raw_base, phase);
     const TypePtr* atp = TypeRawPtr::BOTTOM;
     mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
     mem = phase->transform(mem);
@@ -4132,12 +4147,13 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
   assert((offset % unit) == 0, "");
 
   // Initialize the remaining stuff, if any, with a ClearArray.
-  return clear_memory(ctl, mem, dest, phase->MakeConX(offset), end_offset, phase);
+  return clear_memory(ctl, mem, dest, phase->MakeConX(offset), end_offset, raw_base, phase);
 }
 
 Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
                                    Node* start_offset,
                                    Node* end_offset,
+                                   bool raw_base,
                                    PhaseGVN* phase) {
   if (start_offset == end_offset) {
     // nothing to do
@@ -4157,7 +4173,7 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
 
   // Bulk clear double-words
   Node* zsize = phase->transform(new SubXNode(zend, zbase) );
-  Node* adr = phase->transform(new AddPNode(dest, dest, start_offset) );
+  Node* adr = make_address(dest, start_offset, raw_base, phase);
   mem = new ClearArrayNode(ctl, mem, zsize, adr, false);
   return phase->transform(mem);
 }
@@ -4165,6 +4181,7 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
 Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
                                    intptr_t start_offset,
                                    intptr_t end_offset,
+                                   bool raw_base,
                                    PhaseGVN* phase) {
   if (start_offset == end_offset) {
     // nothing to do
@@ -4178,11 +4195,10 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
   }
   if (done_offset > start_offset) {
     mem = clear_memory(ctl, mem, dest,
-                       start_offset, phase->MakeConX(done_offset), phase);
+                       start_offset, phase->MakeConX(done_offset), raw_base, phase);
   }
   if (done_offset < end_offset) { // emit the final 32-bit store
-    Node* adr = new AddPNode(dest, dest, phase->MakeConX(done_offset));
-    adr = phase->transform(adr);
+    Node* adr = make_address(dest, phase->MakeConX(done_offset), raw_base, phase);
     const TypePtr* atp = TypeRawPtr::BOTTOM;
     mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
     mem = phase->transform(mem);
@@ -5389,6 +5405,7 @@ Node* InitializeNode::complete_stores(Node* rawctl, Node* rawmem, Node* rawptr,
         zeroes_done = align_down(zeroes_done, BytesPerInt);
         rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr,
                                               zeroes_done, zeroes_needed,
+                                              true,
                                               phase);
         zeroes_done = zeroes_needed;
         if (zsize > InitArrayShortSize && ++big_init_gaps > 2)
@@ -5447,7 +5464,7 @@ Node* InitializeNode::complete_stores(Node* rawctl, Node* rawmem, Node* rawptr,
     }
     if (zeroes_done < size_limit) {
       rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr,
-                                            zeroes_done, size_in_bytes, phase);
+                                            zeroes_done, size_in_bytes, true, phase);
     }
   }
 
diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp
index e84556528e6..249df51beb9 100644
--- a/src/hotspot/share/opto/memnode.hpp
+++ b/src/hotspot/share/opto/memnode.hpp
@@ -1072,6 +1072,7 @@ public:
 class ClearArrayNode: public Node {
 private:
   bool _is_large;
+  static Node* make_address(Node* dest, Node* offset, bool raw_base, PhaseGVN* phase);
 public:
   ClearArrayNode( Node *ctrl, Node *arymem, Node *word_cnt, Node *base, bool is_large)
     : Node(ctrl,arymem,word_cnt,base), _is_large(is_large) {
@@ -1099,14 +1100,17 @@ public:
   static Node* clear_memory(Node* control, Node* mem, Node* dest,
                             intptr_t start_offset,
                             intptr_t end_offset,
+                            bool raw_base,
                             PhaseGVN* phase);
   static Node* clear_memory(Node* control, Node* mem, Node* dest,
                             intptr_t start_offset,
                             Node* end_offset,
+                            bool raw_base,
                             PhaseGVN* phase);
   static Node* clear_memory(Node* control, Node* mem, Node* dest,
                             Node* start_offset,
                             Node* end_offset,
+                            bool raw_base,
                             PhaseGVN* phase);
   // Return allocation input memory edge if it is different instance
   // or itself if it is the one we are looking for.
diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp
index 19b302a8924..397a7796f88 100644
--- a/src/hotspot/share/opto/parse.hpp
+++ b/src/hotspot/share/opto/parse.hpp
@@ -442,7 +442,7 @@ class Parse : public GraphKit {
   SafePointNode* create_entry_map();
 
   // OSR helpers
-  Node *fetch_interpreter_state(int index, BasicType bt, Node *local_addrs, Node *local_addrs_base);
+  Node *fetch_interpreter_state(int index, BasicType bt, Node* local_addrs);
   Node* check_interpreter_type(Node* l, const Type* type, SafePointNode* &bad_type_exit);
   void  load_interpreter_state(Node* osr_buf);
 
diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp
index 6122f7e7bfc..2f699650037 100644
--- a/src/hotspot/share/opto/parse1.cpp
+++ b/src/hotspot/share/opto/parse1.cpp
@@ -103,10 +103,9 @@ void Parse::print_statistics() {
 // on stack replacement.
 Node *Parse::fetch_interpreter_state(int index,
                                      BasicType bt,
-                                     Node *local_addrs,
-                                     Node *local_addrs_base) {
+                                     Node* local_addrs) {
   Node *mem = memory(Compile::AliasIdxRaw);
-  Node *adr = basic_plus_adr( local_addrs_base, local_addrs, -index*wordSize );
+  Node *adr = basic_plus_adr(top(), local_addrs, -index*wordSize);
   Node *ctl = control();
 
   // Very similar to LoadNode::make, except we handle un-aligned longs and
@@ -121,7 +120,7 @@ Node *Parse::fetch_interpreter_state(int index,
   case T_DOUBLE: {
     // Since arguments are in reverse order, the argument address 'adr'
     // refers to the back half of the long/double.  Recompute adr.
-    adr = basic_plus_adr(local_addrs_base, local_addrs, -(index+1)*wordSize);
+    adr = basic_plus_adr(top(), local_addrs, -(index+1)*wordSize);
     if (Matcher::misaligned_doubles_ok) {
       l = (bt == T_DOUBLE)
         ? (Node*)new LoadDNode(ctl, mem, adr, TypeRawPtr::BOTTOM, Type::DOUBLE, MemNode::unordered)
@@ -220,7 +219,7 @@ void Parse::load_interpreter_state(Node* osr_buf) {
   // Commute monitors from interpreter frame to compiler frame.
   assert(jvms()->monitor_depth() == 0, "should be no active locks at beginning of osr");
   int mcnt = osr_block->flow()->monitor_count();
-  Node *monitors_addr = basic_plus_adr(osr_buf, osr_buf, (max_locals+mcnt*2-1)*wordSize);
+  Node *monitors_addr = basic_plus_adr(top(), osr_buf, (max_locals+mcnt*2-1)*wordSize);
   for (index = 0; index < mcnt; index++) {
     // Make a BoxLockNode for the monitor.
     BoxLockNode* osr_box = new BoxLockNode(next_monitor());
@@ -241,9 +240,9 @@ void Parse::load_interpreter_state(Node* osr_buf) {
     // Displaced headers and locked objects are interleaved in the
     // temp OSR buffer.  We only copy the locked objects out here.
     // Fetch the locked object from the OSR temp buffer and copy to our fastlock node.
-    Node *lock_object = fetch_interpreter_state(index*2, T_OBJECT, monitors_addr, osr_buf);
+    Node *lock_object = fetch_interpreter_state(index*2, T_OBJECT, monitors_addr);
     // Try and copy the displaced header to the BoxNode
-    Node *displaced_hdr = fetch_interpreter_state((index*2) + 1, T_ADDRESS, monitors_addr, osr_buf);
+    Node *displaced_hdr = fetch_interpreter_state((index*2) + 1, T_ADDRESS, monitors_addr);
 
 
     store_to_memory(control(), box, displaced_hdr, T_ADDRESS, MemNode::unordered);
@@ -271,7 +270,7 @@ void Parse::load_interpreter_state(Node* osr_buf) {
   }
 
   // Extract the needed locals from the interpreter frame.
-  Node *locals_addr = basic_plus_adr(osr_buf, osr_buf, (max_locals-1)*wordSize);
+  Node *locals_addr = basic_plus_adr(top(), osr_buf, (max_locals-1)*wordSize);
 
   // find all the locals that the interpreter thinks contain live oops
   const ResourceBitMap live_oops = method()->live_local_oops_at_bci(osr_bci());
@@ -318,7 +317,7 @@ void Parse::load_interpreter_state(Node* osr_buf) {
       // really for T_OBJECT types so correct it.
       bt = T_OBJECT;
     }
-    Node *value = fetch_interpreter_state(index, bt, locals_addr, osr_buf);
+    Node *value = fetch_interpreter_state(index, bt, locals_addr);
     set_local(index, value);
   }
 
@@ -2128,7 +2127,7 @@ void Parse::call_register_finalizer() {
   Node* klass_addr = basic_plus_adr( receiver, receiver, oopDesc::klass_offset_in_bytes() );
   Node* klass = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), klass_addr, TypeInstPtr::KLASS));
 
-  Node* access_flags_addr = basic_plus_adr(klass, klass, in_bytes(Klass::misc_flags_offset()));
+  Node* access_flags_addr = basic_plus_adr(top(), klass, in_bytes(Klass::misc_flags_offset()));
   Node* access_flags = make_load(nullptr, access_flags_addr, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered);
 
   Node* mask  = _gvn.transform(new AndINode(access_flags, intcon(KlassFlags::_misc_has_finalizer)));
diff --git a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp
index 1a1cea05454..2e8fca68a8d 100644
--- a/src/hotspot/share/opto/parseHelper.cpp
+++ b/src/hotspot/share/opto/parseHelper.cpp
@@ -220,7 +220,7 @@ void Parse::array_store_check() {
 
   // Extract the array element class
   int element_klass_offset = in_bytes(ObjArrayKlass::element_klass_offset());
-  Node* p2 = basic_plus_adr(array_klass, array_klass, element_klass_offset);
+  Node* p2 = basic_plus_adr(top(), array_klass, element_klass_offset);
   Node* a_e_klass = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), p2, tak));
   assert(array_klass->is_Con() == a_e_klass->is_Con() || StressReflectiveCode, "a constant array type must come with a constant element type");
 
diff --git a/src/hotspot/share/opto/subtypenode.cpp b/src/hotspot/share/opto/subtypenode.cpp
index b58194b87d2..8e4c7d829a7 100644
--- a/src/hotspot/share/opto/subtypenode.cpp
+++ b/src/hotspot/share/opto/subtypenode.cpp
@@ -182,7 +182,7 @@ bool SubTypeCheckNode::verify(PhaseGVN* phase) {
         return verify_helper(phase, load_klass(phase), cached_t);
       }
       case Compile::SSC_full_test: {
-        Node* p1 = phase->transform(new AddPNode(superklass, superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset()))));
+        Node* p1 = phase->transform(new AddPNode(C->top(), superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset()))));
         Node* chk_off = phase->transform(new LoadINode(nullptr, C->immutable_memory(), p1, phase->type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered));
         record_for_cleanup(chk_off, phase);
 
@@ -194,7 +194,7 @@ bool SubTypeCheckNode::verify(PhaseGVN* phase) {
 #ifdef _LP64
           chk_off_X = phase->transform(new ConvI2LNode(chk_off_X));
 #endif
-          Node* p2 = phase->transform(new AddPNode(subklass, subklass, chk_off_X));
+          Node* p2 = phase->transform(new AddPNode(C->top(), subklass, chk_off_X));
           Node* nkls = phase->transform(LoadKlassNode::make(*phase, C->immutable_memory(), p2, phase->type(p2)->is_ptr(), TypeInstKlassPtr::OBJECT_OR_NULL));
 
           return verify_helper(phase, nkls, cached_t);

From 0ad81fbd161edbc8479e5af5c0f8d6098f6c72d1 Mon Sep 17 00:00:00 2001
From: Thomas Schatzl 
Date: Thu, 22 Jan 2026 11:04:09 +0000
Subject: [PATCH 112/126] 8375541: G1: Race in
 G1BarrierSet::write_ref_field_post()

Reviewed-by: iwalulya, sjohanss, shade
---
 src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp
index ee2c1450d9b..794e5db0634 100644
--- a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp
@@ -70,7 +70,11 @@ inline void G1BarrierSet::write_ref_field_pre(T* field) {
 
 template 
 inline void G1BarrierSet::write_ref_field_post(T* field) {
-  volatile CardValue* byte = _card_table->byte_for(field);
+  // Make sure that the card table reference is read only once. Otherwise the compiler
+  // might reload that value in the two accesses below, that could cause writes to
+  // the wrong card table.
+  CardTable* card_table = AtomicAccess::load(&_card_table);
+  CardValue* byte = card_table->byte_for(field);
   if (*byte == G1CardTable::clean_card_val()) {
     *byte = G1CardTable::dirty_card_val();
   }

From 66e950e9b6414617952d22200831be5b0cafee85 Mon Sep 17 00:00:00 2001
From: Ivan Walulya 
Date: Thu, 22 Jan 2026 11:07:42 +0000
Subject: [PATCH 113/126] 8340470: G1: Adopt PartialArrayState to consolidate
 marking stack in Full GC

Co-authored-by: Stefan Johansson 
Reviewed-by: sjohanss, tschatzl
---
 src/hotspot/share/gc/g1/g1FullCollector.cpp   | 27 +++--
 src/hotspot/share/gc/g1/g1FullCollector.hpp   |  9 +-
 src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp  |  9 +-
 src/hotspot/share/gc/g1/g1FullGCMarker.cpp    | 46 ++++++---
 src/hotspot/share/gc/g1/g1FullGCMarker.hpp    | 47 ++++-----
 .../share/gc/g1/g1FullGCMarker.inline.hpp     | 98 ++++++-------------
 .../share/gc/g1/g1FullGCOopClosures.cpp       |  2 +-
 .../share/gc/g1/g1FullGCOopClosures.hpp       |  4 +-
 8 files changed, 110 insertions(+), 132 deletions(-)

diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp
index 5ca5dcef001..7395df01760 100644
--- a/src/hotspot/share/gc/g1/g1FullCollector.cpp
+++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp
@@ -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
@@ -116,8 +116,8 @@ G1FullCollector::G1FullCollector(G1CollectedHeap* heap,
     _num_workers(calc_active_workers()),
     _has_compaction_targets(false),
     _has_humongous(false),
-    _oop_queue_set(_num_workers),
-    _array_queue_set(_num_workers),
+    _marking_task_queues(_num_workers),
+    _partial_array_state_manager(nullptr),
     _preserved_marks_set(true),
     _serial_compaction_point(this, nullptr),
     _humongous_compaction_point(this, nullptr),
@@ -140,23 +140,31 @@ G1FullCollector::G1FullCollector(G1CollectedHeap* heap,
     _compaction_tops[j] = nullptr;
   }
 
+  _partial_array_state_manager = new PartialArrayStateManager(_num_workers);
+
   for (uint i = 0; i < _num_workers; i++) {
     _markers[i] = new G1FullGCMarker(this, i, _live_stats);
     _compaction_points[i] = new G1FullGCCompactionPoint(this, _preserved_marks_set.get(i));
-    _oop_queue_set.register_queue(i, marker(i)->oop_stack());
-    _array_queue_set.register_queue(i, marker(i)->objarray_stack());
+    _marking_task_queues.register_queue(i, marker(i)->task_queue());
   }
+
   _serial_compaction_point.set_preserved_stack(_preserved_marks_set.get(0));
   _humongous_compaction_point.set_preserved_stack(_preserved_marks_set.get(0));
   _region_attr_table.initialize(heap->reserved(), G1HeapRegion::GrainBytes);
 }
 
+PartialArrayStateManager* G1FullCollector::partial_array_state_manager() const {
+  return _partial_array_state_manager;
+}
+
 G1FullCollector::~G1FullCollector() {
   for (uint i = 0; i < _num_workers; i++) {
     delete _markers[i];
     delete _compaction_points[i];
   }
 
+  delete _partial_array_state_manager;
+
   FREE_C_HEAP_ARRAY(G1FullGCMarker*, _markers);
   FREE_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _compaction_points);
   FREE_C_HEAP_ARRAY(HeapWord*, _compaction_tops);
@@ -279,8 +287,8 @@ public:
     uint index = (_tm == RefProcThreadModel::Single) ? 0 : worker_id;
     G1FullKeepAliveClosure keep_alive(_collector.marker(index));
     BarrierEnqueueDiscoveredFieldClosure enqueue;
-    G1FollowStackClosure* complete_gc = _collector.marker(index)->stack_closure();
-    _rp_task->rp_work(worker_id, &is_alive, &keep_alive, &enqueue, complete_gc);
+    G1MarkStackClosure* complete_marking = _collector.marker(index)->stack_closure();
+    _rp_task->rp_work(worker_id, &is_alive, &keep_alive, &enqueue, complete_marking);
   }
 };
 
@@ -302,7 +310,7 @@ void G1FullCollector::phase1_mark_live_objects() {
     const ReferenceProcessorStats& stats = reference_processor()->process_discovered_references(task, _heap->workers(), pt);
     scope()->tracer()->report_gc_reference_stats(stats);
     pt.print_all_references();
-    assert(marker(0)->oop_stack()->is_empty(), "Should be no oops on the stack");
+    assert(marker(0)->task_queue()->is_empty(), "Should be no oops on the stack");
   }
 
   {
@@ -328,8 +336,7 @@ void G1FullCollector::phase1_mark_live_objects() {
     scope()->tracer()->report_object_count_after_gc(&_is_alive, _heap->workers());
   }
 #if TASKQUEUE_STATS
-  oop_queue_set()->print_and_reset_taskqueue_stats("Oop Queue");
-  array_queue_set()->print_and_reset_taskqueue_stats("ObjArrayOop Queue");
+  marking_task_queues()->print_and_reset_taskqueue_stats("Marking Task Queue");
 #endif
 }
 
diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp
index 1fb3af17032..28ecffad944 100644
--- a/src/hotspot/share/gc/g1/g1FullCollector.hpp
+++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp
@@ -79,8 +79,8 @@ class G1FullCollector : StackObj {
   bool                      _has_humongous;
   G1FullGCMarker**          _markers;
   G1FullGCCompactionPoint** _compaction_points;
-  OopQueueSet               _oop_queue_set;
-  ObjArrayTaskQueueSet      _array_queue_set;
+  G1MarkTasksQueueSet       _marking_task_queues;
+  PartialArrayStateManager* _partial_array_state_manager;
   PreservedMarksSet         _preserved_marks_set;
   G1FullGCCompactionPoint   _serial_compaction_point;
   G1FullGCCompactionPoint   _humongous_compaction_point;
@@ -113,8 +113,7 @@ public:
   uint                     workers() { return _num_workers; }
   G1FullGCMarker*          marker(uint id) { return _markers[id]; }
   G1FullGCCompactionPoint* compaction_point(uint id) { return _compaction_points[id]; }
-  OopQueueSet*             oop_queue_set() { return &_oop_queue_set; }
-  ObjArrayTaskQueueSet*    array_queue_set() { return &_array_queue_set; }
+  G1MarkTasksQueueSet*     marking_task_queues() { return &_marking_task_queues; }
   PreservedMarksSet*       preserved_mark_set() { return &_preserved_marks_set; }
   G1FullGCCompactionPoint* serial_compaction_point() { return &_serial_compaction_point; }
   G1FullGCCompactionPoint* humongous_compaction_point() { return &_humongous_compaction_point; }
@@ -125,6 +124,8 @@ public:
     return _live_stats[region_index].live_words();
   }
 
+  PartialArrayStateManager* partial_array_state_manager() const;
+
   void before_marking_update_attribute_table(G1HeapRegion* hr);
 
   inline bool is_compacting(oop obj) const;
diff --git a/src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp b/src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp
index 52b0d04a500..f14e1108db8 100644
--- a/src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp
@@ -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
@@ -34,7 +34,7 @@
 G1FullGCMarkTask::G1FullGCMarkTask(G1FullCollector* collector) :
     G1FullGCTask("G1 Parallel Marking Task", collector),
     _root_processor(G1CollectedHeap::heap(), collector->workers()),
-    _terminator(collector->workers(), collector->array_queue_set()) {
+    _terminator(collector->workers(), collector->marking_task_queues()) {
 }
 
 void G1FullGCMarkTask::work(uint worker_id) {
@@ -54,10 +54,9 @@ void G1FullGCMarkTask::work(uint worker_id) {
   }
 
   // Mark stack is populated, now process and drain it.
-  marker->complete_marking(collector()->oop_queue_set(), collector()->array_queue_set(), &_terminator);
+  marker->complete_marking(collector()->marking_task_queues(), &_terminator);
 
   // This is the point where the entire marking should have completed.
-  assert(marker->oop_stack()->is_empty(), "Marking should have completed");
-  assert(marker->objarray_stack()->is_empty(), "Array marking should have completed");
+  assert(marker->task_queue()->is_empty(), "Marking should have completed");
   log_task("Marking task", worker_id, start);
 }
diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp
index aa8f12a2d1b..2b0b78ac1ce 100644
--- a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp
@@ -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,6 +25,8 @@
 #include "classfile/classLoaderData.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "gc/g1/g1FullGCMarker.inline.hpp"
+#include "gc/shared/partialArraySplitter.inline.hpp"
+#include "gc/shared/partialArrayState.hpp"
 #include "gc/shared/referenceProcessor.hpp"
 #include "gc/shared/taskTerminator.hpp"
 #include "gc/shared/verifyOption.hpp"
@@ -36,8 +38,8 @@ G1FullGCMarker::G1FullGCMarker(G1FullCollector* collector,
     _collector(collector),
     _worker_id(worker_id),
     _bitmap(collector->mark_bitmap()),
-    _oop_stack(),
-    _objarray_stack(),
+    _task_queue(),
+    _partial_array_splitter(collector->partial_array_state_manager(), collector->workers(), ObjArrayMarkingStride),
     _mark_closure(worker_id, this, ClassLoaderData::_claim_stw_fullgc_mark, G1CollectedHeap::heap()->ref_processor_stw()),
     _stack_closure(this),
     _cld_closure(mark_closure(), ClassLoaderData::_claim_stw_fullgc_mark),
@@ -47,24 +49,36 @@ G1FullGCMarker::G1FullGCMarker(G1FullCollector* collector,
 }
 
 G1FullGCMarker::~G1FullGCMarker() {
-  assert(is_empty(), "Must be empty at this point");
+  assert(is_task_queue_empty(), "Must be empty at this point");
 }
 
-void G1FullGCMarker::complete_marking(OopQueueSet* oop_stacks,
-                                      ObjArrayTaskQueueSet* array_stacks,
+void G1FullGCMarker::process_partial_array(PartialArrayState* state, bool stolen) {
+  // Access state before release by claim().
+  objArrayOop obj_array = objArrayOop(state->source());
+  PartialArraySplitter::Claim claim =
+    _partial_array_splitter.claim(state, task_queue(), stolen);
+  process_array_chunk(obj_array, claim._start, claim._end);
+}
+
+void G1FullGCMarker::start_partial_array_processing(objArrayOop obj) {
+  mark_closure()->do_klass(obj->klass());
+  // Don't push empty arrays to avoid unnecessary work.
+  size_t array_length = obj->length();
+  if (array_length > 0) {
+    size_t initial_chunk_size = _partial_array_splitter.start(task_queue(), obj, nullptr, array_length);
+    process_array_chunk(obj, 0, initial_chunk_size);
+  }
+}
+
+void G1FullGCMarker::complete_marking(G1ScannerTasksQueueSet* task_queues,
                                       TaskTerminator* terminator) {
   do {
-    follow_marking_stacks();
-    ObjArrayTask steal_array;
-    if (array_stacks->steal(_worker_id, steal_array)) {
-      follow_array_chunk(objArrayOop(steal_array.obj()), steal_array.index());
-    } else {
-      oop steal_oop;
-      if (oop_stacks->steal(_worker_id, steal_oop)) {
-        follow_object(steal_oop);
-      }
+    process_marking_stacks();
+    ScannerTask stolen_task;
+    if (task_queues->steal(_worker_id, stolen_task)) {
+      dispatch_task(stolen_task, true);
     }
-  } while (!is_empty() || !terminator->offer_termination());
+  } while (!is_task_queue_empty() || !terminator->offer_termination());
 }
 
 void G1FullGCMarker::flush_mark_stats_cache() {
diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp
index b1b750eae90..5973cc841c5 100644
--- a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp
@@ -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
@@ -28,6 +28,8 @@
 #include "gc/g1/g1FullGCOopClosures.hpp"
 #include "gc/g1/g1OopClosures.hpp"
 #include "gc/g1/g1RegionMarkStatsCache.hpp"
+#include "gc/shared/partialArraySplitter.hpp"
+#include "gc/shared/partialArrayState.hpp"
 #include "gc/shared/stringdedup/stringDedup.hpp"
 #include "gc/shared/taskqueue.hpp"
 #include "memory/iterator.hpp"
@@ -38,16 +40,15 @@
 #include "utilities/growableArray.hpp"
 #include "utilities/stack.hpp"
 
-typedef OverflowTaskQueue                 OopQueue;
-typedef OverflowTaskQueue        ObjArrayTaskQueue;
 
-typedef GenericTaskQueueSet          OopQueueSet;
-typedef GenericTaskQueueSet ObjArrayTaskQueueSet;
 
 class G1CMBitMap;
 class G1FullCollector;
 class TaskTerminator;
 
+typedef OverflowTaskQueue        G1MarkTasksQueue;
+typedef GenericTaskQueueSet G1MarkTasksQueueSet;
+
 class G1FullGCMarker : public CHeapObj {
   G1FullCollector*   _collector;
 
@@ -56,56 +57,50 @@ class G1FullGCMarker : public CHeapObj {
   G1CMBitMap*        _bitmap;
 
   // Mark stack
-  OopQueue           _oop_stack;
-  ObjArrayTaskQueue  _objarray_stack;
+  G1MarkTasksQueue     _task_queue;
+  PartialArraySplitter _partial_array_splitter;
 
   // Marking closures
   G1MarkAndPushClosure  _mark_closure;
-  G1FollowStackClosure  _stack_closure;
+  G1MarkStackClosure    _stack_closure;
   CLDToOopClosure       _cld_closure;
   StringDedup::Requests _string_dedup_requests;
 
 
   G1RegionMarkStatsCache _mark_stats_cache;
 
-  inline bool is_empty();
-  inline void push_objarray(oop obj, size_t index);
+  inline bool is_task_queue_empty();
   inline bool mark_object(oop obj);
 
   // Marking helpers
-  inline void follow_object(oop obj);
-  inline void follow_array(objArrayOop array);
-  inline void follow_array_chunk(objArrayOop array, int index);
+  inline void process_array_chunk(objArrayOop obj, size_t start, size_t end);
+  inline void dispatch_task(const ScannerTask& task, bool stolen);
+  // Start processing the given objArrayOop by first pushing its continuations and
+  // then scanning the first chunk.
+  void start_partial_array_processing(objArrayOop obj);
+  // Process the given continuation.
+  void process_partial_array(PartialArrayState* state, bool stolen);
 
   inline void publish_and_drain_oop_tasks();
-  // Try to publish all contents from the objArray task queue overflow stack to
-  // the shared objArray stack.
-  // Returns true and a valid task if there has not been enough space in the shared
-  // objArray stack, otherwise returns false and the task is invalid.
-  inline bool publish_or_pop_objarray_tasks(ObjArrayTask& task);
-
 public:
   G1FullGCMarker(G1FullCollector* collector,
                  uint worker_id,
                  G1RegionMarkStats* mark_stats);
   ~G1FullGCMarker();
 
-  // Stack getters
-  OopQueue*          oop_stack()       { return &_oop_stack; }
-  ObjArrayTaskQueue* objarray_stack()  { return &_objarray_stack; }
+  G1MarkTasksQueue* task_queue() { return &_task_queue; }
 
   // Marking entry points
   template  inline void mark_and_push(T* p);
 
-  inline void follow_marking_stacks();
-  void complete_marking(OopQueueSet* oop_stacks,
-                        ObjArrayTaskQueueSet* array_stacks,
+  inline void process_marking_stacks();
+  void complete_marking(G1MarkTasksQueueSet* task_queues,
                         TaskTerminator* terminator);
 
   // Closure getters
   CLDToOopClosure*      cld_closure()   { return &_cld_closure; }
   G1MarkAndPushClosure* mark_closure()  { return &_mark_closure; }
-  G1FollowStackClosure* stack_closure() { return &_stack_closure; }
+  G1MarkStackClosure*   stack_closure() { return &_stack_closure; }
 
   // Flush live bytes to regions
   void flush_mark_stats_cache();
diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp
index 398ef046bf5..a6f45abe005 100644
--- a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp
@@ -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
@@ -42,6 +42,7 @@
 #include "oops/access.inline.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "utilities/checkedCast.hpp"
 #include "utilities/debug.hpp"
 
 inline bool G1FullGCMarker::mark_object(oop obj) {
@@ -71,94 +72,55 @@ template  inline void G1FullGCMarker::mark_and_push(T* p) {
   if (!CompressedOops::is_null(heap_oop)) {
     oop obj = CompressedOops::decode_not_null(heap_oop);
     if (mark_object(obj)) {
-      _oop_stack.push(obj);
+      _task_queue.push(ScannerTask(obj));
     }
     assert(_bitmap->is_marked(obj), "Must be marked");
   }
 }
 
-inline bool G1FullGCMarker::is_empty() {
-  return _oop_stack.is_empty() && _objarray_stack.is_empty();
+inline bool G1FullGCMarker::is_task_queue_empty() {
+  return _task_queue.is_empty();
 }
 
-inline void G1FullGCMarker::push_objarray(oop obj, size_t index) {
-  ObjArrayTask task(obj, index);
-  assert(task.is_valid(), "bad ObjArrayTask");
-  _objarray_stack.push(task);
+inline void G1FullGCMarker::process_array_chunk(objArrayOop obj, size_t start, size_t end) {
+  obj->oop_iterate_elements_range(mark_closure(),
+                                  checked_cast(start),
+                                  checked_cast(end));
 }
 
-inline void G1FullGCMarker::follow_array(objArrayOop array) {
-  mark_closure()->do_klass(array->klass());
-  // Don't push empty arrays to avoid unnecessary work.
-  if (array->length() > 0) {
-    push_objarray(array, 0);
-  }
-}
-
-void G1FullGCMarker::follow_array_chunk(objArrayOop array, int index) {
-  const int len = array->length();
-  const int beg_index = index;
-  assert(beg_index < len || len == 0, "index too large");
-
-  const int stride = MIN2(len - beg_index, (int) ObjArrayMarkingStride);
-  const int end_index = beg_index + stride;
-
-  // Push the continuation first to allow more efficient work stealing.
-  if (end_index < len) {
-    push_objarray(array, end_index);
-  }
-
-  array->oop_iterate_elements_range(mark_closure(), beg_index, end_index);
-}
-
-inline void G1FullGCMarker::follow_object(oop obj) {
-  assert(_bitmap->is_marked(obj), "should be marked");
-  if (obj->is_objArray()) {
-    // Handle object arrays explicitly to allow them to
-    // be split into chunks if needed.
-    follow_array((objArrayOop)obj);
+inline void G1FullGCMarker::dispatch_task(const ScannerTask& task, bool stolen) {
+  if (task.is_partial_array_state()) {
+    assert(_bitmap->is_marked(task.to_partial_array_state()->source()), "should be marked");
+    process_partial_array(task.to_partial_array_state(), stolen);
   } else {
-    obj->oop_iterate(mark_closure());
+    oop obj = task.to_oop();
+    assert(_bitmap->is_marked(obj), "should be marked");
+    if (obj->is_objArray()) {
+      // Handle object arrays explicitly to allow them to
+      // be split into chunks if needed.
+      start_partial_array_processing((objArrayOop)obj);
+    } else {
+      obj->oop_iterate(mark_closure());
+    }
   }
 }
 
 inline void G1FullGCMarker::publish_and_drain_oop_tasks() {
-  oop obj;
-  while (_oop_stack.pop_overflow(obj)) {
-    if (!_oop_stack.try_push_to_taskqueue(obj)) {
-      assert(_bitmap->is_marked(obj), "must be marked");
-      follow_object(obj);
+  ScannerTask task;
+  while (_task_queue.pop_overflow(task)) {
+    if (!_task_queue.try_push_to_taskqueue(task)) {
+      dispatch_task(task, false);
     }
   }
-  while (_oop_stack.pop_local(obj)) {
-    assert(_bitmap->is_marked(obj), "must be marked");
-    follow_object(obj);
+  while (_task_queue.pop_local(task)) {
+    dispatch_task(task, false);
   }
 }
 
-inline bool G1FullGCMarker::publish_or_pop_objarray_tasks(ObjArrayTask& task) {
-  // It is desirable to move as much as possible work from the overflow queue to
-  // the shared queue as quickly as possible.
-  while (_objarray_stack.pop_overflow(task)) {
-    if (!_objarray_stack.try_push_to_taskqueue(task)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-void G1FullGCMarker::follow_marking_stacks() {
+void G1FullGCMarker::process_marking_stacks() {
   do {
-    // First, drain regular oop stack.
     publish_and_drain_oop_tasks();
-
-    // Then process ObjArrays one at a time to avoid marking stack bloat.
-    ObjArrayTask task;
-    if (publish_or_pop_objarray_tasks(task) ||
-        _objarray_stack.pop_local(task)) {
-      follow_array_chunk(objArrayOop(task.obj()), task.index());
-    }
-  } while (!is_empty());
+  } while (!is_task_queue_empty());
 }
 
 #endif // SHARE_GC_G1_G1FULLGCMARKER_INLINE_HPP
diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp
index d9cf64a3655..273508ea9e3 100644
--- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp
@@ -35,7 +35,7 @@
 G1IsAliveClosure::G1IsAliveClosure(G1FullCollector* collector) :
   G1IsAliveClosure(collector, collector->mark_bitmap()) { }
 
-void G1FollowStackClosure::do_void() { _marker->follow_marking_stacks(); }
+void G1MarkStackClosure::do_void() { _marker->process_marking_stacks(); }
 
 void G1FullKeepAliveClosure::do_oop(oop* p) { do_oop_work(p); }
 void G1FullKeepAliveClosure::do_oop(narrowOop* p) { do_oop_work(p); }
diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp
index 388f8032de4..08ed5f982e1 100644
--- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp
@@ -86,11 +86,11 @@ public:
   virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; }
 };
 
-class G1FollowStackClosure: public VoidClosure {
+class G1MarkStackClosure: public VoidClosure {
   G1FullGCMarker* _marker;
 
 public:
-  G1FollowStackClosure(G1FullGCMarker* marker) : _marker(marker) {}
+  G1MarkStackClosure(G1FullGCMarker* marker) : _marker(marker) {}
   virtual void do_void();
 };
 

From 5e0ed3f408b6afd7496e0e0da207f7e372b0d446 Mon Sep 17 00:00:00 2001
From: Thomas Schatzl 
Date: Thu, 22 Jan 2026 11:51:37 +0000
Subject: [PATCH 114/126] 8375982: G1: Convert G1YoungCollector helper classes
 to use Atomic

Reviewed-by: kbarrett, shade
---
 src/hotspot/share/gc/g1/g1YoungCollector.cpp  | 19 ++++++++++---------
 .../gc/g1/g1YoungGCPostEvacuateTasks.cpp      |  7 ++++---
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp
index fa5c617f1db..36cc44a8b7c 100644
--- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp
+++ b/src/hotspot/share/gc/g1/g1YoungCollector.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.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@
 #include "gc/shared/workerThread.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "memory/resourceArea.hpp"
+#include "runtime/atomic.hpp"
 #include "runtime/threads.hpp"
 #include "utilities/ticks.hpp"
 
@@ -459,8 +460,8 @@ class G1PrepareEvacuationTask : public WorkerTask {
 
   G1CollectedHeap* _g1h;
   G1HeapRegionClaimer _claimer;
-  volatile uint _humongous_total;
-  volatile uint _humongous_candidates;
+  Atomic _humongous_total;
+  Atomic _humongous_candidates;
 
   G1MonotonicArenaMemoryStats _all_card_set_stats;
 
@@ -481,19 +482,19 @@ public:
   }
 
   void add_humongous_candidates(uint candidates) {
-    AtomicAccess::add(&_humongous_candidates, candidates);
+    _humongous_candidates.add_then_fetch(candidates);
   }
 
   void add_humongous_total(uint total) {
-    AtomicAccess::add(&_humongous_total, total);
+    _humongous_total.add_then_fetch(total);
   }
 
   uint humongous_candidates() {
-    return _humongous_candidates;
+    return _humongous_candidates.load_relaxed();
   }
 
   uint humongous_total() {
-    return _humongous_total;
+    return _humongous_total.load_relaxed();
   }
 
   const G1MonotonicArenaMemoryStats all_card_set_stats() const {
@@ -698,7 +699,7 @@ protected:
   virtual void evacuate_live_objects(G1ParScanThreadState* pss, uint worker_id) = 0;
 
 private:
-  volatile bool _pinned_regions_recorded;
+  Atomic _pinned_regions_recorded;
 
 public:
   G1EvacuateRegionsBaseTask(const char* name,
@@ -722,7 +723,7 @@ public:
       G1ParScanThreadState* pss = _per_thread_states->state_for_worker(worker_id);
       pss->set_ref_discoverer(_g1h->ref_processor_stw());
 
-      if (!AtomicAccess::cmpxchg(&_pinned_regions_recorded, false, true)) {
+      if (_pinned_regions_recorded.compare_set(false, true)) {
         record_pinned_regions(pss, worker_id);
       }
       scan_roots(pss, worker_id);
diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp
index ec5d2393d8c..46d12df575c 100644
--- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp
+++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp
@@ -46,6 +46,7 @@
 #include "oops/access.inline.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "runtime/atomic.hpp"
 #include "runtime/prefetch.inline.hpp"
 #include "runtime/threads.hpp"
 #include "runtime/threadSMR.hpp"
@@ -759,7 +760,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1
   const size_t*       _surviving_young_words;
   uint                _active_workers;
   G1EvacFailureRegions* _evac_failure_regions;
-  volatile uint       _num_retained_regions;
+  Atomic        _num_retained_regions;
 
   FreeCSetStats* worker_stats(uint worker) {
     return &_worker_stats[worker];
@@ -794,7 +795,7 @@ public:
   virtual ~FreeCollectionSetTask() {
     Ticks serial_time = Ticks::now();
 
-    bool has_new_retained_regions = AtomicAccess::load(&_num_retained_regions) != 0;
+    bool has_new_retained_regions = _num_retained_regions.load_relaxed() != 0;
     if (has_new_retained_regions) {
       G1CollectionSetCandidates* candidates = _g1h->collection_set()->candidates();
       candidates->sort_by_efficiency();
@@ -829,7 +830,7 @@ public:
     // Report per-region type timings.
     cl.report_timing();
 
-    AtomicAccess::add(&_num_retained_regions, cl.num_retained_regions(), memory_order_relaxed);
+    _num_retained_regions.add_then_fetch(cl.num_retained_regions(), memory_order_relaxed);
   }
 };
 

From 0d1d4d07b9fa2368f471f30e176d446698500115 Mon Sep 17 00:00:00 2001
From: Roland Westrelin 
Date: Thu, 22 Jan 2026 12:09:11 +0000
Subject: [PATCH 115/126] 8374725: C2: assert(x_ctrl ==
 get_late_ctrl_with_anti_dep(x->as_Load(), early_ctrl, x_ctrl)) failed:
 anti-dependences were already checked

Reviewed-by: chagedorn, qamai, dfenacci
---
 src/hotspot/share/opto/loopopts.cpp           | 10 +--
 .../loopopts/TestSinkingLoadInputOfPhi.java   | 63 +++++++++++++++++++
 2 files changed, 68 insertions(+), 5 deletions(-)
 create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestSinkingLoadInputOfPhi.java

diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp
index 9e3d4681e7c..1855263539b 100644
--- a/src/hotspot/share/opto/loopopts.cpp
+++ b/src/hotspot/share/opto/loopopts.cpp
@@ -1922,11 +1922,6 @@ bool PhaseIdealLoop::ctrl_of_all_uses_out_of_loop(const Node* n, Node* n_ctrl, I
     if (u->is_Opaque1()) {
       return false;  // Found loop limit, bugfix for 4677003
     }
-    // We can't reuse tags in PhaseIdealLoop::dom_lca_for_get_late_ctrl_internal() so make sure calls to
-    // get_late_ctrl_with_anti_dep() use their own tag
-    _dom_lca_tags_round++;
-    assert(_dom_lca_tags_round != 0, "shouldn't wrap around");
-
     if (u->is_Phi()) {
       for (uint j = 1; j < u->req(); ++j) {
         if (u->in(j) == n && !ctrl_of_use_out_of_loop(n, n_ctrl, n_loop, u->in(0)->in(j))) {
@@ -1960,6 +1955,11 @@ bool PhaseIdealLoop::would_sink_below_pre_loop_exit(IdealLoopTree* n_loop, Node*
 
 bool PhaseIdealLoop::ctrl_of_use_out_of_loop(const Node* n, Node* n_ctrl, IdealLoopTree* n_loop, Node* ctrl) {
   if (n->is_Load()) {
+    // We can't reuse tags in PhaseIdealLoop::dom_lca_for_get_late_ctrl_internal() so make sure each call to
+    // get_late_ctrl_with_anti_dep() uses its own tag
+    _dom_lca_tags_round++;
+    assert(_dom_lca_tags_round != 0, "shouldn't wrap around");
+
     ctrl = get_late_ctrl_with_anti_dep(n->as_Load(), n_ctrl, ctrl);
   }
   IdealLoopTree *u_loop = get_loop(ctrl);
diff --git a/test/hotspot/jtreg/compiler/loopopts/TestSinkingLoadInputOfPhi.java b/test/hotspot/jtreg/compiler/loopopts/TestSinkingLoadInputOfPhi.java
new file mode 100644
index 00000000000..cd563e6caf2
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/loopopts/TestSinkingLoadInputOfPhi.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2026 IBM Corporation. All rights reserved.
+ * 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 8374725
+ * @summary C2: assert(x_ctrl == get_late_ctrl_with_anti_dep(x->as_Load(), early_ctrl, x_ctrl)) failed: anti-dependences were already checked
+ * @run main/othervm  -XX:CompileCommand=compileonly,*TestSinkingLoadInputOfPhi*::* -XX:-TieredCompilation -Xcomp ${test.main.class}
+ * @run main ${test.main.class}
+ */
+
+package compiler.loopopts;
+
+public class TestSinkingLoadInputOfPhi {
+
+  static long lFld;
+  static int iFld = 55;
+  static int iFld2 = 10;
+  static void test() {
+    int iArr[] = new int[iFld2];
+
+    for (int i13 : iArr)
+      switch (iFld) {
+      case 69:
+      case 46:
+      case 65:
+        lFld = i13;
+      case 55: // taken
+        for (int i16 = 1; i16 < 30000; i16++)
+          ;
+      case 71:
+        iArr[iFld2] = 2; // OOB-access
+      }
+  }
+
+  public static void main(String[] strArr) {
+    try {
+      test();
+      } catch (ArrayIndexOutOfBoundsException e) {
+        // expected
+      }
+  }
+}

From eda15aa19c36142984edaa08850132ca6ae7a369 Mon Sep 17 00:00:00 2001
From: Weijun Wang 
Date: Thu, 22 Jan 2026 12:16:09 +0000
Subject: [PATCH 116/126] 8277489: Rewrite JAAS UnixLoginModule with FFM

Co-authored-by: Martin Doerr 
Reviewed-by: mdoerr, ascarpino, erikj
---
 make/modules/jdk.security.auth/Lib.gmk        |  17 +-
 src/java.base/share/classes/module-info.java  |   3 +-
 .../security/auth/module/UnixLoginModule.java |  32 +++-
 .../sun/security/auth/module/UnixSystem.java  | 180 ++++++++++++++++--
 .../unix/native/libjaas/Unix.c                | 130 -------------
 .../security/auth/module/AllPlatforms.java    |  23 ++-
 6 files changed, 220 insertions(+), 165 deletions(-)
 delete mode 100644 src/jdk.security.auth/unix/native/libjaas/Unix.c

diff --git a/make/modules/jdk.security.auth/Lib.gmk b/make/modules/jdk.security.auth/Lib.gmk
index 9ead32dbe12..96d609f08d6 100644
--- a/make/modules/jdk.security.auth/Lib.gmk
+++ b/make/modules/jdk.security.auth/Lib.gmk
@@ -31,13 +31,14 @@ include LibCommon.gmk
 ## Build libjaas
 ################################################################################
 
-$(eval $(call SetupJdkLibrary, BUILD_LIBJAAS, \
-    NAME := jaas, \
-    OPTIMIZATION := LOW, \
-    EXTRA_HEADER_DIRS := java.base:libjava, \
-    LIBS_windows := advapi32.lib mpr.lib netapi32.lib user32.lib, \
-))
-
-TARGETS += $(BUILD_LIBJAAS)
+ifeq ($(call isTargetOs, windows), true)
+  $(eval $(call SetupJdkLibrary, BUILD_LIBJAAS, \
+      NAME := jaas, \
+      OPTIMIZATION := LOW, \
+      EXTRA_HEADER_DIRS := java.base:libjava, \
+      LIBS_windows := advapi32.lib mpr.lib netapi32.lib user32.lib, \
+  ))
 
+  TARGETS += $(BUILD_LIBJAAS)
+endif
 ################################################################################
diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java
index 70a79390828..d20f6311bca 100644
--- a/src/java.base/share/classes/module-info.java
+++ b/src/java.base/share/classes/module-info.java
@@ -274,7 +274,8 @@ module java.base {
         jdk.httpserver,
         jdk.jlink,
         jdk.jpackage,
-        jdk.net;
+        jdk.net,
+        jdk.security.auth;
     exports sun.net to
         java.net.http,
         jdk.naming.dns;
diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixLoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixLoginModule.java
index 785b178e296..4f6d8ca25ef 100644
--- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixLoginModule.java
+++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixLoginModule.java
@@ -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
@@ -26,7 +26,6 @@
 package com.sun.security.auth.module;
 
 import java.util.*;
-import java.io.IOException;
 import javax.security.auth.*;
 import javax.security.auth.callback.*;
 import javax.security.auth.login.*;
@@ -34,6 +33,7 @@ import javax.security.auth.spi.*;
 import com.sun.security.auth.UnixPrincipal;
 import com.sun.security.auth.UnixNumericUserPrincipal;
 import com.sun.security.auth.UnixNumericGroupPrincipal;
+import jdk.internal.util.OperatingSystem;
 
 /**
  * This {@code LoginModule} imports a user's Unix
@@ -121,20 +121,34 @@ public class UnixLoginModule implements LoginModule {
      */
     public boolean login() throws LoginException {
 
-        long[] unixGroups = null;
+        // Fail immediately on Windows to avoid cygwin-like functions
+        // being loaded, which are not supported.
+        if (OperatingSystem.isWindows()) {
+            throw new FailedLoginException
+                    ("Failed in attempt to import " +
+                            "the underlying system identity information" +
+                            " on " + System.getProperty("os.name"));
+        }
 
         try {
             ss = new UnixSystem();
-        } catch (UnsatisfiedLinkError ule) {
+        } catch (ExceptionInInitializerError | UnsatisfiedLinkError ule) {
+            // Errors could happen in either static blocks or the constructor,
+            // both have a cause.
             succeeded = false;
-            throw new FailedLoginException
+            var error = new FailedLoginException
                                 ("Failed in attempt to import " +
                                 "the underlying system identity information" +
                                 " on " + System.getProperty("os.name"));
+            if (ule.getCause() != null) {
+                error.initCause(ule.getCause());
+            }
+            throw error;
         }
         userPrincipal = new UnixPrincipal(ss.getUsername());
         UIDPrincipal = new UnixNumericUserPrincipal(ss.getUid());
         GIDPrincipal = new UnixNumericGroupPrincipal(ss.getGid(), true);
+        long[] unixGroups = null;
         if (ss.getGroups() != null && ss.getGroups().length > 0) {
             unixGroups = ss.getGroups();
             for (int i = 0; i < unixGroups.length; i++) {
@@ -150,9 +164,11 @@ public class UnixLoginModule implements LoginModule {
                     "succeeded importing info: ");
             System.out.println("\t\t\tuid = " + ss.getUid());
             System.out.println("\t\t\tgid = " + ss.getGid());
-            unixGroups = ss.getGroups();
-            for (int i = 0; i < unixGroups.length; i++) {
-                System.out.println("\t\t\tsupp gid = " + unixGroups[i]);
+            System.out.println("\t\t\tusername = " + ss.getUsername());
+            if (unixGroups != null) {
+                for (int i = 0; i < unixGroups.length; i++) {
+                    System.out.println("\t\t\tsupp gid = " + unixGroups[i]);
+                }
             }
         }
         succeeded = true;
diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java
index f3741c10404..c0e47fafc2c 100644
--- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java
+++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/UnixSystem.java
@@ -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
@@ -25,38 +25,194 @@
 
 package com.sun.security.auth.module;
 
+import jdk.internal.util.Architecture;
+import jdk.internal.util.OperatingSystem;
+
+import java.lang.foreign.AddressLayout;
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.GroupLayout;
+import java.lang.foreign.Linker;
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.StructLayout;
+import java.lang.foreign.SymbolLookup;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+
+import static java.lang.foreign.MemoryLayout.PathElement.groupElement;
+
 /**
  * This class implementation retrieves and makes available Unix
  * UID/GID/groups information for the current user.
  *
  * @since 1.4
  */
+@SuppressWarnings("restricted")
 public class UnixSystem {
 
-    private native void getUnixInfo();
-
-    // Warning: the following 4 fields are used by Unix.c
-
-    /** The current username. */
+    /**
+     * The current username.
+     */
     protected String username;
 
-    /** The current user ID. */
+    /**
+     * The current user ID.
+     */
     protected long uid;
 
-    /** The current group ID. */
+    /**
+     * The current group ID.
+     */
     protected long gid;
 
-    /** The current list of groups. */
+    /**
+     * The current list of groups.
+     */
     protected long[] groups;
 
+    private static final Linker LINKER = Linker.nativeLinker();
+    private static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
+            .or(LINKER.defaultLookup());
+
+    private static final ValueLayout.OfByte C_CHAR
+            = (ValueLayout.OfByte) LINKER.canonicalLayouts().get("char");
+    private static final ValueLayout.OfInt C_INT
+            = (ValueLayout.OfInt) LINKER.canonicalLayouts().get("int");
+    private static final ValueLayout.OfLong C_LONG
+            = (ValueLayout.OfLong) LINKER.canonicalLayouts().get("long");
+    private static final AddressLayout C_POINTER
+            = ((AddressLayout) LINKER.canonicalLayouts().get("void*"))
+            .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR));
+    private static final ValueLayout C_SIZE_T
+            = (ValueLayout) LINKER.canonicalLayouts().get("size_t");
+
+    private static final StructLayout CAPTURE_STATE_LAYOUT
+            = Linker.Option.captureStateLayout();
+    private static final VarHandle VH_errno = CAPTURE_STATE_LAYOUT.varHandle(
+            MemoryLayout.PathElement.groupElement("errno"));
+
+    private static final MethodHandle MH_strerror
+            = LINKER.downcallHandle(SYMBOL_LOOKUP.findOrThrow("strerror"),
+                    FunctionDescriptor.of(C_POINTER, C_INT));
+
+    private static final MethodHandle MH_getgroups
+            = LINKER.downcallHandle(SYMBOL_LOOKUP.findOrThrow("getgroups"),
+                    FunctionDescriptor.of(C_INT, C_INT, C_POINTER),
+                    Linker.Option.captureCallState("errno"));
+    private static final MethodHandle MH_getuid
+            = LINKER.downcallHandle(SYMBOL_LOOKUP.findOrThrow("getuid"),
+                    FunctionDescriptor.of(C_INT));
+
+    // Some architectures require appropriate zero or sign extension to 64 bit.
+    // Use long directly before https://bugs.openjdk.org/browse/JDK-8336664 is resolved.
+    private static final boolean calling_convention_requires_int_as_long
+            = Architecture.isPPC64() || Architecture.isPPC64LE() || Architecture.isS390();
+
+    // getpwuid_r does not work on AIX, instead we use another similar function
+    // extern int _posix_getpwuid_r(uid_t, struct passwd *, char *, int, struct passwd **)
+    private static final MethodHandle MH_getpwuid_r
+            = LINKER.downcallHandle(SYMBOL_LOOKUP.findOrThrow(
+                            OperatingSystem.isAix() ? "_posix_getpwuid_r" : "getpwuid_r"),
+                    FunctionDescriptor.of(C_INT,
+                            calling_convention_requires_int_as_long ? C_LONG : C_INT,
+                            C_POINTER, C_POINTER,
+                            OperatingSystem.isAix() ? C_INT : C_SIZE_T,
+                            C_POINTER));
+
+    private static final GroupLayout ML_passwd = MemoryLayout.structLayout(
+            C_POINTER.withName("pw_name"),
+            C_POINTER.withName("pw_passwd"),
+            C_INT.withName("pw_uid"),
+            C_INT.withName("pw_gid"),
+            // Different platforms have different fields in `struct passwd`.
+            // While we don't need those fields here, the struct needs to be
+            // big enough to avoid buffer overflow when `getpwuid_r` is called.
+            MemoryLayout.paddingLayout(100));
+
+    private static final VarHandle VH_pw_uid
+            = ML_passwd.varHandle(groupElement("pw_uid"));
+    private static final VarHandle VH_pw_gid
+            = ML_passwd.varHandle(groupElement("pw_gid"));
+    private static final VarHandle VH_pw_name
+            = ML_passwd.varHandle(groupElement("pw_name"));
+
+    // The buffer size for the getpwuid_r function:
+    // 1. sysconf(_SC_GETPW_R_SIZE_MAX) on macOS is 4096 and 1024 on Linux,
+    //    so we choose a bigger one.
+    // 2. We do not call sysconf() here because even _SC_GETPW_R_SIZE_MAX
+    //    could be different on different platforms.
+    // 3. We choose int instead of long because the buffer_size argument
+    //    might be `int` or `long` and converting from `long` to `int`
+    //    requires an explicit cast.
+    private static final int GETPW_R_SIZE_MAX = 4096;
+
     /**
      * Instantiate a {@code UnixSystem} and load
      * the native library to access the underlying system information.
      */
-    @SuppressWarnings("restricted")
     public UnixSystem() {
-        System.loadLibrary("jaas");
-        getUnixInfo();
+        // The FFM code has only been tested on multiple platforms
+        // (including macOS, Linux, AIX, etc) and might fail on other
+        // *nix systems. Especially, the `passwd` struct could be defined
+        // differently. I've checked several and an extra 100 chars at the
+        // end seems enough.
+        try (Arena scope = Arena.ofConfined()) {
+            MemorySegment capturedState = scope.allocate(CAPTURE_STATE_LAYOUT);
+            int groupnum = (int) MH_getgroups.invokeExact(capturedState, 0, MemorySegment.NULL);
+            if (groupnum == -1) {
+                throw new RuntimeException("getgroups returns " + groupnum);
+            }
+
+            var gs = scope.allocate(C_INT, groupnum);
+            groupnum = (int) MH_getgroups.invokeExact(capturedState, groupnum, gs);
+            if (groupnum == -1) {
+                var errno = (int) VH_errno.get(capturedState, 0L);
+                var errMsg = (MemorySegment) MH_strerror.invokeExact(errno);
+                throw new RuntimeException("getgroups returns " + groupnum
+                        + ". Reason: " + errMsg.reinterpret(Long.MAX_VALUE).getString(0));
+            }
+
+            groups = new long[groupnum];
+            for (int i = 0; i < groupnum; i++) {
+                groups[i] = Integer.toUnsignedLong(gs.getAtIndex(C_INT, i));
+            }
+
+            var pwd = scope.allocate(ML_passwd);
+            var result = scope.allocate(C_POINTER);
+            var buffer = scope.allocate(GETPW_R_SIZE_MAX);
+
+            long tmpUid = Integer.toUnsignedLong((int) MH_getuid.invokeExact());
+
+            // Do not call invokeExact because the type of buffer_size is not
+            // always long in the underlying system.
+            int out;
+            if (calling_convention_requires_int_as_long) {
+                out = (int) MH_getpwuid_r.invoke(
+                        tmpUid, pwd, buffer, GETPW_R_SIZE_MAX, result);
+            } else {
+                out = (int) MH_getpwuid_r.invoke(
+                        (int) tmpUid, pwd, buffer, GETPW_R_SIZE_MAX, result);
+            }
+            if (out != 0) {
+                // If ERANGE (Result too large) is detected in a new platform,
+                // consider adjusting GETPW_R_SIZE_MAX.
+                var err = (MemorySegment) MH_strerror.invokeExact(out);
+                throw new RuntimeException(err.reinterpret(Long.MAX_VALUE).getString(0));
+            } else if (result.get(ValueLayout.ADDRESS, 0).equals(MemorySegment.NULL)) {
+                throw new RuntimeException("the requested entry is not found");
+            } else {
+                // uid_t and gid_t were defined unsigned.
+                uid = Integer.toUnsignedLong((int) VH_pw_uid.get(pwd, 0L));
+                gid = Integer.toUnsignedLong((int) VH_pw_gid.get(pwd, 0L));
+                username = ((MemorySegment) VH_pw_name.get(pwd, 0L)).getString(0);
+            }
+        } catch (Throwable t) {
+            var error = new UnsatisfiedLinkError("FFM calls failed");
+            error.initCause(t);
+            throw error;
+        }
     }
 
     /**
diff --git a/src/jdk.security.auth/unix/native/libjaas/Unix.c b/src/jdk.security.auth/unix/native/libjaas/Unix.c
deleted file mode 100644
index 3a93df9ffc9..00000000000
--- a/src/jdk.security.auth/unix/native/libjaas/Unix.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
- * 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.
- */
-
-#include 
-#include "jni_util.h"
-#include "com_sun_security_auth_module_UnixSystem.h"
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#include 
-
-/*
- * Declare library specific JNI_Onload entry if static build
- */
-DEF_STATIC_JNI_OnLoad
-
-JNIEXPORT void JNICALL
-Java_com_sun_security_auth_module_UnixSystem_getUnixInfo
-                                                (JNIEnv *env, jobject obj) {
-
-    int i;
-    char pwd_buf[1024];
-    struct passwd *pwd = NULL;
-    struct passwd resbuf;
-    jfieldID userNameID;
-    jfieldID userID;
-    jfieldID groupID;
-    jfieldID supplementaryGroupID;
-
-    jstring jstr;
-    jlongArray jgroups;
-    jlong *jgroupsAsArray;
-    jsize numSuppGroups;
-    gid_t *groups;
-    jclass cls;
-
-    numSuppGroups = getgroups(0, NULL);
-    if (numSuppGroups == -1) {
-        return;
-    }
-    groups = (gid_t *)calloc(numSuppGroups, sizeof(gid_t));
-    if (groups == NULL) {
-        jclass cls = (*env)->FindClass(env,"java/lang/OutOfMemoryError");
-        if (cls != NULL) {
-            (*env)->ThrowNew(env, cls, NULL);
-        }
-        return;
-    }
-
-    cls = (*env)->GetObjectClass(env, obj);
-
-    supplementaryGroupID = (*env)->GetFieldID(env, cls, "groups", "[J");
-    if (supplementaryGroupID == 0) {
-        goto cleanUpAndReturn;
-    }
-
-    if (getgroups(numSuppGroups, groups) != -1) {
-        jgroups = (*env)->NewLongArray(env, numSuppGroups);
-        if (jgroups == NULL) {
-            goto cleanUpAndReturn;
-        }
-        jgroupsAsArray = (*env)->GetLongArrayElements(env, jgroups, 0);
-        if (jgroupsAsArray == NULL) {
-            goto cleanUpAndReturn;
-        }
-        for (i = 0; i < numSuppGroups; i++) {
-            jgroupsAsArray[i] = groups[i];
-        }
-        (*env)->ReleaseLongArrayElements(env, jgroups, jgroupsAsArray, 0);
-        (*env)->SetObjectField(env, obj, supplementaryGroupID, jgroups);
-    }
-
-    userNameID = (*env)->GetFieldID(env, cls, "username", "Ljava/lang/String;");
-    if (userNameID == 0) {
-        goto cleanUpAndReturn;
-    }
-
-    userID = (*env)->GetFieldID(env, cls, "uid", "J");
-    if (userID == 0) {
-        goto cleanUpAndReturn;
-    }
-
-    groupID = (*env)->GetFieldID(env, cls, "gid", "J");
-    if (groupID == 0) {
-        goto cleanUpAndReturn;
-    }
-
-    memset(pwd_buf, 0, sizeof(pwd_buf));
-    if (getpwuid_r(getuid(), &resbuf, pwd_buf, sizeof(pwd_buf), &pwd) == 0 &&
-            pwd != NULL) {
-        (*env)->SetLongField(env, obj, userID, pwd->pw_uid);
-        (*env)->SetLongField(env, obj, groupID, pwd->pw_gid);
-        jstr = (*env)->NewStringUTF(env, pwd->pw_name);
-        if (jstr == NULL) {
-            goto cleanUpAndReturn;
-        }
-        (*env)->SetObjectField(env, obj, userNameID, jstr);
-    } else {
-        (*env)->SetLongField(env, obj, userID, getuid());
-        (*env)->SetLongField(env, obj, groupID, getgid());
-    }
-cleanUpAndReturn:
-    free(groups);
-    return;
-}
diff --git a/test/jdk/com/sun/security/auth/module/AllPlatforms.java b/test/jdk/com/sun/security/auth/module/AllPlatforms.java
index 79eea7b4d23..6f7cbfb9c52 100644
--- a/test/jdk/com/sun/security/auth/module/AllPlatforms.java
+++ b/test/jdk/com/sun/security/auth/module/AllPlatforms.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -25,8 +25,11 @@
  * @test
  * @bug 8039951
  * @summary com.sun.security.auth.module missing classes on some platforms
+ * @modules java.base/jdk.internal.util
  * @run main/othervm AllPlatforms
  */
+import jdk.internal.util.OperatingSystem;
+
 import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import java.nio.file.Files;
@@ -39,14 +42,14 @@ public class AllPlatforms {
     private static final String NT_MODULE = "NTLoginModule";
 
     public static void main(String[] args) throws Exception {
-        login("cross-platform",
+        login(true, "cross-platform",
                 UNIX_MODULE, "optional",
                 NT_MODULE, "optional");
-        login("windows", NT_MODULE, "required");
-        login("unix", UNIX_MODULE, "required");
+        login(OperatingSystem.isWindows(), "windows", NT_MODULE, "required");
+        login(!OperatingSystem.isWindows(), "unix", UNIX_MODULE, "required");
     }
 
-    static void login(String test, String... conf) throws Exception {
+    static void login(boolean shouldSucceed, String test, String... conf) throws Exception {
         System.out.println("Testing " + test + "...");
 
         StringBuilder sb = new StringBuilder();
@@ -68,11 +71,19 @@ public class AllPlatforms {
             lc.login();
             System.out.println(lc.getSubject());
             lc.logout();
+            if (!shouldSucceed) {
+                throw new RuntimeException("Should not succeed");
+            }
         } catch (FailedLoginException e) {
+            if (shouldSucceed) {
+                throw new RuntimeException("Should succeed");
+            }
             // This exception can occur in other platform module than the running one.
-            if(e.getMessage().startsWith("Failed in attempt to import")) {
+            if (e.getMessage().startsWith("Failed in attempt to import")) {
                 System.out.println("Expected Exception found.");
                 e.printStackTrace(System.out);
+            } else {
+                throw new RuntimeException("Unexpected error", e);
             }
         }
     }

From 025041ba04f3ae3a149b9d57d0dde4afaef37f4c Mon Sep 17 00:00:00 2001
From: Artur Barashev 
Date: Thu, 22 Jan 2026 13:11:42 +0000
Subject: [PATCH 117/126] 8370885: Default namedGroups values are not being
 filtered against algorithm constraints

Reviewed-by: hchao
---
 .../classes/sun/security/ssl/NamedGroup.java  | 177 ++++++++++--------
 .../ssl/CipherSuite/DefaultNamedGroups.java   |  85 +++++++++
 2 files changed, 189 insertions(+), 73 deletions(-)
 create mode 100644 test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.java

diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java
index abf973727f3..02524e67656 100644
--- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java
+++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.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
@@ -30,10 +30,12 @@ import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.InvalidParameterSpecException;
 import java.security.spec.NamedParameterSpec;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.Set;
 import javax.crypto.KeyAgreement;
 import javax.crypto.spec.DHParameterSpec;
@@ -463,10 +465,9 @@ enum NamedGroup {
             AlgorithmConstraints constraints, NamedGroupSpec type) {
 
         boolean hasFFDHEGroups = false;
-        for (String ng : sslConfig.namedGroups) {
-            NamedGroup namedGroup = NamedGroup.nameOf(ng);
-            if (namedGroup != null &&
-                namedGroup.isAvailable && namedGroup.spec == type) {
+        for (NamedGroup namedGroup :
+                SupportedGroups.getGroupsFromConfig(sslConfig)) {
+            if (namedGroup.isAvailable && namedGroup.spec == type) {
                 if (namedGroup.isPermitted(constraints)) {
                     return true;
                 }
@@ -501,8 +502,8 @@ enum NamedGroup {
     // Is the named group supported?
     static boolean isEnabled(SSLConfiguration sslConfig,
                              NamedGroup namedGroup) {
-        for (String ng : sslConfig.namedGroups) {
-            if (namedGroup.name.equalsIgnoreCase(ng)) {
+        for (NamedGroup ng : SupportedGroups.getGroupsFromConfig(sslConfig)) {
+            if (namedGroup.equals(ng)) {
                 return true;
             }
         }
@@ -516,12 +517,10 @@ enum NamedGroup {
             SSLConfiguration sslConfig,
             ProtocolVersion negotiatedProtocol,
             AlgorithmConstraints constraints, NamedGroupSpec[] types) {
-        for (String name : sslConfig.namedGroups) {
-            NamedGroup ng = NamedGroup.nameOf(name);
-            if (ng != null && ng.isAvailable &&
-                    (NamedGroupSpec.arrayContains(types, ng.spec)) &&
-                    ng.isAvailable(negotiatedProtocol) &&
-                    ng.isPermitted(constraints)) {
+        for (NamedGroup ng : SupportedGroups.getGroupsFromConfig(sslConfig)) {
+            if (ng.isAvailable && NamedGroupSpec.arrayContains(types, ng.spec)
+                    && ng.isAvailable(negotiatedProtocol)
+                    && ng.isPermitted(constraints)) {
                 return ng;
             }
         }
@@ -857,19 +856,92 @@ enum NamedGroup {
         }
     }
 
+    // Inner class encapsulating supported named groups.
     static final class SupportedGroups {
-        // the supported named groups, non-null immutable list
+
+        // Default named groups.
+        private static final NamedGroup[] defaultGroups = new NamedGroup[]{
+                // Hybrid key agreement
+                X25519MLKEM768,
+
+                // Primary XDH (RFC 7748) curves
+                X25519,
+
+                // Primary NIST Suite B curves
+                SECP256_R1,
+                SECP384_R1,
+                SECP521_R1,
+
+                // Secondary XDH curves
+                X448,
+
+                // FFDHE (RFC 7919)
+                FFDHE_2048,
+                FFDHE_3072,
+                FFDHE_4096,
+                FFDHE_6144,
+                FFDHE_8192
+        };
+
+        // Filter default groups names against default constraints.
+        // Those are the values being displayed to the user with
+        // "java -XshowSettings:security:tls" command.
+        private static final String[] defaultNames = Arrays.stream(
+                        defaultGroups)
+                .filter(ng -> ng.isAvailable)
+                .filter(ng -> ng.isPermitted(SSLAlgorithmConstraints.DEFAULT))
+                .map(ng -> ng.name)
+                .toArray(String[]::new);
+
+        private static final NamedGroup[] customizedGroups =
+                getCustomizedNamedGroups();
+
+        // Note: user-passed groups are not being filtered against default
+        // algorithm constraints here. They will be displayed as-is.
+        private static final String[] customizedNames =
+                customizedGroups == null ?
+                        null : Arrays.stream(customizedGroups)
+                        .map(ng -> ng.name)
+                        .toArray(String[]::new);
+
+        // Named group names for SSLConfiguration.
         static final String[] namedGroups;
 
         static {
-            // The value of the System Property defines a list of enabled named
-            // groups in preference order, separated with comma.  For example:
-            //
-            //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
-            //
-            // If the System Property is not defined or the value is empty, the
-            // default groups and preferences will be used.
+            if (customizedNames != null) {
+                namedGroups = customizedNames;
+            } else {
+                if (defaultNames.length == 0) {
+                    SSLLogger.logWarning("ssl", "No default named groups");
+                }
+                namedGroups = defaultNames;
+            }
+        }
+
+        // Avoid the group lookup for default and customized groups.
+        static NamedGroup[] getGroupsFromConfig(SSLConfiguration sslConfig) {
+            if (sslConfig.namedGroups == defaultNames) {
+                return defaultGroups;
+            } else if (sslConfig.namedGroups == customizedNames) {
+                return customizedGroups;
+            } else {
+                return Arrays.stream(sslConfig.namedGroups)
+                        .map(NamedGroup::nameOf)
+                        .filter(Objects::nonNull)
+                        .toArray(NamedGroup[]::new);
+            }
+        }
+
+        // The value of the System Property defines a list of enabled named
+        // groups in preference order, separated with comma.  For example:
+        //
+        //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
+        //
+        // If the System Property is not defined or the value is empty, the
+        // default groups and preferences will be used.
+        private static NamedGroup[] getCustomizedNamedGroups() {
             String property = System.getProperty("jdk.tls.namedGroups");
+
             if (property != null && !property.isEmpty()) {
                 // remove double quote marks from beginning/end of the property
                 if (property.length() > 1 && property.charAt(0) == '"' &&
@@ -878,66 +950,25 @@ enum NamedGroup {
                 }
             }
 
-            ArrayList groupList;
             if (property != null && !property.isEmpty()) {
-                String[] groups = property.split(",");
-                groupList = new ArrayList<>(groups.length);
-                for (String group : groups) {
-                    group = group.trim();
-                    if (!group.isEmpty()) {
-                        NamedGroup namedGroup = nameOf(group);
-                        if (namedGroup != null) {
-                            if (namedGroup.isAvailable) {
-                                groupList.add(namedGroup.name);
-                            }
-                        }   // ignore unknown groups
-                    }
-                }
+                NamedGroup[] ret = Arrays.stream(property.split(","))
+                        .map(String::trim)
+                        .map(NamedGroup::nameOf)
+                        .filter(Objects::nonNull)
+                        .filter(ng -> ng.isAvailable)
+                        .toArray(NamedGroup[]::new);
 
-                if (groupList.isEmpty()) {
+                if (ret.length == 0) {
                     throw new IllegalArgumentException(
                             "System property jdk.tls.namedGroups(" +
-                            property + ") contains no supported named groups");
-                }
-            } else {        // default groups
-                NamedGroup[] groups = new NamedGroup[] {
-
-                        // Hybrid key agreement
-                        X25519MLKEM768,
-
-                        // Primary XDH (RFC 7748) curves
-                        X25519,
-
-                        // Primary NIST Suite B curves
-                        SECP256_R1,
-                        SECP384_R1,
-                        SECP521_R1,
-
-                        // Secondary XDH curves
-                        X448,
-
-                        // FFDHE (RFC 7919)
-                        FFDHE_2048,
-                        FFDHE_3072,
-                        FFDHE_4096,
-                        FFDHE_6144,
-                        FFDHE_8192,
-                    };
-
-                groupList = new ArrayList<>(groups.length);
-                for (NamedGroup group : groups) {
-                    if (group.isAvailable) {
-                        groupList.add(group.name);
-                    }
+                                    property
+                                    + ") contains no supported named groups");
                 }
 
-                if (groupList.isEmpty() &&
-                        SSLLogger.isOn() && SSLLogger.isOn("ssl")) {
-                    SSLLogger.warning("No default named groups");
-                }
+                return ret;
             }
 
-            namedGroups = groupList.toArray(new String[0]);
+            return null;
         }
     }
 }
diff --git a/test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.java b/test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.java
new file mode 100644
index 00000000000..44c9d566dc0
--- /dev/null
+++ b/test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.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.
+ */
+
+import static jdk.test.lib.Asserts.assertFalse;
+import static jdk.test.lib.Asserts.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+import javax.net.ssl.SSLEngine;
+import jdk.test.lib.security.SecurityUtils;
+
+/*
+ * @test
+ * @bug 8370885
+ * @summary Default namedGroups values are not being filtered against
+ *          algorithm constraints
+ * @library /javax/net/ssl/templates
+ *          /test/lib
+ * @run main/othervm DefaultNamedGroups
+ */
+
+public class DefaultNamedGroups extends SSLEngineTemplate {
+
+    protected static final String DISABLED_NG = "secp256r1";
+    protected static final List REFERENCE_NG = Stream.of(
+                    "X25519MLKEM768",
+                    "x25519",
+                    "secp256r1",
+                    "secp384r1",
+                    "secp521r1",
+                    "x448",
+                    "ffdhe2048",
+                    "ffdhe3072",
+                    "ffdhe4096",
+                    "ffdhe6144",
+                    "ffdhe8192")
+            .sorted()
+            .toList();
+
+    protected DefaultNamedGroups() throws Exception {
+        super();
+    }
+
+    public static void main(String[] args) throws Exception {
+        SecurityUtils.addToDisabledTlsAlgs(DISABLED_NG);
+        var test = new DefaultNamedGroups();
+
+        for (SSLEngine engine :
+                new SSLEngine[]{test.serverEngine, test.clientEngine}) {
+            checkEngineDefaultNG(engine);
+        }
+    }
+
+    private static void checkEngineDefaultNG(SSLEngine engine) {
+        var defaultConfigNG = new ArrayList<>(List.of(
+                engine.getSSLParameters().getNamedGroups()));
+
+        assertFalse(defaultConfigNG.contains(DISABLED_NG));
+        defaultConfigNG.add(DISABLED_NG);
+        assertTrue(REFERENCE_NG.equals(
+                        defaultConfigNG.stream().sorted().toList()),
+                "Named groups returned by engine: " + defaultConfigNG);
+    }
+}

From 26aab3cccdbcf98c329c8d67093eb2dbf4b164e5 Mon Sep 17 00:00:00 2001
From: Patricio Chilano Mateo 
Date: Thu, 22 Jan 2026 14:56:23 +0000
Subject: [PATCH 118/126] 8373120: Virtual thread stuck in BLOCKED state

Co-authored-by: Alan Bateman 
Reviewed-by: alanb
---
 .../classes/java/lang/VirtualThread.java      |  25 ++--
 .../stress/NotifiedThenTimedOutWait.java      | 134 ++++++++++++++++++
 2 files changed, 148 insertions(+), 11 deletions(-)
 create mode 100644 test/jdk/java/lang/Thread/virtual/stress/NotifiedThenTimedOutWait.java

diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java
index 93862db9105..5526618c67b 100644
--- a/src/java.base/share/classes/java/lang/VirtualThread.java
+++ b/src/java.base/share/classes/java/lang/VirtualThread.java
@@ -620,9 +620,12 @@ final class VirtualThread extends BaseVirtualThread {
         // Object.wait
         if (s == WAITING || s == TIMED_WAITING) {
             int newState;
+            boolean blocked;
             boolean interruptible = interruptibleWait;
             if (s == WAITING) {
                 setState(newState = WAIT);
+                // may have been notified while in transition
+                blocked = notified && compareAndSetState(WAIT, BLOCKED);
             } else {
                 // For timed-wait, a timeout task is scheduled to execute. The timeout
                 // task will change the thread state to UNBLOCKED and submit the thread
@@ -637,22 +640,22 @@ final class VirtualThread extends BaseVirtualThread {
                     byte seqNo = ++timedWaitSeqNo;
                     timeoutTask = schedule(() -> waitTimeoutExpired(seqNo), timeout, MILLISECONDS);
                     setState(newState = TIMED_WAIT);
+                    // May have been notified while in transition. This must be done while
+                    // holding the monitor to avoid changing the state of a new timed wait call.
+                    blocked = notified && compareAndSetState(TIMED_WAIT, BLOCKED);
                 }
             }
 
-            // may have been notified while in transition to wait state
-            if (notified && compareAndSetState(newState, BLOCKED)) {
-                // may have even been unblocked already
+            if (blocked) {
+                // may have been unblocked already
                 if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) {
-                    submitRunContinuation();
+                    lazySubmitRunContinuation();
+                }
+            } else {
+                // may have been interrupted while in transition to wait state
+                if (interruptible && interrupted && compareAndSetState(newState, UNBLOCKED)) {
+                    lazySubmitRunContinuation();
                 }
-                return;
-            }
-
-            // may have been interrupted while in transition to wait state
-            if (interruptible && interrupted && compareAndSetState(newState, UNBLOCKED)) {
-                submitRunContinuation();
-                return;
             }
             return;
         }
diff --git a/test/jdk/java/lang/Thread/virtual/stress/NotifiedThenTimedOutWait.java b/test/jdk/java/lang/Thread/virtual/stress/NotifiedThenTimedOutWait.java
new file mode 100644
index 00000000000..0734a794b90
--- /dev/null
+++ b/test/jdk/java/lang/Thread/virtual/stress/NotifiedThenTimedOutWait.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.
+ */
+
+/*
+ * @test
+ * @bug 8373120
+ * @summary Stress test two consecutive timed Object.wait calls where only the first one is notified.
+ * @run main/othervm -XX:CompileCommand=exclude,java.lang.VirtualThread::afterYield NotifiedThenTimedOutWait 1 100 100
+ */
+
+/*
+ * @test
+ * @run main/othervm -XX:CompileCommand=exclude,java.lang.VirtualThread::afterYield NotifiedThenTimedOutWait 2 100 100
+ */
+
+import java.time.Instant;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class NotifiedThenTimedOutWait {
+    public static void main(String[] args) throws Exception {
+        int race = (args.length > 0) ? Integer.parseInt(args[0]) : 1;
+        int nruns = (args.length > 1) ? Integer.parseInt(args[1]) : 100;
+        int iterations = (args.length > 2) ? Integer.parseInt(args[2]) : 100;
+
+        for (int i = 1; i <= nruns; i++) {
+            System.out.println(Instant.now() + " => " + i + " of " + nruns);
+            switch (race) {
+                case 1 -> race1(iterations);
+                case 2 -> race2(iterations);
+            }
+        }
+    }
+
+    /**
+     * Barrier in synchronized block.
+     */
+    private static void race1(int iterations) throws InterruptedException {
+        final int timeout = 1;
+        var lock = new Object();
+        var start = new Phaser(2);
+        var end = new Phaser(2);
+
+        var vthread = Thread.ofVirtual().start(() -> {
+            try {
+                for (int j = 0; j < iterations; j++) {
+                    synchronized (lock) {
+                        start.arriveAndAwaitAdvance();
+                        lock.wait(timeout);
+                        lock.wait(timeout);
+                    }
+                    end.arriveAndAwaitAdvance();
+                }
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        });
+
+        ThreadFactory factory = ThreadLocalRandom.current().nextBoolean()
+                    ? Thread.ofPlatform().factory() : Thread.ofVirtual().factory();
+        var notifier = factory.newThread(() -> {
+            for (int j = 0; j < iterations; j++) {
+                start.arriveAndAwaitAdvance();
+                synchronized (lock) {
+                    lock.notify();
+                }
+                end.arriveAndAwaitAdvance();
+            }
+        });
+        notifier.start();
+
+        vthread.join();
+        notifier.join();
+    }
+
+    /**
+     * Barrier before synchronized block.
+     */
+    private static void race2(int iterations) throws InterruptedException {
+        final int timeout = 1;
+        var lock = new Object();
+        var start = new Phaser(2);
+
+        var vthread = Thread.startVirtualThread(() -> {
+            try {
+                for (int i = 0; i < iterations; i++) {
+                    start.arriveAndAwaitAdvance();
+                    synchronized (lock) {
+                        lock.wait(timeout);
+                        lock.wait(timeout);
+                    }
+                }
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        });
+
+        ThreadFactory factory = ThreadLocalRandom.current().nextBoolean()
+                    ? Thread.ofPlatform().factory() : Thread.ofVirtual().factory();
+        var notifier = factory.newThread(() -> {
+            for (int i = 0; i < iterations; i++) {
+                start.arriveAndAwaitAdvance();
+                synchronized (lock) {
+                    lock.notify();
+                }
+            }
+        });
+        notifier.start();
+
+        vthread.join();
+        notifier.join();
+    }
+}

From 07f6617e0b2752b538b6c43250dd0bb65fd8c695 Mon Sep 17 00:00:00 2001
From: Brian Burkhalter 
Date: Thu, 22 Jan 2026 16:11:33 +0000
Subject: [PATCH 119/126] 8367284: (fs) Support current working directory
 target in SecureDirectoryStream.move

Reviewed-by: alanb
---
 .../java/nio/file/SecureDirectoryStream.java  | 15 ++++++-----
 .../sun/nio/fs/UnixSecureDirectoryStream.java | 15 ++++++-----
 .../nio/file/DirectoryStream/SecureDS.java    | 27 +++++++++++++++----
 3 files changed, 38 insertions(+), 19 deletions(-)

diff --git a/src/java.base/share/classes/java/nio/file/SecureDirectoryStream.java b/src/java.base/share/classes/java/nio/file/SecureDirectoryStream.java
index 4348c60f5e2..92a292bbac6 100644
--- a/src/java.base/share/classes/java/nio/file/SecureDirectoryStream.java
+++ b/src/java.base/share/classes/java/nio/file/SecureDirectoryStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2024, 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
@@ -185,8 +185,8 @@ public interface SecureDirectoryStream
     /**
      * Move a file from this directory to another directory.
      *
-     * 

This method works in a similar manner to {@link Files#move move} - * method when the {@link StandardCopyOption#ATOMIC_MOVE ATOMIC_MOVE} option + *

This method works in a similar manner to {@link Files#move Files.move} + * when the {@link StandardCopyOption#ATOMIC_MOVE ATOMIC_MOVE} option * is specified. That is, this method moves a file as an atomic file system * operation. If the {@code srcpath} parameter is an {@link Path#isAbsolute * absolute} path then it locates the source file. If the parameter is a @@ -194,14 +194,15 @@ public interface SecureDirectoryStream * the {@code targetpath} parameter is absolute then it locates the target * file (the {@code targetdir} parameter is ignored). If the parameter is * a relative path it is located relative to the open directory identified - * by the {@code targetdir} parameter. In all cases, if the target file - * exists then it is implementation specific if it is replaced or this - * method fails. + * by the {@code targetdir} parameter, unless {@code targetdir} is + * {@code null}, in which case it is located relative to the current + * working directory. In all cases, if the target file exists then it is + * implementation specific if it is replaced or this method fails. * * @param srcpath * the name of the file to move * @param targetdir - * the destination directory + * the destination directory; can be {@code null} * @param targetpath * the name to give the file in the destination directory * diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixSecureDirectoryStream.java b/src/java.base/unix/classes/sun/nio/fs/UnixSecureDirectoryStream.java index 5c0693870e6..bafcd06d9e7 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixSecureDirectoryStream.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixSecureDirectoryStream.java @@ -202,21 +202,21 @@ class UnixSecureDirectoryStream { UnixPath from = getName(fromObj); UnixPath to = getName(toObj); - if (dir == null) - throw new NullPointerException(); - if (!(dir instanceof UnixSecureDirectoryStream)) + if (dir != null && !(dir instanceof UnixSecureDirectoryStream)) throw new ProviderMismatchException(); UnixSecureDirectoryStream that = (UnixSecureDirectoryStream)dir; + int todfd = that != null ? that.dfd : AT_FDCWD; // lock ordering doesn't matter this.ds.readLock().lock(); try { - that.ds.readLock().lock(); + if (that != null) + that.ds.readLock().lock(); try { - if (!this.ds.isOpen() || !that.ds.isOpen()) + if (!this.ds.isOpen() || (that != null && !that.ds.isOpen())) throw new ClosedDirectoryStreamException(); try { - renameat(this.dfd, from.asByteArray(), that.dfd, to.asByteArray()); + renameat(this.dfd, from.asByteArray(), todfd, to.asByteArray()); } catch (UnixException x) { if (x.errno() == EXDEV) { throw new AtomicMoveNotSupportedException( @@ -225,7 +225,8 @@ class UnixSecureDirectoryStream x.rethrowAsIOException(from, to); } } finally { - that.ds.readLock().unlock(); + if (that != null) + that.ds.readLock().unlock(); } } finally { this.ds.readLock().unlock(); diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index d5d4b1904ea..21204ba980a 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.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 @@ -292,6 +292,27 @@ public class SecureDS { } } + // Test: move to cwd + final String TEXT = "Sous le pont Mirabeau coule la Seine"; + Path file = Path.of("file"); + Path filepath = dir.resolve(file); + Path cwd = Path.of(System.getProperty("user.dir")); + Path result = cwd.resolve(file); + Files.writeString(filepath, TEXT); + try (DirectoryStream ds = Files.newDirectoryStream(dir);) { + if (ds instanceof SecureDirectoryStream sds) { + sds.move(file, null, file); + if (!TEXT.equals(Files.readString(result))) + throw new RuntimeException(result + " content incorrect"); + } else { + throw new RuntimeException("Not a SecureDirectoryStream"); + } + } finally { + boolean fileDeleted = Files.deleteIfExists(filepath); + if (!fileDeleted) + Files.deleteIfExists(result); + } + // clean-up delete(dir1); delete(dir2); @@ -334,10 +355,6 @@ public class SecureDS { stream.move(null, stream, file); shouldNotGetHere(); } catch (NullPointerException x) { } - try { - stream.move(file, null, file); - shouldNotGetHere(); - } catch (NullPointerException x) { } try { stream.move(file, stream, null); shouldNotGetHere(); From 8c82b58db960a178566514731e1f8dcbc59b0161 Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Thu, 22 Jan 2026 16:36:24 +0000 Subject: [PATCH 120/126] 8286258: [Accessibility,macOS,VoiceOver] VoiceOver reads the spinner value wrong and sometime partially Reviewed-by: psadhukhan, asemenov --- .../awt/a11y/NavigableTextAccessibility.h | 3 +- .../awt/a11y/NavigableTextAccessibility.m | 27 +++++- .../awt/a11y/SpinboxAccessibility.m | 27 +++++- .../CustomSpinnerAccessibilityTest.java | 86 +++++++++++++++++++ 4 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 test/jdk/javax/accessibility/JSpinner/CustomSpinnerAccessibilityTest.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h index ebf314c7394..9a528879a5d 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.h @@ -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. * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,5 +29,6 @@ @interface NavigableTextAccessibility : CommonComponentAccessibility @property(readonly) BOOL accessibleIsPasswordText; +@property BOOL announceEditUpdates; @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m index 138d502f10f..8e241e65b96 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m @@ -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. * Copyright (c) 2021, JetBrains s.r.o.. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -60,6 +60,22 @@ static jmethodID sjm_getAccessibleEditableText = NULL; return [fJavaRole isEqualToString:@"passwordtext"]; } +- (id)init { + self = [super init]; + if (self) { + _announceEditUpdates = YES; + } + return self; +} + +- (void)suppressEditUpdates { + _announceEditUpdates = NO; +} + +- (void)resumeEditUpdates { + _announceEditUpdates = YES; +} + // NSAccessibilityElement protocol methods - (NSRect)accessibilityFrameForRange:(NSRange)range @@ -117,6 +133,9 @@ static jmethodID sjm_getAccessibleEditableText = NULL; - (NSString *)accessibilityStringForRange:(NSRange)range { + if (!_announceEditUpdates) { + return @""; + } JNIEnv *env = [ThreadUtilities getJNIEnv]; GET_CACCESSIBLETEXT_CLASS_RETURN(nil); DECLARE_STATIC_METHOD_RETURN(jm_getStringForRange, sjc_CAccessibleText, "getStringForRange", @@ -306,6 +325,12 @@ static jmethodID sjm_getAccessibleEditableText = NULL; return [super accessibilityParent]; } +- (void)postSelectedTextChanged +{ + [super postSelectedTextChanged]; + [self resumeEditUpdates]; +} + /* * Other text methods - (NSRange)accessibilitySharedCharacterRange; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m index 4dac6bd93f9..0cec7f3eb2c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/SpinboxAccessibility.m @@ -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 @@ -24,6 +24,7 @@ */ #import "SpinboxAccessibility.h" +#import "ThreadUtilities.h" #define INCREMENT 0 #define DECREMENT 1 @@ -44,7 +45,15 @@ - (id _Nullable)accessibilityValue { - return [super accessibilityValue]; + id val = [super accessibilityValue]; + NSArray *clist = [super accessibilityChildren]; + for (NSUInteger i = 0; i < [clist count]; i++) { + id child = [clist objectAtIndex:i]; + if ([child conformsToProtocol:@protocol(NSAccessibilityNavigableStaticText)]) { + val = [child accessibilityValue]; + } + } + return val; } - (BOOL)accessibilityPerformIncrement @@ -68,4 +77,18 @@ return [super accessibilityParent]; } +- (void)postValueChanged +{ + AWT_ASSERT_APPKIT_THREAD; + NSAccessibilityPostNotification(self, NSAccessibilityValueChangedNotification); + NSArray *clist = [super accessibilityChildren]; + for (NSUInteger i = 0; i < [clist count]; i++) { + id child = [clist objectAtIndex:i]; + if ([child conformsToProtocol:@protocol(NSAccessibilityNavigableStaticText)]) { + NSAccessibilityPostNotification(child, NSAccessibilityLayoutChangedNotification); + [child suppressEditUpdates]; + } + } +} + @end diff --git a/test/jdk/javax/accessibility/JSpinner/CustomSpinnerAccessibilityTest.java b/test/jdk/javax/accessibility/JSpinner/CustomSpinnerAccessibilityTest.java new file mode 100644 index 00000000000..12c98c95110 --- /dev/null +++ b/test/jdk/javax/accessibility/JSpinner/CustomSpinnerAccessibilityTest.java @@ -0,0 +1,86 @@ +/* + * 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.awt.GridLayout; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.SpinnerListModel; + +/* + * @test + * @bug 8286258 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "mac") + * @summary Checks that JSpinner with custom model announces + * the value every time it is changed + * @run main/manual CustomSpinnerAccessibilityTest + */ + +public class CustomSpinnerAccessibilityTest extends JPanel { + private static final String INSTRUCTIONS = """ + 1. Turn on VoiceOver + 2. In the window named "Test UI" click on the text editor inside the + spinner component + 3. Using up and down arrows change current month + 4. Wait for the VoiceOver to finish speaking + 5. Repeat steps 3 and 4 couple more times + + If every time value of the spinner is changed VoiceOver + announces the new value click "Pass". + If instead the value is narrated only partially + and the new value is never fully narrated press "Fail". + """; + + public CustomSpinnerAccessibilityTest() { + super(new GridLayout(0, 2)); + String[] monthStrings = new java.text.DateFormatSymbols().getMonths(); + int lastIndex = monthStrings.length - 1; + if (monthStrings[lastIndex] == null + || monthStrings[lastIndex].length() <= 0) { + String[] tmp = new String[lastIndex]; + System.arraycopy(monthStrings, 0, + tmp, 0, lastIndex); + monthStrings = tmp; + } + + SpinnerListModel model = new SpinnerListModel(monthStrings); + JLabel label = new JLabel("Month: "); + add(label); + JSpinner spinner = new JSpinner(model); + label.setLabelFor(spinner); + add(spinner); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Custom Spinner Accessibility Test") + .instructions(INSTRUCTIONS) + .testUI(CustomSpinnerAccessibilityTest::new) + .build() + .awaitAndCheck(); + } +} From 5dfda66e13df5a88a66a6e4b1ae1bcd4e20ac674 Mon Sep 17 00:00:00 2001 From: Henry Jen Date: Thu, 22 Jan 2026 17:21:44 +0000 Subject: [PATCH 121/126] 8373928: 4 Dangling pointer defect groups in java.c Reviewed-by: bpb, alanb, jpai, jwaters --- src/java.base/share/native/libjli/java.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c index 6072bff50c6..4621ab588d1 100644 --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c @@ -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 @@ -1505,6 +1505,7 @@ InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) r = ifn->CreateJavaVM(pvm, (void **)penv, &args); JLI_MemFree(options); + options = NULL; return r == JNI_OK; } @@ -2203,6 +2204,7 @@ FreeKnownVMs() knownVMs[i].name = NULL; } JLI_MemFree(knownVMs); + knownVMs = NULL; } /* @@ -2276,8 +2278,9 @@ ShowSplashScreen() (void)UnsetEnv(SPLASH_JAR_ENV_ENTRY); JLI_MemFree(splash_jar_entry); + splash_jar_entry = NULL; JLI_MemFree(splash_file_entry); - + splash_file_entry = NULL; } static const char* GetFullVersion() From 96a2649e29b8b4ff9b65b2314d430bc7637c5c61 Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Thu, 22 Jan 2026 17:41:00 +0000 Subject: [PATCH 122/126] 8373408: SHA1withECDSA is not required for ECDHE and ECDSA Reviewed-by: djelinski, ascarpino --- src/java.base/share/classes/sun/security/ssl/JsseJce.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/JsseJce.java b/src/java.base/share/classes/sun/security/ssl/JsseJce.java index 3ffc2d84168..1e610eeab1d 100644 --- a/src/java.base/share/classes/sun/security/ssl/JsseJce.java +++ b/src/java.base/share/classes/sun/security/ssl/JsseJce.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, 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 @@ -165,7 +165,6 @@ final class JsseJce { static { boolean mediator = true; try { - Signature.getInstance(SIGNATURE_ECDSA); Signature.getInstance(SIGNATURE_RAWECDSA); KeyAgreement.getInstance("ECDH"); KeyFactory.getInstance("EC"); From f3121d10237a933087dde926f83a12ce826cde02 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 22 Jan 2026 20:16:44 +0000 Subject: [PATCH 123/126] 8373931: Test javax/sound/sampled/Clip/AutoCloseTimeCheck.java timed out Reviewed-by: dholmes, dnguyen, kizune --- test/jdk/javax/sound/sampled/Clip/AutoCloseTimeCheck.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/jdk/javax/sound/sampled/Clip/AutoCloseTimeCheck.java b/test/jdk/javax/sound/sampled/Clip/AutoCloseTimeCheck.java index f823175ede8..edacc50fa42 100644 --- a/test/jdk/javax/sound/sampled/Clip/AutoCloseTimeCheck.java +++ b/test/jdk/javax/sound/sampled/Clip/AutoCloseTimeCheck.java @@ -39,6 +39,7 @@ import static javax.sound.sampled.AudioSystem.NOT_SPECIFIED; /** * @test + * @key sound * @bug 8202264 */ public final class AutoCloseTimeCheck { From d6ebcf8a4f42b8e157083be90271e0df3b631033 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Thu, 22 Jan 2026 21:28:57 +0000 Subject: [PATCH 124/126] 8357471: GenShen: Share collector reserves between young and old Reviewed-by: wkemper --- .../shenandoahAdaptiveHeuristics.cpp | 7 +- .../shenandoahAdaptiveHeuristics.hpp | 6 +- .../shenandoahAggressiveHeuristics.cpp | 7 +- .../shenandoahAggressiveHeuristics.hpp | 6 +- .../shenandoahCompactHeuristics.cpp | 7 +- .../shenandoahCompactHeuristics.hpp | 6 +- .../shenandoahGenerationalHeuristics.cpp | 20 +- .../shenandoahGenerationalHeuristics.hpp | 2 +- .../heuristics/shenandoahGlobalHeuristics.cpp | 244 +++++++--- .../heuristics/shenandoahGlobalHeuristics.hpp | 6 +- .../heuristics/shenandoahHeuristics.cpp | 4 +- .../heuristics/shenandoahHeuristics.hpp | 13 +- .../heuristics/shenandoahOldHeuristics.cpp | 380 +++++++++------ .../heuristics/shenandoahOldHeuristics.hpp | 52 +- .../shenandoahPassiveHeuristics.cpp | 7 +- .../shenandoahPassiveHeuristics.hpp | 6 +- .../heuristics/shenandoahStaticHeuristics.cpp | 7 +- .../heuristics/shenandoahStaticHeuristics.hpp | 6 +- .../heuristics/shenandoahYoungHeuristics.cpp | 17 +- .../heuristics/shenandoahYoungHeuristics.hpp | 6 +- .../gc/shenandoah/shenandoahCollectionSet.cpp | 4 + .../gc/shenandoah/shenandoahCollectionSet.hpp | 7 + .../gc/shenandoah/shenandoahConcurrentGC.cpp | 12 +- .../share/gc/shenandoah/shenandoahFreeSet.cpp | 452 ++++++++++-------- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 94 ++-- .../share/gc/shenandoah/shenandoahFullGC.cpp | 10 +- .../gc/shenandoah/shenandoahGeneration.cpp | 135 +++--- .../gc/shenandoah/shenandoahGeneration.hpp | 21 +- .../shenandoahGenerationalFullGC.cpp | 10 +- .../shenandoah/shenandoahGenerationalHeap.cpp | 222 ++++++--- .../shenandoah/shenandoahGenerationalHeap.hpp | 2 +- .../share/gc/shenandoah/shenandoahHeap.cpp | 47 +- .../share/gc/shenandoah/shenandoahHeap.hpp | 2 + .../gc/shenandoah/shenandoahHeapRegion.cpp | 4 + .../share/gc/shenandoah/shenandoahOldGC.cpp | 9 +- .../gc/shenandoah/shenandoahOldGeneration.cpp | 3 +- .../gc/shenandoah/shenandoahOldGeneration.hpp | 4 +- .../shenandoah/shenandoahScanRemembered.cpp | 3 +- .../gc/shenandoah/shenandoahVerifier.cpp | 9 +- .../gc/shenandoah/shenandoah_globals.hpp | 35 +- .../test_shenandoahOldHeuristic.cpp | 48 +- 41 files changed, 1228 insertions(+), 714 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 7a8bd55c795..46d9f19d35f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -68,9 +68,9 @@ ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics(ShenandoahSpaceInfo* ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() {} -void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +size_t ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { size_t garbage_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100; // The logic for cset selection in adaptive is as follows: @@ -124,6 +124,7 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand cur_garbage = new_garbage; } } + return 0; } void ShenandoahAdaptiveHeuristics::record_cycle_start() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index 1ba18f37c2b..c4fdf819391 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -108,9 +108,9 @@ public: virtual ~ShenandoahAdaptiveHeuristics(); - virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; virtual void record_cycle_start() override; virtual void record_success_concurrent() override; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp index a833e39631c..990b59ec853 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp @@ -39,15 +39,16 @@ ShenandoahAggressiveHeuristics::ShenandoahAggressiveHeuristics(ShenandoahSpaceIn SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahEvacReserveOverflow); } -void ShenandoahAggressiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free) { +size_t ShenandoahAggressiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free) { for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); if (r->garbage() > 0) { cset->add_region(r); } } + return 0; } bool ShenandoahAggressiveHeuristics::should_start_gc() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp index 5075258f1ce..25c8635489f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp @@ -35,9 +35,9 @@ class ShenandoahAggressiveHeuristics : public ShenandoahHeuristics { public: ShenandoahAggressiveHeuristics(ShenandoahSpaceInfo* space_info); - virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free); + virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free); virtual bool should_start_gc(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp index 28673b28612..09a8394a4b1 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp @@ -76,9 +76,9 @@ bool ShenandoahCompactHeuristics::should_start_gc() { return ShenandoahHeuristics::should_start_gc(); } -void ShenandoahCompactHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +size_t ShenandoahCompactHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { // Do not select too large CSet that would overflow the available free space size_t max_cset = actual_free * 3 / 4; @@ -97,4 +97,5 @@ void ShenandoahCompactHeuristics::choose_collection_set_from_regiondata(Shenando cset->add_region(r); } } + return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp index 21ec99eabc0..4988d5d495d 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp @@ -37,9 +37,9 @@ public: virtual bool should_start_gc(); - virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free); + virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free); virtual const char* name() { return "Compact"; } virtual bool is_diagnostic() { return false; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index b14d72f249b..ee315ce5c7e 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -37,7 +37,7 @@ ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGen : ShenandoahAdaptiveHeuristics(generation), _generation(generation) { } -void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { +size_t ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { assert(collection_set->is_empty(), "Must be empty"); auto heap = ShenandoahGenerationalHeap::heap(); @@ -168,16 +168,12 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio byte_size_in_proper_unit(total_garbage), proper_unit_for_byte_size(total_garbage)); size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); - bool doing_promote_in_place = (humongous_regions_promoted + regular_regions_promoted_in_place > 0); - if (doing_promote_in_place || (preselected_candidates > 0) || (immediate_percent <= ShenandoahImmediateThreshold)) { - // Only young collections need to prime the collection set. - if (_generation->is_young()) { - heap->old_generation()->heuristics()->prime_collection_set(collection_set); - } + size_t add_regions_to_old = 0; + if (doing_promote_in_place || (preselected_candidates > 0) || (immediate_percent <= ShenandoahImmediateThreshold)) { // Call the subclasses to add young-gen regions into the collection set. - choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); + add_regions_to_old = choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); } if (collection_set->has_old_regions()) { @@ -194,6 +190,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio regular_regions_promoted_free, immediate_regions, immediate_garbage); + return add_regions_to_old; } @@ -210,13 +207,6 @@ size_t ShenandoahGenerationalHeuristics::add_preselected_regions_to_collection_s assert(ShenandoahGenerationalHeap::heap()->is_tenurable(r), "Preselected regions must have tenure age"); // Entire region will be promoted, This region does not impact young-gen or old-gen evacuation reserve. // This region has been pre-selected and its impact on promotion reserve is already accounted for. - - // r->used() is r->garbage() + r->get_live_data_bytes() - // Since all live data in this region is being evacuated from young-gen, it is as if this memory - // is garbage insofar as young-gen is concerned. Counting this as garbage reduces the need to - // reclaim highly utilized young-gen regions just for the sake of finding min_garbage to reclaim - // within young-gen memory. - cur_young_garbage += r->garbage(); cset->add_region(r); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index 31c016bb4b7..9b4c93af9b4 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -44,7 +44,7 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { public: explicit ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation); - void choose_collection_set(ShenandoahCollectionSet* collection_set) override; + size_t choose_collection_set(ShenandoahCollectionSet* collection_set) override; protected: ShenandoahGeneration* _generation; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index 51805b205dd..f47371c14d5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -24,6 +24,7 @@ */ #include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp" +#include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp" #include "gc/shenandoah/shenandoahGlobalGeneration.hpp" @@ -35,13 +36,14 @@ ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneratio } -void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +size_t ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { // Better select garbage-first regions QuickSort::sort(data, (int) size, compare_by_garbage); choose_global_collection_set(cset, data, size, actual_free, 0 /* cur_young_garbage */); + return 0; } @@ -49,94 +51,212 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti const ShenandoahHeuristics::RegionData* data, size_t size, size_t actual_free, size_t cur_young_garbage) const { + shenandoah_assert_heaplocked_or_safepoint(); auto heap = ShenandoahGenerationalHeap::heap(); + auto free_set = heap->free_set(); size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); size_t capacity = heap->soft_max_capacity(); + size_t garbage_threshold = region_size_bytes * ShenandoahGarbageThreshold / 100; size_t ignore_threshold = region_size_bytes * ShenandoahIgnoreGarbageThreshold / 100; size_t young_evac_reserve = heap->young_generation()->get_evacuation_reserve(); + size_t original_young_evac_reserve = young_evac_reserve; size_t old_evac_reserve = heap->old_generation()->get_evacuation_reserve(); - size_t max_young_cset = (size_t) (young_evac_reserve / ShenandoahEvacWaste); - size_t young_cur_cset = 0; - size_t max_old_cset = (size_t) (old_evac_reserve / ShenandoahOldEvacWaste); - size_t old_cur_cset = 0; + size_t old_promo_reserve = heap->old_generation()->get_promoted_reserve(); - // Figure out how many unaffiliated young regions are dedicated to mutator and to evacuator. Allow the young - // collector's unaffiliated regions to be transferred to old-gen if old-gen has more easily reclaimed garbage - // than young-gen. At the end of this cycle, any excess regions remaining in old-gen will be transferred back - // to young. Do not transfer the mutator's unaffiliated regions to old-gen. Those must remain available - // to the mutator as it needs to be able to consume this memory during concurrent GC. - - size_t unaffiliated_young_regions = heap->young_generation()->free_unaffiliated_regions(); + size_t unaffiliated_young_regions = free_set->collector_unaffiliated_regions(); size_t unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes; + size_t unaffiliated_old_regions = free_set->old_collector_unaffiliated_regions(); + size_t unaffiliated_old_memory = unaffiliated_old_regions * region_size_bytes; - if (unaffiliated_young_memory > max_young_cset) { - size_t unaffiliated_mutator_memory = unaffiliated_young_memory - max_young_cset; - unaffiliated_young_memory -= unaffiliated_mutator_memory; - unaffiliated_young_regions = unaffiliated_young_memory / region_size_bytes; // round down - unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes; + // Figure out how many unaffiliated regions are dedicated to Collector and OldCollector reserves. Let these + // be shuffled between young and old generations in order to expedite evacuation of whichever regions have the + // most garbage, regardless of whether these garbage-first regions reside in young or old generation. + // Excess reserves will be transferred back to the mutator after collection set has been chosen. At the end + // of evacuation, any reserves not consumed by evacuation will also be transferred to the mutator free set. + + // Truncate reserves to only target unaffiliated memory + size_t shared_reserve_regions = 0; + if (young_evac_reserve > unaffiliated_young_memory) { + shared_reserve_regions += unaffiliated_young_regions; + } else { + size_t delta_regions = young_evac_reserve / region_size_bytes; + shared_reserve_regions += delta_regions; } + young_evac_reserve = 0; + size_t total_old_reserve = old_evac_reserve + old_promo_reserve; + if (total_old_reserve > unaffiliated_old_memory) { + // Give all the unaffiliated memory to the shared reserves. Leave the rest for promo reserve. + shared_reserve_regions += unaffiliated_old_regions; + old_promo_reserve = total_old_reserve - unaffiliated_old_memory; + } else { + size_t delta_regions = old_evac_reserve / region_size_bytes; + shared_reserve_regions += delta_regions; + } + old_evac_reserve = 0; + assert(shared_reserve_regions <= + (heap->young_generation()->free_unaffiliated_regions() + heap->old_generation()->free_unaffiliated_regions()), + "simple math"); - // We'll affiliate these unaffiliated regions with either old or young, depending on need. - max_young_cset -= unaffiliated_young_memory; + size_t shared_reserves = shared_reserve_regions * region_size_bytes; + size_t committed_from_shared_reserves = 0; - // Keep track of how many regions we plan to transfer from young to old. - size_t regions_transferred_to_old = 0; + size_t promo_bytes = 0; + size_t old_evac_bytes = 0; + size_t young_evac_bytes = 0; - size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_young_cset; + size_t consumed_by_promo = 0; // promo_bytes * ShenandoahPromoEvacWaste + size_t consumed_by_old_evac = 0; // old_evac_bytes * ShenandoahOldEvacWaste + size_t consumed_by_young_evac = 0; // young_evac_bytes * ShenandoahEvacWaste + + // Of the memory reclaimed by GC, some of this will need to be reserved for the next GC collection. Use the current + // young reserve as an approximation of the future Collector reserve requirement. Try to end with at least + // (capacity * ShenandoahMinFreeThreshold) / 100 bytes available to the mutator. + size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + original_young_evac_reserve; size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0; - log_info(gc, ergo)("Adaptive CSet Selection for GLOBAL. Max Young Evacuation: %zu" - "%s, Max Old Evacuation: %zu%s, Max Either Evacuation: %zu%s, Actual Free: %zu%s.", - byte_size_in_proper_unit(max_young_cset), proper_unit_for_byte_size(max_young_cset), - byte_size_in_proper_unit(max_old_cset), proper_unit_for_byte_size(max_old_cset), - byte_size_in_proper_unit(unaffiliated_young_memory), proper_unit_for_byte_size(unaffiliated_young_memory), - byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free)); + size_t aged_regions_promoted = 0; + size_t young_regions_evacuated = 0; + size_t old_regions_evacuated = 0; + log_info(gc, ergo)("Adaptive CSet Selection for GLOBAL. Discretionary evacuation budget (for either old or young): %zu%s" + ", Actual Free: %zu%s.", + byte_size_in_proper_unit(shared_reserves), proper_unit_for_byte_size(shared_reserves), + byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free)); + + size_t cur_garbage = cur_young_garbage; for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); assert(!cset->is_preselected(r->index()), "There should be no preselected regions during GLOBAL GC"); bool add_region = false; - if (r->is_old() || heap->is_tenurable(r)) { - size_t new_cset = old_cur_cset + r->get_live_data_bytes(); - if ((r->garbage() > garbage_threshold)) { - while ((new_cset > max_old_cset) && (unaffiliated_young_regions > 0)) { - unaffiliated_young_regions--; - regions_transferred_to_old++; - max_old_cset += region_size_bytes / ShenandoahOldEvacWaste; + size_t region_garbage = r->garbage(); + size_t new_garbage = cur_garbage + region_garbage; + bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage); + size_t live_bytes = r->get_live_data_bytes(); + if (add_regardless || (region_garbage >= garbage_threshold)) { + if (r->is_old()) { + size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahOldEvacWaste); + size_t new_old_consumption = consumed_by_old_evac + anticipated_consumption; + size_t new_old_evac_reserve = old_evac_reserve; + size_t proposed_old_region_expansion = 0; + while ((new_old_consumption > new_old_evac_reserve) && (committed_from_shared_reserves < shared_reserves)) { + committed_from_shared_reserves += region_size_bytes; + proposed_old_region_expansion++; + new_old_evac_reserve += region_size_bytes; } - } - if ((new_cset <= max_old_cset) && (r->garbage() > garbage_threshold)) { - add_region = true; - old_cur_cset = new_cset; - } - } else { - assert(r->is_young() && !heap->is_tenurable(r), "DeMorgan's law (assuming r->is_affiliated)"); - size_t new_cset = young_cur_cset + r->get_live_data_bytes(); - size_t region_garbage = r->garbage(); - size_t new_garbage = cur_young_garbage + region_garbage; - bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage); - - if (add_regardless || (r->garbage() > garbage_threshold)) { - while ((new_cset > max_young_cset) && (unaffiliated_young_regions > 0)) { - unaffiliated_young_regions--; - max_young_cset += region_size_bytes / ShenandoahEvacWaste; + // If this region has free memory and we choose to place it in the collection set, its free memory is no longer + // available to hold promotion results. So we behave as if its free memory is consumed within the promotion reserve. + size_t anticipated_loss_from_promo_reserve = r->free(); + size_t new_promo_consumption = consumed_by_promo + anticipated_loss_from_promo_reserve; + size_t new_promo_reserve = old_promo_reserve; + while ((new_promo_consumption > new_promo_reserve) && (committed_from_shared_reserves < shared_reserves)) { + committed_from_shared_reserves += region_size_bytes; + proposed_old_region_expansion++; + new_promo_reserve += region_size_bytes; + } + if ((new_old_consumption <= new_old_evac_reserve) && (new_promo_consumption <= new_promo_reserve)) { + add_region = true; + old_evac_reserve = new_old_evac_reserve; + old_promo_reserve = new_promo_reserve; + old_evac_bytes += live_bytes; + consumed_by_old_evac = new_old_consumption; + consumed_by_promo = new_promo_consumption; + cur_garbage = new_garbage; + old_regions_evacuated++; + } else { + // We failed to sufficiently expand old so unwind proposed expansion + committed_from_shared_reserves -= proposed_old_region_expansion * region_size_bytes; + } + } else if (heap->is_tenurable(r)) { + size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahPromoEvacWaste); + size_t new_promo_consumption = consumed_by_promo + anticipated_consumption; + size_t new_promo_reserve = old_promo_reserve; + size_t proposed_old_region_expansion = 0; + while ((new_promo_consumption > new_promo_reserve) && (committed_from_shared_reserves < shared_reserves)) { + committed_from_shared_reserves += region_size_bytes; + proposed_old_region_expansion++; + new_promo_reserve += region_size_bytes; + } + if (new_promo_consumption <= new_promo_reserve) { + add_region = true; + old_promo_reserve = new_promo_reserve; + promo_bytes += live_bytes; + consumed_by_promo = new_promo_consumption; + cur_garbage = new_garbage; + aged_regions_promoted++; + } else { + // We failed to sufficiently expand old so unwind proposed expansion + committed_from_shared_reserves -= proposed_old_region_expansion * region_size_bytes; + } + } else { + assert(r->is_young() && !heap->is_tenurable(r), "DeMorgan's law (assuming r->is_affiliated)"); + size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahEvacWaste); + size_t new_young_evac_consumption = consumed_by_young_evac + anticipated_consumption; + size_t new_young_evac_reserve = young_evac_reserve; + size_t proposed_young_region_expansion = 0; + while ((new_young_evac_consumption > new_young_evac_reserve) && (committed_from_shared_reserves < shared_reserves)) { + committed_from_shared_reserves += region_size_bytes; + proposed_young_region_expansion++; + new_young_evac_reserve += region_size_bytes; + } + if (new_young_evac_consumption <= new_young_evac_reserve) { + add_region = true; + young_evac_reserve = new_young_evac_reserve; + young_evac_bytes += live_bytes; + consumed_by_young_evac = new_young_evac_consumption; + cur_garbage = new_garbage; + young_regions_evacuated++; + } else { + // We failed to sufficiently expand old so unwind proposed expansion + committed_from_shared_reserves -= proposed_young_region_expansion * region_size_bytes; } - } - if ((new_cset <= max_young_cset) && (add_regardless || (region_garbage > garbage_threshold))) { - add_region = true; - young_cur_cset = new_cset; - cur_young_garbage = new_garbage; } } if (add_region) { cset->add_region(r); } } - if (regions_transferred_to_old > 0) { - assert(young_evac_reserve > regions_transferred_to_old * region_size_bytes, "young reserve cannot be negative"); - heap->young_generation()->set_evacuation_reserve(young_evac_reserve - regions_transferred_to_old * region_size_bytes); - heap->old_generation()->set_evacuation_reserve(old_evac_reserve + regions_transferred_to_old * region_size_bytes); + + if (committed_from_shared_reserves < shared_reserves) { + // Give all the rest to promotion + old_promo_reserve += (shared_reserves - committed_from_shared_reserves); + // dead code: committed_from_shared_reserves = shared_reserves; } + + // Consider the effects of round-off: + // 1. We know that the sum over each evacuation mutiplied by Evacuation Waste is <= total evacuation reserve + // 2. However, the reserve for each individual evacuation may be rounded down. In the worst case, we will be over budget + // by the number of regions evacuated, since each region's reserve might be under-estimated by at most 1 + // 3. Likewise, if we take the sum of bytes evacuated and multiply this by the Evacuation Waste and then round down + // to nearest integer, the calculated reserve will underestimate the true reserve needs by at most 1. + // 4. This explains the adjustments to subtotals in the assert statements below. + assert(young_evac_bytes * ShenandoahEvacWaste <= young_evac_reserve + young_regions_evacuated, + "budget: %zu <= %zu", (size_t) (young_evac_bytes * ShenandoahEvacWaste), young_evac_reserve); + assert(old_evac_bytes * ShenandoahOldEvacWaste <= old_evac_reserve + old_regions_evacuated, + "budget: %zu <= %zu", (size_t) (old_evac_bytes * ShenandoahOldEvacWaste), old_evac_reserve); + assert(promo_bytes * ShenandoahPromoEvacWaste <= old_promo_reserve + aged_regions_promoted, + "budget: %zu <= %zu", (size_t) (promo_bytes * ShenandoahPromoEvacWaste), old_promo_reserve); + assert(young_evac_reserve + old_evac_reserve + old_promo_reserve <= + heap->young_generation()->get_evacuation_reserve() + heap->old_generation()->get_evacuation_reserve() + + heap->old_generation()->get_promoted_reserve(), "Exceeded budget"); + + if (heap->young_generation()->get_evacuation_reserve() < young_evac_reserve) { + size_t delta_bytes = young_evac_reserve - heap->young_generation()->get_evacuation_reserve(); + size_t delta_regions = delta_bytes / region_size_bytes; + size_t regions_to_transfer = MIN2(unaffiliated_old_regions, delta_regions); + log_info(gc)("Global GC moves %zu unaffiliated regions from old collector to young collector reserves", regions_to_transfer); + ssize_t negated_regions = -regions_to_transfer; + heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(negated_regions); + } else if (heap->young_generation()->get_evacuation_reserve() > young_evac_reserve) { + size_t delta_bytes = heap->young_generation()->get_evacuation_reserve() - young_evac_reserve; + size_t delta_regions = delta_bytes / region_size_bytes; + size_t regions_to_transfer = MIN2(unaffiliated_young_regions, delta_regions); + log_info(gc)("Global GC moves %zu unaffiliated regions from young collector to old collector reserves", regions_to_transfer); + heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(regions_to_transfer); + } + + heap->young_generation()->set_evacuation_reserve(young_evac_reserve); + heap->old_generation()->set_evacuation_reserve(old_evac_reserve); + heap->old_generation()->set_promoted_reserve(old_promo_reserve); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp index 1f95f75c521..e0513f60da9 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp @@ -39,9 +39,9 @@ class ShenandoahGlobalHeuristics : public ShenandoahGenerationalHeuristics { public: ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation); - void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; private: void choose_global_collection_set(ShenandoahCollectionSet* cset, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index eb740cfac61..aeb64b6f1df 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -72,7 +72,7 @@ ShenandoahHeuristics::~ShenandoahHeuristics() { FREE_C_HEAP_ARRAY(RegionGarbage, _region_data); } -void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { +size_t ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { ShenandoahHeap* heap = ShenandoahHeap::heap(); assert(collection_set->is_empty(), "Must be empty"); @@ -153,8 +153,8 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec if (immediate_percent <= ShenandoahImmediateThreshold) { choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); } - collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); + return 0; } void ShenandoahHeuristics::record_cycle_start() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index e1139765022..ae34a9743a9 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -183,9 +183,12 @@ protected: static int compare_by_garbage(RegionData a, RegionData b); - virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, - RegionData* data, size_t data_size, - size_t free) = 0; + // This is a helper function to choose_collection_set(), returning the number of regions that need to be transferred to + // the old reserve from the young reserve in order to effectively evacuate the chosen collection set. In non-generational + // mode, the return value is 0. + virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free) = 0; void adjust_penalty(intx step); @@ -233,7 +236,9 @@ public: virtual void record_requested_gc(); - virtual void choose_collection_set(ShenandoahCollectionSet* collection_set); + // Choose the collection set, returning the number of regions that need to be transferred to the old reserve from the young + // reserve in order to effectively evacuate the chosen collection set. In non-generational mode, the return value is 0. + virtual size_t choose_collection_set(ShenandoahCollectionSet* collection_set); virtual bool can_unload_classes(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index f2c6e427ea8..f47d0cbe819 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -26,9 +26,11 @@ #include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/log.hpp" #include "utilities/quickSort.hpp" @@ -77,15 +79,17 @@ ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* genera } bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* collection_set) { - if (unprocessed_old_collection_candidates() == 0) { - return false; - } + _mixed_evac_cset = collection_set; + _included_old_regions = 0; + _evacuated_old_bytes = 0; + _collected_old_bytes = 0; if (_old_generation->is_preparing_for_mark()) { // We have unprocessed old collection candidates, but the heuristic has given up on evacuating them. // This is most likely because they were _all_ pinned at the time of the last mixed evacuation (and // this in turn is most likely because there are just one or two candidate regions remaining). - log_info(gc, ergo)("Remaining " UINT32_FORMAT " old regions are being coalesced and filled", unprocessed_old_collection_candidates()); + log_info(gc, ergo)("Remaining " UINT32_FORMAT + " old regions are being coalesced and filled", unprocessed_old_collection_candidates()); return false; } @@ -111,150 +115,44 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll // of memory that can still be evacuated. We address this by reducing the evacuation budget by the amount // of live memory in that region and by the amount of unallocated memory in that region if the evacuation // budget is constrained by availability of free memory. - const size_t old_evacuation_reserve = _old_generation->get_evacuation_reserve(); - const size_t old_evacuation_budget = (size_t) ((double) old_evacuation_reserve / ShenandoahOldEvacWaste); - size_t unfragmented_available = _old_generation->free_unaffiliated_regions() * ShenandoahHeapRegion::region_size_bytes(); - size_t fragmented_available; - size_t excess_fragmented_available; + _old_evacuation_reserve = _old_generation->get_evacuation_reserve(); + _old_evacuation_budget = (size_t) ((double) _old_evacuation_reserve / ShenandoahOldEvacWaste); - if (unfragmented_available > old_evacuation_budget) { - unfragmented_available = old_evacuation_budget; - fragmented_available = 0; - excess_fragmented_available = 0; + // fragmented_available is the amount of memory within partially consumed old regions that may be required to + // hold the results of old evacuations. If all of the memory required by the old evacuation reserve is available + // in unfragmented regions (unaffiliated old regions), then fragmented_available is zero because we do not need + // to evacuate into the existing partially consumed old regions. + + // if fragmented_available is non-zero, excess_fragmented_old_budget represents the amount of fragmented memory + // that is available within old, but is not required to hold the resuilts of old evacuation. As old-gen regions + // are added into the collection set, their free memory is subtracted from excess_fragmented_old_budget until the + // excess is exhausted. For old-gen regions subsequently added to the collection set, their free memory is + // subtracted from fragmented_available and from the old_evacuation_budget (since the budget decreases when this + // fragmented_available memory decreases). After fragmented_available has been exhausted, any further old regions + // selected for the cset do not further decrease the old_evacuation_budget because all further evacuation is targeted + // to unfragmented regions. + + size_t unaffiliated_available = _old_generation->free_unaffiliated_regions() * ShenandoahHeapRegion::region_size_bytes(); + if (unaffiliated_available > _old_evacuation_reserve) { + _unspent_unfragmented_old_budget = _old_evacuation_budget; + _unspent_fragmented_old_budget = 0; + _excess_fragmented_old_budget = 0; } else { - assert(_old_generation->available() >= old_evacuation_budget, "Cannot budget more than is available"); - fragmented_available = _old_generation->available() - unfragmented_available; - assert(fragmented_available + unfragmented_available >= old_evacuation_budget, "Budgets do not add up"); - if (fragmented_available + unfragmented_available > old_evacuation_budget) { - excess_fragmented_available = (fragmented_available + unfragmented_available) - old_evacuation_budget; - fragmented_available -= excess_fragmented_available; + assert(_old_generation->available() >= _old_evacuation_reserve, "Cannot reserve more than is available"); + size_t affiliated_available = _old_generation->available() - unaffiliated_available; + assert(affiliated_available + unaffiliated_available >= _old_evacuation_reserve, "Budgets do not add up"); + if (affiliated_available + unaffiliated_available > _old_evacuation_reserve) { + _excess_fragmented_old_budget = (affiliated_available + unaffiliated_available) - _old_evacuation_reserve; + affiliated_available -= _excess_fragmented_old_budget; } + _unspent_fragmented_old_budget = (size_t) ((double) affiliated_available / ShenandoahOldEvacWaste); + _unspent_unfragmented_old_budget = (size_t) ((double) unaffiliated_available / ShenandoahOldEvacWaste); } - size_t remaining_old_evacuation_budget = old_evacuation_budget; - log_debug(gc)("Choose old regions for mixed collection: old evacuation budget: %zu%s, candidates: %u", - byte_size_in_proper_unit(old_evacuation_budget), proper_unit_for_byte_size(old_evacuation_budget), + log_debug(gc)("Choose old regions for mixed collection: old evacuation budget: " PROPERFMT ", candidates: %u", + PROPERFMTARGS(_old_evacuation_budget), unprocessed_old_collection_candidates()); - - size_t lost_evacuation_capacity = 0; - - // The number of old-gen regions that were selected as candidates for collection at the end of the most recent old-gen - // concurrent marking phase and have not yet been collected is represented by unprocessed_old_collection_candidates(). - // Candidate regions are ordered according to increasing amount of live data. If there is not sufficient room to - // evacuate region N, then there is no need to even consider evacuating region N+1. - while (unprocessed_old_collection_candidates() > 0) { - // Old collection candidates are sorted in order of decreasing garbage contained therein. - ShenandoahHeapRegion* r = next_old_collection_candidate(); - if (r == nullptr) { - break; - } - assert(r->is_regular(), "There should be no humongous regions in the set of mixed-evac candidates"); - - // If region r is evacuated to fragmented memory (to free memory within a partially used region), then we need - // to decrease the capacity of the fragmented memory by the scaled loss. - - const size_t live_data_for_evacuation = r->get_live_data_bytes(); - size_t lost_available = r->free(); - - if ((lost_available > 0) && (excess_fragmented_available > 0)) { - if (lost_available < excess_fragmented_available) { - excess_fragmented_available -= lost_available; - lost_evacuation_capacity -= lost_available; - lost_available = 0; - } else { - lost_available -= excess_fragmented_available; - lost_evacuation_capacity -= excess_fragmented_available; - excess_fragmented_available = 0; - } - } - size_t scaled_loss = (size_t) ((double) lost_available / ShenandoahOldEvacWaste); - if ((lost_available > 0) && (fragmented_available > 0)) { - if (scaled_loss + live_data_for_evacuation < fragmented_available) { - fragmented_available -= scaled_loss; - scaled_loss = 0; - } else { - // We will have to allocate this region's evacuation memory from unfragmented memory, so don't bother - // to decrement scaled_loss - } - } - if (scaled_loss > 0) { - // We were not able to account for the lost free memory within fragmented memory, so we need to take this - // allocation out of unfragmented memory. Unfragmented memory does not need to account for loss of free. - if (live_data_for_evacuation > unfragmented_available) { - // There is no room to evacuate this region or any that come after it in within the candidates array. - log_debug(gc, cset)("Not enough unfragmented memory (%zu) to hold evacuees (%zu) from region: (%zu)", - unfragmented_available, live_data_for_evacuation, r->index()); - break; - } else { - unfragmented_available -= live_data_for_evacuation; - } - } else { - // Since scaled_loss == 0, we have accounted for the loss of free memory, so we can allocate from either - // fragmented or unfragmented available memory. Use up the fragmented memory budget first. - size_t evacuation_need = live_data_for_evacuation; - - if (evacuation_need > fragmented_available) { - evacuation_need -= fragmented_available; - fragmented_available = 0; - } else { - fragmented_available -= evacuation_need; - evacuation_need = 0; - } - if (evacuation_need > unfragmented_available) { - // There is no room to evacuate this region or any that come after it in within the candidates array. - log_debug(gc, cset)("Not enough unfragmented memory (%zu) to hold evacuees (%zu) from region: (%zu)", - unfragmented_available, live_data_for_evacuation, r->index()); - break; - } else { - unfragmented_available -= evacuation_need; - // dead code: evacuation_need == 0; - } - } - collection_set->add_region(r); - included_old_regions++; - evacuated_old_bytes += live_data_for_evacuation; - collected_old_bytes += r->garbage(); - consume_old_collection_candidate(); - } - - if (_first_pinned_candidate != NOT_FOUND) { - // Need to deal with pinned regions - slide_pinned_regions_to_front(); - } - decrease_unprocessed_old_collection_candidates_live_memory(evacuated_old_bytes); - if (included_old_regions > 0) { - log_info(gc, ergo)("Old-gen piggyback evac (" UINT32_FORMAT " regions, evacuating " PROPERFMT ", reclaiming: " PROPERFMT ")", - included_old_regions, PROPERFMTARGS(evacuated_old_bytes), PROPERFMTARGS(collected_old_bytes)); - } - - if (unprocessed_old_collection_candidates() == 0) { - // We have added the last of our collection candidates to a mixed collection. - // Any triggers that occurred during mixed evacuations may no longer be valid. They can retrigger if appropriate. - clear_triggers(); - - _old_generation->complete_mixed_evacuations(); - } else if (included_old_regions == 0) { - // We have candidates, but none were included for evacuation - are they all pinned? - // or did we just not have enough room for any of them in this collection set? - // We don't want a region with a stuck pin to prevent subsequent old collections, so - // if they are all pinned we transition to a state that will allow us to make these uncollected - // (pinned) regions parsable. - if (all_candidates_are_pinned()) { - log_info(gc, ergo)("All candidate regions " UINT32_FORMAT " are pinned", unprocessed_old_collection_candidates()); - _old_generation->abandon_mixed_evacuations(); - } else { - log_info(gc, ergo)("No regions selected for mixed collection. " - "Old evacuation budget: " PROPERFMT ", Remaining evacuation budget: " PROPERFMT - ", Lost capacity: " PROPERFMT - ", Next candidate: " UINT32_FORMAT ", Last candidate: " UINT32_FORMAT, - PROPERFMTARGS(old_evacuation_reserve), - PROPERFMTARGS(remaining_old_evacuation_budget), - PROPERFMTARGS(lost_evacuation_capacity), - _next_old_collection_candidate, _last_old_collection_candidate); - } - } - - return (included_old_regions > 0); + return add_old_regions_to_cset(); } bool ShenandoahOldHeuristics::all_candidates_are_pinned() { @@ -328,6 +226,187 @@ void ShenandoahOldHeuristics::slide_pinned_regions_to_front() { _next_old_collection_candidate = write_index + 1; } +bool ShenandoahOldHeuristics::add_old_regions_to_cset() { + if (unprocessed_old_collection_candidates() == 0) { + return false; + } + _first_pinned_candidate = NOT_FOUND; + + // The number of old-gen regions that were selected as candidates for collection at the end of the most recent old-gen + // concurrent marking phase and have not yet been collected is represented by unprocessed_old_collection_candidates(). + // Candidate regions are ordered according to increasing amount of live data. If there is not sufficient room to + // evacuate region N, then there is no need to even consider evacuating region N+1. + while (unprocessed_old_collection_candidates() > 0) { + // Old collection candidates are sorted in order of decreasing garbage contained therein. + ShenandoahHeapRegion* r = next_old_collection_candidate(); + if (r == nullptr) { + break; + } + assert(r->is_regular(), "There should be no humongous regions in the set of mixed-evac candidates"); + + // If region r is evacuated to fragmented memory (to free memory within a partially used region), then we need + // to decrease the capacity of the fragmented memory by the scaled loss. + + const size_t live_data_for_evacuation = r->get_live_data_bytes(); + size_t lost_available = r->free(); + + ssize_t fragmented_delta = 0; + ssize_t unfragmented_delta = 0; + ssize_t excess_delta = 0; + + // We must decrease our mixed-evacuation budgets proportional to the lost available memory. This memory that is no + // longer available was likely "promised" to promotions, so we must decrease our mixed evacuations now. + // (e.g. if we loose 14 bytes of available old memory, we must decrease the evacuation budget by 10 bytes.) + size_t scaled_loss = (size_t) (((double) lost_available) / ShenandoahOldEvacWaste); + if (lost_available > 0) { + // We need to subtract lost_available from our working evacuation budgets + if (scaled_loss < _excess_fragmented_old_budget) { + excess_delta -= scaled_loss; + _excess_fragmented_old_budget -= scaled_loss; + } else { + excess_delta -= _excess_fragmented_old_budget; + _excess_fragmented_old_budget = 0; + } + + if (scaled_loss < _unspent_fragmented_old_budget) { + _unspent_fragmented_old_budget -= scaled_loss; + fragmented_delta = -scaled_loss; + scaled_loss = 0; + } else { + scaled_loss -= _unspent_fragmented_old_budget; + fragmented_delta = -_unspent_fragmented_old_budget; + _unspent_fragmented_old_budget = 0; + } + + if (scaled_loss < _unspent_unfragmented_old_budget) { + _unspent_unfragmented_old_budget -= scaled_loss; + unfragmented_delta = -scaled_loss; + scaled_loss = 0; + } else { + scaled_loss -= _unspent_unfragmented_old_budget; + fragmented_delta = -_unspent_unfragmented_old_budget; + _unspent_unfragmented_old_budget = 0; + } + } + + // Allocate replica from unfragmented memory if that exists + size_t evacuation_need = live_data_for_evacuation; + if (evacuation_need < _unspent_unfragmented_old_budget) { + _unspent_unfragmented_old_budget -= evacuation_need; + } else { + if (_unspent_unfragmented_old_budget > 0) { + evacuation_need -= _unspent_unfragmented_old_budget; + unfragmented_delta -= _unspent_unfragmented_old_budget; + _unspent_unfragmented_old_budget = 0; + } + // Take the remaining allocation out of fragmented available + if (_unspent_fragmented_old_budget > evacuation_need) { + _unspent_fragmented_old_budget -= evacuation_need; + } else { + // We cannot add this region into the collection set. We're done. Undo the adjustments to available. + _unspent_fragmented_old_budget -= fragmented_delta; + _unspent_unfragmented_old_budget -= unfragmented_delta; + _excess_fragmented_old_budget -= excess_delta; + break; + } + } + _mixed_evac_cset->add_region(r); + _included_old_regions++; + _evacuated_old_bytes += live_data_for_evacuation; + _collected_old_bytes += r->garbage(); + consume_old_collection_candidate(); + } + return true; +} + +bool ShenandoahOldHeuristics::finalize_mixed_evacs() { + if (_first_pinned_candidate != NOT_FOUND) { + // Need to deal with pinned regions + slide_pinned_regions_to_front(); + } + decrease_unprocessed_old_collection_candidates_live_memory(_evacuated_old_bytes); + if (_included_old_regions > 0) { + log_info(gc)("Old-gen mixed evac (%zu regions, evacuating %zu%s, reclaiming: %zu%s)", + _included_old_regions, + byte_size_in_proper_unit(_evacuated_old_bytes), proper_unit_for_byte_size(_evacuated_old_bytes), + byte_size_in_proper_unit(_collected_old_bytes), proper_unit_for_byte_size(_collected_old_bytes)); + } + + if (unprocessed_old_collection_candidates() == 0) { + // We have added the last of our collection candidates to a mixed collection. + // Any triggers that occurred during mixed evacuations may no longer be valid. They can retrigger if appropriate. + clear_triggers(); + _old_generation->complete_mixed_evacuations(); + } else if (_included_old_regions == 0) { + // We have candidates, but none were included for evacuation - are they all pinned? + // or did we just not have enough room for any of them in this collection set? + // We don't want a region with a stuck pin to prevent subsequent old collections, so + // if they are all pinned we transition to a state that will allow us to make these uncollected + // (pinned) regions parsable. + if (all_candidates_are_pinned()) { + log_info(gc)("All candidate regions " UINT32_FORMAT " are pinned", unprocessed_old_collection_candidates()); + _old_generation->abandon_mixed_evacuations(); + } else { + log_info(gc)("No regions selected for mixed collection. " + "Old evacuation budget: " PROPERFMT ", Next candidate: " UINT32_FORMAT ", Last candidate: " UINT32_FORMAT, + PROPERFMTARGS(_old_evacuation_reserve), + _next_old_collection_candidate, _last_old_collection_candidate); + } + } + return (_included_old_regions > 0); +} + +bool ShenandoahOldHeuristics::top_off_collection_set(size_t &add_regions_to_old) { + if (unprocessed_old_collection_candidates() == 0) { + add_regions_to_old = 0; + return false; + } else { + ShenandoahYoungGeneration* young_generation = _heap->young_generation(); + size_t young_unaffiliated_regions = young_generation->free_unaffiliated_regions(); + size_t max_young_cset = young_generation->get_evacuation_reserve(); + + // We have budgeted to assure the live_bytes_in_tenurable_regions() get evacuated into old generation. Young reserves + // only for untenurable region evacuations. + size_t planned_young_evac = _mixed_evac_cset->get_live_bytes_in_untenurable_regions(); + size_t consumed_from_young_cset = (size_t) (planned_young_evac * ShenandoahEvacWaste); + + size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + size_t regions_required_for_collector_reserve = (consumed_from_young_cset + region_size_bytes - 1) / region_size_bytes; + + assert(consumed_from_young_cset <= max_young_cset, "sanity"); + assert(max_young_cset <= young_unaffiliated_regions * region_size_bytes, "sanity"); + + size_t regions_for_old_expansion; + if (consumed_from_young_cset < max_young_cset) { + size_t excess_young_reserves = max_young_cset - consumed_from_young_cset; + // We can only transfer empty regions from young to old. Furthermore, we must be careful to assure that the young + // Collector reserve that remains after transfer is comprised entirely of empty (unaffiliated) regions. + size_t consumed_unaffiliated_regions = (consumed_from_young_cset + region_size_bytes - 1) / region_size_bytes; + size_t available_unaffiliated_regions = ((young_unaffiliated_regions > consumed_unaffiliated_regions)? + young_unaffiliated_regions - consumed_unaffiliated_regions: 0); + regions_for_old_expansion = MIN2(available_unaffiliated_regions, excess_young_reserves / region_size_bytes); + } else { + regions_for_old_expansion = 0; + } + if (regions_for_old_expansion > 0) { + log_info(gc)("Augmenting old-gen evacuation budget from unexpended young-generation reserve by %zu regions", + regions_for_old_expansion); + add_regions_to_old = regions_for_old_expansion; + size_t budget_supplement = region_size_bytes * regions_for_old_expansion; + size_t supplement_without_waste = (size_t) (((double) budget_supplement) / ShenandoahOldEvacWaste); + _old_evacuation_budget += supplement_without_waste; + _unspent_unfragmented_old_budget += supplement_without_waste; + _old_generation->augment_evacuation_reserve(budget_supplement); + young_generation->set_evacuation_reserve(max_young_cset - budget_supplement); + + return add_old_regions_to_cset(); + } else { + add_regions_to_old = 0; + return false; + } + } +} + void ShenandoahOldHeuristics::prepare_for_old_collections() { ShenandoahHeap* heap = ShenandoahHeap::heap(); @@ -336,7 +415,6 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { size_t immediate_garbage = 0; size_t immediate_regions = 0; size_t live_data = 0; - RegionData* candidates = _region_data; for (size_t i = 0; i < num_regions; i++) { ShenandoahHeapRegion* region = heap->get_region(i); @@ -355,10 +433,10 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { // else, regions that were promoted in place had 0 old live data at mark start if (region->is_regular() || region->is_regular_pinned()) { - // Only place regular or pinned regions with live data into the candidate set. - // Pinned regions cannot be evacuated, but we are not actually choosing candidates - // for the collection set here. That happens later during the next young GC cycle, - // by which time, the pinned region may no longer be pinned. + // Only place regular or pinned regions with live data into the candidate set. + // Pinned regions cannot be evacuated, but we are not actually choosing candidates + // for the collection set here. That happens later during the next young GC cycle, + // by which time, the pinned region may no longer be pinned. if (!region->has_live()) { assert(!region->is_pinned(), "Pinned region should have live (pinned) objects."); region->make_trash_immediate(); @@ -561,6 +639,7 @@ unsigned int ShenandoahOldHeuristics::get_coalesce_and_fill_candidates(Shenandoa void ShenandoahOldHeuristics::abandon_collection_candidates() { _last_old_collection_candidate = 0; _next_old_collection_candidate = 0; + _live_bytes_in_unprocessed_candidates = 0; _last_old_region = 0; } @@ -805,8 +884,9 @@ bool ShenandoahOldHeuristics::is_experimental() { return true; } -void ShenandoahOldHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, - ShenandoahHeuristics::RegionData* data, - size_t data_size, size_t free) { +size_t ShenandoahOldHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + ShenandoahHeuristics::RegionData* data, + size_t data_size, size_t free) { ShouldNotReachHere(); + return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index f38194c1ee7..97a5b1ebf24 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -102,6 +102,30 @@ private: size_t _fragmentation_first_old_region; size_t _fragmentation_last_old_region; + // State variables involved in construction of a mixed-evacuation collection set. These variables are initialized + // when client code invokes prime_collection_set(). They are consulted, and sometimes modified, when client code + // calls top_off_collection_set() to possibly expand the number of old-gen regions in a mixed evacuation cset, and by + // finalize_mixed_evacs(), which prepares the way for mixed evacuations to begin. + ShenandoahCollectionSet* _mixed_evac_cset; + size_t _evacuated_old_bytes; + size_t _collected_old_bytes; + size_t _included_old_regions; + size_t _old_evacuation_reserve; + size_t _old_evacuation_budget; + + // This represents the amount of memory that can be evacuated from old into initially empty regions during a mixed evacuation. + // This is the total amount of unfragmented free memory in old divided by ShenandoahOldEvacWaste. + size_t _unspent_unfragmented_old_budget; + + // This represents the amount of memory that can be evacuated from old into initially non-empty regions during a mixed + // evacuation. This is the total amount of initially fragmented free memory in old divided by ShenandoahOldEvacWaste. + size_t _unspent_fragmented_old_budget; + + // If there is more available memory in old than is required by the intended mixed evacuation, the amount of excess + // memory is represented by _excess_fragmented_old. To convert this value into a promotion budget, multiply by + // ShenandoahOldEvacWaste and divide by ShenandoahPromoWaste. + size_t _excess_fragmented_old_budget; + // The value of command-line argument ShenandoahOldGarbageThreshold represents the percent of garbage that must // be present within an old-generation region before that region is considered a good candidate for inclusion in // the collection set under normal circumstances. For our purposes, normal circustances are when the memory consumed @@ -131,7 +155,15 @@ private: void set_trigger_if_old_is_overgrown(); protected: - void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, RegionData* data, size_t data_size, size_t free) override; + size_t + choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, RegionData* data, size_t data_size, size_t free) override; + + // This internal helper routine adds as many mixed evacuation candidate regions as fit within the old-gen evacuation budget + // to the collection set. This may be called twice to prepare for any given mixed evacuation cycle, the first time with + // a conservative old evacuation budget, and the second time with a larger more aggressive old evacuation budget. Returns + // true iff we need to finalize mixed evacs. (If no regions are added to the collection set, there is no need to finalize + // mixed evacuations.) + bool add_old_regions_to_cset(); public: explicit ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap); @@ -139,8 +171,22 @@ public: // Prepare for evacuation of old-gen regions by capturing the mark results of a recently completed concurrent mark pass. void prepare_for_old_collections(); - // Return true iff the collection set is primed with at least one old-gen region. - bool prime_collection_set(ShenandoahCollectionSet* set); + // Initialize instance variables to support the preparation of a mixed-evacuation collection set. Adds as many + // old candidate regions into the collection set as can fit within the iniital conservative old evacuation budget. + // Returns true iff we need to finalize mixed evacs. + bool prime_collection_set(ShenandoahCollectionSet* collection_set); + + // If young evacuation did not consume all of its available evacuation reserve, add as many additional mixed- + // evacuation candidate regions into the collection set as will fit within this excess repurposed reserved. + // Returns true iff we need to finalize mixed evacs. Upon return, the var parameter regions_to_xfer holds the + // number of regions to transfer from young to old. + bool top_off_collection_set(size_t &add_regions_to_old); + + // Having added all eligible mixed-evacuation candidates to the collection set, this function updates the total count + // of how much old-gen memory remains to be evacuated and adjusts the representation of old-gen regions that remain to + // be evacuated, giving special attention to regions that are currently pinned. It outputs relevant log messages and + // returns true iff the collection set holds at least one unpinned mixed evacuation candidate. + bool finalize_mixed_evacs(); // How many old-collection candidates have not yet been processed? uint unprocessed_old_collection_candidates() const; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp index b5e9cc433ea..d4a38278161 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp @@ -50,9 +50,9 @@ bool ShenandoahPassiveHeuristics::should_degenerate_cycle() { return ShenandoahDegeneratedGC; } -void ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +size_t ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { assert(ShenandoahDegeneratedGC, "This path is only taken for Degenerated GC"); // Do not select too large CSet that would overflow the available free space. @@ -76,4 +76,5 @@ void ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(Shenando cset->add_region(r); } } + return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp index be4e91b1800..7a64fad7cc9 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp @@ -46,9 +46,9 @@ public: virtual bool should_degenerate_cycle(); - virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, - RegionData* data, size_t data_size, - size_t free); + virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free); virtual const char* name() { return "Passive"; } virtual bool is_diagnostic() { return true; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp index d4d66fef6a1..3843e434781 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp @@ -59,9 +59,9 @@ bool ShenandoahStaticHeuristics::should_start_gc() { return ShenandoahHeuristics::should_start_gc(); } -void ShenandoahStaticHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free) { +size_t ShenandoahStaticHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free) { size_t threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100; for (size_t idx = 0; idx < size; idx++) { @@ -70,4 +70,5 @@ void ShenandoahStaticHeuristics::choose_collection_set_from_regiondata(Shenandoa cset->add_region(r); } } + return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp index 24cb5547921..27dc3c8e0ae 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp @@ -40,9 +40,9 @@ public: virtual bool should_start_gc(); - virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free); + virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free); virtual const char* name() { return "Static"; } virtual bool is_diagnostic() { return false; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 15d1058d7cd..01c3873df72 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -33,11 +33,11 @@ #include "utilities/quickSort.hpp" ShenandoahYoungHeuristics::ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation) - : ShenandoahGenerationalHeuristics(generation) { + : ShenandoahGenerationalHeuristics(generation) { } -void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, +size_t ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, RegionData* data, size_t size, size_t actual_free) { // See comments in ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(): @@ -48,6 +48,8 @@ void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenandoah // array before younger regions that typically contain more garbage. This is one reason why, // for example, we continue examining regions even after rejecting a region that has // more live data than we can evacuate. + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + bool need_to_finalize_mixed = heap->old_generation()->heuristics()->prime_collection_set(cset); // Better select garbage-first regions QuickSort::sort(data, (int) size, compare_by_garbage); @@ -55,6 +57,17 @@ void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenandoah size_t cur_young_garbage = add_preselected_regions_to_collection_set(cset, data, size); choose_young_collection_set(cset, data, size, actual_free, cur_young_garbage); + + // Especially when young-gen trigger is expedited in order to finish mixed evacuations, there may not be + // enough consolidated garbage to make effective use of young-gen evacuation reserve. If there is still + // young-gen reserve available following selection of the young-gen collection set, see if we can use + // this memory to expand the old-gen evacuation collection set. + size_t add_regions_to_old; + need_to_finalize_mixed |= heap->old_generation()->heuristics()->top_off_collection_set(add_regions_to_old); + if (need_to_finalize_mixed) { + heap->old_generation()->heuristics()->finalize_mixed_evacs(); + } + return add_regions_to_old; } void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollectionSet* cset, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp index b9d64059680..85587887663 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp @@ -38,9 +38,9 @@ public: explicit ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation); - void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; bool should_start_gc() override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp index e58a7f40796..c1c6b876d90 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp @@ -50,6 +50,8 @@ ShenandoahCollectionSet::ShenandoahCollectionSet(ShenandoahHeap* heap, ReservedS _region_count(0), _old_garbage(0), _preselected_regions(nullptr), + _young_available_bytes_collected(0), + _old_available_bytes_collected(0), _current_index(0) { // The collection set map is reserved to cover the entire heap *and* zero addresses. @@ -104,6 +106,7 @@ void ShenandoahCollectionSet::add_region(ShenandoahHeapRegion* r) { } } else if (r->is_old()) { _old_bytes_to_evacuate += live; + _old_available_bytes_collected += free; _old_garbage += garbage; } @@ -140,6 +143,7 @@ void ShenandoahCollectionSet::clear() { _old_bytes_to_evacuate = 0; _young_available_bytes_collected = 0; + _old_available_bytes_collected = 0; _has_old_regions = false; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp index a1b77baa2d3..c99271de1fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp @@ -75,6 +75,10 @@ private: // should be subtracted from what's available. size_t _young_available_bytes_collected; + // When a region having memory available to be allocated is added to the collection set, the region's available memory + // should be subtracted from what's available. + size_t _old_available_bytes_collected; + shenandoah_padding(0); volatile size_t _current_index; shenandoah_padding(1); @@ -121,6 +125,9 @@ public: // Returns the amount of free bytes in young regions in the collection set. size_t get_young_available_bytes_collected() const { return _young_available_bytes_collected; } + // Returns the amount of free bytes in old regions in the collection set. + size_t get_old_available_bytes_collected() const { return _old_available_bytes_collected; } + // Returns the amount of garbage in old regions in the collection set. inline size_t get_old_garbage() const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index cee8727a3f4..364279deafe 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -204,9 +204,8 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { return false; } - entry_concurrent_update_refs_prepare(heap); - // Perform update-refs phase. + entry_concurrent_update_refs_prepare(heap); if (ShenandoahVerify) { vmop_entry_init_update_refs(); } @@ -227,6 +226,7 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // Update references freed up collection set, kick the cleanup to reclaim the space. entry_cleanup_complete(); } else { + _abbreviated = true; if (!entry_final_roots()) { assert(_degen_point != _degenerated_unset, "Need to know where to start degenerated cycle"); return false; @@ -235,7 +235,6 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { if (VerifyAfterGC) { vmop_entry_verify_final_roots(); } - _abbreviated = true; } // We defer generation resizing actions until after cset regions have been recycled. We do this even following an @@ -282,7 +281,6 @@ bool ShenandoahConcurrentGC::complete_abbreviated_cycle() { return true; } - void ShenandoahConcurrentGC::vmop_entry_init_mark() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->stw_collection_counters()); @@ -536,6 +534,12 @@ void ShenandoahConcurrentGC::entry_cleanup_early() { // This phase does not use workers, no need for setup heap->try_inject_alloc_failure(); op_cleanup_early(); + if (!heap->is_evacuation_in_progress()) { + // This is an abbreviated cycle. Rebuild the freeset in order to establish reserves for the next GC cycle. Doing + // the rebuild ASAP also expedites availability of immediate trash, reducing the likelihood that we will degenerate + // during promote-in-place processing. + heap->rebuild_free_set(true /*concurrent*/); + } } void ShenandoahConcurrentGC::entry_evacuate() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index a8c97801824..c4fe9103fcb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -326,7 +326,7 @@ void ShenandoahRegionPartitions::initialize_old_collector() { } void ShenandoahRegionPartitions::make_all_regions_unavailable() { - shenandoah_assert_heaplocked(); + shenandoah_assert_heaplocked_or_safepoint(); for (size_t partition_id = 0; partition_id < IntNumPartitions; partition_id++) { _membership[partition_id].clear_all(); _leftmosts[partition_id] = _max; @@ -439,6 +439,13 @@ void ShenandoahRegionPartitions::set_capacity_of(ShenandoahFreeSetPartitionId wh _available[int(which_partition)] = value - _used[int(which_partition)]; } +void ShenandoahRegionPartitions::set_used_by(ShenandoahFreeSetPartitionId which_partition, size_t value) { + shenandoah_assert_heaplocked(); + assert (which_partition < NumPartitions, "selected free set must be valid"); + _used[int(which_partition)] = value; + _available[int(which_partition)] = _capacity[int(which_partition)] - value; +} + void ShenandoahRegionPartitions::increase_capacity(ShenandoahFreeSetPartitionId which_partition, size_t bytes) { shenandoah_assert_heaplocked(); @@ -900,7 +907,7 @@ idx_t ShenandoahRegionPartitions::rightmost_empty(ShenandoahFreeSetPartitionId w #ifdef ASSERT -void ShenandoahRegionPartitions::assert_bounds(bool validate_totals) { +void ShenandoahRegionPartitions::assert_bounds() { size_t capacities[UIntNumPartitions]; size_t used[UIntNumPartitions]; @@ -936,7 +943,7 @@ void ShenandoahRegionPartitions::assert_bounds(bool validate_totals) { switch (partition) { case ShenandoahFreeSetPartitionId::NotFree: { - assert(!validate_totals || (capacity != _region_size_bytes), "Should not be retired if empty"); + assert(capacity != _region_size_bytes, "Should not be retired if empty"); ShenandoahHeapRegion* r = ShenandoahHeap::heap()->get_region(i); if (r->is_humongous()) { if (r->is_old()) { @@ -976,12 +983,12 @@ void ShenandoahRegionPartitions::assert_bounds(bool validate_totals) { case ShenandoahFreeSetPartitionId::Collector: case ShenandoahFreeSetPartitionId::OldCollector: { + ShenandoahHeapRegion* r = ShenandoahHeap::heap()->get_region(i); assert(capacity > 0, "free regions must have allocation capacity"); bool is_empty = (capacity == _region_size_bytes); regions[int(partition)]++; used[int(partition)] += _region_size_bytes - capacity; capacities[int(partition)] += _region_size_bytes; - if (i < leftmosts[int(partition)]) { leftmosts[int(partition)] = i; } @@ -1020,20 +1027,20 @@ void ShenandoahRegionPartitions::assert_bounds(bool validate_totals) { idx_t beg_off = leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)]; idx_t end_off = rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)]; assert (beg_off >= leftmost(ShenandoahFreeSetPartitionId::Mutator), - "Mutator free regions before the leftmost: %zd, bound %zd", + "Mutator free region before the leftmost: %zd, bound %zd", beg_off, leftmost(ShenandoahFreeSetPartitionId::Mutator)); assert (end_off <= rightmost(ShenandoahFreeSetPartitionId::Mutator), - "Mutator free regions past the rightmost: %zd, bound %zd", + "Mutator free region past the rightmost: %zd, bound %zd", end_off, rightmost(ShenandoahFreeSetPartitionId::Mutator)); beg_off = empty_leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)]; end_off = empty_rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)]; - assert (beg_off >= leftmost_empty(ShenandoahFreeSetPartitionId::Mutator), - "Mutator free empty regions before the leftmost: %zd, bound %zd", - beg_off, leftmost_empty(ShenandoahFreeSetPartitionId::Mutator)); - assert (end_off <= rightmost_empty(ShenandoahFreeSetPartitionId::Mutator), - "Mutator free empty regions past the rightmost: %zd, bound %zd", - end_off, rightmost_empty(ShenandoahFreeSetPartitionId::Mutator)); + assert (beg_off >= _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + "free empty region (%zd) before the leftmost bound %zd", + beg_off, _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)]); + assert (end_off <= _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + "free empty region (%zd) past the rightmost bound %zd", + end_off, _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)]); // Performance invariants. Failing these would not break the free partition, but performance would suffer. assert (leftmost(ShenandoahFreeSetPartitionId::Collector) <= _max, "leftmost in bounds: %zd < %zd", @@ -1053,20 +1060,20 @@ void ShenandoahRegionPartitions::assert_bounds(bool validate_totals) { beg_off = leftmosts[int(ShenandoahFreeSetPartitionId::Collector)]; end_off = rightmosts[int(ShenandoahFreeSetPartitionId::Collector)]; assert (beg_off >= leftmost(ShenandoahFreeSetPartitionId::Collector), - "Collector free regions before the leftmost: %zd, bound %zd", + "Collector free region before the leftmost: %zd, bound %zd", beg_off, leftmost(ShenandoahFreeSetPartitionId::Collector)); assert (end_off <= rightmost(ShenandoahFreeSetPartitionId::Collector), - "Collector free regions past the rightmost: %zd, bound %zd", + "Collector free region past the rightmost: %zd, bound %zd", end_off, rightmost(ShenandoahFreeSetPartitionId::Collector)); beg_off = empty_leftmosts[int(ShenandoahFreeSetPartitionId::Collector)]; end_off = empty_rightmosts[int(ShenandoahFreeSetPartitionId::Collector)]; assert (beg_off >= _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], - "Collector free empty regions before the leftmost: %zd, bound %zd", - beg_off, leftmost_empty(ShenandoahFreeSetPartitionId::Collector)); + "Collector free empty region before the leftmost: %zd, bound %zd", + beg_off, _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)]); assert (end_off <= _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], - "Collector free empty regions past the rightmost: %zd, bound %zd", - end_off, rightmost_empty(ShenandoahFreeSetPartitionId::Collector)); + "Collector free empty region past the rightmost: %zd, bound %zd", + end_off, _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)]); // Performance invariants. Failing these would not break the free partition, but performance would suffer. assert (leftmost(ShenandoahFreeSetPartitionId::OldCollector) <= _max, "OldCollector leftmost in bounds: %zd < %zd", @@ -1083,106 +1090,109 @@ void ShenandoahRegionPartitions::assert_bounds(bool validate_totals) { ShenandoahFreeSetPartitionId::OldCollector), "OldCollector rightmost region should be free: %zd", rightmost(ShenandoahFreeSetPartitionId::OldCollector)); + // Concurrent recycling of trash recycles a region (changing its state from is_trash to is_empty without the heap lock), + // If OldCollector partition is empty, leftmosts will both equal max, rightmosts will both equal zero. // Likewise for empty region partitions. beg_off = leftmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; end_off = rightmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; - assert (beg_off >= leftmost(ShenandoahFreeSetPartitionId::OldCollector), - "OldCollector free regions before the leftmost: %zd, bound %zd", + assert (beg_off >= leftmost(ShenandoahFreeSetPartitionId::OldCollector), "free regions before the leftmost: %zd, bound %zd", beg_off, leftmost(ShenandoahFreeSetPartitionId::OldCollector)); - assert (end_off <= rightmost(ShenandoahFreeSetPartitionId::OldCollector), - "OldCollector free regions past the rightmost: %zd, bound %zd", + assert (end_off <= rightmost(ShenandoahFreeSetPartitionId::OldCollector), "free regions past the rightmost: %zd, bound %zd", end_off, rightmost(ShenandoahFreeSetPartitionId::OldCollector)); beg_off = empty_leftmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; end_off = empty_rightmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; assert (beg_off >= _leftmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)], - "OldCollector free empty regions before the leftmost: %zd, bound %zd", - beg_off, leftmost_empty(ShenandoahFreeSetPartitionId::OldCollector)); + "free empty region (%zd) before the leftmost bound %zd, region %s trash", + beg_off, _leftmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)], + ((beg_off >= _max)? "out of bounds is not": + (ShenandoahHeap::heap()->get_region(_leftmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)])->is_trash()? + "is": "is not"))); assert (end_off <= _rightmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)], - "OldCollector free empty regions past the rightmost: %zd, bound %zd", - end_off, rightmost_empty(ShenandoahFreeSetPartitionId::OldCollector)); + "free empty region (%zd) past the rightmost bound %zd, region %s trash", + end_off, _rightmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)], + ((end_off < 0)? "out of bounds is not" : + (ShenandoahHeap::heap()->get_region(_rightmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)])->is_trash()? + "is": "is not"))); - if (validate_totals) { - // young_retired_regions need to be added to either Mutator or Collector partitions, 100% used. - // Give enough of young_retired_regions, young_retired_capacity, young_retired_user - // to the Mutator partition to top it off so that it matches the running totals. - // - // Give any remnants to the Collector partition. After topping off the Collector partition, its values - // should also match running totals. + // young_retired_regions need to be added to either Mutator or Collector partitions, 100% used. + // Give enough of young_retired_regions, young_retired_capacity, young_retired_user + // to the Mutator partition to top it off so that it matches the running totals. + // + // Give any remnants to the Collector partition. After topping off the Collector partition, its values + // should also match running totals. + assert(young_retired_regions * _region_size_bytes == young_retired_capacity, "sanity"); + assert(young_retired_capacity == young_retired_used, "sanity"); - assert(young_retired_regions * _region_size_bytes == young_retired_capacity, "sanity"); - assert(young_retired_capacity == young_retired_used, "sanity"); + assert(capacities[int(ShenandoahFreeSetPartitionId::OldCollector)] + == _capacity[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old collector capacities must match (%zu != %zu)", + capacities[int(ShenandoahFreeSetPartitionId::OldCollector)], + _capacity[int(ShenandoahFreeSetPartitionId::OldCollector)]); + assert(used[int(ShenandoahFreeSetPartitionId::OldCollector)] + == _used[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old collector used must match"); + assert(regions[int(ShenandoahFreeSetPartitionId::OldCollector)] + == _capacity[int(ShenandoahFreeSetPartitionId::OldCollector)] / _region_size_bytes, "Old collector regions must match"); + assert(_capacity[int(ShenandoahFreeSetPartitionId::OldCollector)] + >= _used[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old Collector capacity must be >= used"); + assert(_available[int(ShenandoahFreeSetPartitionId::OldCollector)] == + (_capacity[int(ShenandoahFreeSetPartitionId::OldCollector)] - _used[int(ShenandoahFreeSetPartitionId::OldCollector)]), + "Old Collector available must equal capacity minus used"); + assert(_humongous_waste[int(ShenandoahFreeSetPartitionId::OldCollector)] == + humongous_waste[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old Collector humongous waste must match"); + assert(_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] >= capacities[int(ShenandoahFreeSetPartitionId::Mutator)], + "Capacity total must be >= counted tally"); + size_t mutator_capacity_shortfall = + _capacity[int(ShenandoahFreeSetPartitionId::Mutator)] - capacities[int(ShenandoahFreeSetPartitionId::Mutator)]; + assert(mutator_capacity_shortfall <= young_retired_capacity, "sanity"); + capacities[int(ShenandoahFreeSetPartitionId::Mutator)] += mutator_capacity_shortfall; + young_retired_capacity -= mutator_capacity_shortfall; + capacities[int(ShenandoahFreeSetPartitionId::Collector)] += young_retired_capacity; - assert(capacities[int(ShenandoahFreeSetPartitionId::OldCollector)] - == _capacity[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old collector capacities must match"); - assert(used[int(ShenandoahFreeSetPartitionId::OldCollector)] - == _used[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old collector used must match"); - assert(regions[int(ShenandoahFreeSetPartitionId::OldCollector)] - == _capacity[int(ShenandoahFreeSetPartitionId::OldCollector)] / _region_size_bytes, "Old collector regions must match"); - assert(_capacity[int(ShenandoahFreeSetPartitionId::OldCollector)] - >= _used[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old Collector capacity must be >= used"); - assert(_available[int(ShenandoahFreeSetPartitionId::OldCollector)] == - (_capacity[int(ShenandoahFreeSetPartitionId::OldCollector)] - _used[int(ShenandoahFreeSetPartitionId::OldCollector)]), - "Old Collector available must equal capacity minus used"); - assert(_humongous_waste[int(ShenandoahFreeSetPartitionId::OldCollector)] == - humongous_waste[int(ShenandoahFreeSetPartitionId::OldCollector)], "Old Collector humongous waste must match"); + assert(_used[int(ShenandoahFreeSetPartitionId::Mutator)] >= used[int(ShenandoahFreeSetPartitionId::Mutator)], + "Used total must be >= counted tally"); + size_t mutator_used_shortfall = + _used[int(ShenandoahFreeSetPartitionId::Mutator)] - used[int(ShenandoahFreeSetPartitionId::Mutator)]; + assert(mutator_used_shortfall <= young_retired_used, "sanity"); + used[int(ShenandoahFreeSetPartitionId::Mutator)] += mutator_used_shortfall; + young_retired_used -= mutator_used_shortfall; + used[int(ShenandoahFreeSetPartitionId::Collector)] += young_retired_used; - assert(_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] >= capacities[int(ShenandoahFreeSetPartitionId::Mutator)], - "Capacity total must be >= counted tally"); - size_t mutator_capacity_shortfall = - _capacity[int(ShenandoahFreeSetPartitionId::Mutator)] - capacities[int(ShenandoahFreeSetPartitionId::Mutator)]; - assert(mutator_capacity_shortfall <= young_retired_capacity, "sanity"); - capacities[int(ShenandoahFreeSetPartitionId::Mutator)] += mutator_capacity_shortfall; - young_retired_capacity -= mutator_capacity_shortfall; - capacities[int(ShenandoahFreeSetPartitionId::Collector)] += young_retired_capacity; + assert(_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] / _region_size_bytes + >= regions[int(ShenandoahFreeSetPartitionId::Mutator)], "Region total must be >= counted tally"); + size_t mutator_regions_shortfall = (_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] / _region_size_bytes + - regions[int(ShenandoahFreeSetPartitionId::Mutator)]); + assert(mutator_regions_shortfall <= young_retired_regions, "sanity"); + regions[int(ShenandoahFreeSetPartitionId::Mutator)] += mutator_regions_shortfall; + young_retired_regions -= mutator_regions_shortfall; + regions[int(ShenandoahFreeSetPartitionId::Collector)] += young_retired_regions; + assert(capacities[int(ShenandoahFreeSetPartitionId::Collector)] == _capacity[int(ShenandoahFreeSetPartitionId::Collector)], + "Collector capacities must match"); + assert(used[int(ShenandoahFreeSetPartitionId::Collector)] == _used[int(ShenandoahFreeSetPartitionId::Collector)], + "Collector used must match"); + assert(regions[int(ShenandoahFreeSetPartitionId::Collector)] + == _capacity[int(ShenandoahFreeSetPartitionId::Collector)] / _region_size_bytes, "Collector regions must match"); + assert(_capacity[int(ShenandoahFreeSetPartitionId::Collector)] >= _used[int(ShenandoahFreeSetPartitionId::Collector)], + "Collector Capacity must be >= used"); + assert(_available[int(ShenandoahFreeSetPartitionId::Collector)] == + (_capacity[int(ShenandoahFreeSetPartitionId::Collector)] - _used[int(ShenandoahFreeSetPartitionId::Collector)]), + "Collector Available must equal capacity minus used"); - assert(_used[int(ShenandoahFreeSetPartitionId::Mutator)] >= used[int(ShenandoahFreeSetPartitionId::Mutator)], - "Used total must be >= counted tally"); - size_t mutator_used_shortfall = - _used[int(ShenandoahFreeSetPartitionId::Mutator)] - used[int(ShenandoahFreeSetPartitionId::Mutator)]; - assert(mutator_used_shortfall <= young_retired_used, "sanity"); - used[int(ShenandoahFreeSetPartitionId::Mutator)] += mutator_used_shortfall; - young_retired_used -= mutator_used_shortfall; - used[int(ShenandoahFreeSetPartitionId::Collector)] += young_retired_used; - - assert(_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] / _region_size_bytes - >= regions[int(ShenandoahFreeSetPartitionId::Mutator)], "Region total must be >= counted tally"); - size_t mutator_regions_shortfall = (_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] / _region_size_bytes - - regions[int(ShenandoahFreeSetPartitionId::Mutator)]); - assert(mutator_regions_shortfall <= young_retired_regions, "sanity"); - regions[int(ShenandoahFreeSetPartitionId::Mutator)] += mutator_regions_shortfall; - young_retired_regions -= mutator_regions_shortfall; - regions[int(ShenandoahFreeSetPartitionId::Collector)] += young_retired_regions; - - assert(capacities[int(ShenandoahFreeSetPartitionId::Collector)] == _capacity[int(ShenandoahFreeSetPartitionId::Collector)], - "Collector capacities must match"); - assert(used[int(ShenandoahFreeSetPartitionId::Collector)] == _used[int(ShenandoahFreeSetPartitionId::Collector)], - "Collector used must match"); - assert(regions[int(ShenandoahFreeSetPartitionId::Collector)] - == _capacity[int(ShenandoahFreeSetPartitionId::Collector)] / _region_size_bytes, "Collector regions must match"); - assert(_capacity[int(ShenandoahFreeSetPartitionId::Collector)] >= _used[int(ShenandoahFreeSetPartitionId::Collector)], - "Collector Capacity must be >= used"); - assert(_available[int(ShenandoahFreeSetPartitionId::Collector)] == - (_capacity[int(ShenandoahFreeSetPartitionId::Collector)] - _used[int(ShenandoahFreeSetPartitionId::Collector)]), - "Collector Available must equal capacity minus used"); - - assert(capacities[int(ShenandoahFreeSetPartitionId::Mutator)] == _capacity[int(ShenandoahFreeSetPartitionId::Mutator)], - "Mutator capacities must match"); - assert(used[int(ShenandoahFreeSetPartitionId::Mutator)] == _used[int(ShenandoahFreeSetPartitionId::Mutator)], - "Mutator used must match"); - assert(regions[int(ShenandoahFreeSetPartitionId::Mutator)] - == _capacity[int(ShenandoahFreeSetPartitionId::Mutator)] / _region_size_bytes, "Mutator regions must match"); - assert(_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] >= _used[int(ShenandoahFreeSetPartitionId::Mutator)], - "Mutator capacity must be >= used"); - assert(_available[int(ShenandoahFreeSetPartitionId::Mutator)] == - (_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] - _used[int(ShenandoahFreeSetPartitionId::Mutator)]), - "Mutator available must equal capacity minus used"); - assert(_humongous_waste[int(ShenandoahFreeSetPartitionId::Mutator)] == young_humongous_waste, - "Mutator humongous waste must match"); - } + assert(capacities[int(ShenandoahFreeSetPartitionId::Mutator)] == _capacity[int(ShenandoahFreeSetPartitionId::Mutator)], + "Mutator capacities must match"); + assert(used[int(ShenandoahFreeSetPartitionId::Mutator)] == _used[int(ShenandoahFreeSetPartitionId::Mutator)], + "Mutator used must match"); + assert(regions[int(ShenandoahFreeSetPartitionId::Mutator)] + == _capacity[int(ShenandoahFreeSetPartitionId::Mutator)] / _region_size_bytes, "Mutator regions must match"); + assert(_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] >= _used[int(ShenandoahFreeSetPartitionId::Mutator)], + "Mutator capacity must be >= used"); + assert(_available[int(ShenandoahFreeSetPartitionId::Mutator)] == + (_capacity[int(ShenandoahFreeSetPartitionId::Mutator)] - _used[int(ShenandoahFreeSetPartitionId::Mutator)]), + "Mutator available must equal capacity minus used"); + assert(_humongous_waste[int(ShenandoahFreeSetPartitionId::Mutator)] == young_humongous_waste, + "Mutator humongous waste must match"); } #endif @@ -1206,6 +1216,36 @@ ShenandoahFreeSet::ShenandoahFreeSet(ShenandoahHeap* heap, size_t max_regions) : clear_internal(); } +void ShenandoahFreeSet::move_unaffiliated_regions_from_collector_to_old_collector(ssize_t count) { + shenandoah_assert_heaplocked(); + size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + + size_t old_capacity = _partitions.get_capacity(ShenandoahFreeSetPartitionId::OldCollector); + size_t collector_capacity = _partitions.get_capacity(ShenandoahFreeSetPartitionId::Collector); + if (count > 0) { + size_t ucount = count; + size_t bytes_moved = ucount * region_size_bytes; + assert(collector_capacity >= bytes_moved, "Cannot transfer"); + assert(_partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::Collector) >= ucount, + "Cannot transfer %zu of %zu", ucount, _partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::Collector)); + _partitions.decrease_empty_region_counts(ShenandoahFreeSetPartitionId::Collector, ucount); + _partitions.set_capacity_of(ShenandoahFreeSetPartitionId::Collector, collector_capacity - bytes_moved); + _partitions.set_capacity_of(ShenandoahFreeSetPartitionId::OldCollector, old_capacity + bytes_moved); + _partitions.increase_empty_region_counts(ShenandoahFreeSetPartitionId::OldCollector, ucount); + } else if (count < 0) { + size_t ucount = -count; + size_t bytes_moved = ucount * region_size_bytes; + assert(old_capacity >= bytes_moved, "Cannot transfer"); + assert(_partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::OldCollector) >= ucount, + "Cannot transfer %zu of %zu", ucount, _partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::OldCollector)); + _partitions.decrease_empty_region_counts(ShenandoahFreeSetPartitionId::OldCollector, ucount); + _partitions.set_capacity_of(ShenandoahFreeSetPartitionId::OldCollector, old_capacity - bytes_moved); + _partitions.set_capacity_of(ShenandoahFreeSetPartitionId::Collector, collector_capacity + bytes_moved); + _partitions.increase_empty_region_counts(ShenandoahFreeSetPartitionId::Collector, ucount); + } + // else, do nothing +} + // was pip_pad_bytes void ShenandoahFreeSet::add_promoted_in_place_region_to_old_collector(ShenandoahHeapRegion* region) { shenandoah_assert_heaplocked(); @@ -1261,7 +1301,7 @@ void ShenandoahFreeSet::add_promoted_in_place_region_to_old_collector(Shenandoah /* CollectorSizeChanged */ true, /* OldCollectorSizeChanged */ true, /* AffiliatedChangesAreYoungNeutral */ false, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ true>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); } template @@ -1496,9 +1536,12 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah return nullptr; } HeapWord* result = nullptr; + // We must call try_recycle_under_lock() even if !r->is_trash(). The reason is that if r is being recycled at this + // moment by a GC worker thread, it may appear to be not trash even though it has not yet been fully recycled. If + // we proceed without waiting for the worker to finish recycling the region, the worker thread may overwrite the + // region's affiliation with FREE after we set the region's affiliation to req.afiliation() below r->try_recycle_under_lock(); in_new_region = r->is_empty(); - if (in_new_region) { log_debug(gc, free)("Using new region (%zu) for %s (" PTR_FORMAT ").", r->index(), req.type_string(), p2i(&req)); @@ -1668,7 +1711,7 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah default: assert(false, "won't happen"); } - _partitions.assert_bounds(true); + _partitions.assert_bounds(); return result; } @@ -1799,6 +1842,7 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req, bo increase_bytes_allocated(waste_bytes); } } + _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, total_used); increase_bytes_allocated(total_used); req.set_actual_size(words_size); @@ -1819,14 +1863,16 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req, bo /* CollectorSizeChanged */ false, /* OldCollectorSizeChanged */ false, /* AffiliatedChangesAreYoungNeutral */ false, /* AffiliatedChangesAreGlobalNeutral */ false, /* UnaffiliatedChangesAreYoungNeutral */ false>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); return _heap->get_region(beg)->bottom(); } class ShenandoahRecycleTrashedRegionClosure final : public ShenandoahHeapRegionClosure { public: void heap_region_do(ShenandoahHeapRegion* r) { - r->try_recycle(); + if (r->is_trash()) { + r->try_recycle(); + } } bool is_thread_safe() { @@ -1861,7 +1907,7 @@ bool ShenandoahFreeSet::transfer_one_region_from_mutator_to_old_collector(size_t /* CollectorSizeChanged */ false, /* OldCollectorSizeChanged */ true, /* AffiliatedChangesAreYoungNeutral */ true, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ false>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); return true; } else { return false; @@ -1914,7 +1960,7 @@ bool ShenandoahFreeSet::flip_to_old_gc(ShenandoahHeapRegion* r) { /* CollectorSizeChanged */ false, /* OldCollectorSizeChanged */ true, /* AffiliatedChangesAreYoungNeutral */ true, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ false>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); // 4. Do not adjust capacities for generations, we just swapped the regions that have already // been accounted for. However, we should adjust the evacuation reserves as those may have changed. shenandoah_assert_heaplocked(); @@ -1945,7 +1991,7 @@ void ShenandoahFreeSet::flip_to_gc(ShenandoahHeapRegion* r) { /* CollectorSizeChanged */ true, /* OldCollectorSizeChanged */ false, /* AffiliatedChangesAreYoungNeutral */ true, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ true>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); // We do not ensure that the region is no longer trash, relying on try_allocate_in(), which always comes next, // to recycle trash before attempting to allocate anything in the region. } @@ -2025,16 +2071,23 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_r for (size_t idx = 0; idx < num_regions; idx++) { ShenandoahHeapRegion* region = _heap->get_region(idx); if (region->is_trash()) { - // Trashed regions represent immediate garbage identified by final mark and regions that had been in the collection - // partition but have not yet been "cleaned up" following update refs. + // Trashed regions represent regions that had been in the collection set (or may have been identified as immediate garbage) + // but have not yet been "cleaned up". The cset regions are not "trashed" until we have finished update refs. if (region->is_old()) { + // We're going to place this region into the Mutator set. We increment old_trashed_regions because this count represents + // regions that the old generation is entitled to without any transfer from young. We do not place this region into + // the OldCollector partition at this time. Instead, we let reserve_regions() decide whether to place this region + // into the OldCollector partition. Deferring the decision allows reserve_regions() to more effectively pack the + // OldCollector regions into high-address memory. We do not adjust capacities of old and young generations at this + // time. At the end of finish_rebuild(), the capacities are adjusted based on the results of reserve_regions(). old_trashed_regions++; } else { assert(region->is_young(), "Trashed region should be old or young"); young_trashed_regions++; } } else if (region->is_old()) { - // count both humongous and regular regions, but don't count trash (cset) regions. + // We count humongous and regular regions as "old regions". We do not count trashed regions that are old. Those + // are counted (above) as old_trashed_regions. old_region_count++; if (first_old_region > idx) { first_old_region = idx; @@ -2048,7 +2101,7 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_r size_t ac = alloc_capacity(region); if (ac >= PLAB::min_size() * HeapWordSize) { if (region->is_trash() || !region->is_old()) { - // Both young and old collected regions (trashed) are placed into the Mutator set + // Both young and old (possibly immediately) collected regions (trashed) are placed into the Mutator set _partitions.raw_assign_membership(idx, ShenandoahFreeSetPartitionId::Mutator); if (idx < mutator_leftmost) { mutator_leftmost = idx; @@ -2111,10 +2164,19 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_r assert(_partitions.membership(idx) == ShenandoahFreeSetPartitionId::NotFree, "Region should have been retired"); size_t humongous_waste_bytes = 0; if (region->is_humongous_start()) { - oop obj = cast_to_oop(region->bottom()); - size_t byte_size = obj->size() * HeapWordSize; - size_t region_span = ShenandoahHeapRegion::required_regions(byte_size); - humongous_waste_bytes = region_span * ShenandoahHeapRegion::region_size_bytes() - byte_size; + // Since rebuild does not necessarily happen at a safepoint, a newly allocated humongous object may not have been + // fully initialized. Therefore, we cannot safely consult its header. + ShenandoahHeapRegion* last_of_humongous_continuation = region; + size_t next_idx; + for (next_idx = idx + 1; next_idx < num_regions; next_idx++) { + ShenandoahHeapRegion* humongous_cont_candidate = _heap->get_region(next_idx); + if (!humongous_cont_candidate->is_humongous_continuation()) { + break; + } + last_of_humongous_continuation = humongous_cont_candidate; + } + // For humongous regions, used() is established while holding the global heap lock so it is reliable here + humongous_waste_bytes = ShenandoahHeapRegion::region_size_bytes() - last_of_humongous_continuation->used(); } if (region->is_old()) { old_collector_used += region_size_bytes; @@ -2183,7 +2245,7 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_trashed_r /* CollectorSizeChanged */ true, /* OldCollectorSizeChanged */ true, /* AffiliatedChangesAreYoungNeutral */ false, /* AffiliatedChangesAreGlobalNeutral */ false, /* UnaffiliatedChangesAreYoungNeutral */ false>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); #ifdef ASSERT if (_heap->mode()->is_generational()) { assert(young_affiliated_regions() == _heap->young_generation()->get_affiliated_region_count(), "sanity"); @@ -2221,7 +2283,7 @@ void ShenandoahFreeSet::transfer_humongous_regions_from_mutator_to_old_collector /* CollectorSizeChanged */ false, /* OldCollectorSizeChanged */ true, /* AffiliatedChangesAreYoungNeutral */ false, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ true>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); // global_used is unaffected by this transfer // No need to adjust ranges because humongous regions are not allocatable @@ -2303,7 +2365,7 @@ void ShenandoahFreeSet::transfer_empty_regions_from_to(ShenandoahFreeSetPartitio /* UnaffiliatedChangesAreYoungNeutral */ true>(); } } - _partitions.assert_bounds(true); + _partitions.assert_bounds(); } // Returns number of regions transferred, adds transferred bytes to var argument bytes_transferred @@ -2370,7 +2432,7 @@ size_t ShenandoahFreeSet::transfer_empty_regions_from_collector_set_to_mutator_s /* AffiliatedChangesAreYoungNeutral */ true, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ true>(); } - _partitions.assert_bounds(true); + _partitions.assert_bounds(); return transferred_regions; } @@ -2445,7 +2507,7 @@ transfer_non_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPa /* AffiliatedChangesAreYoungNeutral */ true, /* AffiliatedChangesAreGlobalNeutral */ true, /* UnaffiliatedChangesAreYoungNeutral */ true>(); } - _partitions.assert_bounds(true); + _partitions.assert_bounds(); return transferred_regions; } @@ -2507,14 +2569,13 @@ void ShenandoahFreeSet::prepare_to_rebuild(size_t &young_trashed_regions, size_t first_old_region, last_old_region, old_region_count); } -void ShenandoahFreeSet::finish_rebuild(size_t young_trashed_regions, size_t old_trashed_regions, size_t old_region_count, - bool have_evacuation_reserves) { + +void ShenandoahFreeSet::finish_rebuild(size_t young_cset_regions, size_t old_cset_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_trashed_regions, old_trashed_regions, have_evacuation_reserves, - young_reserve, old_reserve); + compute_young_and_old_reserves(young_cset_regions, old_cset_regions, young_reserve, old_reserve); } else { young_reserve = (_heap->max_capacity() / 100) * ShenandoahEvacReserve; old_reserve = 0; @@ -2531,8 +2592,41 @@ void ShenandoahFreeSet::finish_rebuild(size_t young_trashed_regions, size_t old_ // Release the rebuild lock now. What remains in this function is read-only rebuild_lock()->unlock(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); log_status(); + if (_heap->mode()->is_generational()) { + // Clear the region balance until it is adjusted in preparation for a subsequent GC cycle. + _heap->old_generation()->set_region_balance(0); + } +} + + +// Reduce old reserve (when there are insufficient resources to satisfy the original request). +void ShenandoahFreeSet::reduce_old_reserve(size_t adjusted_old_reserve, size_t requested_old_reserve) { + ShenandoahOldGeneration* const old_generation = _heap->old_generation(); + size_t requested_promoted_reserve = old_generation->get_promoted_reserve(); + size_t requested_old_evac_reserve = old_generation->get_evacuation_reserve(); + assert(adjusted_old_reserve < requested_old_reserve, "Only allow reduction"); + assert(requested_promoted_reserve + requested_old_evac_reserve >= adjusted_old_reserve, "Sanity"); + size_t delta = requested_old_reserve - adjusted_old_reserve; + + if (requested_promoted_reserve >= delta) { + requested_promoted_reserve -= delta; + old_generation->set_promoted_reserve(requested_promoted_reserve); + } else { + delta -= requested_promoted_reserve; + requested_promoted_reserve = 0; + requested_old_evac_reserve -= delta; + old_generation->set_promoted_reserve(requested_promoted_reserve); + old_generation->set_evacuation_reserve(requested_old_evac_reserve); + } +} + +// Reduce young reserve (when there are insufficient resources to satisfy the original request). +void ShenandoahFreeSet::reduce_young_reserve(size_t adjusted_young_reserve, size_t requested_young_reserve) { + ShenandoahYoungGeneration* const young_generation = _heap->young_generation(); + assert(adjusted_young_reserve < requested_young_reserve, "Only allow reduction"); + young_generation->set_evacuation_reserve(adjusted_young_reserve); } /** @@ -2549,7 +2643,6 @@ void ShenandoahFreeSet::finish_rebuild(size_t young_trashed_regions, size_t old_ * this value should computed by ShenandoahGenerationalHeap::compute_old_generation_balance(). */ void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_trashed_regions, size_t old_trashed_regions, - bool have_evacuation_reserves, size_t& young_reserve_result, size_t& old_reserve_result) const { shenandoah_assert_generational(); shenandoah_assert_heaplocked(); @@ -2566,6 +2659,15 @@ void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_trashed_regi old_available += old_trashed_regions * region_size_bytes; young_unaffiliated_regions += young_trashed_regions; + assert(young_capacity >= young_generation->used(), + "Young capacity (%zu) must exceed used (%zu)", young_capacity, young_generation->used()); + + size_t young_available = young_capacity - young_generation->used(); + young_available += young_trashed_regions * region_size_bytes; + + assert(young_available >= young_unaffiliated_regions * region_size_bytes, "sanity"); + assert(old_available >= old_unaffiliated_regions * region_size_bytes, "sanity"); + // Consult old-region balance to make adjustments to current generation capacities and availability. // The generation region transfers take place after we rebuild. old_region_balance represents number of regions // to transfer from old to young. @@ -2585,6 +2687,7 @@ void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_trashed_regi ssize_t xfer_bytes = old_region_balance * checked_cast(region_size_bytes); old_available -= xfer_bytes; old_unaffiliated_regions -= old_region_balance; + young_available += xfer_bytes; young_capacity += xfer_bytes; young_unaffiliated_regions += old_region_balance; } @@ -2593,41 +2696,22 @@ void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_trashed_regi // promotions and evacuations. The partition between which old memory is reserved for evacuation and // which is reserved for promotion is enforced using thread-local variables that prescribe intentions for // each PLAB's available memory. - if (have_evacuation_reserves) { - // We are rebuilding at the end of final mark, having already established evacuation budgets for this GC pass. - const size_t promoted_reserve = old_generation->get_promoted_reserve(); - const size_t old_evac_reserve = old_generation->get_evacuation_reserve(); - young_reserve_result = young_generation->get_evacuation_reserve(); - old_reserve_result = promoted_reserve + old_evac_reserve; - if (old_reserve_result > old_available) { - // Try to transfer memory from young to old. - size_t old_deficit = old_reserve_result - old_available; - size_t old_region_deficit = (old_deficit + region_size_bytes - 1) / region_size_bytes; - if (young_unaffiliated_regions < old_region_deficit) { - old_region_deficit = young_unaffiliated_regions; - } - young_unaffiliated_regions -= old_region_deficit; - old_unaffiliated_regions += old_region_deficit; - old_region_balance -= old_region_deficit; - old_generation->set_region_balance(old_region_balance); - } - } else { - // We are rebuilding at end of GC, so we set aside budgets specified on command line (or defaults) - young_reserve_result = (young_capacity * ShenandoahEvacReserve) / 100; - // The auto-sizer has already made old-gen large enough to hold all anticipated evacuations and promotions. - // Affiliated old-gen regions are already in the OldCollector free set. Add in the relevant number of - // unaffiliated regions. - old_reserve_result = old_available; - } + const size_t promoted_reserve = old_generation->get_promoted_reserve(); + const size_t old_evac_reserve = old_generation->get_evacuation_reserve(); + young_reserve_result = young_generation->get_evacuation_reserve(); + old_reserve_result = promoted_reserve + old_evac_reserve; + assert(old_reserve_result + young_reserve_result <= old_available + young_available, + "Cannot reserve (%zu + %zu + %zu) more than is available: %zu + %zu", + promoted_reserve, old_evac_reserve, young_reserve_result, old_available, young_available); // Old available regions that have less than PLAB::min_size() of available memory are not placed into the OldCollector // free set. Because of this, old_available may not have enough memory to represent the intended reserve. Adjust // the reserve downward to account for this possibility. This loss is part of the reason why the original budget // was adjusted with ShenandoahOldEvacWaste and ShenandoahOldPromoWaste multipliers. if (old_reserve_result > - _partitions.capacity_of(ShenandoahFreeSetPartitionId::OldCollector) + old_unaffiliated_regions * region_size_bytes) { + _partitions.available_in(ShenandoahFreeSetPartitionId::OldCollector) + old_unaffiliated_regions * region_size_bytes) { old_reserve_result = - _partitions.capacity_of(ShenandoahFreeSetPartitionId::OldCollector) + old_unaffiliated_regions * region_size_bytes; + _partitions.available_in(ShenandoahFreeSetPartitionId::OldCollector) + old_unaffiliated_regions * region_size_bytes; } if (young_reserve_result > young_unaffiliated_regions * region_size_bytes) { @@ -2791,19 +2875,17 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old ShenandoahFreeSetPartitionId p = _partitions.membership(idx); size_t ac = alloc_capacity(r); assert(ac != region_size_bytes, "Empty regions should be in Mutator partion at entry to reserve_regions"); - if (p == ShenandoahFreeSetPartitionId::Collector) { - if (ac != region_size_bytes) { - young_used_regions++; - young_used_bytes = region_size_bytes - ac; - } - // else, unaffiliated region has no used - } else if (p == ShenandoahFreeSetPartitionId::OldCollector) { - if (ac != region_size_bytes) { - old_used_regions++; - old_used_bytes = region_size_bytes - ac; - } - // else, unaffiliated region has no used - } else if (p == ShenandoahFreeSetPartitionId::NotFree) { + assert(p != ShenandoahFreeSetPartitionId::Collector, "Collector regions must be converted from Mutator regions"); + if (p == ShenandoahFreeSetPartitionId::OldCollector) { + assert(!r->is_empty(), "Empty regions should be in Mutator partition at entry to reserve_regions"); + old_used_regions++; + old_used_bytes = region_size_bytes - ac; + // This region is within the range for OldCollector partition, as established by find_regions_with_alloc_capacity() + assert((_partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector) <= idx) && + (_partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector) >= idx), + "find_regions_with_alloc_capacity() should have established this is in range"); + } else { + assert(p == ShenandoahFreeSetPartitionId::NotFree, "sanity"); // This region has been retired if (r->is_old()) { old_used_regions++; @@ -2813,21 +2895,6 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old young_used_regions++; young_used_bytes += region_size_bytes - ac; } - } else { - assert(p == ShenandoahFreeSetPartitionId::OldCollector, "Not mutator and not NotFree, so must be OldCollector"); - assert(!r->is_empty(), "Empty regions should be in Mutator partition at entry to reserve_regions"); - if (idx < old_collector_low_idx) { - old_collector_low_idx = idx; - } - if (idx > old_collector_high_idx) { - old_collector_high_idx = idx; - } - if (idx < old_collector_empty_low_idx) { - old_collector_empty_low_idx = idx; - } - if (idx > old_collector_empty_high_idx) { - old_collector_empty_high_idx = idx; - } } } } @@ -2856,14 +2923,14 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old _partitions.increase_used(ShenandoahFreeSetPartitionId::OldCollector, used_to_old_collector); } - _partitions.expand_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId::Collector, - collector_low_idx, collector_high_idx, - collector_empty_low_idx, collector_empty_high_idx); + _partitions.establish_interval(ShenandoahFreeSetPartitionId::Mutator, + mutator_low_idx, mutator_high_idx, mutator_empty_low_idx, mutator_empty_high_idx); + _partitions.establish_interval(ShenandoahFreeSetPartitionId::Collector, + collector_low_idx, collector_high_idx, collector_empty_low_idx, collector_empty_high_idx); + _partitions.expand_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId::OldCollector, old_collector_low_idx, old_collector_high_idx, old_collector_empty_low_idx, old_collector_empty_high_idx); - _partitions.establish_interval(ShenandoahFreeSetPartitionId::Mutator, - mutator_low_idx, mutator_high_idx, mutator_empty_low_idx, mutator_empty_high_idx); recompute_total_used(); @@ -2872,17 +2939,22 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old /* CollectorSizeChanged */ true, /* OldCollectorSizeChanged */ true, /* AffiliatedChangesAreYoungNeutral */ false, /* AffiliatedChangesAreGlobalNeutral */ false, /* UnaffiliatedChangesAreYoungNeutral */ false>(); - _partitions.assert_bounds(true); + _partitions.assert_bounds(); if (LogTarget(Info, gc, free)::is_enabled()) { size_t old_reserve = _partitions.available_in(ShenandoahFreeSetPartitionId::OldCollector); if (old_reserve < to_reserve_old) { log_info(gc, free)("Wanted " PROPERFMT " for old reserve, but only reserved: " PROPERFMT, PROPERFMTARGS(to_reserve_old), PROPERFMTARGS(old_reserve)); + assert(_heap->mode()->is_generational(), "to_old_reserve > 0 implies generational mode"); + reduce_old_reserve(old_reserve, to_reserve_old); } size_t reserve = _partitions.available_in(ShenandoahFreeSetPartitionId::Collector); if (reserve < to_reserve) { + if (_heap->mode()->is_generational()) { + reduce_young_reserve(reserve, to_reserve); + } log_info(gc, free)("Wanted " PROPERFMT " for young reserve, but only reserved: " PROPERFMT, - PROPERFMTARGS(to_reserve), PROPERFMTARGS(reserve)); + PROPERFMTARGS(to_reserve), PROPERFMTARGS(reserve)); } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 364637740f2..4e0aea80a9b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -224,6 +224,10 @@ public: void transfer_used_capacity_from_to(ShenandoahFreeSetPartitionId from_partition, ShenandoahFreeSetPartitionId to_partition, size_t regions); + // For recycled region r in the OldCollector partition but possibly not within the interval for empty OldCollector regions, + // expand the empty interval to include this region. + inline void adjust_interval_for_recycled_old_region_under_lock(ShenandoahHeapRegion* r); + const char* partition_membership_name(idx_t idx) const; // Return the index of the next available region >= start_index, or maximum_regions if not found. @@ -373,12 +377,7 @@ public: inline void set_capacity_of(ShenandoahFreeSetPartitionId which_partition, size_t value); - inline void set_used_by(ShenandoahFreeSetPartitionId which_partition, size_t value) { - shenandoah_assert_heaplocked(); - assert (which_partition < NumPartitions, "selected free set must be valid"); - _used[int(which_partition)] = value; - _available[int(which_partition)] = _capacity[int(which_partition)] - value; - } + inline void set_used_by(ShenandoahFreeSetPartitionId which_partition, size_t value); inline size_t count(ShenandoahFreeSetPartitionId which_partition) const { return _region_counts[int(which_partition)]; } @@ -402,7 +401,7 @@ public: // idx >= leftmost && // idx <= rightmost // } - void assert_bounds(bool validate_totals) NOT_DEBUG_RETURN; + void assert_bounds() NOT_DEBUG_RETURN; }; // Publicly, ShenandoahFreeSet represents memory that is available to mutator threads. The public capacity(), used(), @@ -634,7 +633,11 @@ private: void establish_old_collector_alloc_bias(); size_t get_usable_free_words(size_t free_bytes) const; + void reduce_young_reserve(size_t adjusted_young_reserve, size_t requested_young_reserve); + void reduce_old_reserve(size_t adjusted_old_reserve, size_t requested_old_reserve); + void log_freeset_stats(ShenandoahFreeSetPartitionId partition_id, LogStream& ls); + // log status, assuming lock has already been acquired by the caller. void log_status(); @@ -685,35 +688,46 @@ public: return _total_global_used; } - size_t global_unaffiliated_regions() { + // A negative argument results in moving from old_collector to collector + void move_unaffiliated_regions_from_collector_to_old_collector(ssize_t regions); + + inline size_t global_unaffiliated_regions() { return _global_unaffiliated_regions; } - size_t young_unaffiliated_regions() { + inline size_t young_unaffiliated_regions() { return _young_unaffiliated_regions; } - size_t old_unaffiliated_regions() { + inline size_t collector_unaffiliated_regions() { + return _partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::Collector); + } + + inline size_t old_collector_unaffiliated_regions() { return _partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::OldCollector); } - size_t young_affiliated_regions() { + inline size_t old_unaffiliated_regions() { + return _partitions.get_empty_region_counts(ShenandoahFreeSetPartitionId::OldCollector); + } + + inline size_t young_affiliated_regions() { return _young_affiliated_regions; } - size_t old_affiliated_regions() { + inline size_t old_affiliated_regions() { return _old_affiliated_regions; } - size_t global_affiliated_regions() { + inline size_t global_affiliated_regions() { return _global_affiliated_regions; } - size_t total_young_regions() { + inline size_t total_young_regions() { return _total_young_regions; } - size_t total_old_regions() { + inline size_t total_old_regions() { return _partitions.get_capacity(ShenandoahFreeSetPartitionId::OldCollector) / ShenandoahHeapRegion::region_size_bytes(); } @@ -725,36 +739,27 @@ public: // Examine the existing free set representation, capturing the current state into var arguments: // - // young_cset_regions is the number of regions currently in the young cset if we are starting to evacuate, or zero - // old_cset_regions is the number of regions currently in the old cset if we are starting a mixed evacuation, or zero + // young_trashed_regions is the number of trashed regions (immediate garbage at final mark, cset regions after update refs) + // old_trashed_regions is the number of trashed regions + // (immediate garbage at final old mark, cset regions after update refs for mixed evac) // 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 prepare_to_rebuild(size_t &young_cset_regions, size_t &old_cset_regions, + void prepare_to_rebuild(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); // At the end of final mark, but before we begin evacuating, heuristics calculate how much memory is required to - // hold the results of evacuating to young-gen and to old-gen, and have_evacuation_reserves should be true. - // These quantities, stored as reserves for their respective generations, are consulted prior to rebuilding - // the free set (ShenandoahFreeSet) in preparation for evacuation. When the free set is rebuilt, we make sure - // to reserve sufficient memory in the collector and old_collector sets to hold evacuations. + // hold the results of evacuating to young-gen and to old-gen. These quantities, stored in reserves for their + // respective generations, are consulted prior to rebuilding the free set (ShenandoahFreeSet) in preparation for + // evacuation. When the free set is rebuilt, we make sure to reserve sufficient memory in the collector and + // old_collector sets to hold evacuations. Likewise, at the end of update refs, we rebuild the free set in order + // to set aside reserves to be consumed during the next GC cycle. // - // We also rebuild the free set at the end of GC, as we prepare to idle GC until the next trigger. In this case, - // have_evacuation_reserves is false because we don't yet know how much memory will need to be evacuated in the - // next GC cycle. When have_evacuation_reserves is false, the free set rebuild operation reserves for the collector - // and old_collector sets based on alternative mechanisms, such as ShenandoahEvacReserve, ShenandoahOldEvacReserve, and - // ShenandoahOldCompactionReserve. In a future planned enhancement, the reserve for old_collector set when the - // evacuation reserves are unknown, is based in part on anticipated promotion as determined by analysis of live data - // found during the previous GC pass which is one less than the current tenure age. - // - // young_cset_regions is the number of regions currently in the young cset if we are starting to evacuate, or zero - // old_cset_regions is the number of regions currently in the old cset if we are starting a mixed evacuation, or zero + // young_trashed_regions is the number of trashed regions (immediate garbage at final mark, cset regions after update refs) + // old_trashed_regions is the number of trashed regions + // (immediate garbage at final old mark, cset regions after update refs for mixed evac) // num_old_regions is the number of old-gen regions that have available memory for further allocations (excluding old cset) - // have_evacuation_reserves is true iff the desired values of young-gen and old-gen evacuation reserves and old-gen - // promotion reserve have been precomputed (and can be obtained by invoking - // ->get_evacuation_reserve() or old_gen->get_promoted_reserve() - void finish_rebuild(size_t young_cset_regions, size_t old_cset_regions, size_t num_old_regions, - bool have_evacuation_reserves = false); + void finish_rebuild(size_t young_trashed_regions, size_t old_trashed_regions, size_t num_old_regions); // When a region is promoted in place, we add the region's available memory if it is greater than plab_min_size() // into the old collector partition by invoking this method. @@ -806,9 +811,18 @@ public: return _partitions.available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId::Mutator); } + // Use this version of available() if the heap lock is held. + inline size_t available_locked() const { + return _partitions.available_in(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t total_humongous_waste() const { return _total_humongous_waste; } - inline size_t humongous_waste_in_mutator() const { return _partitions.humongous_waste(ShenandoahFreeSetPartitionId::Mutator); } - inline size_t humongous_waste_in_old() const { return _partitions.humongous_waste(ShenandoahFreeSetPartitionId::OldCollector); } + inline size_t humongous_waste_in_mutator() const { + return _partitions.humongous_waste(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t humongous_waste_in_old() const { + return _partitions.humongous_waste(ShenandoahFreeSetPartitionId::OldCollector); + } void decrease_humongous_waste_for_regular_bypass(ShenandoahHeapRegion* r, size_t waste); @@ -874,7 +888,7 @@ public: // Reserve space for evacuations, with regions reserved for old evacuations placed to the right // of regions reserved of young evacuations. - void compute_young_and_old_reserves(size_t young_cset_regions, size_t old_cset_regions, bool have_evacuation_reserves, + void compute_young_and_old_reserves(size_t young_cset_regions, size_t old_cset_regions, size_t &young_reserve_result, size_t &old_reserve_result) const; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index fa3a7a42209..3c92750cc0c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -522,6 +522,7 @@ public: void heap_region_do(ShenandoahHeapRegion* r) override { if (r->is_trash()) { r->try_recycle_under_lock(); + // No need to adjust_interval_for_recycled_old_region. That will be taken care of during freeset rebuild. } if (r->is_cset()) { // Leave affiliation unchanged @@ -966,6 +967,7 @@ public: if (r->is_trash()) { live = 0; r->try_recycle_under_lock(); + // No need to adjust_interval_for_recycled_old_region. That will be taken care of during freeset rebuild. } else { if (r->is_old()) { ShenandoahGenerationalFullGC::account_for_region(r, _old_regions, _old_usage, _old_humongous_waste); @@ -1113,16 +1115,16 @@ void ShenandoahFullGC::phase5_epilog() { ShenandoahPostCompactClosure post_compact; heap->heap_region_iterate(&post_compact); heap->collection_set()->clear(); - size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; - ShenandoahFreeSet* free_set = heap->free_set(); { - free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); + ShenandoahFreeSet* free_set = heap->free_set(); + size_t young_trashed_regions, old_trashed_regions, first_old, last_old, num_old; + free_set->prepare_to_rebuild(young_trashed_regions, old_trashed_regions, first_old, last_old, num_old); // We also do not expand old generation size following Full GC because we have scrambled age populations and // no longer have objects separated by age into distinct regions. if (heap->mode()->is_generational()) { ShenandoahGenerationalFullGC::compute_balances(); } - free_set->finish_rebuild(young_cset_regions, old_cset_regions, num_old); + 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 a5d8cca458d..cdc7e1a328a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -250,6 +250,7 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap ShenandoahOldGeneration* const old_generation = heap->old_generation(); ShenandoahYoungGeneration* const young_generation = heap->young_generation(); + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); // During initialization and phase changes, it is more likely that fewer objects die young and old-gen // memory is not yet full (or is in the process of being replaced). During these times especially, it @@ -263,15 +264,15 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap // First priority is to reclaim the easy garbage out of young-gen. - // maximum_young_evacuation_reserve is upper bound on memory to be evacuated out of young - const size_t maximum_young_evacuation_reserve = (young_generation->max_capacity() * ShenandoahEvacReserve) / 100; - size_t young_evacuation_reserve = MIN2(maximum_young_evacuation_reserve, young_generation->available_with_reserve()); + // maximum_young_evacuation_reserve is upper bound on memory to be evacuated into young Collector Reserve. This is + // bounded at the end of previous GC cycle, based on available memory and balancing of evacuation to old and young. + size_t maximum_young_evacuation_reserve = young_generation->get_evacuation_reserve(); // maximum_old_evacuation_reserve is an upper bound on memory evacuated from old and evacuated to old (promoted), // clamped by the old generation space available. // // Here's the algebra. - // Let SOEP = ShenandoahOldEvacRatioPercent, + // Let SOEP = ShenandoahOldEvacPercent, // OE = old evac, // YE = young evac, and // TE = total evac = OE + YE @@ -283,12 +284,14 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap // => OE = YE*SOEP/(100-SOEP) // We have to be careful in the event that SOEP is set to 100 by the user. - assert(ShenandoahOldEvacRatioPercent <= 100, "Error"); + assert(ShenandoahOldEvacPercent <= 100, "Error"); const size_t old_available = old_generation->available(); - const size_t maximum_old_evacuation_reserve = (ShenandoahOldEvacRatioPercent == 100) ? - old_available : MIN2((maximum_young_evacuation_reserve * ShenandoahOldEvacRatioPercent) / (100 - ShenandoahOldEvacRatioPercent), + const size_t maximum_old_evacuation_reserve = (ShenandoahOldEvacPercent == 100) ? + old_available : MIN2((maximum_young_evacuation_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), old_available); + // In some cases, maximum_old_reserve < old_available (when limited by ShenandoahOldEvacPercent) + // This limit affects mixed evacuations, but does not affect promotions. // Second priority is to reclaim garbage out of old-gen if there are old-gen collection candidates. Third priority // is to promote as much as we have room to promote. However, if old-gen memory is in short supply, this means young @@ -305,10 +308,8 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap // evacuation and update-refs, we give emphasis to reclaiming garbage first, wherever that garbage is found. // Global GC will adjust generation sizes to accommodate the collection set it chooses. - // Set old_promo_reserve to enforce that no regions are preselected for promotion. Such regions typically - // have relatively high memory utilization. We still call select_aged_regions() because this will prepare for - // promotions in place, if relevant. - old_promo_reserve = 0; + // Use remnant of old_available to hold promotions. + old_promo_reserve = old_available - maximum_old_evacuation_reserve; // Dedicate all available old memory to old_evacuation reserve. This may be small, because old-gen is only // expanded based on an existing mixed evacuation workload at the end of the previous GC cycle. We'll expand @@ -319,43 +320,48 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap // mixed evacuation, reserve all of this memory for compaction of old-gen and do not promote. Prioritize compaction // over promotion in order to defragment OLD so that it will be better prepared to efficiently receive promoted memory. old_evacuation_reserve = maximum_old_evacuation_reserve; - old_promo_reserve = 0; + old_promo_reserve = old_available - maximum_old_evacuation_reserve; } else { // Make all old-evacuation memory for promotion, but if we can't use it all for promotion, we'll allow some evacuation. - old_evacuation_reserve = 0; + old_evacuation_reserve = old_available - maximum_old_evacuation_reserve; old_promo_reserve = maximum_old_evacuation_reserve; } assert(old_evacuation_reserve <= old_available, "Error"); + // We see too many old-evacuation failures if we force ourselves to evacuate into regions that are not initially empty. // So we limit the old-evacuation reserve to unfragmented memory. Even so, old-evacuation is free to fill in nooks and // crannies within existing partially used regions and it generally tries to do so. - const size_t old_free_unfragmented = old_generation->free_unaffiliated_regions() * ShenandoahHeapRegion::region_size_bytes(); + const size_t old_free_unfragmented = old_generation->free_unaffiliated_regions() * region_size_bytes; if (old_evacuation_reserve > old_free_unfragmented) { const size_t delta = old_evacuation_reserve - old_free_unfragmented; old_evacuation_reserve -= delta; - // Let promo consume fragments of old-gen memory if not global - if (!is_global()) { - old_promo_reserve += delta; - } + // Let promo consume fragments of old-gen memory + old_promo_reserve += delta; } - // Preselect regions for promotion by evacuation (obtaining the live data to seed promoted_reserve), - // and identify regions that will promote in place. These use the tenuring threshold. - const size_t consumed_by_advance_promotion = select_aged_regions(old_promo_reserve); - assert(consumed_by_advance_promotion <= maximum_old_evacuation_reserve, "Cannot promote more than available old-gen memory"); + // If is_global(), we let garbage-first heuristic determine cset membership. Otherwise, we give priority + // to tenurable regions by preselecting regions for promotion by evacuation (obtaining the live data to seed promoted_reserve). + // This also identifies regions that will be promoted in place. These use the tenuring threshold. + const size_t consumed_by_advance_promotion = select_aged_regions(is_global()? 0: old_promo_reserve); + assert(consumed_by_advance_promotion <= old_promo_reserve, "Do not promote more than budgeted"); + + // The young evacuation reserve can be no larger than young_unaffiliated. Planning to evacuate into partially consumed + // young regions is doomed to failure if any of those partially consumed regions is selected for the collection set. + size_t young_unaffiliated = young_generation->free_unaffiliated_regions() * region_size_bytes; // If any regions have been selected for promotion in place, this has the effect of decreasing available within mutator // and collector partitions, due to padding of remnant memory within each promoted in place region. This will affect // young_evacuation_reserve but not old_evacuation_reserve or consumed_by_advance_promotion. So recompute. - young_evacuation_reserve = MIN2(young_evacuation_reserve, young_generation->available_with_reserve()); + size_t young_evacuation_reserve = MIN2(maximum_young_evacuation_reserve, young_unaffiliated); // Note that unused old_promo_reserve might not be entirely consumed_by_advance_promotion. Do not transfer this // to old_evacuation_reserve because this memory is likely very fragmented, and we do not want to increase the likelihood - // of old evacuation failure. + // of old evacuation failure. Leave this memory in the promoted reserve as it may be targeted by opportunistic + // promotions (found during evacuation of young regions). young_generation->set_evacuation_reserve(young_evacuation_reserve); old_generation->set_evacuation_reserve(old_evacuation_reserve); - old_generation->set_promoted_reserve(consumed_by_advance_promotion); + old_generation->set_promoted_reserve(old_promo_reserve); // There is no need to expand OLD because all memory used here was set aside at end of previous GC, except in the // case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand. @@ -363,8 +369,8 @@ void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap // Having chosen the collection set, adjust the budgets for generational mode based on its composition. Note // that young_generation->available() now knows about recently discovered immediate garbage. -// -void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, ShenandoahCollectionSet* const collection_set) { +void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, + ShenandoahCollectionSet* const collection_set, size_t add_regions_to_old) { shenandoah_assert_generational(); // We may find that old_evacuation_reserve and/or loaned_for_young_evacuation are not fully consumed, in which case we may // be able to increase regions_available_to_loan @@ -398,7 +404,8 @@ void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, // Leave old_evac_reserve as previously configured } else if (old_evacuated_committed < old_evacuation_reserve) { // This happens if the old-gen collection consumes less than full budget. - log_debug(gc, cset)("Shrinking old evac reserve to match old_evac_commited: " PROPERFMT, PROPERFMTARGS(old_evacuated_committed)); + log_debug(gc, cset)("Shrinking old evac reserve to match old_evac_commited: " PROPERFMT, + PROPERFMTARGS(old_evacuated_committed)); old_evacuation_reserve = old_evacuated_committed; old_generation->set_evacuation_reserve(old_evacuation_reserve); } @@ -409,11 +416,17 @@ void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, size_t young_evacuated = collection_set->get_live_bytes_in_untenurable_regions(); size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated)); - size_t total_young_available = young_generation->available_with_reserve(); - assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate more than is available in young"); + size_t total_young_available = young_generation->available_with_reserve() - add_regions_to_old * region_size_bytes;; + assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate (%zu) more than is available in young (%zu)", + young_evacuated_reserve_used, total_young_available); young_generation->set_evacuation_reserve(young_evacuated_reserve_used); - size_t old_available = old_generation->available(); + // We have not yet rebuilt the free set. Some of the memory that is thought to be avaiable within old may no + // longer be available if that memory had been free within regions that were selected for the collection set. + // Make the necessary adjustments to old_available. + size_t old_available = + old_generation->available() + add_regions_to_old * region_size_bytes - collection_set->get_old_available_bytes_collected(); + // Now that we've established the collection set, we know how much memory is really required by old-gen for evacuation // and promotion reserves. Try shrinking OLD now in case that gives us a bit more runway for mutator allocations during // evac and update phases. @@ -422,21 +435,27 @@ void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, if (old_available < old_consumed) { // This can happen due to round-off errors when adding the results of truncated integer arithmetic. // We've already truncated old_evacuated_committed. Truncate young_advance_promoted_reserve_used here. + assert(young_advance_promoted_reserve_used <= (33 * (old_available - old_evacuated_committed)) / 32, "Round-off errors should be less than 3.125%%, committed: %zu, reserved: %zu", young_advance_promoted_reserve_used, old_available - old_evacuated_committed); - young_advance_promoted_reserve_used = old_available - old_evacuated_committed; + if (old_available > old_evacuated_committed) { + young_advance_promoted_reserve_used = old_available - old_evacuated_committed; + } else { + young_advance_promoted_reserve_used = 0; + old_evacuated_committed = old_available; + } + // TODO: reserve for full promotion reserve, not just for advance (preselected) promotion old_consumed = old_evacuated_committed + young_advance_promoted_reserve_used; } assert(old_available >= old_consumed, "Cannot consume (%zu) more than is available (%zu)", old_consumed, old_available); size_t excess_old = old_available - old_consumed; - size_t unaffiliated_old_regions = old_generation->free_unaffiliated_regions(); + size_t unaffiliated_old_regions = old_generation->free_unaffiliated_regions() + add_regions_to_old; size_t unaffiliated_old = unaffiliated_old_regions * region_size_bytes; - assert(old_available >= unaffiliated_old, - "Unaffiliated old (%zu is %zu * %zu) is a subset of old available (%zu)", - unaffiliated_old, unaffiliated_old_regions, region_size_bytes, old_available); + assert(unaffiliated_old >= old_evacuated_committed, "Do not evacuate (%zu) more than unaffiliated old (%zu)", + old_evacuated_committed, unaffiliated_old); // Make sure old_evac_committed is unaffiliated if (old_evacuated_committed > 0) { @@ -454,20 +473,22 @@ void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, } // If we find that OLD has excess regions, give them back to YOUNG now to reduce likelihood we run out of allocation - // runway during evacuation and update-refs. - size_t regions_to_xfer = 0; + // runway during evacuation and update-refs. We may make further adjustments to balance. + ssize_t add_regions_to_young = 0; if (excess_old > unaffiliated_old) { // we can give back unaffiliated_old (all of unaffiliated is excess) if (unaffiliated_old_regions > 0) { - regions_to_xfer = unaffiliated_old_regions; + add_regions_to_young = unaffiliated_old_regions; } } else if (unaffiliated_old_regions > 0) { // excess_old < unaffiliated old: we can give back MIN(excess_old/region_size_bytes, unaffiliated_old_regions) size_t excess_regions = excess_old / region_size_bytes; - regions_to_xfer = MIN2(excess_regions, unaffiliated_old_regions); + add_regions_to_young = MIN2(excess_regions, unaffiliated_old_regions); } - if (regions_to_xfer > 0) { - excess_old -= regions_to_xfer * region_size_bytes; + + if (add_regions_to_young > 0) { + assert(excess_old >= add_regions_to_young * region_size_bytes, "Cannot xfer more than excess old"); + excess_old -= add_regions_to_young * region_size_bytes; log_debug(gc, ergo)("Before start of evacuation, total_promotion reserve is young_advance_promoted_reserve: %zu " "plus excess: old: %zu", young_advance_promoted_reserve_used, excess_old); } @@ -475,6 +496,7 @@ void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, // Add in the excess_old memory to hold unanticipated promotions, if any. If there are more unanticipated // promotions than fit in reserved memory, they will be deferred until a future GC pass. size_t total_promotion_reserve = young_advance_promoted_reserve_used + excess_old; + old_generation->set_promoted_reserve(total_promotion_reserve); old_generation->reset_promoted_expended(); } @@ -782,17 +804,13 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { ShenandoahCollectionSetPreselector preselector(collection_set, heap->num_regions()); // Find the amount that will be promoted, regions that will be promoted in - // place, and preselect older regions that will be promoted by evacuation. + // place, and preselected older regions that will be promoted by evacuation. compute_evacuation_budgets(heap); - // Choose the collection set, including the regions preselected above for - // promotion into the old generation. - _heuristics->choose_collection_set(collection_set); - if (!collection_set->is_empty()) { - // only make use of evacuation budgets when we are evacuating - adjust_evacuation_budgets(heap, collection_set); - } - + // Choose the collection set, including the regions preselected above for promotion into the old generation. + size_t add_regions_to_old = _heuristics->choose_collection_set(collection_set); + // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. + adjust_evacuation_budgets(heap, collection_set, add_regions_to_old); if (is_global()) { // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will @@ -816,17 +834,16 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); ShenandoahHeapLocker locker(heap->lock()); - // We are preparing for evacuation. At this time, we ignore cset region tallies. - size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; - _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); - + // We are preparing for evacuation. + size_t young_trashed_regions, old_trashed_regions, first_old, last_old, num_old; + _free_set->prepare_to_rebuild(young_trashed_regions, old_trashed_regions, first_old, last_old, num_old); if (heap->mode()->is_generational()) { ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); - gen_heap->compute_old_generation_balance(young_cset_regions, old_cset_regions); + size_t allocation_runway = + gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trashed_regions); + gen_heap->compute_old_generation_balance(allocation_runway, old_trashed_regions, young_trashed_regions); } - - // Free set construction uses reserve quantities, because they are known to be valid here - _free_set->finish_rebuild(young_cset_regions, old_cset_regions, num_old, true); + _free_set->finish_rebuild(young_trashed_regions, old_trashed_regions, num_old); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index 06cf132f946..d49e3bed5f8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -63,9 +63,10 @@ private: // Compute evacuation budgets prior to choosing collection set. void compute_evacuation_budgets(ShenandoahHeap* heap); - // Adjust evacuation budgets after choosing collection set. + // Adjust evacuation budgets after choosing collection set. The argument regions_to_xfer represents regions to be + // transfered to old based on decisions made in top_off_collection_set() void adjust_evacuation_budgets(ShenandoahHeap* heap, - ShenandoahCollectionSet* collection_set); + ShenandoahCollectionSet* collection_set, size_t regions_to_xfer); // Preselect for possible inclusion into the collection set exactly the most // garbage-dense regions, including those that satisfy criteria 1 & 2 below, @@ -144,6 +145,22 @@ private: virtual void prepare_gc(); // Called during final mark, chooses collection set, rebuilds free set. + // Upon return from prepare_regions_and_collection_set(), certain parameters have been established to govern the + // evacuation efforts that are about to begin. In particular: + // + // old_generation->get_promoted_reserve() represents the amount of memory within old-gen's available memory that has + // been set aside to hold objects promoted from young-gen memory. This represents an estimated percentage + // of the live young-gen memory within the collection set. If there is more data ready to be promoted than + // can fit within this reserve, the promotion of some objects will be deferred until a subsequent evacuation + // pass. + // + // old_generation->get_evacuation_reserve() represents the amount of memory within old-gen's available memory that has been + // set aside to hold objects evacuated from the old-gen collection set. + // + // young_generation->get_evacuation_reserve() represents the amount of memory within young-gen's available memory that has + // been set aside to hold objects evacuated from the young-gen collection set. Conservatively, this value + // equals the entire amount of live young-gen memory within the collection set, even though some of this memory + // will likely be promoted. virtual void prepare_regions_and_collection_set(bool concurrent); // Cancel marking (used by Full collect and when cancelling cycle). diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp index 78672ee10a5..1b11c696d18 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp @@ -55,9 +55,6 @@ void ShenandoahGenerationalFullGC::prepare() { // Since we may arrive here from degenerated GC failure of either young or old, establish generation as GLOBAL. heap->set_active_generation(heap->global_generation()); - // No need for old_gen->increase_used() as this was done when plabs were allocated. - heap->reset_generation_reserves(); - // Full GC supersedes any marking or coalescing in old generation. heap->old_generation()->cancel_gc(); } @@ -156,8 +153,11 @@ void ShenandoahGenerationalFullGC::compute_balances() { // In case this Full GC resulted from degeneration, clear the tally on anticipated promotion. heap->old_generation()->set_promotion_potential(0); - // Invoke this in case we are able to transfer memory from OLD to YOUNG. - heap->compute_old_generation_balance(0, 0); + + // Invoke this in case we are able to transfer memory from OLD to YOUNG + size_t allocation_runway = + heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(0L); + heap->compute_old_generation_balance(allocation_runway, 0, 0); } ShenandoahPrepareForGenerationalCompactionObjectClosure::ShenandoahPrepareForGenerationalCompactionObjectClosure(PreservedMarks* preserved_marks, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index fa78e02e6af..36ea0b9e497 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -299,9 +299,9 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint alloc_from_lab = false; } // else, we leave copy equal to nullptr, signaling a promotion failure below if appropriate. - // We choose not to promote objects smaller than PLAB::min_size() by way of shared allocations, as this is too + // We choose not to promote objects smaller than size_threshold by way of shared allocations as this is too // costly. Instead, we'll simply "evacuate" to young-gen memory (using a GCLAB) and will promote in a future - // evacuation pass. This condition is denoted by: is_promotion && has_plab && (size <= PLAB::min_size()) + // evacuation pass. This condition is denoted by: is_promotion && has_plab && (size <= size_threshhold). } #ifdef ASSERT } @@ -576,19 +576,18 @@ void ShenandoahGenerationalHeap::retire_plab(PLAB* plab) { // Make sure old-generation is large enough, but no larger than is necessary, to hold mixed evacuations // and promotions, if we anticipate either. Any deficit is provided by the young generation, subject to -// xfer_limit, and any surplus is transferred to the young generation. -// -// xfer_limit is the maximum we're able to transfer from young to old based on either: -// 1. an assumption that we will be able to replenish memory "borrowed" from young at the end of collection, or -// 2. there is sufficient excess in the allocation runway during GC idle cycles -void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t old_xfer_limit, size_t old_cset_regions) { - +// mutator_xfer_limit, and any surplus is transferred to the young generation. mutator_xfer_limit is +// the maximum we're able to transfer from young to old. This is called at the end of GC, as we prepare +// for the idle span that precedes the next GC. +void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_xfer_limit, + size_t old_trashed_regions, size_t young_trashed_regions) { + shenandoah_assert_heaplocked(); // We can limit the old reserve to the size of anticipated promotions: // max_old_reserve is an upper bound on memory evacuated from old and promoted to old, // clamped by the old generation space available. // // Here's the algebra. - // Let SOEP = ShenandoahOldEvacRatioPercent, + // Let SOEP = ShenandoahOldEvacPercent, // OE = old evac, // YE = young evac, and // TE = total evac = OE + YE @@ -600,81 +599,171 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t old_xfer_ // => OE = YE*SOEP/(100-SOEP) // We have to be careful in the event that SOEP is set to 100 by the user. - assert(ShenandoahOldEvacRatioPercent <= 100, "Error"); - const size_t old_available = old_generation()->available(); - // The free set will reserve this amount of memory to hold young evacuations - const size_t young_reserve = (young_generation()->max_capacity() * ShenandoahEvacReserve) / 100; - - // In the case that ShenandoahOldEvacRatioPercent equals 100, max_old_reserve is limited only by xfer_limit. - - const double bound_on_old_reserve = old_available + old_xfer_limit + young_reserve; - const double max_old_reserve = ((ShenandoahOldEvacRatioPercent == 100)? bound_on_old_reserve: - MIN2(double(young_reserve * ShenandoahOldEvacRatioPercent) - / double(100 - ShenandoahOldEvacRatioPercent), bound_on_old_reserve)); - + assert(ShenandoahOldEvacPercent <= 100, "Error"); const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + 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; + + ShenandoahYoungGeneration* young_gen = young_generation(); + size_t young_capacity = young_gen->max_capacity(); + size_t young_usage = young_gen->used(); // includes humongous waste + size_t young_available = ((young_capacity >= young_usage)? young_capacity - young_usage: 0); + size_t freeset_available = free_set()->available_locked(); + if (young_available > freeset_available) { + young_available = freeset_available; + } + young_available += young_trashed_regions * region_size_bytes; + + // The free set will reserve this amount of memory to hold young evacuations (initialized to the ideal reserve) + 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; + size_t proposed_max_old = ((ShenandoahOldEvacPercent == 100)? + bound_on_old_reserve: + MIN2((young_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), + bound_on_old_reserve)); + if (young_reserve > young_available) { + young_reserve = young_available; + } + // Decide how much old space we should reserve for a mixed collection - double reserve_for_mixed = 0; - if (old_generation()->has_unprocessed_collection_candidates()) { + size_t reserve_for_mixed = 0; + const size_t old_fragmented_available = + old_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. + proposed_max_old = old_fragmented_available; + } + size_t 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. - const double max_evac_need = - (double(old_generation()->unprocessed_collection_candidates_live_memory()) * ShenandoahOldEvacWaste); + const size_t max_evac_need = (size_t) (mixed_candidate_live_memory * ShenandoahOldEvacWaste); assert(old_available >= old_generation()->free_unaffiliated_regions() * region_size_bytes, "Unaffiliated available must be less than total available"); - const double old_fragmented_available = - double(old_available - old_generation()->free_unaffiliated_regions() * region_size_bytes); - reserve_for_mixed = max_evac_need + old_fragmented_available; - if (reserve_for_mixed > max_old_reserve) { - reserve_for_mixed = max_old_reserve; + + // 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; + } else { + excess_reserves -= reserve_for_promo; + reserve_for_promo = 0; + reserve_for_mixed -= excess_reserves; + } } } - // Decide how much space we should reserve for promotions from young - size_t reserve_for_promo = 0; + // 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're promoting and have a bound on the maximum amount that can be promoted - assert(max_old_reserve >= reserve_for_mixed, "Sanity"); - const size_t available_for_promotions = max_old_reserve - reserve_for_mixed; - reserve_for_promo = MIN2((size_t)(promo_load * ShenandoahPromoEvacWaste), available_for_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); + } + // 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. } - // This is the total old we want to ideally reserve - const size_t old_reserve = reserve_for_mixed + reserve_for_promo; - assert(old_reserve <= max_old_reserve, "cannot reserve more than max for old evacuations"); + // This is the total old we want to reserve (initialized to the ideal reserve) + size_t old_reserve = reserve_for_mixed + reserve_for_promo; // We now check if the old generation is running a surplus or a deficit. - const size_t max_old_available = old_generation()->available() + old_cset_regions * region_size_bytes; - if (max_old_available >= old_reserve) { - // We are running a surplus, so the old region surplus can go to young - const size_t old_surplus = (max_old_available - old_reserve) / region_size_bytes; - const size_t unaffiliated_old_regions = old_generation()->free_unaffiliated_regions() + old_cset_regions; - const size_t old_region_surplus = MIN2(old_surplus, unaffiliated_old_regions); - old_generation()->set_region_balance(checked_cast(old_region_surplus)); - } else { - // We are running a deficit which we'd like to fill from young. - // Ignore that this will directly impact young_generation()->max_capacity(), - // indirectly impacting young_reserve and old_reserve. These computations are conservative. - // Note that deficit is rounded up by one region. - const size_t old_need = (old_reserve - max_old_available + region_size_bytes - 1) / region_size_bytes; - const size_t max_old_region_xfer = old_xfer_limit / region_size_bytes; + size_t old_region_deficit = 0; + size_t old_region_surplus = 0; - // Round down the regions we can transfer from young to old. If we're running short - // on young-gen memory, we restrict the xfer. Old-gen collection activities will be - // curtailed if the budget is restricted. - const size_t old_region_deficit = MIN2(old_need, max_old_region_xfer); + size_t mutator_region_xfer_limit = mutator_xfer_limit / region_size_bytes; + // align the mutator_xfer_limit on region size + mutator_xfer_limit = mutator_region_xfer_limit * region_size_bytes; + + if (old_available >= 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; + 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_region_deficit = (old_deficit + region_size_bytes - 1) / region_size_bytes; + old_generation()->set_region_balance(0 - checked_cast(old_region_deficit)); + } 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; + + // 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; + } + } + + // 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; old_generation()->set_region_balance(0 - checked_cast(old_region_deficit)); } -} -void ShenandoahGenerationalHeap::reset_generation_reserves() { - ShenandoahHeapLocker locker(lock()); - young_generation()->set_evacuation_reserve(0); - old_generation()->set_evacuation_reserve(0); - old_generation()->set_promoted_reserve(0); + 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, + "Cannot reserve more memory than is available: %zu + %zu + %zu <= %zu + %zu", + young_reserve, reserve_for_mixed, reserve_for_promo, old_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); } void ShenandoahGenerationalHeap::coalesce_and_fill_old_regions(bool concurrent) { @@ -1015,10 +1104,6 @@ void ShenandoahGenerationalHeap::final_update_refs_update_region_states() { void ShenandoahGenerationalHeap::complete_degenerated_cycle() { shenandoah_assert_heaplocked_or_safepoint(); - // In case degeneration interrupted concurrent evacuation or update references, we need to clean up - // transient state. Otherwise, these actions have no effect. - reset_generation_reserves(); - if (!old_generation()->is_parsable()) { ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_coalesce_and_fill); coalesce_and_fill_old_regions(false); @@ -1036,7 +1121,6 @@ void ShenandoahGenerationalHeap::complete_concurrent_cycle() { // throw off the heuristics. entry_global_coalesce_and_fill(); } - reset_generation_reserves(); } void ShenandoahGenerationalHeap::entry_global_coalesce_and_fill() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index a2ae4a68cd0..719bae52a83 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -136,7 +136,7 @@ public: void reset_generation_reserves(); // Computes the optimal size for the old generation, represented as a surplus or deficit of old regions - void compute_old_generation_balance(size_t old_xfer_limit, size_t old_cset_regions); + void compute_old_generation_balance(size_t old_xfer_limit, size_t old_trashed_regions, size_t young_trashed_regions); // Balances generations, coalesces and fills old regions if necessary void complete_degenerated_cycle(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 683e2959a92..ef99bd98c93 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -425,20 +425,29 @@ jint ShenandoahHeap::initialize() { _affiliations[i] = ShenandoahAffiliation::FREE; } + + if (mode()->is_generational()) { + size_t young_reserve = (soft_max_capacity() * ShenandoahEvacReserve) / 100; + young_generation()->set_evacuation_reserve(young_reserve); + old_generation()->set_evacuation_reserve((size_t) 0); + old_generation()->set_promoted_reserve((size_t) 0); + } + _free_set = new ShenandoahFreeSet(this, _num_regions); post_initialize_heuristics(); + // We are initializing free set. We ignore cset region tallies. - size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; - _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); + size_t young_trashed_regions, old_trashed_regions, first_old, last_old, num_old; + _free_set->prepare_to_rebuild(young_trashed_regions, old_trashed_regions, first_old, last_old, num_old); if (mode()->is_generational()) { ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); // We cannot call // gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_cset_regions) // until after the heap is fully initialized. So we make up a safe value here. size_t allocation_runway = InitialHeapSize / 2; - gen_heap->compute_old_generation_balance(allocation_runway, old_cset_regions); + gen_heap->compute_old_generation_balance(allocation_runway, old_trashed_regions, young_trashed_regions); } - _free_set->finish_rebuild(young_cset_regions, old_cset_regions, num_old); + _free_set->finish_rebuild(young_trashed_regions, old_trashed_regions, num_old); } if (AlwaysPreTouch) { @@ -2521,13 +2530,10 @@ void ShenandoahHeap::final_update_refs_update_region_states() { parallel_heap_region_iterate(&cl); } -void ShenandoahHeap::rebuild_free_set(bool concurrent) { - ShenandoahGCPhase phase(concurrent ? - ShenandoahPhaseTimings::final_update_refs_rebuild_freeset : - ShenandoahPhaseTimings::degen_gc_final_update_refs_rebuild_freeset); +void ShenandoahHeap::rebuild_free_set_within_phase() { ShenandoahHeapLocker locker(lock()); - size_t young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count; - _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count); + size_t young_trashed_regions, old_trashed_regions, first_old_region, last_old_region, old_region_count; + _free_set->prepare_to_rebuild(young_trashed_regions, old_trashed_regions, first_old_region, last_old_region, old_region_count); // If there are no old regions, first_old_region will be greater than last_old_region assert((first_old_region > last_old_region) || ((last_old_region + 1 - first_old_region >= old_region_count) && @@ -2546,19 +2552,11 @@ void ShenandoahHeap::rebuild_free_set(bool concurrent) { // available for transfer to old. Note that transfer of humongous regions does not impact available. ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); size_t allocation_runway = - gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_cset_regions); - gen_heap->compute_old_generation_balance(allocation_runway, old_cset_regions); - - // Total old_available may have been expanded to hold anticipated promotions. We trigger if the fragmented available - // memory represents more than 16 regions worth of data. Note that fragmentation may increase when we promote regular - // regions in place when many of these regular regions have an abundant amount of available memory within them. - // Fragmentation will decrease as promote-by-copy consumes the available memory within these partially consumed regions. - // - // We consider old-gen to have excessive fragmentation if more than 12.5% of old-gen is free memory that resides - // within partially consumed regions of memory. + gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trashed_regions); + gen_heap->compute_old_generation_balance(allocation_runway, old_trashed_regions, young_trashed_regions); } // Rebuild free set based on adjusted generation sizes. - _free_set->finish_rebuild(young_cset_regions, old_cset_regions, old_region_count); + _free_set->finish_rebuild(young_trashed_regions, old_trashed_regions, old_region_count); if (mode()->is_generational()) { ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); @@ -2567,6 +2565,13 @@ void ShenandoahHeap::rebuild_free_set(bool concurrent) { } } +void ShenandoahHeap::rebuild_free_set(bool concurrent) { + ShenandoahGCPhase phase(concurrent ? + ShenandoahPhaseTimings::final_update_refs_rebuild_freeset : + ShenandoahPhaseTimings::degen_gc_final_update_refs_rebuild_freeset); + rebuild_free_set_within_phase(); +} + bool ShenandoahHeap::is_bitmap_slice_committed(ShenandoahHeapRegion* r, bool skip_self) { size_t slice = r->index() / _bitmap_regions_per_slice; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 65e3803627c..174001170f4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -481,7 +481,9 @@ private: void rendezvous_threads(const char* name); void recycle_trash(); public: + // The following two functions rebuild the free set at the end of GC, in preparation for an idle phase. void rebuild_free_set(bool concurrent); + void rebuild_free_set_within_phase(); void notify_gc_progress(); void notify_gc_no_progress(); size_t get_gc_no_progress_count() const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index e794a86e473..6bb8382de0a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -595,6 +595,8 @@ void ShenandoahHeapRegion::try_recycle_under_lock() { _recycling.unset(); } else { // Ensure recycling is unset before returning to mutator to continue memory allocation. + // Otherwise, the mutator might see region as fully recycled and might change its affiliation only to have + // the racing GC worker thread overwrite its affiliation to FREE. while (_recycling.is_set()) { if (os::is_MP()) { SpinPause(); @@ -605,6 +607,8 @@ void ShenandoahHeapRegion::try_recycle_under_lock() { } } +// Note that return from try_recycle() does not mean the region has been recycled. It only means that +// some GC worker thread has taken responsibility to recycle the region, eventually. void ShenandoahHeapRegion::try_recycle() { shenandoah_assert_not_heaplocked(); if (is_trash() && _recycling.try_set()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index a44a831ef3d..ff441a0c868 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -128,8 +128,6 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { // the space. This would be the last action if there is nothing to evacuate. entry_cleanup_early(); - heap->free_set()->log_status_under_lock(); - assert(!heap->is_concurrent_strong_root_in_progress(), "No evacuations during old gc."); // We must execute this vm operation if we completed final mark. We cannot @@ -138,7 +136,10 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { // collection. heap->concurrent_final_roots(); - size_t allocation_runway = heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(0); - heap->compute_old_generation_balance(allocation_runway, 0); + // After concurrent old marking finishes, we reclaim immediate garbage. Further, we may also want to expand OLD in order + // to make room for anticipated promotions and/or for mixed evacuations. Mixed evacuations are especially likely to + // follow the end of OLD marking. + heap->rebuild_free_set_within_phase(); + heap->free_set()->log_status_under_lock(); return true; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index c795eda3d96..aed768b9db1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -427,8 +427,7 @@ void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); size_t allocation_runway = gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trash_regions); - gen_heap->compute_old_generation_balance(allocation_runway, old_trash_regions); - + gen_heap->compute_old_generation_balance(allocation_runway, old_trash_regions, young_trash_regions); heap->free_set()->finish_rebuild(young_trash_regions, old_trash_regions, num_old); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 90c1458ac97..633d2c9f617 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -66,8 +66,8 @@ private: // remaining in a PLAB when it is retired. size_t _promoted_expended; - // Represents the quantity of live bytes we expect to promote during the next evacuation - // cycle. This value is used by the young heuristic to trigger mixed collections. + // Represents the quantity of live bytes we expect to promote during the next GC cycle, either by + // evacuation or by promote-in-place. This value is used by the young heuristic to trigger mixed collections. // It is also used when computing the optimum size for the old generation. size_t _promotion_potential; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index a3c96a7d53b..05af25f13ad 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -243,8 +243,7 @@ HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, con #ifdef ASSERT assert(ShenandoahHeap::heap()->mode()->is_generational(), "Do not use in non-generational mode"); assert(region->is_old(), "Do not use for young regions"); - // For HumongousRegion:s it's more efficient to jump directly to the - // start region. + // For humongous regions it's more efficient to jump directly to the start region. assert(!region->is_humongous(), "Use region->humongous_start_region() instead"); #endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 543df2422c0..0cc6d4c6ed4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -420,7 +420,14 @@ public: // span is the total memory affiliated with these stats (some of which is in use and other is available) size_t span() const { return _regions * ShenandoahHeapRegion::region_size_bytes(); } - size_t non_trashed_span() const { return (_regions - _trashed_regions) * ShenandoahHeapRegion::region_size_bytes(); } + size_t non_trashed_span() const { + assert(_regions >= _trashed_regions, "sanity"); + return (_regions - _trashed_regions) * ShenandoahHeapRegion::region_size_bytes(); + } + size_t non_trashed_committed() const { + assert(_committed >= _trashed_regions * ShenandoahHeapRegion::region_size_bytes(), "sanity"); + return _committed - (_trashed_regions * ShenandoahHeapRegion::region_size_bytes()); + } }; class ShenandoahGenerationStatsClosure : public ShenandoahHeapRegionClosure { diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 254483d1923..3eb1a06a911 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -400,27 +400,20 @@ "reserve/waste is incorrect, at the risk that application " \ "runs out of memory too early.") \ \ - product(uintx, ShenandoahOldEvacRatioPercent, 75, EXPERIMENTAL, \ - "The maximum proportion of evacuation from old-gen memory, " \ - "expressed as a percentage. The default value 75 denotes that " \ - "no more than 75% of the collection set evacuation workload may " \ - "be towards evacuation of old-gen heap regions. This limits both "\ - "the promotion of aged regions and the compaction of existing " \ - "old regions. A value of 75 denotes that the total evacuation " \ - "work may increase to up to four times the young gen evacuation " \ - "work. A larger value allows quicker promotion and allows " \ - "a smaller number of mixed evacuations to process " \ - "the entire list of old-gen collection candidates at the cost " \ - "of an increased disruption of the normal cadence of young-gen " \ - "collections. A value of 100 allows a mixed evacuation to " \ - "focus entirely on old-gen memory, allowing no young-gen " \ - "regions to be collected, likely resulting in subsequent " \ - "allocation failures because the allocation pool is not " \ - "replenished. A value of 0 allows a mixed evacuation to " \ - "focus entirely on young-gen memory, allowing no old-gen " \ - "regions to be collected, likely resulting in subsequent " \ - "promotion failures and triggering of stop-the-world full GC " \ - "events.") \ + product(uintx, ShenandoahOldEvacPercent, 75, EXPERIMENTAL, \ + "The maximum evacuation to old-gen expressed as a percent of " \ + "the total live memory within the collection set. With the " \ + "default setting, if collection set evacuates X, no more than " \ + "75% of X may hold objects evacuated from old or promoted to " \ + "old from young. A value of 100 allows the entire collection " \ + "set to be comprised of old-gen regions and young regions that " \ + "have reached the tenure age. Larger values allow fewer mixed " \ + "evacuations to reclaim all the garbage from old. Smaller " \ + "values result in less variation in GC cycle times between " \ + "young vs. mixed cycles. A value of 0 prevents mixed " \ + "evacations from running and blocks promotion of aged regions " \ + "by evacuation. Setting the value to 0 does not prevent " \ + "regions from being promoted in place.") \ range(0,100) \ \ product(bool, ShenandoahEvacTracking, false, DIAGNOSTIC, \ diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp index b184b19ce6c..c0ec1bcf3c9 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp @@ -201,7 +201,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, prime_one_old_region) { size_t garbage = make_garbage_above_collection_threshold(10); _heuristics->prepare_for_old_collections(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(10UL)); EXPECT_EQ(garbage, _collection_set->get_old_garbage()); @@ -214,7 +216,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, prime_many_old_regions) { size_t g1 = make_garbage_above_collection_threshold(100); size_t g2 = make_garbage_above_collection_threshold(101); _heuristics->prepare_for_old_collections(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(100UL, 101UL)); EXPECT_EQ(g1 + g2, _collection_set->get_old_garbage()); @@ -226,7 +230,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, require_multiple_mixed_evacuations) { size_t garbage = create_too_much_garbage_for_one_mixed_evacuation(); _heuristics->prepare_for_old_collections(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_LT(_collection_set->get_old_garbage(), garbage); EXPECT_GT(_heuristics->unprocessed_old_collection_candidates(), 0UL); @@ -248,7 +254,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, skip_pinned_regions) { ASSERT_EQ(3UL, _heuristics->unprocessed_old_collection_candidates()); // Here the region is still pinned, so it cannot be added to the collection set. - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } // The two unpinned regions should be added to the collection set and the pinned // region should be retained at the front of the list of candidates as it would be @@ -261,7 +269,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, skip_pinned_regions) { // the now unpinned region should be added to the collection set. make_unpinned(1); _collection_set->clear(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_EQ(_collection_set->get_old_garbage(), g2); EXPECT_TRUE(collection_set_is(1UL)); @@ -278,14 +288,18 @@ TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_first) { make_pinned(0); _heuristics->prepare_for_old_collections(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(1UL, 2UL)); EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); make_unpinned(0); _collection_set->clear(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(0UL)); EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); @@ -301,7 +315,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_last) { make_pinned(2); _heuristics->prepare_for_old_collections(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(0UL, 1UL)); EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g2); @@ -309,7 +325,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_last) { make_unpinned(2); _collection_set->clear(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(2UL)); EXPECT_EQ(_collection_set->get_old_garbage(), g3); @@ -327,7 +345,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, unpinned_region_is_middle) { make_pinned(0); make_pinned(2); _heuristics->prepare_for_old_collections(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(1UL)); EXPECT_EQ(_collection_set->get_old_garbage(), g2); @@ -336,7 +356,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, unpinned_region_is_middle) { make_unpinned(0); make_unpinned(2); _collection_set->clear(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } EXPECT_TRUE(collection_set_is(0UL, 2UL)); EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3); @@ -354,7 +376,9 @@ TEST_VM_F(ShenandoahOldHeuristicTest, all_candidates_are_pinned) { make_pinned(1); make_pinned(2); _heuristics->prepare_for_old_collections(); - _heuristics->prime_collection_set(_collection_set); + if (_heuristics->prime_collection_set(_collection_set)) { + _heuristics->finalize_mixed_evacs(); + } // In the case when all candidates are pinned, we want to abandon // this set of mixed collection candidates so that another old collection From 25d2b52ab97d116024872e567c1c1ffd814616d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Thu, 22 Jan 2026 21:48:28 +0000 Subject: [PATCH 125/126] 8328046: Need to keep leading zeros in TlsPremasterSecret of TLS1.3 DHKeyAgreement Reviewed-by: hchao --- .../share/classes/sun/security/ssl/KAKeyDerivation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index 39e82b50435..af62faf4706 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.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 @@ -214,13 +214,13 @@ public class KAKeyDerivation implements SSLKeyDerivation { var decapsulator = kem.newDecapsulator(localPrivateKey); sharedSecret = decapsulator.decapsulate( keyshare, 0, decapsulator.secretSize(), - "TlsPremasterSecret"); + "Generic"); } else { // Using traditional DH-style Key Agreement KeyAgreement ka = KeyAgreement.getInstance(algorithmName); ka.init(localPrivateKey); ka.doPhase(peerPublicKey, true); - sharedSecret = ka.generateSecret("TlsPremasterSecret"); + sharedSecret = ka.generateSecret("Generic"); } return deriveHandshakeSecret(type, sharedSecret); From 0f087a7fef2d3979badefde02a1e85351379f18c Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 23 Jan 2026 00:57:25 +0000 Subject: [PATCH 126/126] 8376051: gc/stress/TestStressG1Uncommit.java fails assertLessThan: expected that xxx < xxx Reviewed-by: tschatzl, shade --- test/hotspot/jtreg/gc/stress/TestStressG1Uncommit.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/gc/stress/TestStressG1Uncommit.java b/test/hotspot/jtreg/gc/stress/TestStressG1Uncommit.java index 9d1f95aa411..4e2b9dd5be7 100644 --- a/test/hotspot/jtreg/gc/stress/TestStressG1Uncommit.java +++ b/test/hotspot/jtreg/gc/stress/TestStressG1Uncommit.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 @@ -55,6 +55,8 @@ public class TestStressG1Uncommit { public static void main(String[] args) throws Exception { ArrayList options = new ArrayList<>(); Collections.addAll(options, + "-XX:MinHeapFreeRatio=40", + "-XX:MaxHeapFreeRatio=70", "-Xlog:gc,gc+heap+region=debug", "-XX:+UseG1GC", "-Xmx1g",