From d720a8491b2556373b2686a129c306deefafd671 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 24 Oct 2025 00:16:18 +0000 Subject: [PATCH 0001/1004] 8343220: Add test cases to AppContentTest jpackage test Reviewed-by: almatvee --- .../jdk/jpackage/internal/util/FileUtils.java | 12 + .../jpackage/test/ConfigurationTarget.java | 21 + .../helpers/jdk/jpackage/test/TKit.java | 10 +- .../tools/jpackage/share/AppContentTest.java | 728 ++++++++++++++---- 4 files changed, 639 insertions(+), 132 deletions(-) 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 8ac88c13e1d..cb1bcaa51b1 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 @@ -30,6 +30,7 @@ import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; @@ -106,6 +107,17 @@ public final class FileUtils { private static record CopyAction(Path src, Path dest) { void apply(CopyOption... options) throws IOException { + if (List.of(options).contains(StandardCopyOption.REPLACE_EXISTING)) { + // They requested copying with replacing the existing content. + if (src == null && Files.isRegularFile(dest)) { + // This copy action creates a directory, but a file at the same path already exists, so delete it. + Files.deleteIfExists(dest); + } else if (src != null && Files.isDirectory(dest)) { + // This copy action copies a file, but a directory at the same path exists already, so delete it. + deleteRecursive(dest); + } + } + if (src == null) { Files.createDirectories(dest); } else { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java index ba3131a7680..0d68d055b92 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigurationTarget.java @@ -62,6 +62,27 @@ public record ConfigurationTarget(Optional cmd, Optional verifier) { + cmd.ifPresent(Objects.requireNonNull(verifier)); + test.ifPresent(v -> { + v.addInstallVerifier(verifier::accept); + }); + return this; + } + + public ConfigurationTarget addRunOnceInitializer(Consumer initializer) { + Objects.requireNonNull(initializer); + cmd.ifPresent(_ -> { + initializer.accept(this); + }); + test.ifPresent(v -> { + v.addRunOnceInitializer(() -> { + initializer.accept(this); + }); + }); + return this; + } + public ConfigurationTarget add(AdditionalLauncher addLauncher) { return apply(addLauncher::applyTo, addLauncher::applyTo); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index a19b3697a81..baeeda4e569 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -26,6 +26,7 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import static java.util.stream.Collectors.toSet; import static jdk.jpackage.internal.util.function.ThrowingBiFunction.toBiFunction; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.Closeable; @@ -896,7 +897,14 @@ public final class TKit { public static void assertSymbolicLinkExists(Path path) { assertPathExists(path, true); assertTrue(Files.isSymbolicLink(path), String.format - ("Check [%s] is a symbolic link", path)); + ("Check [%s] is a symbolic link", Objects.requireNonNull(path))); + } + + public static void assertSymbolicLinkTarget(Path symlinkPath, Path expectedTargetPath) { + assertSymbolicLinkExists(symlinkPath); + var targetPath = toFunction(Files::readSymbolicLink).apply(symlinkPath); + assertEquals(expectedTargetPath, targetPath, + String.format("Check the target of the symbolic link [%s]", symlinkPath)); } public static void assertFileExists(Path path) { diff --git a/test/jdk/tools/jpackage/share/AppContentTest.java b/test/jdk/tools/jpackage/share/AppContentTest.java index 2c6498a631b..5b5734df61d 100644 --- a/test/jdk/tools/jpackage/share/AppContentTest.java +++ b/test/jdk/tools/jpackage/share/AppContentTest.java @@ -21,24 +21,43 @@ * questions. */ -import static jdk.internal.util.OperatingSystem.LINUX; -import static jdk.internal.util.OperatingSystem.MACOS; +import static java.util.Map.entry; import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toMap; +import static jdk.internal.util.OperatingSystem.MACOS; +import static jdk.internal.util.OperatingSystem.WINDOWS; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import jdk.jpackage.internal.util.FileUtils; -import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.IdentityWrapper; +import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.TKit; /** @@ -57,68 +76,23 @@ import jdk.jpackage.test.JPackageStringBundle; */ public class AppContentTest { - private static final String TEST_JAVA = "apps/PrintEnv.java"; - private static final String TEST_DUKE = "apps/dukeplug.png"; - private static final String TEST_DUKE_LINK = "dukeplugLink.txt"; - private static final String TEST_DIR = "apps"; - private static final String TEST_BAD = "non-existant"; - - // On OSX `--app-content` paths will be copied into the "Contents" folder - // of the output app image. - // "codesign" imposes restrictions on the directory structure of "Contents" folder. - // In particular, random files should be placed in "Contents/Resources" folder - // otherwise "codesign" will fail to sign. - // Need to prepare arguments for `--app-content` accordingly. - private static final boolean copyInResources = TKit.isOSX(); - - private static final String RESOURCES_DIR = "Resources"; - private static final String LINKS_DIR = "Links"; + @Test + @ParameterSupplier + @ParameterSupplier(value="testSymlink", ifNotOS = WINDOWS) + public void test(TestSpec testSpec) throws Exception { + testSpec.test(new ConfigurationTarget(new PackageTest().configureHelloApp())); + } @Test - // include two files in two options - @Parameter({TEST_JAVA, TEST_DUKE}) - // try to include non-existant content - @Parameter({TEST_JAVA, TEST_BAD}) - // two files in one option and a dir tree in another option. - @Parameter({TEST_JAVA + "," + TEST_DUKE, TEST_DIR}) - // include one file and one link to the file - @Parameter(value = {TEST_JAVA, TEST_DUKE_LINK}, ifOS = {MACOS,LINUX}) - public void test(String... args) throws Exception { - final List testPathArgs = List.of(args); - final int expectedJPackageExitCode; - if (testPathArgs.contains(TEST_BAD)) { - expectedJPackageExitCode = 1; - } else { - expectedJPackageExitCode = 0; - } - - var appContentInitializer = new AppContentInitializer(testPathArgs); - - new PackageTest().configureHelloApp() - .addRunOnceInitializer(appContentInitializer::initAppContent) - .addInitializer(appContentInitializer::applyTo) - .addInstallVerifier(cmd -> { - for (String arg : testPathArgs) { - List paths = Arrays.asList(arg.split(",")); - for (String p : paths) { - Path name = Path.of(p).getFileName(); - if (isSymlinkPath(name)) { - TKit.assertSymbolicLinkExists(getAppContentRoot(cmd) - .resolve(LINKS_DIR).resolve(name)); - } else { - TKit.assertPathExists(getAppContentRoot(cmd) - .resolve(name), true); - } - } - } - }) - .setExpectedExitCode(expectedJPackageExitCode) - .run(); + @ParameterSupplier("test") + @ParameterSupplier(value="testSymlink", ifNotOS = WINDOWS) + public void testAppImage(TestSpec testSpec) throws Exception { + testSpec.test(new ConfigurationTarget(JPackageCommand.helloAppImage())); } @Test(ifOS = MACOS) - @Parameter({TEST_DIR, "warning.non.standard.contents.sub.dir"}) - @Parameter({TEST_DUKE, "warning.app.content.is.not.dir"}) + @Parameter({"apps", "warning.non.standard.contents.sub.dir"}) + @Parameter({"apps/dukeplug.png", "warning.app.content.is.not.dir"}) public void testWarnings(String testPath, String warningId) throws Exception { final var appContentValue = TKit.TEST_SRC_ROOT.resolve(testPath); final var expectedWarning = JPackageStringBundle.MAIN.cannedFormattedString( @@ -126,96 +100,588 @@ public class AppContentTest { JPackageCommand.helloAppImage() .addArguments("--app-content", appContentValue) + .setFakeRuntime() .validateOutput(expectedWarning) .executeIgnoreExitCode(); } + public static Collection test() { + return Stream.of( + build().add(TEST_JAVA).add(TEST_DUKE), + build().add(TEST_JAVA).add(TEST_BAD), + build().startGroup().add(TEST_JAVA).add(TEST_DUKE).endGroup().add(TEST_DIR), + // Same directory specified multiple times. + build().add(TEST_DIR).add(TEST_DIR), + // Same file specified multiple times. + build().add(TEST_JAVA).add(TEST_JAVA), + // Two files with the same name but different content. + build() + .add(createTextFileContent("welcome.txt", "Welcome")) + .add(createTextFileContent("welcome.txt", "Benvenuti")), + // Same name: one is a directory, another is a file. + build().add(createTextFileContent("a/b/c/d", "Foo")).add(createTextFileContent("a", "Bar")), + // Same name: one is a file, another is a directory. + build().add(createTextFileContent("a", "Bar")).add(createTextFileContent("a/b/c/d", "Foo")) + ).map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); + } + + public static Collection testSymlink() { + return Stream.of( + build().add(TEST_JAVA) + .add(new SymlinkContentFactory("Links", "duke-link", "duke-target")) + .add(new SymlinkContentFactory("", "a/b/foo-link", "c/bar-target")) + ).map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); + } + + public record TestSpec(List> contentFactories) { + public TestSpec { + contentFactories.stream().flatMap(List::stream).forEach(Objects::requireNonNull); + } + + @Override + public String toString() { + return contentFactories.stream().map(group -> { + return group.stream().map(ContentFactory::toString).collect(joining(",")); + }).collect(joining("; ")); + } + + void test(ConfigurationTarget target) { + final int expectedJPackageExitCode; + if (contentFactories.stream().flatMap(List::stream).anyMatch(TEST_BAD::equals)) { + expectedJPackageExitCode = 1; + } else { + expectedJPackageExitCode = 0; + } + + final List> allContent = new ArrayList<>(); + + target.addInitializer(JPackageCommand::setFakeRuntime) + .addRunOnceInitializer(_ -> { + contentFactories.stream().map(group -> { + return group.stream().map(ContentFactory::create).toList(); + }).forEach(allContent::add); + }).addInitializer(cmd -> { + allContent.stream().map(group -> { + return Stream.of("--app-content", group.stream() + .map(Content::paths) + .flatMap(List::stream) + .map(appContentArg -> { + if (COPY_IN_RESOURCES && Optional.ofNullable(appContentArg.getParent()) + .map(Path::getFileName) + .map(RESOURCES_DIR::equals) + .orElse(false)) { + return appContentArg.getParent(); + } else { + return appContentArg; + } + }) + .map(Path::toString) + .collect(joining(","))); + }).flatMap(x -> x).forEachOrdered(cmd::addArgument); + }); + + target.cmd().ifPresent(cmd -> { + if (expectedJPackageExitCode == 0) { + cmd.executeAndAssertImageCreated(); + } else { + cmd.execute(expectedJPackageExitCode); + } + }); + + target.addInstallVerifier(cmd -> { + var appContentRoot = getAppContentRoot(cmd); + + Set disabledVerifiers = new HashSet<>(); + + var verifiers = allContent.stream().flatMap(List::stream).flatMap(content -> { + return StreamSupport.stream(content.verifiers(appContentRoot).spliterator(), false).map(verifier -> { + return new PathVerifierWithOrigin(verifier, content); + }); + }).collect(toMap(PathVerifierWithOrigin::path, x -> x, (first, second) -> { + // The same file in the content directory is sourced from multiple origins. + // jpackage will handle this case such that the following origins overwrite preceding origins. + // Scratch all path verifiers affected by overrides. + first.getNestedVerifiers(appContentRoot, first.path()).forEach(disabledVerifiers::add); + return second; + }, TreeMap::new)).values().stream() + .map(PathVerifierWithOrigin::verifier) + .filter(Predicate.not(disabledVerifiers::contains)) + .filter(verifier -> { + if (!(verifier instanceof DirectoryVerifier dirVerifier)) { + return true; + } else { + try { + // Run the directory verifier if the directory is empty. + // Otherwise, it just pollutes the test log. + return isDirectoryEmpty(verifier.path()); + } catch (NoSuchFileException ex) { + // If an MSI contains an empty directory, it will be installed but not created when the MSI is unpacked. + // In the latter the control flow will reach this point. + if (dirVerifier.isEmpty() + && PackageType.WINDOWS.contains(cmd.packageType()) + && cmd.isPackageUnpacked(String.format( + "Expected empty directory [%s] is missing", verifier.path()))) { + return false; + } + throw new UncheckedIOException(ex); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + }) + .toList(); + + verifiers.forEach(PathVerifier::verify); + }); + + target.test().ifPresent(test -> { + test.setExpectedExitCode(expectedJPackageExitCode).run(); + }); + } + + static final class Builder { + TestSpec create() { + return new TestSpec(groups); + } + + final class GroupBuilder { + GroupBuilder add(ContentFactory cf) { + group.add(Objects.requireNonNull(cf)); + return this; + } + + Builder endGroup() { + if (!group.isEmpty()) { + groups.add(group); + } + return Builder.this; + } + + private final List group = new ArrayList<>(); + } + + Builder add(ContentFactory cf) { + return startGroup().add(cf).endGroup(); + } + + GroupBuilder startGroup() { + return new GroupBuilder(); + } + + private final List> groups = new ArrayList<>(); + } + + private record PathVerifierWithOrigin(PathVerifier verifier, Content origin) { + PathVerifierWithOrigin { + Objects.requireNonNull(verifier); + Objects.requireNonNull(origin); + } + + Path path() { + return verifier.path(); + } + + Stream getNestedVerifiers(Path appContentRoot, Path path) { + if (!path.startsWith(appContentRoot)) { + throw new IllegalArgumentException(); + } + + return StreamSupport.stream(origin.verifiers(appContentRoot).spliterator(), false).filter(v -> { + return v.path().getNameCount() > path.getNameCount() && v.path().startsWith(path); + }); + } + } + } + + private static TestSpec.Builder build() { + return new TestSpec.Builder(); + } + private static Path getAppContentRoot(JPackageCommand cmd) { - Path contentDir = cmd.appLayout().contentDirectory(); - if (copyInResources) { + final Path contentDir = cmd.appLayout().contentDirectory(); + if (COPY_IN_RESOURCES) { return contentDir.resolve(RESOURCES_DIR); } else { return contentDir; } } - private static boolean isSymlinkPath(Path v) { - return v.getFileName().toString().contains("Link"); + private static Path createAppContentRoot() { + if (COPY_IN_RESOURCES) { + return TKit.createTempDirectory("app-content").resolve(RESOURCES_DIR); + } else { + return TKit.createTempDirectory("app-content"); + } } - private static final class AppContentInitializer { - AppContentInitializer(List appContentArgs) { - appContentPathGroups = appContentArgs.stream().map(arg -> { - return Stream.of(arg.split(",")).map(Path::of).toList(); - }).toList(); + private static boolean isDirectoryEmpty(Path path) throws IOException { + if (Files.exists(path) && !Files.isDirectory(path)) { + throw new IllegalArgumentException(); } - void initAppContent() { - jpackageArgs = appContentPathGroups.stream() - .map(AppContentInitializer::initAppContentPaths) - .mapMulti((appContentPaths, consumer) -> { - consumer.accept("--app-content"); - consumer.accept( - appContentPaths.stream().map(Path::toString).collect( - joining(","))); - }).toList(); + try (var files = Files.list(path)) { + return files.findAny().isEmpty(); + } + } + + @FunctionalInterface + private interface ContentFactory { + Content create(); + } + + private interface Content { + List paths(); + Iterable verifiers(Path appContentRoot); + } + + private sealed interface PathVerifier permits + RegularFileVerifier, + DirectoryVerifier, + SymlinkTargetVerifier, + NoPathVerifier { + + Path path(); + void verify(); + } + + private record RegularFileVerifier(Path path, Path srcFile) implements PathVerifier { + RegularFileVerifier { + Objects.requireNonNull(path); + Objects.requireNonNull(srcFile); } - void applyTo(JPackageCommand cmd) { - cmd.addArguments(jpackageArgs); + @Override + public void verify() { + TKit.assertSameFileContent(srcFile, path); + } + } + + private record DirectoryVerifier(Path path, boolean isEmpty, IdentityWrapper origin) implements PathVerifier { + DirectoryVerifier { + Objects.requireNonNull(path); } - private static Path copyAppContentPath(Path appContentPath) throws IOException { - var appContentArg = TKit.createTempDirectory("app-content").resolve(RESOURCES_DIR); - var srcPath = TKit.TEST_SRC_ROOT.resolve(appContentPath); - var dstPath = appContentArg.resolve(srcPath.getFileName()); - FileUtils.copyRecursive(srcPath, dstPath); - return appContentArg; - } - - private static Path createAppContentLink(Path appContentPath) throws IOException { - var appContentArg = TKit.createTempDirectory("app-content"); - Path dstPath; - if (copyInResources) { - appContentArg = appContentArg.resolve(RESOURCES_DIR); - dstPath = appContentArg.resolve(LINKS_DIR) - .resolve(appContentPath.getFileName()); + @Override + public void verify() { + if (isEmpty) { + TKit.assertDirectoryEmpty(path); } else { - appContentArg = appContentArg.resolve(LINKS_DIR); - dstPath = appContentArg.resolve(appContentPath.getFileName()); + TKit.assertDirectoryExists(path); + } + } + } + + private record SymlinkTargetVerifier(Path path, Path targetPath) implements PathVerifier { + SymlinkTargetVerifier { + Objects.requireNonNull(path); + Objects.requireNonNull(targetPath); + } + + @Override + public void verify() { + TKit.assertSymbolicLinkTarget(path, targetPath); + } + } + + private record NoPathVerifier(Path path) implements PathVerifier { + NoPathVerifier { + Objects.requireNonNull(path); + } + + @Override + public void verify() { + TKit.assertPathExists(path, false); + } + } + + private record FileContent(Path path, int level) implements Content { + + FileContent { + Objects.requireNonNull(path); + if ((level < 0) || (path.getNameCount() <= level)) { + throw new IllegalArgumentException(); + } + } + + @Override + public List paths() { + return List.of(appContentOptionValue()); + } + + @Override + public Iterable verifiers(Path appContentRoot) { + List verifiers = new ArrayList<>(); + + var appContentPath = appContentRoot.resolve(pathInAppContentRoot()); + + if (Files.isDirectory(path)) { + try (var walk = Files.walk(path)) { + verifiers.addAll(walk.map(srcFile -> { + var dstFile = appContentPath.resolve(path.relativize(srcFile)); + if (Files.isRegularFile(srcFile)) { + return new RegularFileVerifier(dstFile, srcFile); + } else { + return new DirectoryVerifier(dstFile, + toFunction(AppContentTest::isDirectoryEmpty).apply(srcFile), + new IdentityWrapper<>(this)); + } + }).toList()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } else if (Files.isRegularFile(path)) { + verifiers.add(new RegularFileVerifier(appContentPath, path)); + } else { + verifiers.add(new NoPathVerifier(appContentPath)); } - Files.createDirectories(dstPath.getParent()); - - // Create target file for a link - String tagetName = dstPath.getFileName().toString().replace("Link", ""); - Path targetPath = dstPath.getParent().resolve(tagetName); - Files.write(targetPath, "foo".getBytes()); - // Create link - Files.createSymbolicLink(dstPath, targetPath.getFileName()); - - return appContentArg; - } - - private static List initAppContentPaths(List appContentPaths) { - return appContentPaths.stream().map(appContentPath -> { - if (appContentPath.endsWith(TEST_BAD)) { - return appContentPath; - } else if (isSymlinkPath(appContentPath)) { - return ThrowingFunction.toFunction( - AppContentInitializer::createAppContentLink).apply( - appContentPath); - } else if (copyInResources) { - return ThrowingFunction.toFunction( - AppContentInitializer::copyAppContentPath).apply( - appContentPath); - } else { - return TKit.TEST_SRC_ROOT.resolve(appContentPath); + if (level > 0) { + var cur = appContentPath; + for (int i = 0; i != level; i++) { + cur = cur.getParent(); + verifiers.add(new DirectoryVerifier(cur, false, new IdentityWrapper<>(this))); } - }).toList(); + } + + return verifiers; } - private List jpackageArgs; - private final List> appContentPathGroups; + private Path appContentOptionValue() { + var cur = path; + for (int i = 0; i != level; i++) { + cur = cur.getParent(); + } + return cur; + } + + private Path pathInAppContentRoot() { + return StreamSupport.stream(path.spliterator(), false) + .skip(path.getNameCount() - level - 1) + .reduce(Path::resolve).orElseThrow(); + } } + + /** + * Non-existing content. + */ + private static final class NonExistantPath implements ContentFactory { + @Override + public Content create() { + var nonExistant = TKit.createTempFile("non-existant"); + try { + TKit.deleteIfExists(nonExistant); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return new FileContent(nonExistant, 0); + } + + @Override + public String toString() { + return "*non-existant*"; + } + } + + /** + * Creates a content from a directory tree. + * + * @param path name of directory where to create a directory tree + */ + private static ContentFactory createDirTreeContent(Path path) { + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + + return new FileContentFactory(() -> { + var basedir = TKit.createTempDirectory("content").resolve(path); + + for (var textFile : Map.ofEntries( + entry("woods/moose", "The moose"), + entry("woods/bear", "The bear"), + entry("woods/trees/jay", "The gray jay") + ).entrySet()) { + var src = basedir.resolve(textFile.getKey()); + Files.createDirectories(src.getParent()); + TKit.createTextFile(src, Stream.of(textFile.getValue())); + } + + for (var emptyDir : List.of("sky")) { + Files.createDirectories(basedir.resolve(emptyDir)); + } + + return basedir; + }, path); + } + + private static ContentFactory createDirTreeContent(String path) { + return createDirTreeContent(Path.of(path)); + } + + /** + * Creates a content from a text file. + * + * @param path the path where to copy the text file in app image's content directory + * @param lines the content of the source text file + */ + private static ContentFactory createTextFileContent(Path path, String ... lines) { + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + + return new FileContentFactory(() -> { + var srcPath = TKit.createTempDirectory("content").resolve(path); + Files.createDirectories(srcPath.getParent()); + TKit.createTextFile(srcPath, Stream.of(lines)); + return srcPath; + }, path); + } + + private static ContentFactory createTextFileContent(String path, String ... lines) { + return createTextFileContent(Path.of(path), lines); + } + + /** + * Symlink content factory. + * + * @path basedir the directory where to write the content in app image's content + * directory + * @param symlink the path to the symlink relative to {@code basedir} path + * @param symlinked the path to the source file for the symlink + */ + private record SymlinkContentFactory(Path basedir, Path symlink, Path symlinked) implements ContentFactory { + SymlinkContentFactory { + for (final var path : List.of(basedir, symlink, symlinked)) { + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + } + + SymlinkContentFactory(String basedir, String symlink, String symlinked) { + this(Path.of(basedir), Path.of(symlink), Path.of(symlinked)); + } + + @Override + public Content create() { + final var appContentRoot = createAppContentRoot(); + + final var symlinkPath = appContentRoot.resolve(symlinkPath()); + final var symlinkedPath = appContentRoot.resolve(symlinkedPath()); + try { + Files.createDirectories(symlinkPath.getParent()); + Files.createDirectories(symlinkedPath.getParent()); + // Create the target file for the link. + Files.writeString(symlinkedPath, symlinkedPath().toString()); + // Create the link. + Files.createSymbolicLink(symlinkPath, symlinkTarget()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + List contentPaths; + if (COPY_IN_RESOURCES) { + contentPaths = List.of(appContentRoot); + } else if (basedir.equals(Path.of(""))) { + contentPaths = Stream.of(symlinkPath(), symlinkedPath()).map(path -> { + return path.getName(0); + }).map(appContentRoot::resolve).toList(); + } else { + contentPaths = List.of(appContentRoot.resolve(basedir)); + } + + return new Content() { + @Override + public List paths() { + return contentPaths; + } + + @Override + public Iterable verifiers(Path appContentRoot) { + return List.of( + new RegularFileVerifier(appContentRoot.resolve(symlinkedPath()), symlinkedPath), + new SymlinkTargetVerifier(appContentRoot.resolve(symlinkPath()), symlinkTarget()) + ); + } + }; + } + + @Override + public String toString() { + return String.format("symlink:[%s]->[%s][%s]", symlinkPath(), symlinkedPath(), symlinkTarget()); + } + + private Path symlinkPath() { + return basedir.resolve(symlink); + } + + private Path symlinkedPath() { + return basedir.resolve(symlinked); + } + + private Path symlinkTarget() { + return Optional.ofNullable(symlinkPath().getParent()).map(dir -> { + return dir.relativize(symlinkedPath()); + }).orElseGet(this::symlinkedPath); + } + } + + private static final class FileContentFactory implements ContentFactory { + + FileContentFactory(ThrowingSupplier factory, Path pathInAppContentRoot) { + this.factory = ThrowingSupplier.toSupplier(factory); + this.pathInAppContentRoot = pathInAppContentRoot; + if (pathInAppContentRoot.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + + @Override + public Content create() { + Path srcPath = factory.get(); + if (!srcPath.endsWith(pathInAppContentRoot)) { + throw new IllegalArgumentException(); + } + + Path dstPath; + if (!COPY_IN_RESOURCES) { + dstPath = srcPath; + } else { + var contentDir = createAppContentRoot(); + dstPath = contentDir.resolve(pathInAppContentRoot); + try { + FileUtils.copyRecursive(srcPath, dstPath); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + return new FileContent(dstPath, pathInAppContentRoot.getNameCount() - 1); + } + + @Override + public String toString() { + return pathInAppContentRoot.toString(); + } + + private final Supplier factory; + private final Path pathInAppContentRoot; + } + + 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(); + + // On OSX `--app-content` paths will be copied into the "Contents" folder + // of the output app image. + // "codesign" imposes restrictions on the directory structure of "Contents" folder. + // In particular, random files should be placed in "Contents/Resources" folder + // otherwise "codesign" will fail to sign. + // Need to prepare arguments for `--app-content` accordingly. + private static final boolean COPY_IN_RESOURCES = TKit.isOSX(); + + private static final Path RESOURCES_DIR = Path.of("Resources"); } From 586235896536cde293402167775d4d60f1426a9e Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 24 Oct 2025 00:40:13 +0000 Subject: [PATCH 0002/1004] 8370013: Refactor Double.toHexString to eliminate regex and StringBuilder Reviewed-by: rgiulietti, darcy --- .../share/classes/java/lang/Double.java | 120 +++++++++++------- test/jdk/java/lang/Double/ToHexString.java | 21 +++ .../{FloatingDecimal.java => Doubles.java} | 13 +- 3 files changed, 105 insertions(+), 49 deletions(-) rename test/micro/org/openjdk/bench/java/lang/{FloatingDecimal.java => Doubles.java} (89%) diff --git a/src/java.base/share/classes/java/lang/Double.java b/src/java.base/share/classes/java/lang/Double.java index 6cee75cea2b..53b027dd773 100644 --- a/src/java.base/share/classes/java/lang/Double.java +++ b/src/java.base/share/classes/java/lang/Double.java @@ -1,5 +1,6 @@ /* * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +34,7 @@ import java.util.Optional; import jdk.internal.math.FloatingDecimal; import jdk.internal.math.DoubleConsts; import jdk.internal.math.DoubleToDecimal; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.IntrinsicCandidate; /** @@ -699,56 +701,80 @@ public final class Double extends Number * 7.19.6.1; however, the output of this method is more * tightly specified. */ - if (!isFinite(d) ) + if (!isFinite(d)) { // For infinity and NaN, use the decimal output. return Double.toString(d); - else { - // Initialized to maximum size of output. - StringBuilder answer = new StringBuilder(24); - - if (Math.copySign(1.0, d) == -1.0) // value is negative, - answer.append("-"); // so append sign info - - answer.append("0x"); - - d = Math.abs(d); - - if(d == 0.0) { - answer.append("0.0p0"); - } else { - boolean subnormal = (d < Double.MIN_NORMAL); - - // Isolate significand bits and OR in a high-order bit - // so that the string representation has a known - // length. - long signifBits = (Double.doubleToLongBits(d) - & DoubleConsts.SIGNIF_BIT_MASK) | - 0x1000000000000000L; - - // Subnormal values have a 0 implicit bit; normal - // values have a 1 implicit bit. - answer.append(subnormal ? "0." : "1."); - - // Isolate the low-order 13 digits of the hex - // representation. If all the digits are zero, - // replace with a single 0; otherwise, remove all - // trailing zeros. - String signif = Long.toHexString(signifBits).substring(3,16); - answer.append(signif.equals("0000000000000") ? // 13 zeros - "0": - signif.replaceFirst("0{1,12}$", "")); - - answer.append('p'); - // If the value is subnormal, use the E_min exponent - // value for double; otherwise, extract and report d's - // exponent (the representation of a subnormal uses - // E_min -1). - answer.append(subnormal ? - Double.MIN_EXPONENT: - Math.getExponent(d)); - } - return answer.toString(); } + + long doubleToLongBits = Double.doubleToLongBits(d); + boolean negative = doubleToLongBits < 0; + + if (d == 0.0) { + return negative ? "-0x0.0p0" : "0x0.0p0"; + } + d = Math.abs(d); + // Check if the value is subnormal (less than the smallest normal value) + boolean subnormal = d < Double.MIN_NORMAL; + + // Isolate significand bits and OR in a high-order bit + // so that the string representation has a known length. + // This ensures we always have 13 hex digits to work with (52 bits / 4 bits per hex digit) + long signifBits = doubleToLongBits & DoubleConsts.SIGNIF_BIT_MASK; + + // Calculate the number of trailing zeros in the significand (in groups of 4 bits) + // This is used to remove trailing zeros from the hex representation + // We limit to 12 because we want to keep at least 1 hex digit (13 total - 12 = 1) + // assert 0 <= trailingZeros && trailingZeros <= 12 + int trailingZeros = Long.numberOfTrailingZeros(signifBits | 1L << 4 * 12) >> 2; + + // Determine the exponent value based on whether the number is subnormal or normal + // Subnormal numbers use the minimum exponent, normal numbers use the actual exponent + int exp = subnormal ? Double.MIN_EXPONENT : Math.getExponent(d); + + // Calculate the total length of the resulting string: + // Sign (optional) + prefix "0x" + implicit bit + "." + hex digits + "p" + exponent + int charlen = (negative ? 1 : 0) // sign character + + 4 // "0x1." or "0x0." + + 13 - trailingZeros // hex digits (13 max, minus trailing zeros) + + 1 // "p" + + DecimalDigits.stringSize(exp) // exponent + ; + + // Create a byte array to hold the result characters + byte[] chars = new byte[charlen]; + int index = 0; + + // Add the sign character if the number is negative + if (negative) { // value is negative + chars[index++] = '-'; + } + + // Add the prefix and the implicit bit ('1' for normal, '0' for subnormal) + // Subnormal values have a 0 implicit bit; normal values have a 1 implicit bit. + chars[index ] = '0'; // Hex prefix + chars[index + 1] = 'x'; // Hex prefix + chars[index + 2] = (byte) (subnormal ? '0' : '1'); // Implicit bit + chars[index + 3] = '.'; // Decimal point + index += 4; + + // Convert significand to hex digits manually to avoid creating temporary strings + // Extract the 13 hex digits (52 bits) from signifBits + // We need to extract bits 48-51, 44-47, ..., 0-3 (13 groups of 4 bits) + for (int sh = 4 * 12, end = 4 * trailingZeros; sh >= end; sh -= 4) { + // Extract 4 bits at a time from left to right + // Shift right by sh positions and mask with 0xF + // Integer.digits maps values 0-15 to '0'-'f' characters + chars[index++] = Integer.digits[((int)(signifBits >> sh)) & 0xF]; + } + + // Add the exponent indicator + chars[index] = 'p'; + + // Append the exponent value to the character array + // This method writes the decimal representation of exp directly into the byte array + DecimalDigits.uncheckedGetCharsLatin1(exp, charlen, chars); + + return String.newStringWithLatin1Bytes(chars); } /** diff --git a/test/jdk/java/lang/Double/ToHexString.java b/test/jdk/java/lang/Double/ToHexString.java index 912835b7aeb..c408f74fa63 100644 --- a/test/jdk/java/lang/Double/ToHexString.java +++ b/test/jdk/java/lang/Double/ToHexString.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -174,6 +175,26 @@ public class ToHexString { {"+4.9e-324", "0000000000000001"}, {"-4.9e-324", "8000000000000001"}, + // Test cases for trailing zeros in significand + // These test the removal of trailing zeros in the hexadecimal representation + // The comments indicate the number of trailing zeros removed from the significand + // For "0x1.0p1", there are 13 trailing zeros in the significand, but only 12 are removed + // as we always keep at least one hex digit in the significand + {"0x1.0p1", "4000000000000000"}, // 12 trailing zeros removed (13 total, but only 12 removed) + {"0x1.1p1", "4001000000000000"}, // 12 trailing zeros removed (all zeros after '1') + {"0x1.01p1", "4000100000000000"}, // 11 trailing zeros removed + {"0x1.001p1", "4000010000000000"}, // 10 trailing zeros removed + {"0x1.0001p1", "4000001000000000"}, // 9 trailing zeros removed + {"0x1.00001p1", "4000000100000000"}, // 8 trailing zeros removed + {"0x1.000001p1", "4000000010000000"}, // 7 trailing zeros removed + {"0x1.0000001p1", "4000000001000000"}, // 6 trailing zeros removed + {"0x1.00000001p1", "4000000000100000"}, // 5 trailing zeros removed + {"0x1.000000001p1", "4000000000010000"}, // 4 trailing zeros removed + {"0x1.0000000001p1", "4000000000001000"}, // 3 trailing zeros removed + {"0x1.00000000001p1", "4000000000000100"}, // 2 trailing zeros removed + {"0x1.000000000001p1", "4000000000000010"}, // 1 trailing zero removed (minimum) + {"0x1.0000000000001p1", "4000000000000001"}, // 0 trailing zeros removed (no trailing zeros to remove) + // fdlibm k_sin.c {"+5.00000000000000000000e-01", "3FE0000000000000"}, {"-1.66666666666666324348e-01", "BFC5555555555549"}, diff --git a/test/micro/org/openjdk/bench/java/lang/FloatingDecimal.java b/test/micro/org/openjdk/bench/java/lang/Doubles.java similarity index 89% rename from test/micro/org/openjdk/bench/java/lang/FloatingDecimal.java rename to test/micro/org/openjdk/bench/java/lang/Doubles.java index b8d29aabc84..50c295900af 100644 --- a/test/micro/org/openjdk/bench/java/lang/FloatingDecimal.java +++ b/test/micro/org/openjdk/bench/java/lang/Doubles.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +48,7 @@ import java.util.concurrent.TimeUnit; @Warmup(iterations = 10, time = 1) @Measurement(iterations = 5, time = 2) @Fork(3) -public class FloatingDecimal { +public class Doubles { private double[] randomArray, twoDecimalsArray, integerArray; private static final int TESTSIZE = 1000; @@ -65,6 +66,14 @@ public class FloatingDecimal { } } + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void toHexString(Blackhole bh) { + for (double d : randomArray) { + bh.consume(Double.toHexString(d)); + } + } + /** Tests Double.toString on double values generated from Random.nextDouble() */ @Benchmark @OperationsPerInvocation(TESTSIZE) From 87645afa052a87ab2af9602c8fafc2a707c77c19 Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Fri, 24 Oct 2025 05:43:16 +0000 Subject: [PATCH 0003/1004] 8370389: JavaFrameAnchor on s390 has unnecessary barriers Reviewed-by: lucy, aph --- src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp b/src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp index ae8b8766159..307034ca0cd 100644 --- a/src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp +++ b/src/hotspot/cpu/s390/javaFrameAnchor_s390.hpp @@ -35,38 +35,32 @@ // 3 - restoring an old state (javaCalls). inline void clear(void) { + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and only observers the thread its running on. + // Clearing _last_Java_sp must be first. - OrderAccess::release(); + _last_Java_sp = nullptr; - // Fence? - OrderAccess::fence(); _last_Java_pc = nullptr; } inline void set(intptr_t* sp, address pc) { _last_Java_pc = pc; - - OrderAccess::release(); _last_Java_sp = sp; } void copy(JavaFrameAnchor* src) { - // In order to make sure the transition state is valid for "this" + // No hardware barriers are necessary. All members are volatile and the profiler + // is run from a signal handler and only observers the thread its running on. + // we must clear _last_Java_sp before copying the rest of the new data. - // Hack Alert: Temporary bugfix for 4717480/4721647 - // To act like previous version (pd_cache_state) don't null _last_Java_sp - // unless the value is changing. - // if (_last_Java_sp != src->_last_Java_sp) { - OrderAccess::release(); _last_Java_sp = nullptr; - OrderAccess::fence(); } _last_Java_pc = src->_last_Java_pc; // Must be last so profiler will always see valid frame if has_last_frame() is true. - OrderAccess::release(); _last_Java_sp = src->_last_Java_sp; } @@ -80,7 +74,7 @@ intptr_t* last_Java_fp(void) { return nullptr; } intptr_t* last_Java_sp() const { return _last_Java_sp; } - void set_last_Java_sp(intptr_t* sp) { OrderAccess::release(); _last_Java_sp = sp; } + void set_last_Java_sp(intptr_t* sp) { _last_Java_sp = sp; } address last_Java_pc(void) { return _last_Java_pc; } From 26eed3b61e4987a2998f941d7d26790493850612 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 24 Oct 2025 07:25:53 +0000 Subject: [PATCH 0004/1004] 8068293: [TEST_BUG] Test closed/com/sun/java/swing/plaf/motif/InternalFrame/4150591/bug4150591.java fails with GTKLookAndFeel Reviewed-by: serb, tr --- test/jdk/javax/swing/plaf/motif/bug4150591.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/jdk/javax/swing/plaf/motif/bug4150591.java b/test/jdk/javax/swing/plaf/motif/bug4150591.java index 66c668a441c..f3614908cff 100644 --- a/test/jdk/javax/swing/plaf/motif/bug4150591.java +++ b/test/jdk/javax/swing/plaf/motif/bug4150591.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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,6 +23,7 @@ import com.sun.java.swing.plaf.motif.MotifInternalFrameTitlePane; import javax.swing.JInternalFrame; +import javax.swing.UIManager; /* * @test @@ -36,7 +37,8 @@ import javax.swing.JInternalFrame; */ public class bug4150591 { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); MotifInternalFrameTitlePane mtp = new MotifInternalFrameTitlePane(new JInternalFrame()); } } From b31bbfcf2f13fa5b16762f5384d95c2b5d9c5705 Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Fri, 24 Oct 2025 08:26:24 +0000 Subject: [PATCH 0005/1004] 8368787: Error reporting: hs_err files should show instructions when referencing code in nmethods Reviewed-by: stuefe, aph, mbaesken, shade --- src/hotspot/share/code/codeBlob.cpp | 1 + src/hotspot/share/code/nmethod.cpp | 40 +++++++++++++++++++++++++++++ src/hotspot/share/code/nmethod.hpp | 1 + 3 files changed, 42 insertions(+) diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 18e77520139..e901d560616 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -910,6 +910,7 @@ void CodeBlob::dump_for_addr(address addr, outputStream* st, bool verbose) const nm->print_nmethod(true); } else { nm->print_on(st); + nm->print_code_snippet(st, addr); } return; } diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index a99e753180d..8e6a1797480 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -4308,6 +4308,46 @@ void nmethod::print_value_on_impl(outputStream* st) const { #endif } +void nmethod::print_code_snippet(outputStream* st, address addr) const { + if (entry_point() <= addr && addr < code_end()) { + // Pointing into the nmethod's code. Try to disassemble some instructions around addr. + // Determine conservative start and end points. + address start; + if (frame_complete_offset() != CodeOffsets::frame_never_safe && + addr >= code_begin() + frame_complete_offset()) { + start = code_begin() + frame_complete_offset(); + } else { + start = (addr < verified_entry_point()) ? entry_point() : verified_entry_point(); + } + address start_for_hex_dump = start; // We can choose a different starting point for hex dump, below. + address end = code_end(); + + // Try using relocations to find closer instruction start and end points. + // (Some platforms have variable length instructions and can only + // disassemble correctly at instruction start addresses.) + RelocIterator iter((nmethod*)this, start); + while (iter.next() && iter.addr() < addr) { // find relocation before addr + // Note: There's a relocation which doesn't point to an instruction start: + // ZBarrierRelocationFormatStoreGoodAfterMov with ZGC on x86_64 + // We could detect and skip it, but hex dump is still usable when + // disassembler produces garbage in such a very rare case. + start = iter.addr(); + // We want at least 64 Bytes ahead in hex dump. + if (iter.addr() <= (addr - 64)) start_for_hex_dump = iter.addr(); + } + if (iter.has_current()) { + if (iter.addr() == addr) iter.next(); // find relocation after addr + if (iter.has_current()) end = iter.addr(); + } + + // Always print hex. Disassembler may still have problems when hitting an incorrect instruction start. + os::print_hex_dump(st, start_for_hex_dump, end, 1, /* print_ascii=*/false); + if (!Disassembler::is_abstract()) { + Disassembler::decode(start, end, st); + } + } +} + #ifndef PRODUCT void nmethod::print_calls(outputStream* st) { diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 3763b81f887..bce0181a3ec 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -1008,6 +1008,7 @@ public: void print_on_impl(outputStream* st) const; void print_code(); void print_value_on_impl(outputStream* st) const; + void print_code_snippet(outputStream* st, address addr) const; #if defined(SUPPORT_DATA_STRUCTS) // print output in opt build for disassembler library From b7a4c9ced82717434e43b3f3a0a57083f4005f32 Mon Sep 17 00:00:00 2001 From: Johannes Bechberger Date: Fri, 24 Oct 2025 08:55:17 +0000 Subject: [PATCH 0006/1004] 8366240: Improve memory ordering in new CPU Time Profiler Reviewed-by: jbachorik, krk, zgu --- .../sampling/jfrCPUTimeThreadSampler.cpp | 38 ++++++++----------- .../sampling/jfrCPUTimeThreadSampler.hpp | 20 ++++++---- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp index 7507b9c994e..031dfb7e8ad 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp @@ -82,14 +82,7 @@ JfrCPUTimeTraceQueue::~JfrCPUTimeTraceQueue() { bool JfrCPUTimeTraceQueue::enqueue(JfrCPUTimeSampleRequest& request) { assert(JavaThread::current()->jfr_thread_local()->is_cpu_time_jfr_enqueue_locked(), "invariant"); assert(&JavaThread::current()->jfr_thread_local()->cpu_time_jfr_queue() == this, "invariant"); - u4 elementIndex; - do { - elementIndex = AtomicAccess::load_acquire(&_head); - if (elementIndex >= _capacity) { - return false; - } - } while (AtomicAccess::cmpxchg(&_head, elementIndex, elementIndex + 1) != elementIndex); - _data[elementIndex] = request; + _data[_head++] = request; return true; } @@ -101,19 +94,19 @@ JfrCPUTimeSampleRequest& JfrCPUTimeTraceQueue::at(u4 index) { static volatile u4 _lost_samples_sum = 0; u4 JfrCPUTimeTraceQueue::size() const { - return AtomicAccess::load_acquire(&_head); + return _head; } void JfrCPUTimeTraceQueue::set_size(u4 size) { - AtomicAccess::release_store(&_head, size); + _head = size; } u4 JfrCPUTimeTraceQueue::capacity() const { - return AtomicAccess::load_acquire(&_capacity); + return _capacity; } void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) { - if (capacity == AtomicAccess::load(&_capacity)) { + if (capacity == _capacity) { return; } _head = 0; @@ -126,15 +119,15 @@ void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) { } else { _data = nullptr; } - AtomicAccess::release_store(&_capacity, capacity); + _capacity = capacity; } bool JfrCPUTimeTraceQueue::is_empty() const { - return AtomicAccess::load_acquire(&_head) == 0; + return _head == 0; } u4 JfrCPUTimeTraceQueue::lost_samples() const { - return AtomicAccess::load(&_lost_samples); + return _lost_samples; } void JfrCPUTimeTraceQueue::increment_lost_samples() { @@ -143,7 +136,7 @@ void JfrCPUTimeTraceQueue::increment_lost_samples() { } void JfrCPUTimeTraceQueue::increment_lost_samples_due_to_queue_full() { - AtomicAccess::inc(&_lost_samples_due_to_queue_full); + _lost_samples_due_to_queue_full++; } u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples() { @@ -151,7 +144,9 @@ u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples() { } u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples_due_to_queue_full() { - return AtomicAccess::xchg(&_lost_samples_due_to_queue_full, (u4)0); + u4 lost = _lost_samples_due_to_queue_full; + _lost_samples_due_to_queue_full = 0; + return lost; } void JfrCPUTimeTraceQueue::init() { @@ -159,7 +154,7 @@ void JfrCPUTimeTraceQueue::init() { } void JfrCPUTimeTraceQueue::clear() { - AtomicAccess::release_store(&_head, (u4)0); + _head = 0; } void JfrCPUTimeTraceQueue::resize_if_needed() { @@ -167,9 +162,8 @@ void JfrCPUTimeTraceQueue::resize_if_needed() { if (lost_samples_due_to_queue_full == 0) { return; } - u4 capacity = AtomicAccess::load(&_capacity); - if (capacity < CPU_TIME_QUEUE_MAX_CAPACITY) { - float ratio = (float)lost_samples_due_to_queue_full / (float)capacity; + if (_capacity < CPU_TIME_QUEUE_MAX_CAPACITY) { + float ratio = (float)lost_samples_due_to_queue_full / (float)_capacity; int factor = 1; if (ratio > 8) { // idea is to quickly scale the queue in the worst case factor = ratio; @@ -181,7 +175,7 @@ void JfrCPUTimeTraceQueue::resize_if_needed() { factor = 2; } if (factor > 1) { - u4 new_capacity = MIN2(CPU_TIME_QUEUE_MAX_CAPACITY, capacity * factor); + u4 new_capacity = MIN2(CPU_TIME_QUEUE_MAX_CAPACITY, _capacity * factor); set_capacity(new_capacity); } } diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp index e7c915fc8be..48fe28d22f0 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp @@ -43,19 +43,24 @@ struct JfrCPUTimeSampleRequest { // Fixed size async-signal-safe SPSC linear queue backed by an array. // Designed to be only used under lock and read linearly +// The lock in question is the tri-state CPU time JFR lock in JfrThreadLocal +// This allows us to skip most of the atomic accesses and memory barriers, +// holding a lock acts as a memory barrier +// Only the _lost_samples property is atomic, as it can be accessed even after +// acquiring the lock failed. +// Important to note is that the queue is also only accessed under lock in signal +// handlers. class JfrCPUTimeTraceQueue { - // the default queue capacity, scaled if the sampling period is smaller than 10ms - // when the thread is started - static const u4 CPU_TIME_QUEUE_CAPACITY = 500; - JfrCPUTimeSampleRequest* _data; - volatile u4 _capacity; + u4 _capacity; // next unfilled index - volatile u4 _head; + u4 _head; + // the only property accessible without a lock volatile u4 _lost_samples; - volatile u4 _lost_samples_due_to_queue_full; + + u4 _lost_samples_due_to_queue_full; static const u4 CPU_TIME_QUEUE_INITIAL_CAPACITY = 20; static const u4 CPU_TIME_QUEUE_MAX_CAPACITY = 2000; @@ -82,6 +87,7 @@ public: u4 lost_samples() const; + // the only method callable without holding a lock void increment_lost_samples(); void increment_lost_samples_due_to_queue_full(); From f73e56e24f0edfaeb99e2106a56725ea033bd6d6 Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Fri, 24 Oct 2025 09:14:04 +0000 Subject: [PATCH 0007/1004] 8361894: sun/security/krb5/config/native/TestDynamicStore.java ensure that the test is run with sudo Reviewed-by: rhalade --- .../krb5/config/native/TestDynamicStore.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/jdk/sun/security/krb5/config/native/TestDynamicStore.java b/test/jdk/sun/security/krb5/config/native/TestDynamicStore.java index 7e396013a71..0ee559f33e4 100644 --- a/test/jdk/sun/security/krb5/config/native/TestDynamicStore.java +++ b/test/jdk/sun/security/krb5/config/native/TestDynamicStore.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,13 +27,15 @@ * @summary SCDynamicStoreConfig works * @modules java.security.jgss/sun.security.krb5 * @library /test/lib - * @run main/manual/native TestDynamicStore + * @run main/manual/native/timeout=180 TestDynamicStore * @requires (os.family == "mac") */ import jdk.test.lib.Asserts; import sun.security.krb5.Config; +import javax.swing.JOptionPane; + // =================== Attention =================== // This test calls a native method implemented in libTestDynamicStore.m // to modify system-level Kerberos 5 settings stored in the dynamic store. @@ -56,6 +58,17 @@ public class TestDynamicStore { public static void main(String[] args) throws Exception { + // Show a popup to remind to run this test as sudo user + // this will only trigger if sudo (root) user is not detected + if (!"root".equals(System.getProperty("user.name"))) { + + JOptionPane.showMessageDialog(null, """ + This test MUST be run as ROOT.\s + Please close and RESTART the test."""); + + Asserts.assertFalse(true, "This test must be run as ROOT"); + } + System.loadLibrary("TestDynamicStore"); Config cfg = Config.getInstance(); From 470eedb1e9d67058ff8d67a5b0c2250d9f9b3fa5 Mon Sep 17 00:00:00 2001 From: Alexander Zvegintsev Date: Fri, 24 Oct 2025 09:46:00 +0000 Subject: [PATCH 0008/1004] 8370511: test/jdk/javax/swing/JSlider/bug4382876.java does not release previously pressed keys Reviewed-by: psadhukhan, serb, honkar --- test/jdk/javax/swing/JSlider/bug4382876.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/jdk/javax/swing/JSlider/bug4382876.java b/test/jdk/javax/swing/JSlider/bug4382876.java index b9ec64aab21..b0988de3cab 100644 --- a/test/jdk/javax/swing/JSlider/bug4382876.java +++ b/test/jdk/javax/swing/JSlider/bug4382876.java @@ -48,8 +48,8 @@ public class bug4382876 { private static Robot r; private static JFrame f; private static JSlider slider; - private static boolean upFail; - private static boolean downFail; + private static volatile boolean upFail; + private static volatile boolean downFail; public static void main(String[] args) throws Exception { try { @@ -70,23 +70,30 @@ public class bug4382876 { r.delay(1000); r.keyPress(KeyEvent.VK_PAGE_UP); + r.keyRelease(KeyEvent.VK_PAGE_UP); + SwingUtilities.invokeAndWait(() -> { if (slider.getValue() < -1000) { System.out.println("PAGE_UP VAL: " + slider.getValue()); upFail = true; } }); + if (upFail) { writeFailImage(); throw new RuntimeException("Slider value did NOT change with PAGE_UP"); } + r.keyPress(KeyEvent.VK_PAGE_DOWN); + r.keyRelease(KeyEvent.VK_PAGE_DOWN); + SwingUtilities.invokeAndWait(() -> { if (slider.getValue() > -1000) { System.out.println("PAGE_DOWN VAL: " + slider.getValue()); downFail = true; } }); + if (downFail) { writeFailImage(); throw new RuntimeException("Slider value did NOT change with PAGE_DOWN"); From cc9483b4da1a0f65f8773d0c7f35f2e6a7e1bd4f Mon Sep 17 00:00:00 2001 From: Matthew Donovan Date: Fri, 24 Oct 2025 11:10:59 +0000 Subject: [PATCH 0009/1004] 8366182: Some PKCS11Tests are being skipped when they shouldn't Reviewed-by: rhalade --- .../security/pkcs11/Cipher/TestKATForGCM.java | 13 ++- .../pkcs11/KeyStore/SecretKeysBasic.java | 17 ++-- test/jdk/sun/security/pkcs11/PKCS11Test.java | 79 +++++++++---------- .../pkcs11/Secmod/AddTrustedCert.java | 10 +-- .../pkcs11/Signature/TestDSAKeyLength.java | 18 +++-- test/jdk/sun/security/pkcs11/ec/TestECDH.java | 1 + 6 files changed, 73 insertions(+), 65 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java b/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java index 9844e8ecfd2..e5e8284e6f4 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java +++ b/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java @@ -321,14 +321,19 @@ public class TestKATForGCM extends PKCS11Test { System.out.println("Test Passed!"); } } catch (Exception e) { - System.out.println("Exception occured using " + p.getName() + " version " + p.getVersionStr()); + System.out.println("Exception occured using " + p.getName() + + " version " + p.getVersionStr()); if (isNSS(p)) { - double ver = getNSSInfo("nss"); + Version ver = getNSSInfo("nss"); String osName = System.getProperty("os.name"); - if (ver > 3.139 && ver < 3.15 && osName.equals("Linux")) { + + if (osName.equals("Linux") && + ver.major() == 3 && ver.minor() < 15 + && (ver.minor() > 13 && ver.patch() >= 9)) { // warn about buggy behaviour on Linux with nss 3.14 - System.out.println("Warning: old NSS " + ver + " might be problematic, consider upgrading it"); + System.out.println("Warning: old NSS " + ver + + " might be problematic, consider upgrading it"); } } throw e; diff --git a/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java b/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java index 4d876604c01..1ff80fcaf07 100644 --- a/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java +++ b/test/jdk/sun/security/pkcs11/KeyStore/SecretKeysBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -116,11 +116,14 @@ public class SecretKeysBasic extends PKCS11Test { // A bug in NSS 3.12 (Mozilla bug 471665) causes AES key lengths // to be read incorrectly. Checking for improper 16 byte length // in key string. - if (isNSS(provider) && expected.getAlgorithm().equals("AES") && - (getNSSVersion() >= 3.12 && getNSSVersion() <= 3.122)) { - System.out.println("NSS 3.12 bug returns incorrect AES key "+ - "length breaking key storage. Aborting..."); - return true; + if (isNSS(provider) && expected.getAlgorithm().equals("AES")) { + Version version = getNSSVersion(); + if (version.major() == 3 && version.minor() == 12 + && version.patch() <= 2) { + System.out.println("NSS 3.12 bug returns incorrect AES key " + + "length breaking key storage. Aborting..."); + return true; + } } if (saveBeforeCheck) { @@ -168,7 +171,7 @@ public class SecretKeysBasic extends PKCS11Test { private static void doTest() throws Exception { // Make sure both NSS libraries are the same version. if (isNSS(provider) && - (getLibsoftokn3Version() != getLibnss3Version())) { + (!getLibsoftokn3Version().equals(getLibnss3Version()))) { System.out.println("libsoftokn3 and libnss3 versions do not match. Aborting test..."); return; } diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java index 8454f3ac463..98343d9b7e6 100644 --- a/test/jdk/sun/security/pkcs11/PKCS11Test.java +++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java @@ -83,7 +83,7 @@ public abstract class PKCS11Test { private static final String NSS_BUNDLE_VERSION = "3.111"; private static final String NSSLIB = "jpg.tests.jdk.nsslib"; - static double nss_version = -1; + static Version nss_version = null; static ECCState nss_ecc_status = ECCState.Basic; // The NSS library we need to search for in getNSSLibDir() @@ -93,8 +93,8 @@ public abstract class PKCS11Test { // NSS versions of each library. It is simpler to keep nss_version // for quick checking for generic testing than many if-else statements. - static double softoken3_version = -1; - static double nss3_version = -1; + static Version softoken3_version = null; + static Version nss3_version = null; static Provider pkcs11 = newPKCS11Provider(); private static String PKCS11_BASE; private static Map osMap; @@ -269,13 +269,29 @@ public abstract class PKCS11Test { } static boolean isBadNSSVersion(Provider p) { - double nssVersion = getNSSVersion(); - if (isNSS(p) && nssVersion >= 3.11 && nssVersion < 3.12) { - System.out.println("NSS 3.11 has a DER issue that recent " + - "version do not, skipping"); - return true; + Version nssVersion = getNSSVersion(); + if (isNSS(p)) { + // bad version is just between [3.11,3.12) + return nssVersion.major == 3 && 11 == nssVersion.minor; + } else { + return false; } - return false; + } + + public record Version(int major, int minor, int patch) {} + + protected static Version parseVersionString(String version) { + String [] parts = version.split("\\."); + int major = Integer.parseInt(parts[0]); + int minor = 0; + int patch = 0; + if (parts.length >= 2) { + minor = Integer.parseInt(parts[1]); + } + if (parts.length >= 3) { + patch = Integer.parseInt(parts[2]); + } + return new Version(major, minor, patch); } protected static void safeReload(String lib) { @@ -304,26 +320,26 @@ public abstract class PKCS11Test { return p.getName().equalsIgnoreCase("SUNPKCS11-NSS"); } - static double getNSSVersion() { - if (nss_version == -1) + static Version getNSSVersion() { + if (nss_version == null) getNSSInfo(); return nss_version; } static ECCState getNSSECC() { - if (nss_version == -1) + if (nss_version == null) getNSSInfo(); return nss_ecc_status; } - public static double getLibsoftokn3Version() { - if (softoken3_version == -1) + public static Version getLibsoftokn3Version() { + if (softoken3_version == null) return getNSSInfo("softokn3"); return softoken3_version; } - public static double getLibnss3Version() { - if (nss3_version == -1) + public static Version getLibnss3Version() { + if (nss3_version == null) return getNSSInfo("nss3"); return nss3_version; } @@ -338,7 +354,7 @@ public abstract class PKCS11Test { // $Header: NSS // Version: NSS // Here, stands for NSS version. - static double getNSSInfo(String library) { + static Version getNSSInfo(String library) { // look for two types of headers in NSS libraries String nssHeader1 = "$Header: NSS"; String nssHeader2 = "Version: NSS"; @@ -347,15 +363,15 @@ public abstract class PKCS11Test { int i = 0; Path libfile = null; - if (library.compareTo("softokn3") == 0 && softoken3_version > -1) + if (library.compareTo("softokn3") == 0 && softoken3_version != null) return softoken3_version; - if (library.compareTo("nss3") == 0 && nss3_version > -1) + if (library.compareTo("nss3") == 0 && nss3_version != null) return nss3_version; try { libfile = getNSSLibPath(); if (libfile == null) { - return 0.0; + return parseVersionString("0.0"); } try (InputStream is = Files.newInputStream(libfile)) { byte[] data = new byte[1000]; @@ -391,7 +407,7 @@ public abstract class PKCS11Test { if (!found) { System.out.println("lib" + library + " version not found, set to 0.0: " + libfile); - nss_version = 0.0; + nss_version = parseVersionString("0.0"); return nss_version; } @@ -404,26 +420,7 @@ public abstract class PKCS11Test { version.append(c); } - // If a "dot dot" release, strip the extra dots for double parsing - String[] dot = version.toString().split("\\."); - if (dot.length > 2) { - version = new StringBuilder(dot[0] + "." + dot[1]); - for (int j = 2; dot.length > j; j++) { - version.append(dot[j]); - } - } - - // Convert to double for easier version value checking - try { - nss_version = Double.parseDouble(version.toString()); - } catch (NumberFormatException e) { - System.out.println("===== Content start ====="); - System.out.println(s); - System.out.println("===== Content end ====="); - System.out.println("Failed to parse lib" + library + - " version. Set to 0.0"); - e.printStackTrace(); - } + nss_version = parseVersionString(version.toString()); System.out.print("library: " + library + ", version: " + version + ". "); diff --git a/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java b/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java index 880adc954ea..7b4a5075da8 100644 --- a/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java +++ b/test/jdk/sun/security/pkcs11/Secmod/AddTrustedCert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, 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 @@ -121,10 +121,10 @@ public class AddTrustedCert extends SecmodTest { } private static boolean improperNSSVersion(Provider p) { - double nssVersion = getNSSVersion(); - if (p.getName().equalsIgnoreCase("SunPKCS11-NSSKeyStore") - && nssVersion >= 3.28 && nssVersion < 3.35) { - return true; + Version nssVersion = getNSSVersion(); + if (p.getName().equalsIgnoreCase("SunPKCS11-NSSKeyStore")) { + return nssVersion.major() == 3 && + (nssVersion.minor() >= 28 && nssVersion.minor() < 35); } return false; diff --git a/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java b/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java index a9b43a647a9..ffd7b9e3ee0 100644 --- a/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java +++ b/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -47,13 +47,15 @@ public class TestDSAKeyLength extends PKCS11Test { @Override protected boolean skipTest(Provider provider) { - double version = getNSSVersion(); - String[] versionStrs = Double.toString(version).split("\\."); - int major = Integer.parseInt(versionStrs[0]); - int minor = Integer.parseInt(versionStrs[1]); - if (isNSS(provider) && (version == 0.0 || (major >= 3 && minor >= 14))) { - System.out.println("Skip testing NSS " + version); - return true; + if (isNSS(provider)) { + Version version = getNSSVersion(); + if (version == null) { + return true; + } + if (version.major() >= 3 && version.minor() >= 14){ + System.out.println("Skip testing NSS " + version); + return true; + } } return false; diff --git a/test/jdk/sun/security/pkcs11/ec/TestECDH.java b/test/jdk/sun/security/pkcs11/ec/TestECDH.java index b6821b88372..2900656f626 100644 --- a/test/jdk/sun/security/pkcs11/ec/TestECDH.java +++ b/test/jdk/sun/security/pkcs11/ec/TestECDH.java @@ -111,6 +111,7 @@ public class TestECDH extends PKCS11Test { * PKCS11Test.main will remove this provider if needed */ Providers.setAt(p, 1); + System.out.println("Testing provider " + p.getName()); if (false) { KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", p); From fd23a61cd48be5ae2c7f76cc88af3da5b4a27e3d Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 24 Oct 2025 16:43:57 +0000 Subject: [PATCH 0010/1004] 8370503: Use String.newStringWithLatin1Bytes to simplify Integer/Long toString method Reviewed-by: rgiulietti, rriggs --- .../share/classes/java/lang/Integer.java | 44 +++--------------- .../share/classes/java/lang/Long.java | 45 +++---------------- 2 files changed, 12 insertions(+), 77 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 6e49f1983aa..20d1edb6d5f 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -363,15 +363,9 @@ public final class Integer extends Number // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { - byte[] buf = new byte[chars]; - formatUnsignedInt(val, shift, buf, chars); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[chars * 2]; - formatUnsignedIntUTF16(val, shift, buf, chars); - return new String(buf, UTF16); - } + byte[] buf = new byte[chars]; + formatUnsignedInt(val, shift, buf, chars); + return String.newStringWithLatin1Bytes(buf); } /** @@ -394,26 +388,6 @@ public final class Integer extends Number } while (charPos > 0); } - /** - * Format an {@code int} (treated as unsigned) into a byte buffer (UTF16 version). If - * {@code len} exceeds the formatted ASCII representation of {@code val}, - * {@code buf} will be padded with leading zeroes. - * - * @param val the unsigned int to format - * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) - * @param buf the byte buffer to write to - * @param len the number of characters to write - */ - private static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int len) { - int charPos = len; - int radix = 1 << shift; - int mask = radix - 1; - do { - StringUTF16.putChar(buf, --charPos, Integer.digits[val & mask]); - val >>>= shift; - } while (charPos > 0); - } - /** * Returns a {@code String} object representing the * specified integer. The argument is converted to signed decimal @@ -427,15 +401,9 @@ public final class Integer extends Number @IntrinsicCandidate public static String toString(int i) { int size = DecimalDigits.stringSize(i); - if (COMPACT_STRINGS) { - byte[] buf = new byte[size]; - DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[size * 2]; - DecimalDigits.uncheckedGetCharsUTF16(i, size, buf); - return new String(buf, UTF16); - } + byte[] buf = new byte[size]; + DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); + return String.newStringWithLatin1Bytes(buf); } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 90249cb1edb..b0477fdab6d 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -391,15 +391,9 @@ public final class Long extends Number // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { - byte[] buf = new byte[chars]; - formatUnsignedLong0(val, shift, buf, 0, chars); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[chars * 2]; - formatUnsignedLong0UTF16(val, shift, buf, 0, chars); - return new String(buf, UTF16); - } + byte[] buf = new byte[chars]; + formatUnsignedLong0(val, shift, buf, 0, chars); + return String.newStringWithLatin1Bytes(buf); } /** @@ -423,27 +417,6 @@ public final class Long extends Number } while (charPos > offset); } - /** - * Format a long (treated as unsigned) into a byte buffer (UTF16 version). If - * {@code len} exceeds the formatted ASCII representation of {@code val}, - * {@code buf} will be padded with leading zeroes. - * - * @param val the unsigned long to format - * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary) - * @param buf the byte buffer to write to - * @param offset the offset in the destination buffer to start at - * @param len the number of characters to write - */ - private static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) { - int charPos = offset + len; - int radix = 1 << shift; - int mask = radix - 1; - do { - StringUTF16.putChar(buf, --charPos, Integer.digits[((int) val) & mask]); - val >>>= shift; - } while (charPos > offset); - } - /** * Returns a {@code String} object representing the specified * {@code long}. The argument is converted to signed decimal @@ -456,15 +429,9 @@ public final class Long extends Number */ public static String toString(long i) { int size = DecimalDigits.stringSize(i); - if (COMPACT_STRINGS) { - byte[] buf = new byte[size]; - DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); - return new String(buf, LATIN1); - } else { - byte[] buf = new byte[size * 2]; - DecimalDigits.uncheckedGetCharsUTF16(i, size, buf); - return new String(buf, UTF16); - } + byte[] buf = new byte[size]; + DecimalDigits.uncheckedGetCharsLatin1(i, size, buf); + return String.newStringWithLatin1Bytes(buf); } /** From 13adcd99db4f14caf90de7f59e341380cfa354b0 Mon Sep 17 00:00:00 2001 From: Anass Baya Date: Fri, 24 Oct 2025 17:04:28 +0000 Subject: [PATCH 0011/1004] 8274082: Wrong test name in jtreg run tag for java/awt/print/PrinterJob/SwingUIText.java Co-authored-by: Lawrence Andrews Reviewed-by: honkar, dnguyen --- .../awt/print/PrinterJob/SwingUIText.java | 216 +++++------------- 1 file changed, 56 insertions(+), 160 deletions(-) diff --git a/test/jdk/java/awt/print/PrinterJob/SwingUIText.java b/test/jdk/java/awt/print/PrinterJob/SwingUIText.java index 5fcd5e39158..6ef5064fc30 100644 --- a/test/jdk/java/awt/print/PrinterJob/SwingUIText.java +++ b/test/jdk/java/awt/print/PrinterJob/SwingUIText.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,44 +21,70 @@ * questions. */ -/** +/* * @test * @bug 6488219 6560738 7158350 8017469 * @key printer * @summary Test that text printed in Swing UI measures and looks OK. - * @run main/manual=yesno PrintTextTest + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual SwingUIText */ -import java.awt.*; -import javax.swing.*; -import java.awt.print.*; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterJob; +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import jtreg.SkippedException; public class SwingUIText implements Printable { + private static JFrame frame; + private static final String INSTRUCTIONS = """ + This test checks that when a Swing UI is printed, + the text in each component aligns with the component’s length as seen on-screen. + It also ensures the text spacing is reasonably even, though this is subjective. + The comparison should be made with JDK 1.5 GA or JDK 1.6 GA. - static String[] instructions = { - "This tests that when a Swing UI is printed, that the text", - "in each component properly matches the length of the component", - "as seen on-screen, and that the spacing of the text is of", - "reasonable even-ness. This latter part is very subjective and", - "the comparison has to be with JDK1.5 GA, or JDK 1.6 GA", - }; + Steps: + 1. Press the "Print" or "OK" button on the Print dialog. + This will print the content of the "Swing UI Text Printing Test" JFrame. + 2. Compare the printout with the content of the JFrame. + 3. If they match, press Pass; otherwise, press Fail. + """; - static JFrame frame; + public static void main(String args[]) throws Exception { + PrinterJob job = PrinterJob.getPrinterJob(); + if (job.getPrintService() == null) { + throw new SkippedException("Printer not configured or available."); + } - public static void main(String args[]) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - createUI(); - } - }); + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(SwingUIText::createTestUI) + .build(); + + job.setPrintable(new SwingUIText()); + if (job.printDialog()) { + job.print(); + } + + passFailJFrame.awaitAndCheck(); } - public static void createUI() { - - Sysout.createDialogWithInstructions(instructions); - + public static JFrame createTestUI() { + frame = new JFrame(); JPanel panel = new JPanel(); - panel.setLayout(new GridLayout(4,1)); + panel.setLayout(new GridLayout(4, 1)); String text = "marvelous suspicious solving"; displayText(panel, text); @@ -89,24 +115,12 @@ public class SwingUIText implements Printable { frame = new JFrame("Swing UI Text Printing Test"); frame.getContentPane().add(panel); frame.pack(); - frame.setVisible(true); - - PrinterJob job = PrinterJob.getPrinterJob(); - PageFormat pf = job.defaultPage(); - job.setPrintable(new SwingUIText(), pf); - if (job.printDialog()) { - try { job.print(); } - catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } + return frame; } - static void displayText(JPanel p, String text) { JPanel panel = new JPanel(); - panel.setLayout(new GridLayout(2,1)); + panel.setLayout(new GridLayout(2, 1)); JPanel row = new JPanel(); Font font = new Font("Dialog", Font.PLAIN, 12); @@ -114,7 +128,7 @@ public class SwingUIText implements Printable { label.setFont(font); row.add(label); - JButton button = new JButton("Print "+text); + JButton button = new JButton("Print " + text); button.setMnemonic('P'); button.setFont(font); row.add(button); @@ -133,132 +147,14 @@ public class SwingUIText implements Printable { p.add(panel); } - public int print(Graphics g, PageFormat pf, int pageIndex) - throws PrinterException { - + public int print(Graphics g, PageFormat pf, int pageIndex) { if (pageIndex >= 1) { return Printable.NO_SUCH_PAGE; } + g.translate((int)pf.getImageableX(), (int)pf.getImageableY()); frame.printAll(g); - return Printable.PAGE_EXISTS; } } - -class Sysout - { - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.show(); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - - }// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog - { - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 10, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("South", messageText); - - pack(); - - show(); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - } - -}// TestDialog class From 2ee34391c152abeb06a6baf69f4420988b8c838e Mon Sep 17 00:00:00 2001 From: Francesco Andreuzzi Date: Fri, 24 Oct 2025 17:43:41 +0000 Subject: [PATCH 0012/1004] 8368975: Windows ProcessImpl.java has dead code Reviewed-by: ayang, rriggs --- .../windows/classes/java/lang/ProcessImpl.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/java.base/windows/classes/java/lang/ProcessImpl.java b/src/java.base/windows/classes/java/lang/ProcessImpl.java index 7f7c1e75013..78180cce678 100644 --- a/src/java.base/windows/classes/java/lang/ProcessImpl.java +++ b/src/java.base/windows/classes/java/lang/ProcessImpl.java @@ -199,7 +199,6 @@ final class ProcessImpl extends Process { } private static final int VERIFICATION_CMD_BAT = 0; - private static final int VERIFICATION_WIN32 = 1; private static final int VERIFICATION_WIN32_SAFE = 2; // inside quotes not allowed private static final int VERIFICATION_LEGACY = 3; // See Command shell overview for documentation of special characters. @@ -384,12 +383,6 @@ final class ProcessImpl extends Process { return (upName.endsWith(".EXE") || upName.indexOf('.') < 0); } - // Old version that can be bypassed - private boolean isShellFile(String executablePath) { - String upPath = executablePath.toUpperCase(Locale.ROOT); - return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); - } - private String quoteString(String arg) { StringBuilder argbuf = new StringBuilder(arg.length() + 2); return argbuf.append('"').append(arg).append('"').toString(); @@ -472,12 +465,10 @@ final class ProcessImpl extends Process { // Quotation protects from interpretation of the [path] argument as // start of longer path with spaces. Quotation has no influence to // [.exe] extension heuristic. - boolean isShell = allowAmbiguousCommands ? isShellFile(executablePath) - : !isExe(executablePath); + boolean isShell = !isExe(executablePath); cmdstr = createCommandLine( // We need the extended verification procedures - isShell ? VERIFICATION_CMD_BAT - : (allowAmbiguousCommands ? VERIFICATION_WIN32 : VERIFICATION_WIN32_SAFE), + isShell ? VERIFICATION_CMD_BAT : VERIFICATION_WIN32_SAFE, quoteString(executablePath), cmd); } From 97e5ac6e728baeae4341c6235d026ecd99bc600e Mon Sep 17 00:00:00 2001 From: Mikhailo Seledtsov Date: Fri, 24 Oct 2025 18:04:32 +0000 Subject: [PATCH 0013/1004] 8370514: Problemlist nio/channels/AsyncCloseAndInterrupt until JDK-8368290 is resolved Reviewed-by: bpb --- test/jdk/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 1f6bea97407..f95b3681723 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -587,6 +587,7 @@ java/net/MulticastSocket/Test.java 7145658,8308807 # jdk_nio java/nio/channels/Channels/SocketChannelStreams.java 8317838 aix-ppc64 +java/nio/channels/AsyncCloseAndInterrupt.java 8368290 macosx-26.0.1 java/nio/channels/DatagramChannel/AdaptorMulticasting.java 8308807,8144003 aix-ppc64,macosx-all java/nio/channels/DatagramChannel/AfterDisconnect.java 8308807 aix-ppc64 From a4eaeb47c9c42d8da4e3814c80247f40236a03a2 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 24 Oct 2025 22:24:28 +0000 Subject: [PATCH 0014/1004] 6453640: BandedSampleModel.createCompatibleSampleModel() API docs are wrong Reviewed-by: azvegint, serb --- .../java/awt/image/BandedSampleModel.java | 13 +-- .../BSMCreateCompatibleSMTest.java | 100 ++++++++++++++++++ 2 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 test/jdk/java/awt/image/BandedSampleModel/BSMCreateCompatibleSMTest.java diff --git a/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java index bd955e35870..bad9abc6130 100644 --- a/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java @@ -141,12 +141,9 @@ public final class BandedSampleModel extends ComponentSampleModel * @param h the height of the resulting {@code BandedSampleModel} * @return a new {@code BandedSampleModel} with the specified * width and height. - * @throws IllegalArgumentException if {@code w} or - * {@code h} equals either - * {@code Integer.MAX_VALUE} or - * {@code Integer.MIN_VALUE} - * @throws IllegalArgumentException if {@code dataType} is not - * one of the supported data types + * @throws IllegalArgumentException if the product of {@code w} + * and {@code h} is greater than {@code Integer.MAX_VALUE} + * or {@code w} or {@code h} is not greater than 0. */ public SampleModel createCompatibleSampleModel(int w, int h) { int[] bandOffs; @@ -172,8 +169,8 @@ public final class BandedSampleModel extends ComponentSampleModel * of the original BandedSampleModel/DataBuffer combination. * @throws RasterFormatException if the number of bands is greater than * the number of banks in this sample model. - * @throws IllegalArgumentException if {@code dataType} is not - * one of the supported data types + * @throws IllegalArgumentException if the number of bands is not greater than 0 + * @throws ArrayIndexOutOfBoundsException if any of the bank indices is out of bounds */ public SampleModel createSubsetSampleModel(int[] bands) { if (bands.length > bankIndices.length) diff --git a/test/jdk/java/awt/image/BandedSampleModel/BSMCreateCompatibleSMTest.java b/test/jdk/java/awt/image/BandedSampleModel/BSMCreateCompatibleSMTest.java new file mode 100644 index 00000000000..a47659300e8 --- /dev/null +++ b/test/jdk/java/awt/image/BandedSampleModel/BSMCreateCompatibleSMTest.java @@ -0,0 +1,100 @@ +/* + * 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 6453640 + * @summary Verify BandedSampleModel.createCompatibleSampleModel + * and createSubsetSampleModel behaviour + * @run main BSMCreateCompatibleSMTest + */ + +import java.awt.image.BandedSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.RasterFormatException; + +public class BSMCreateCompatibleSMTest { + + public static void main(String[] args) { + + // These should all be OK + BandedSampleModel bsm = new BandedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 1); + bsm.createCompatibleSampleModel(20_000, 20_000); + int[] bands = { 0 } ; + bsm.createSubsetSampleModel(bands); + + // These should all throw an exception + try { + bsm.createCompatibleSampleModel(-1, 1); + throw new RuntimeException("No exception for illegal w"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + bsm.createCompatibleSampleModel(1, 0); + throw new RuntimeException("No exception for illegal h"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + bsm.createCompatibleSampleModel(-1, -1); + throw new RuntimeException("No exception for illegal w+h"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + bsm.createCompatibleSampleModel(50_000, 50_000); + throw new RuntimeException("No exception for too large dims"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + int[] bands0 = { } ; + bsm.createSubsetSampleModel(bands0); + throw new RuntimeException("No exception for empty bands[]"); + } catch (IllegalArgumentException e) { + System.out.println(e); + } + + try { + int[] bands1 = { 1 } ; + bsm.createSubsetSampleModel(bands1); + throw new RuntimeException("No exception for out of bounds band"); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e); + } + + try { + int[] bands2 = { 0, 0 } ; + bsm.createSubsetSampleModel(bands2); + throw new RuntimeException("No exception for too many bands"); + } catch (RasterFormatException e) { + System.out.println(e); + } + } + +} From 35fdda0889bd6a83027089672b643ef7ffc8a40c Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Fri, 24 Oct 2025 23:03:50 +0000 Subject: [PATCH 0015/1004] 7105350: HttpExchange's attributes are the same as HttpContext's attributes Reviewed-by: michaelm, jpai, dfuchs --- .../share/classes/module-info.java | 10 +++++++ .../sun/net/httpserver/ExchangeImpl.java | 30 ++++++++----------- .../net/httpserver/ExchangeAttributeTest.java | 20 ++++++++++--- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/jdk.httpserver/share/classes/module-info.java b/src/jdk.httpserver/share/classes/module-info.java index 15e9e2cc36d..ac147582b14 100644 --- a/src/jdk.httpserver/share/classes/module-info.java +++ b/src/jdk.httpserver/share/classes/module-info.java @@ -23,6 +23,8 @@ * questions. */ +import com.sun.net.httpserver.*; + /** * Defines the JDK-specific HTTP server API, and provides the jwebserver tool * for running a minimal HTTP server. @@ -109,6 +111,14 @@ * and implementation of the server does not intend to be a full-featured, high performance * HTTP server. * + * @implNote + * Prior to JDK 26, in the JDK default implementation, the {@link HttpExchange} attribute map was + * shared with the enclosing {@link HttpContext}. + * Since JDK 26, by default, exchange attributes are per-exchange and the context attributes must + * be accessed by calling {@link HttpExchange#getHttpContext() getHttpContext()}{@link + * HttpContext#getAttributes() .getAttributes()}.
+ * A new system property, {@systemProperty jdk.httpserver.attributes} (default value: {@code ""}) + * allows to revert this new behavior. Set this property to "context" to restore the pre JDK 26 behavior. * @toolGuide jwebserver * * @uses com.sun.net.httpserver.spi.HttpServerProvider 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 1119d1e386b..0899952b495 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, 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 @@ -29,6 +29,7 @@ import java.io.*; import java.net.*; import javax.net.ssl.*; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.text.*; @@ -59,6 +60,9 @@ class ExchangeImpl { /* for formatting the Date: header */ private static final DateTimeFormatter FORMATTER; + private static final boolean perExchangeAttributes = + !System.getProperty("jdk.httpserver.attributes", "") + .equals("context"); static { String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz"; FORMATTER = DateTimeFormatter.ofPattern(pattern, Locale.US) @@ -76,7 +80,7 @@ class ExchangeImpl { PlaceholderOutputStream uos_orig; boolean sentHeaders; /* true after response headers sent */ - Map attributes; + final Map attributes; int rcode = -1; HttpPrincipal principal; ServerImpl server; @@ -91,6 +95,9 @@ class ExchangeImpl { this.uri = u; this.connection = connection; this.reqContentLen = len; + this.attributes = perExchangeAttributes + ? new ConcurrentHashMap<>() + : getHttpContext().getAttributes(); /* ros only used for headers, body written directly to stream */ this.ros = req.outputStream(); this.ris = req.inputStream(); @@ -361,26 +368,15 @@ class ExchangeImpl { } public Object getAttribute (String name) { - if (name == null) { - throw new NullPointerException ("null name parameter"); - } - if (attributes == null) { - attributes = getHttpContext().getAttributes(); - } - return attributes.get (name); + return attributes.get(Objects.requireNonNull(name, "null name parameter")); } public void setAttribute (String name, Object value) { - if (name == null) { - throw new NullPointerException ("null name parameter"); - } - if (attributes == null) { - attributes = getHttpContext().getAttributes(); - } + var key = Objects.requireNonNull(name, "null name parameter"); if (value != null) { - attributes.put (name, value); + attributes.put(key, value); } else { - attributes.remove (name); + attributes.remove(key); } } diff --git a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java index 2ce3dfd016d..e7bea2814db 100644 --- a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java +++ b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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,9 @@ * @summary Tests for HttpExchange set/getAttribute * @library /test/lib * @run junit/othervm ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes=context ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes=random-string ExchangeAttributeTest + * @run junit/othervm -Djdk.httpserver.attributes ExchangeAttributeTest */ import com.sun.net.httpserver.HttpExchange; @@ -71,7 +74,7 @@ public class ExchangeAttributeTest { public void testExchangeAttributes() throws Exception { var handler = new AttribHandler(); var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10); - server.createContext("/", handler); + server.createContext("/", handler).getAttributes().put("attr", "context-val"); server.start(); try { var client = HttpClient.newBuilder().proxy(NO_PROXY).build(); @@ -101,8 +104,17 @@ public class ExchangeAttributeTest { @java.lang.Override public void handle(HttpExchange exchange) throws IOException { try { - exchange.setAttribute("attr", "val"); - assertEquals("val", exchange.getAttribute("attr")); + if ("context".equals(System.getProperty("jdk.httpserver.attributes"))) { + exchange.setAttribute("attr", "val"); + assertEquals("val", exchange.getAttribute("attr")); + assertEquals("val", exchange.getHttpContext().getAttributes().get("attr")); + } else { + assertNull(exchange.getAttribute("attr")); + assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr")); + exchange.setAttribute("attr", "val"); + assertEquals("val", exchange.getAttribute("attr")); + assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr")); + } exchange.setAttribute("attr", null); assertNull(exchange.getAttribute("attr")); exchange.sendResponseHeaders(200, -1); From 32697bf652429fa7247047465e365835dfa24b39 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Sat, 25 Oct 2025 01:54:03 +0000 Subject: [PATCH 0016/1004] 8370501: vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java intermittent timed out Reviewed-by: tschatzl --- .../compact/Humongous_NonbranchyTree5M/TestDescription.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java b/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java index 1d5a6f0cf11..a539fb7cea2 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/vm/gc/compact/Humongous_NonbranchyTree5M/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -36,7 +36,7 @@ * * @library /vmTestbase * /test/lib - * @run main/othervm + * @run main/othervm/timeout=480 * -XX:-UseGCOverheadLimit * vm.gc.compact.Compact * -gp nonbranchyTree(high) From c3449de23f4fa74590494b8677f6832d47f12dea Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Sat, 25 Oct 2025 15:27:03 +0000 Subject: [PATCH 0017/1004] 8360395: sun/security/tools/keytool/i18n.java user country is current user location instead of the language Reviewed-by: rhalade --- test/jdk/sun/security/tools/keytool/i18n.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/jdk/sun/security/tools/keytool/i18n.java b/test/jdk/sun/security/tools/keytool/i18n.java index ab9da8b1d3e..030735e966c 100644 --- a/test/jdk/sun/security/tools/keytool/i18n.java +++ b/test/jdk/sun/security/tools/keytool/i18n.java @@ -28,7 +28,7 @@ * @author charlie lai * @modules java.base/sun.security.tools.keytool * @library /test/lib - * @run main/manual/othervm -Duser.language=en i18n + * @run main/manual/othervm -Duser.language=en -Duser.country=USA i18n */ /* @@ -38,7 +38,7 @@ * @author charlie lai * @modules java.base/sun.security.tools.keytool * @library /test/lib - * @run main/manual/othervm -Duser.language=de i18n + * @run main/manual/othervm -Duser.language=de -Duser.country=DE i18n */ /* @@ -48,7 +48,7 @@ * @author charlie lai * @modules java.base/sun.security.tools.keytool * @library /test/lib - * @run main/manual/othervm -Duser.language=ja i18n + * @run main/manual/othervm -Duser.language=ja -Duser.country=JP i18n */ /* From e7c7892b9f0fcee37495cce312fdd67dc800f9c9 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Sun, 26 Oct 2025 06:04:02 +0000 Subject: [PATCH 0018/1004] 8370197: Add missing @Override annotations in com.sun.beans package Reviewed-by: prr --- .../classes/com/sun/beans/WildcardTypeImpl.java | 4 +++- .../sun/beans/decoder/NullElementHandler.java | 4 +++- .../com/sun/beans/decoder/ValueObjectImpl.java | 4 +++- .../com/sun/beans/editors/BooleanEditor.java | 6 +++++- .../com/sun/beans/editors/ByteEditor.java | 4 +++- .../com/sun/beans/editors/ColorEditor.java | 17 ++++++++++++++++- .../com/sun/beans/editors/DoubleEditor.java | 3 ++- .../com/sun/beans/editors/EnumEditor.java | 14 +++++++++++++- .../com/sun/beans/editors/FloatEditor.java | 4 +++- .../com/sun/beans/editors/FontEditor.java | 16 +++++++++++++++- .../com/sun/beans/editors/IntegerEditor.java | 3 ++- .../com/sun/beans/editors/LongEditor.java | 4 +++- .../com/sun/beans/editors/NumberEditor.java | 3 ++- .../com/sun/beans/editors/ShortEditor.java | 4 +++- .../com/sun/beans/editors/StringEditor.java | 4 +++- .../com/sun/beans/infos/ComponentBeanInfo.java | 3 ++- .../share/classes/com/sun/beans/util/Cache.java | 17 ++++++++++++++++- 17 files changed, 97 insertions(+), 17 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java b/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java index 28e316c90ec..ebbc8d2cb26 100644 --- a/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java +++ b/src/java.desktop/share/classes/com/sun/beans/WildcardTypeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, 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 @@ -73,6 +73,7 @@ final class WildcardTypeImpl implements WildcardType { * @return an array of types representing * the upper bound(s) of this type variable */ + @Override public Type[] getUpperBounds() { return this.upperBounds.clone(); } @@ -87,6 +88,7 @@ final class WildcardTypeImpl implements WildcardType { * @return an array of types representing * the lower bound(s) of this type variable */ + @Override public Type[] getLowerBounds() { return this.lowerBounds.clone(); } diff --git a/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java b/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java index f865535e4fb..d5ac5368f9a 100644 --- a/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java +++ b/src/java.desktop/share/classes/com/sun/beans/decoder/NullElementHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -61,6 +61,7 @@ class NullElementHandler extends ElementHandler implements ValueObject { * * @return {@code null} by default */ + @Override public Object getValue() { return null; } @@ -70,6 +71,7 @@ class NullElementHandler extends ElementHandler implements ValueObject { * * @return {@code false} always */ + @Override public final boolean isVoid() { return false; } diff --git a/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java b/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java index 6fa46c93fa8..54c73381191 100644 --- a/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java +++ b/src/java.desktop/share/classes/com/sun/beans/decoder/ValueObjectImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -72,6 +72,7 @@ final class ValueObjectImpl implements ValueObject { * * @return the result of method execution */ + @Override public Object getValue() { return this.value; } @@ -82,6 +83,7 @@ final class ValueObjectImpl implements ValueObject { * @return {@code true} if value should be ignored, * {@code false} otherwise */ + @Override public boolean isVoid() { return this.isVoid; } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java index 69aca3238c9..79900b5deb1 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/BooleanEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class BooleanEditor extends PropertyEditorSupport { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class BooleanEditor extends PropertyEditorSupport { : "null"; } + @Override public String getAsText() { Object value = getValue(); return (value instanceof Boolean) @@ -48,6 +50,7 @@ public class BooleanEditor extends PropertyEditorSupport { : null; } + @Override public void setAsText(String text) throws java.lang.IllegalArgumentException { if (text == null) { setValue(null); @@ -60,6 +63,7 @@ public class BooleanEditor extends PropertyEditorSupport { } } + @Override public String[] getTags() { return new String[] {getValidName(true), getValidName(false)}; } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java index 2f4f342774f..fe927fda74d 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/ByteEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class ByteEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class ByteEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Byte.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java index 3c3207ccd15..a5cf00923dd 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/ColorEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, 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 @@ -79,16 +79,19 @@ public class ColorEditor extends Panel implements PropertyEditor { resize(ourWidth,40); } + @Override public void setValue(Object o) { Color c = (Color)o; changeColor(c); } + @Override @SuppressWarnings("deprecation") public Dimension preferredSize() { return new Dimension(ourWidth, 40); } + @Override @SuppressWarnings("deprecation") public boolean keyUp(Event e, int key) { if (e.target == text) { @@ -101,6 +104,7 @@ public class ColorEditor extends Panel implements PropertyEditor { return (false); } + @Override public void setAsText(String s) throws java.lang.IllegalArgumentException { if (s == null) { changeColor(null); @@ -124,6 +128,7 @@ public class ColorEditor extends Panel implements PropertyEditor { } + @Override @SuppressWarnings("deprecation") public boolean action(Event e, Object arg) { if (e.target == chooser) { @@ -132,6 +137,7 @@ public class ColorEditor extends Panel implements PropertyEditor { return false; } + @Override public String getJavaInitializationString() { return (this.color != null) ? "new java.awt.Color(" + this.color.getRGB() + ",true)" @@ -165,14 +171,17 @@ public class ColorEditor extends Panel implements PropertyEditor { support.firePropertyChange("", null, null); } + @Override public Object getValue() { return color; } + @Override public boolean isPaintable() { return true; } + @Override public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { Color oldColor = gfx.getColor(); gfx.setColor(Color.black); @@ -182,28 +191,34 @@ public class ColorEditor extends Panel implements PropertyEditor { gfx.setColor(oldColor); } + @Override public String getAsText() { return (this.color != null) ? this.color.getRed() + "," + this.color.getGreen() + "," + this.color.getBlue() : null; } + @Override public String[] getTags() { return null; } + @Override public java.awt.Component getCustomEditor() { return this; } + @Override public boolean supportsCustomEditor() { return true; } + @Override public void addPropertyChangeListener(PropertyChangeListener l) { support.addPropertyChangeListener(l); } + @Override public void removePropertyChangeListener(PropertyChangeListener l) { support.removePropertyChangeListener(l); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java index 55d5a0528a4..3803cca7d7c 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/DoubleEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class DoubleEditor extends NumberEditor { + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Double.valueOf(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java index b7f5ada0d1f..b5316a04d65 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/EnumEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, 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 @@ -63,10 +63,12 @@ public final class EnumEditor implements PropertyEditor { } } + @Override public Object getValue() { return this.value; } + @Override public void setValue( Object value ) { if ( ( value != null ) && !this.type.isInstance( value ) ) { throw new IllegalArgumentException( "Unsupported value: " + value ); @@ -92,12 +94,14 @@ public final class EnumEditor implements PropertyEditor { } } + @Override public String getAsText() { return ( this.value != null ) ? ( ( Enum )this.value ).name() : null; } + @Override public void setAsText( String text ) { @SuppressWarnings("unchecked") Object tmp = ( text != null ) @@ -106,10 +110,12 @@ public final class EnumEditor implements PropertyEditor { setValue(tmp); } + @Override public String[] getTags() { return this.tags.clone(); } + @Override public String getJavaInitializationString() { String name = getAsText(); return ( name != null ) @@ -117,27 +123,33 @@ public final class EnumEditor implements PropertyEditor { : "null"; } + @Override public boolean isPaintable() { return false; } + @Override public void paintValue( Graphics gfx, Rectangle box ) { } + @Override public boolean supportsCustomEditor() { return false; } + @Override public Component getCustomEditor() { return null; } + @Override public void addPropertyChangeListener( PropertyChangeListener listener ) { synchronized ( this.listeners ) { this.listeners.add( listener ); } } + @Override public void removePropertyChangeListener( PropertyChangeListener listener ) { synchronized ( this.listeners ) { this.listeners.remove( listener ); diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java index 4723c489cc0..5820c00d82e 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/FloatEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class FloatEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class FloatEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Float.valueOf(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java index cf2fdd26307..26d4ab2b182 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/FontEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, 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 @@ -78,11 +78,13 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { } + @Override @SuppressWarnings("deprecation") public Dimension preferredSize() { return new Dimension(300, 40); } + @Override public void setValue(Object o) { font = (Font) o; if (this.font == null) @@ -130,10 +132,12 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { support.firePropertyChange("", null, null); } + @Override public Object getValue() { return (font); } + @Override public String getJavaInitializationString() { if (this.font == null) return "null"; @@ -142,6 +146,7 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { font.getStyle() + ", " + font.getSize() + ")"; } + @Override @SuppressWarnings("deprecation") public boolean action(Event e, Object arg) { String family = familyChoser.getSelectedItem(); @@ -158,10 +163,12 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { } + @Override public boolean isPaintable() { return true; } + @Override public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) { // Silent noop. Font oldFont = gfx.getFont(); @@ -172,6 +179,7 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { gfx.setFont(oldFont); } + @Override public String getAsText() { if (this.font == null) { return null; @@ -195,26 +203,32 @@ public class FontEditor extends Panel implements java.beans.PropertyEditor { return sb.toString(); } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Font.decode(text)); } + @Override public String[] getTags() { return null; } + @Override public java.awt.Component getCustomEditor() { return this; } + @Override public boolean supportsCustomEditor() { return true; } + @Override public void addPropertyChangeListener(PropertyChangeListener l) { support.addPropertyChangeListener(l); } + @Override public void removePropertyChangeListener(PropertyChangeListener l) { support.removePropertyChangeListener(l); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java index 066b7143ac6..65b4d1dcf19 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/IntegerEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, 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 @@ -35,6 +35,7 @@ import java.beans.*; public class IntegerEditor extends NumberEditor { + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Integer.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java index 3a8efbba53c..ed4d12ac505 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/LongEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class LongEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -41,6 +42,7 @@ public class LongEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Long.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java index 9097546d2e0..3c0c5bb6c9f 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/NumberEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public abstract class NumberEditor extends PropertyEditorSupport { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java index cf82eef215d..6be5b14b90f 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/ShortEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -35,6 +35,7 @@ import java.beans.*; public class ShortEditor extends NumberEditor { + @Override public String getJavaInitializationString() { Object value = getValue(); return (value != null) @@ -42,6 +43,7 @@ public class ShortEditor extends NumberEditor { : "null"; } + @Override public void setAsText(String text) throws IllegalArgumentException { setValue((text == null) ? null : Short.decode(text)); } diff --git a/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java b/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java index 2f1cde46ea0..b064ccbddbb 100644 --- a/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java +++ b/src/java.desktop/share/classes/com/sun/beans/editors/StringEditor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -30,6 +30,7 @@ import java.beans.*; public class StringEditor extends PropertyEditorSupport { + @Override public String getJavaInitializationString() { Object value = getValue(); if (value == null) @@ -67,6 +68,7 @@ public class StringEditor extends PropertyEditorSupport { return sb.toString(); } + @Override public void setAsText(String text) { setValue(text); } diff --git a/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java b/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java index 1514b005074..39d7cbb2146 100644 --- a/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/infos/ComponentBeanInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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 @@ -34,6 +34,7 @@ import java.beans.*; public class ComponentBeanInfo extends SimpleBeanInfo { private static final Class beanClass = java.awt.Component.class; + @Override public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor diff --git a/src/java.desktop/share/classes/com/sun/beans/util/Cache.java b/src/java.desktop/share/classes/com/sun/beans/util/Cache.java index 2cb21791416..58151e3a56f 100644 --- a/src/java.desktop/share/classes/com/sun/beans/util/Cache.java +++ b/src/java.desktop/share/classes/com/sun/beans/util/Cache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -405,11 +405,13 @@ public abstract class Cache { */ public static enum Kind { STRONG { + @Override Ref create(Object owner, T value, ReferenceQueue queue) { return new Strong<>(owner, value); } }, SOFT { + @Override Ref create(Object owner, T referent, ReferenceQueue queue) { return (referent == null) ? new Strong<>(owner, referent) @@ -417,6 +419,7 @@ public abstract class Cache { } }, WEAK { + @Override Ref create(Object owner, T referent, ReferenceQueue queue) { return (referent == null) ? new Strong<>(owner, referent) @@ -463,6 +466,7 @@ public abstract class Cache { * * @return the owner of the reference or {@code null} if the owner is unknown */ + @Override public Object getOwner() { return this.owner; } @@ -472,6 +476,7 @@ public abstract class Cache { * * @return the referred object */ + @Override public T getReferent() { return this.referent; } @@ -481,6 +486,7 @@ public abstract class Cache { * * @return {@code true} if the referred object was collected */ + @Override public boolean isStale() { return false; } @@ -488,6 +494,7 @@ public abstract class Cache { /** * Marks this reference as removed from the cache. */ + @Override public void removeOwner() { this.owner = null; } @@ -522,6 +529,7 @@ public abstract class Cache { * * @return the owner of the reference or {@code null} if the owner is unknown */ + @Override public Object getOwner() { return this.owner; } @@ -531,6 +539,7 @@ public abstract class Cache { * * @return the referred object or {@code null} if it was collected */ + @Override public T getReferent() { return get(); } @@ -540,6 +549,7 @@ public abstract class Cache { * * @return {@code true} if the referred object was collected */ + @Override public boolean isStale() { return null == get(); } @@ -547,6 +557,7 @@ public abstract class Cache { /** * Marks this reference as removed from the cache. */ + @Override public void removeOwner() { this.owner = null; } @@ -581,6 +592,7 @@ public abstract class Cache { * * @return the owner of the reference or {@code null} if the owner is unknown */ + @Override public Object getOwner() { return this.owner; } @@ -590,6 +602,7 @@ public abstract class Cache { * * @return the referred object or {@code null} if it was collected */ + @Override public T getReferent() { return get(); } @@ -599,6 +612,7 @@ public abstract class Cache { * * @return {@code true} if the referred object was collected */ + @Override public boolean isStale() { return null == get(); } @@ -606,6 +620,7 @@ public abstract class Cache { /** * Marks this reference as removed from the cache. */ + @Override public void removeOwner() { this.owner = null; } From bfc1db7ed6bf9563c0441b24abe6943607b532e7 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 27 Oct 2025 05:17:43 +0000 Subject: [PATCH 0019/1004] 8370560: Remove non-public API reference from public API javadoc Reviewed-by: prr --- src/java.desktop/share/classes/java/awt/Component.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.desktop/share/classes/java/awt/Component.java b/src/java.desktop/share/classes/java/awt/Component.java index e78cab2a14c..e48255aaf00 100644 --- a/src/java.desktop/share/classes/java/awt/Component.java +++ b/src/java.desktop/share/classes/java/awt/Component.java @@ -6287,7 +6287,7 @@ public abstract class Component implements ImageObserver, MenuContainer, * and paint (and update) events. * For mouse move events the last event is always returned, causing * intermediate moves to be discarded. For paint events, the new - * event is coalesced into a complex {@code RepaintArea} in the peer. + * event is coalesced into a complex repaint area in the peer. * The new {@code AWTEvent} is always returned. * * @param existingEvent the event already on the {@code EventQueue} From 3d2ce8045f9ea52c6559638f9cc7e0a0544b4540 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 27 Oct 2025 06:53:08 +0000 Subject: [PATCH 0020/1004] 8212084: G1: Implement UseGCOverheadLimit Reviewed-by: ayang, iwalulya, fandreuzzi --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 110 ++++++++++++++---- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 11 ++ .../share/gc/parallel/parallelArguments.cpp | 5 - .../gc/parallel/parallelScavengeHeap.cpp | 9 +- .../gc/parallel/parallelScavengeHeap.hpp | 2 +- src/hotspot/share/gc/shared/gc_globals.hpp | 2 +- .../jtreg/gc/TestUseGCOverheadLimit.java | 98 ++++++++++++++++ 7 files changed, 204 insertions(+), 33 deletions(-) create mode 100644 test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index d3e02df3e09..d37ae512023 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -467,8 +467,20 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu words", Thread::current()->name(), word_size); + if (is_shutting_down()) { + stall_for_vm_shutdown(); + return nullptr; + } + + // Has the gc overhead limit been reached in the meantime? If so, this mutator + // should receive null even when unsuccessfully scheduling a collection as well + // for global consistency. + if (gc_overhead_limit_exceeded()) { + return nullptr; + } + // We can reach here if we were unsuccessful in scheduling a collection (because - // another thread beat us to it). In this case immeditealy retry the allocation + // another thread beat us to it). In this case immediately retry the allocation // attempt because another thread successfully performed a collection and possibly // reclaimed enough space. The first attempt (without holding the Heap_lock) is // here and the follow-on attempt will be at the start of the next loop @@ -485,11 +497,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_ log_warning(gc, alloc)("%s: Retried allocation %u times for %zu words", Thread::current()->name(), try_count, word_size); } - - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } } ShouldNotReachHere(); @@ -714,6 +721,18 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu", Thread::current()->name(), word_size); + if (is_shutting_down()) { + stall_for_vm_shutdown(); + return nullptr; + } + + // Has the gc overhead limit been reached in the meantime? If so, this mutator + // should receive null even when unsuccessfully scheduling a collection as well + // for global consistency. + if (gc_overhead_limit_exceeded()) { + return nullptr; + } + // We can reach here if we were unsuccessful in scheduling a collection (because // another thread beat us to it). // Humongous object allocation always needs a lock, so we wait for the retry @@ -725,11 +744,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { log_warning(gc, alloc)("%s: Retried allocation %u times for %zu words", Thread::current()->name(), try_count, word_size); } - - if (is_shutting_down()) { - stall_for_vm_shutdown(); - return nullptr; - } } ShouldNotReachHere(); @@ -955,25 +969,62 @@ void G1CollectedHeap::resize_heap_after_young_collection(size_t allocation_word_ phase_times()->record_resize_heap_time((Ticks::now() - start).seconds() * 1000.0); } +void G1CollectedHeap::update_gc_overhead_counter() { + assert(SafepointSynchronize::is_at_safepoint(), "precondition"); + + if (!UseGCOverheadLimit) { + return; + } + + bool gc_time_over_limit = (_policy->analytics()->long_term_gc_time_ratio() * 100) >= GCTimeLimit; + double free_space_percent = percent_of(num_available_regions() * G1HeapRegion::GrainBytes, max_capacity()); + bool free_space_below_limit = free_space_percent < GCHeapFreeLimit; + + log_debug(gc)("GC Overhead Limit: GC Time %f Free Space %f Counter %zu", + (_policy->analytics()->long_term_gc_time_ratio() * 100), + free_space_percent, + _gc_overhead_counter); + + if (gc_time_over_limit && free_space_below_limit) { + _gc_overhead_counter++; + } else { + _gc_overhead_counter = 0; + } +} + +bool G1CollectedHeap::gc_overhead_limit_exceeded() { + return _gc_overhead_counter >= GCOverheadLimitThreshold; +} + HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size, bool do_gc, bool maximal_compaction, bool expect_null_mutator_alloc_region) { - // Let's attempt the allocation first. - HeapWord* result = - attempt_allocation_at_safepoint(word_size, - expect_null_mutator_alloc_region); - if (result != nullptr) { - return result; - } + // Skip allocation if GC overhead limit has been exceeded to let the mutator run + // into an OOME. It can either exit "gracefully" or try to free up memory asap. + // For the latter situation, keep running GCs. If the mutator frees up enough + // memory quickly enough, the overhead(s) will go below the threshold(s) again + // and the VM may continue running. + // If we did not continue garbage collections, the (gc overhead) limit may decrease + // enough by itself to not count as exceeding the limit any more, in the worst + // case bouncing back-and-forth all the time. + if (!gc_overhead_limit_exceeded()) { + // Let's attempt the allocation first. + HeapWord* result = + attempt_allocation_at_safepoint(word_size, + expect_null_mutator_alloc_region); + if (result != nullptr) { + return result; + } - // In a G1 heap, we're supposed to keep allocation from failing by - // incremental pauses. Therefore, at least for now, we'll favor - // expansion over collection. (This might change in the future if we can - // do something smarter than full collection to satisfy a failed alloc.) - result = expand_and_allocate(word_size); - if (result != nullptr) { - return result; + // In a G1 heap, we're supposed to keep allocation from failing by + // incremental pauses. Therefore, at least for now, we'll favor + // expansion over collection. (This might change in the future if we can + // do something smarter than full collection to satisfy a failed alloc.) + result = expand_and_allocate(word_size); + if (result != nullptr) { + return result; + } } if (do_gc) { @@ -997,6 +1048,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size, HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { assert_at_safepoint_on_vm_thread(); + // Update GC overhead limits after the initial garbage collection leading to this + // allocation attempt. + update_gc_overhead_counter(); + // Attempts to allocate followed by Full GC. HeapWord* result = satisfy_failed_allocation_helper(word_size, @@ -1028,6 +1083,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { return result; } + if (gc_overhead_limit_exceeded()) { + log_info(gc)("GC Overhead Limit exceeded too often (%zu).", GCOverheadLimitThreshold); + } + // What else? We might try synchronous finalization later. If the total // space available is large enough for the allocation, then a more // complete compaction phase than we've tried so far might be @@ -1209,6 +1268,7 @@ public: G1CollectedHeap::G1CollectedHeap() : CollectedHeap(), + _gc_overhead_counter(0), _service_thread(nullptr), _periodic_gc_task(nullptr), _free_arena_memory_task(nullptr), diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 0d354525d89..c5b7da613d0 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -169,6 +169,17 @@ class G1CollectedHeap : public CollectedHeap { friend class G1CheckRegionAttrTableClosure; private: + // GC Overhead Limit functionality related members. + // + // The goal is to return null for allocations prematurely (before really going + // OOME) in case both GC CPU usage (>= GCTimeLimit) and not much available free + // memory (<= GCHeapFreeLimit) so that applications can exit gracefully or try + // to keep running by easing off memory. + uintx _gc_overhead_counter; // The number of consecutive garbage collections we were over the limits. + + void update_gc_overhead_counter(); + bool gc_overhead_limit_exceeded(); + G1ServiceThread* _service_thread; G1ServiceTask* _periodic_gc_task; G1MonotonicArenaFreeMemoryTask* _free_arena_memory_task; diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp index 2d267951f79..629690a6258 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp @@ -66,11 +66,6 @@ void ParallelArguments::initialize() { } } - // True in product build, since tests using debug build often stress GC - if (FLAG_IS_DEFAULT(UseGCOverheadLimit)) { - FLAG_SET_DEFAULT(UseGCOverheadLimit, trueInProduct); - } - if (InitialSurvivorRatio < MinSurvivorRatio) { if (FLAG_IS_CMDLINE(InitialSurvivorRatio)) { if (FLAG_IS_CMDLINE(MinSurvivorRatio)) { diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index eef9dfbc97c..f1baa4c4ff7 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -374,6 +374,13 @@ bool ParallelScavengeHeap::check_gc_overhead_limit() { bool little_mutator_time = _size_policy->mutator_time_percent() * 100 < (100 - GCTimeLimit); bool little_free_space = check_gc_heap_free_limit(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()) && check_gc_heap_free_limit( _old_gen->free_in_bytes(), _old_gen->capacity_in_bytes()); + + log_debug(gc)("GC Overhead Limit: GC Time %f Free Space Young %f Old %f Counter %zu", + (100 - _size_policy->mutator_time_percent()), + percent_of(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), + percent_of(_old_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), + _gc_overhead_counter); + if (little_mutator_time && little_free_space) { _gc_overhead_counter++; if (_gc_overhead_counter >= GCOverheadLimitThreshold) { @@ -426,7 +433,7 @@ HeapWord* ParallelScavengeHeap::satisfy_failed_allocation(size_t size, bool is_t } if (check_gc_overhead_limit()) { - log_info(gc)("GCOverheadLimitThreshold %zu reached.", GCOverheadLimitThreshold); + log_info(gc)("GC Overhead Limit exceeded too often (%zu).", GCOverheadLimitThreshold); return nullptr; } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index 84732a86880..962a3c4b15b 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -88,7 +88,7 @@ class ParallelScavengeHeap : public CollectedHeap { WorkerThreads _workers; - uint _gc_overhead_counter; + uintx _gc_overhead_counter; bool _is_heap_almost_full; diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 956bffde156..6f754dbc39d 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -357,7 +357,7 @@ "Initial ratio of young generation/survivor space size") \ range(3, max_uintx) \ \ - product(bool, UseGCOverheadLimit, true, \ + product(bool, UseGCOverheadLimit, falseInDebug, \ "Use policy to limit of proportion of time spent in GC " \ "before an OutOfMemory error is thrown") \ \ diff --git a/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java new file mode 100644 index 00000000000..bc4c6bd6278 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestUseGCOverheadLimit.java @@ -0,0 +1,98 @@ +/* + * 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. + */ + +package gc; + +/* + * @test id=Parallel + * @requires vm.gc.Parallel + * @requires !vm.debug + * @summary Verifies that the UseGCOverheadLimit functionality works in Parallel GC. + * @library /test/lib + * @run driver gc.TestUseGCOverheadLimit Parallel + */ + +/* + * @test id=G1 + * @requires vm.gc.G1 + * @requires !vm.debug + * @summary Verifies that the UseGCOverheadLimit functionality works in G1 GC. + * @library /test/lib + * @run driver gc.TestUseGCOverheadLimit G1 + */ + +import java.util.Arrays; +import java.util.stream.Stream; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestUseGCOverheadLimit { + public static void main(String args[]) throws Exception { + String[] parallelArgs = { + "-XX:+UseParallelGC", + "-XX:NewSize=122m", + "-XX:SurvivorRatio=99", + "-XX:GCHeapFreeLimit=10" + }; + String[] g1Args = { + "-XX:+UseG1GC", + "-XX:GCHeapFreeLimit=5" + }; + + String[] selectedArgs = args[0].equals("G1") ? g1Args : parallelArgs; + + final String[] commonArgs = { + "-XX:-UseCompactObjectHeaders", // Object sizes are calculated such that the heap is tight. + "-XX:ParallelGCThreads=1", // Make GCs take longer. + "-XX:+UseGCOverheadLimit", + "-Xlog:gc=debug", + "-XX:GCTimeLimit=90", // Ease the CPU requirement a little. + "-Xmx128m", + Allocating.class.getName() + }; + + String[] vmArgs = Stream.concat(Arrays.stream(selectedArgs), Arrays.stream(commonArgs)).toArray(String[]::new); + OutputAnalyzer output = ProcessTools.executeLimitedTestJava(vmArgs); + output.shouldNotHaveExitValue(0); + + System.out.println(output.getStdout()); + + output.stdoutShouldContain("GC Overhead Limit exceeded too often (5)."); + } + + static class Allocating { + public static void main(String[] args) { + Object[] cache = new Object[1024 * 1024 * 2]; + + // Allocate random objects, keeping around data, causing garbage + // collections. + for (int i = 0; i < 1024* 1024 * 30; i++) { + Object[] obj = new Object[10]; + cache[i % cache.length] = obj; + } + + System.out.println(cache); + } + } +} From f5ef01d4bfcf960b6a46844818138ee798532d45 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Mon, 27 Oct 2025 07:38:28 +0000 Subject: [PATCH 0021/1004] 8370368: Apply java.io.Serial annotations in java.security.jgss Reviewed-by: mullan --- .../classes/sun/security/jgss/krb5/Krb5Context.java | 6 ++++-- .../sun/security/jgss/krb5/Krb5InitCredential.java | 2 +- .../share/classes/sun/security/krb5/Asn1Exception.java | 3 +++ .../classes/sun/security/krb5/KrbCryptoException.java | 3 +++ .../share/classes/sun/security/krb5/KrbException.java | 4 +++- .../share/classes/sun/security/krb5/RealmException.java | 5 ++++- .../classes/sun/security/krb5/internal/KRBError.java | 9 ++++++--- .../sun/security/krb5/internal/KdcErrException.java | 5 ++++- .../sun/security/krb5/internal/KrbApErrException.java | 5 ++++- .../sun/security/krb5/internal/KrbErrException.java | 3 +++ .../classes/sun/security/krb5/internal/tools/Ktab.java | 5 +++-- 11 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java index 7df3d8d2de0..b118c9ee215 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -37,6 +37,7 @@ import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.OutputStream; +import java.io.Serial; import java.security.*; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosCredMessage; @@ -1332,6 +1333,7 @@ class Krb5Context implements GSSContextSpi { * The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY) */ static class KerberosSessionKey implements Key { + @Serial private static final long serialVersionUID = 699307378954123869L; @SuppressWarnings("serial") // Not statically typed as Serializable @@ -1369,7 +1371,7 @@ class Krb5Context implements GSSContextSpi { * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ - @java.io.Serial + @Serial private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { throw new InvalidObjectException diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index 29176ba3c2b..7aaa8975185 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -402,7 +402,7 @@ public class Krb5InitCredential * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ - @java.io.Serial + @Serial private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { throw new InvalidObjectException("Krb5InitCredential not deserializable"); diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java b/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java index 7899a571589..8fe5591ffbc 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Asn1Exception.java @@ -30,8 +30,11 @@ package sun.security.krb5; +import java.io.Serial; + public class Asn1Exception extends KrbException { + @Serial private static final long serialVersionUID = 8291288984575084132L; public Asn1Exception(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java b/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java index eda5fcec397..24cda1849ff 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbCryptoException.java @@ -30,6 +30,8 @@ package sun.security.krb5; +import java.io.Serial; + /** * KrbCryptoException is a wrapper exception for exceptions thrown by JCE. * @@ -37,6 +39,7 @@ package sun.security.krb5; */ public class KrbCryptoException extends KrbException { + @Serial private static final long serialVersionUID = -1657367919979982250L; public KrbCryptoException(String s) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.java b/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.java index 3fae5c7c2c5..434a0b401dd 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, 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 @@ -31,6 +31,7 @@ package sun.security.krb5; +import java.io.Serial; import java.util.Objects; import sun.security.krb5.internal.Krb5; @@ -38,6 +39,7 @@ import sun.security.krb5.internal.KRBError; public class KrbException extends Exception { + @Serial private static final long serialVersionUID = -4993302876451928596L; private int returnCode; diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java b/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java index 124d1b63ed4..461da49c757 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/RealmException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, 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 @@ -31,8 +31,11 @@ package sun.security.krb5; +import java.io.Serial; + public class RealmException extends KrbException { + @Serial private static final long serialVersionUID = -9100385213693792864L; public RealmException(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java index db6192ce9ee..46c733824c5 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, 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 @@ -40,6 +40,7 @@ import sun.security.krb5.RealmException; import sun.security.util.*; import java.io.IOException; import java.io.ObjectInputStream; +import java.io.Serial; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -85,7 +86,8 @@ import static sun.security.krb5.internal.Krb5.DEBUG; */ public class KRBError implements java.io.Serializable { - static final long serialVersionUID = 3643809337475284503L; + @Serial + private static final long serialVersionUID = 3643809337475284503L; private transient int pvno; private transient int msgType; @@ -112,7 +114,7 @@ public class KRBError implements java.io.Serializable { * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ - @java.io.Serial + @Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { @@ -123,6 +125,7 @@ public class KRBError implements java.io.Serializable { } } + @Serial private void writeObject(ObjectOutputStream os) throws IOException { try { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java index c55670f4b23..744ba9ba2ac 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KdcErrException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2004, 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 @@ -31,8 +31,11 @@ package sun.security.krb5.internal; +import java.io.Serial; + public class KdcErrException extends sun.security.krb5.KrbException { + @Serial private static final long serialVersionUID = -8788186031117310306L; public KdcErrException(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java index 04048cb73bb..4fd0aec5fa8 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbApErrException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2003, 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 @@ -31,8 +31,11 @@ package sun.security.krb5.internal; +import java.io.Serial; + public class KrbApErrException extends sun.security.krb5.KrbException { + @Serial private static final long serialVersionUID = 7545264413323118315L; public KrbApErrException(int i) { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java index 62e84959ca9..8e5a49dd802 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KrbErrException.java @@ -30,8 +30,11 @@ package sun.security.krb5.internal; +import java.io.Serial; + public class KrbErrException extends sun.security.krb5.KrbException { + @Serial private static final long serialVersionUID = 2186533836785448317L; public KrbErrException(int i) { diff --git a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java index ffe2e3196c1..bf1ceed8d22 100644 --- a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java +++ b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Ktab.java @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.Reader; +import java.io.Serial; import java.nio.charset.Charset; import java.text.DateFormat; import java.util.Arrays; @@ -82,8 +83,8 @@ public class Ktab { } private static class ExitException extends RuntimeException { - @java.io.Serial - static final long serialVersionUID = 0L; + @Serial + private static final long serialVersionUID = 0L; private final int errorCode; public ExitException(int errorCode) { this.errorCode = errorCode; From e9479b517ad8b6eac7244057644f90e710bd74b7 Mon Sep 17 00:00:00 2001 From: Raffaello Giulietti Date: Mon, 27 Oct 2025 08:15:00 +0000 Subject: [PATCH 0022/1004] 8370628: Rename BigInteger::nthRoot to rootn, and similarly for nthRootAndRemainder Reviewed-by: darcy --- .../share/classes/java/math/BigInteger.java | 14 +-- .../classes/java/math/MutableBigInteger.java | 16 ++-- .../java/math/BigInteger/BigIntegerTest.java | 88 +++++++++---------- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 6253adffb2b..ed26f5c1211 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2768,9 +2768,9 @@ public class BigInteger extends Number implements Comparable { * @throws ArithmeticException if {@code n} is even and {@code this} is negative. * @see #sqrt() * @since 26 - * @apiNote Note that calling {@code nthRoot(2)} is equivalent to calling {@code sqrt()}. + * @apiNote Note that calling {@code rootn(2)} is equivalent to calling {@code sqrt()}. */ - public BigInteger nthRoot(int n) { + public BigInteger rootn(int n) { if (n == 1) return this; @@ -2778,7 +2778,7 @@ public class BigInteger extends Number implements Comparable { return sqrt(); checkRootDegree(n); - return new MutableBigInteger(this.mag).nthRootRem(n)[0].toBigInteger(signum); + return new MutableBigInteger(this.mag).rootnRem(n)[0].toBigInteger(signum); } /** @@ -2793,12 +2793,12 @@ public class BigInteger extends Number implements Comparable { * @throws ArithmeticException if {@code n} is even and {@code this} is negative. * @see #sqrt() * @see #sqrtAndRemainder() - * @see #nthRoot(int) + * @see #rootn(int) * @since 26 - * @apiNote Note that calling {@code nthRootAndRemainder(2)} is equivalent to calling + * @apiNote Note that calling {@code rootnAndRemainder(2)} is equivalent to calling * {@code sqrtAndRemainder()}. */ - public BigInteger[] nthRootAndRemainder(int n) { + public BigInteger[] rootnAndRemainder(int n) { if (n == 1) return new BigInteger[] { this, ZERO }; @@ -2806,7 +2806,7 @@ public class BigInteger extends Number implements Comparable { return sqrtAndRemainder(); checkRootDegree(n); - MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).nthRootRem(n); + MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).rootnRem(n); return new BigInteger[] { rootRem[0].toBigInteger(signum), rootRem[1].toBigInteger(signum) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index dd1da29ddd2..1ede4cf32f8 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1906,7 +1906,7 @@ class MutableBigInteger { * @param n the root degree * @return the integer {@code n}th root of {@code this} and the remainder */ - MutableBigInteger[] nthRootRem(int n) { + MutableBigInteger[] rootnRem(int n) { // Special cases. if (this.isZero() || this.isOne()) return new MutableBigInteger[] { this, new MutableBigInteger() }; @@ -1923,7 +1923,7 @@ class MutableBigInteger { if (bitLength <= Long.SIZE) { // Initial estimate is the root of the unsigned long value. final long x = this.toLong(); - long sLong = (long) nthRootApprox(Math.nextUp(x >= 0 ? x : x + 0x1p64), n) + 1L; + long sLong = (long) rootnApprox(Math.nextUp(x >= 0 ? x : x + 0x1p64), n) + 1L; /* The integer-valued recurrence formula in the algorithm of Brent&Zimmermann * simply discards the fraction part of the real-valued Newton recurrence * on the function f discussed in the referenced work. @@ -1996,7 +1996,7 @@ class MutableBigInteger { // Use the root of the shifted value as an estimate. // rad ≤ 2^ME, so Math.nextUp(rad) < Double.MAX_VALUE rad = Math.nextUp(rad); - approx = nthRootApprox(rad, n); + approx = rootnApprox(rad, n); } else { // fp arithmetic gives too few correct bits // Set the root shift to the root's bit length minus 1 // The initial estimate will be 2^rootLen == 2 << (rootLen - 1) @@ -2050,7 +2050,7 @@ class MutableBigInteger { MutableBigInteger x = new MutableBigInteger(this); x.rightShift(rootSh * n); - newtonRecurrenceNthRoot(x, s, n, s.toBigInteger().pow(n - 1)); + newtonRecurrenceRootn(x, s, n, s.toBigInteger().pow(n - 1)); s.add(ONE); // round up to ensure s is an upper bound of the root } @@ -2060,7 +2060,7 @@ class MutableBigInteger { } // Do the 1st iteration outside the loop to ensure an overestimate - newtonRecurrenceNthRoot(this, s, n, s.toBigInteger().pow(n - 1)); + newtonRecurrenceRootn(this, s, n, s.toBigInteger().pow(n - 1)); // Refine the estimate. do { BigInteger sBig = s.toBigInteger(); @@ -2069,18 +2069,18 @@ class MutableBigInteger { if (rem.subtract(this) <= 0) return new MutableBigInteger[] { s, rem }; - newtonRecurrenceNthRoot(this, s, n, sToN1); + newtonRecurrenceRootn(this, s, n, sToN1); } while (true); } - private static double nthRootApprox(double x, int n) { + private static double rootnApprox(double x, int n) { return Math.nextUp(n == 3 ? Math.cbrt(x) : Math.pow(x, Math.nextUp(1.0 / n))); } /** * Computes {@code ((n-1)*s + x/sToN1)/n} and places the result in {@code s}. */ - private static void newtonRecurrenceNthRoot( + private static void newtonRecurrenceRootn( MutableBigInteger x, MutableBigInteger s, int n, BigInteger sToN1) { MutableBigInteger dividend = new MutableBigInteger(); s.mul(n - 1, dividend); diff --git a/test/jdk/java/math/BigInteger/BigIntegerTest.java b/test/jdk/java/math/BigInteger/BigIntegerTest.java index 0e847f5ff6c..84aa8d15676 100644 --- a/test/jdk/java/math/BigInteger/BigIntegerTest.java +++ b/test/jdk/java/math/BigInteger/BigIntegerTest.java @@ -24,7 +24,7 @@ /* * @test * @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 4026465 - * 8074460 8078672 8032027 8229845 8077587 8367365 + * 8074460 8078672 8032027 8229845 8077587 8367365 8370628 * @summary tests methods in BigInteger (use -Dseed=X to set PRNG seed) * @key randomness * @library /test/lib @@ -232,7 +232,7 @@ public class BigIntegerTest { failCount1++; failCount1 += checkResult(x.signum() < 0 && power % 2 == 0 ? x.negate() : x, - y.nthRoot(power), "BigInteger.pow() inconsistent with BigInteger.nthRoot()"); + y.rootn(power), "BigInteger.pow() inconsistent with BigInteger.rootn()"); } report("pow for " + order + " bits", failCount1); } @@ -414,7 +414,7 @@ public class BigIntegerTest { BigInteger.valueOf(x)).collect(Collectors.summingInt(g))); } - private static void nthRootSmall() { + private static void rootnSmall() { int failCount = 0; // A non-positive degree should cause an exception. @@ -422,10 +422,10 @@ public class BigIntegerTest { BigInteger x = BigInteger.ONE; BigInteger s; try { - s = x.nthRoot(n); - // If nthRoot() does not throw an exception that is a failure. + s = x.rootn(n); + // If rootn() does not throw an exception that is a failure. failCount++; - printErr("nthRoot() of non-positive degree did not throw an exception"); + printErr("rootn() of non-positive degree did not throw an exception"); } catch (ArithmeticException expected) { // Not a failure } @@ -434,41 +434,41 @@ public class BigIntegerTest { n = 4; x = BigInteger.valueOf(-1); try { - s = x.nthRoot(n); - // If nthRoot() does not throw an exception that is a failure. + s = x.rootn(n); + // If rootn() does not throw an exception that is a failure. failCount++; - printErr("nthRoot() of negative number and even degree did not throw an exception"); + printErr("rootn() of negative number and even degree did not throw an exception"); } catch (ArithmeticException expected) { // Not a failure } - // A negative value with odd degree should return -nthRoot(-x, n) + // A negative value with odd degree should return -rootn(-x, n) n = 3; x = BigInteger.valueOf(-8); - failCount += checkResult(x.negate().nthRoot(n).negate(), x.nthRoot(n), - "nthRoot(" + x + ", " + n + ") != -nthRoot(" + x.negate() + ", " + n + ")"); + failCount += checkResult(x.negate().rootn(n).negate(), x.rootn(n), + "rootn(" + x + ", " + n + ") != -rootn(" + x.negate() + ", " + n + ")"); // A zero value should return BigInteger.ZERO. - failCount += checkResult(BigInteger.ZERO, BigInteger.ZERO.nthRoot(n), - "nthRoot(0, " + n + ") != 0"); + failCount += checkResult(BigInteger.ZERO, BigInteger.ZERO.rootn(n), + "rootn(0, " + n + ") != 0"); // A one degree should return x. x = BigInteger.TWO; - failCount += checkResult(x, x.nthRoot(1), "nthRoot(" + x + ", 1) != " + x); + failCount += checkResult(x, x.rootn(1), "rootn(" + x + ", 1) != " + x); n = 8; // 1 <= value < 2^n should return BigInteger.ONE. int end = 1 << n; for (int i = 1; i < end; i++) { failCount += checkResult(BigInteger.ONE, - BigInteger.valueOf(i).nthRoot(n), "nthRoot(" + i + ", " + n + ") != 1"); + BigInteger.valueOf(i).rootn(n), "rootn(" + i + ", " + n + ") != 1"); } - report("nthRootSmall", failCount); + report("rootnSmall", failCount); } - public static void nthRoot() { - nthRootSmall(); + public static void rootn() { + rootnSmall(); ToIntFunction f = (x) -> { int n = random.nextInt(x.bitLength()) + 2; @@ -476,28 +476,28 @@ public class BigIntegerTest { // nth root of x^n -> x BigInteger xN = x.pow(n); - failCount += checkResult(x, xN.nthRoot(n), "nthRoot() x^n -> x"); + failCount += checkResult(x, xN.rootn(n), "rootn() x^n -> x"); // nth root of x^n + 1 -> x BigInteger xNup = xN.add(BigInteger.ONE); - failCount += checkResult(x, xNup.nthRoot(n), "nthRoot() x^n + 1 -> x"); + failCount += checkResult(x, xNup.rootn(n), "rootn() x^n + 1 -> x"); // nth root of (x + 1)^n - 1 -> x BigInteger up = x.add(BigInteger.ONE).pow(n).subtract(BigInteger.ONE); - failCount += checkResult(x, up.nthRoot(n), "nthRoot() (x + 1)^n - 1 -> x"); + failCount += checkResult(x, up.rootn(n), "rootn() (x + 1)^n - 1 -> x"); - // nthRoot(x, n)^n <= x - BigInteger r = x.nthRoot(n); + // rootn(x, n)^n <= x + BigInteger r = x.rootn(n); if (r.pow(n).compareTo(x) > 0) { failCount++; - printErr("nthRoot(x, n)^n > x for x = " + x + ", n = " + n); + printErr("rootn(x, n)^n > x for x = " + x + ", n = " + n); } - // (nthRoot(x, n) + 1)^n > x + // (rootn(x, n) + 1)^n > x if (r.add(BigInteger.ONE).pow(n).compareTo(x) <= 0) { failCount++; - printErr("(nthRoot(x, n) + 1)^n <= x for x = " + x + ", n = " + n); + printErr("(rootn(x, n) + 1)^n <= x for x = " + x + ", n = " + n); } return failCount; @@ -513,52 +513,52 @@ public class BigIntegerTest { } sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger()); sb.add((new BigDecimal(Double.MAX_VALUE)).toBigInteger().add(BigInteger.ONE)); - report("nthRoot for 2^N, 2^N - 1 and 2^N + 1, 1 <= N <= " + maxExponent, + report("rootn for 2^N, 2^N - 1 and 2^N + 1, 1 <= N <= " + maxExponent, sb.build().collect(Collectors.summingInt(f))); IntStream ints = random.ints(SIZE, 2, Integer.MAX_VALUE); - report("nthRoot for int", ints.mapToObj(x -> + report("rootn for int", ints.mapToObj(x -> BigInteger.valueOf(x)).collect(Collectors.summingInt(f))); LongStream longs = random.longs(SIZE, Integer.MAX_VALUE + 1L, Long.MAX_VALUE); - report("nthRoot for long", longs.mapToObj(x -> + report("rootn for long", longs.mapToObj(x -> BigInteger.valueOf(x)).collect(Collectors.summingInt(f))); DoubleStream doubles = random.doubles(SIZE, 0x1p63, Math.scalb(1.0, maxExponent)); - report("nthRoot for double", doubles.mapToObj(x -> + report("rootn for double", doubles.mapToObj(x -> BigDecimal.valueOf(x).toBigInteger()).collect(Collectors.summingInt(f))); } - public static void nthRootAndRemainder() { + public static void rootnAndRemainder() { ToIntFunction g = (x) -> { int failCount = 0; int n = random.nextInt(x.bitLength()) + 2; BigInteger xN = x.pow(n); // nth root of x^n -> x - BigInteger[] actual = xN.nthRootAndRemainder(n); - failCount += checkResult(x, actual[0], "nthRootAndRemainder()[0]"); - failCount += checkResult(BigInteger.ZERO, actual[1], "nthRootAndRemainder()[1]"); + BigInteger[] actual = xN.rootnAndRemainder(n); + failCount += checkResult(x, actual[0], "rootnAndRemainder()[0]"); + failCount += checkResult(BigInteger.ZERO, actual[1], "rootnAndRemainder()[1]"); // nth root of x^n + 1 -> x BigInteger xNup = xN.add(BigInteger.ONE); - actual = xNup.nthRootAndRemainder(n); - failCount += checkResult(x, actual[0], "nthRootAndRemainder()[0]"); - failCount += checkResult(BigInteger.ONE, actual[1], "nthRootAndRemainder()[1]"); + actual = xNup.rootnAndRemainder(n); + failCount += checkResult(x, actual[0], "rootnAndRemainder()[0]"); + failCount += checkResult(BigInteger.ONE, actual[1], "rootnAndRemainder()[1]"); // nth root of (x + 1)^n - 1 -> x BigInteger up = x.add(BigInteger.ONE).pow(n).subtract(BigInteger.ONE); - actual = up.nthRootAndRemainder(n); - failCount += checkResult(x, actual[0], "nthRootAndRemainder()[0]"); + actual = up.rootnAndRemainder(n); + failCount += checkResult(x, actual[0], "rootnAndRemainder()[0]"); BigInteger r = up.subtract(xN); - failCount += checkResult(r, actual[1], "nthRootAndRemainder()[1]"); + failCount += checkResult(r, actual[1], "rootnAndRemainder()[1]"); return failCount; }; IntStream bits = random.ints(SIZE, 3, Short.MAX_VALUE); - report("nthRootAndRemainder", bits.mapToObj(x -> + report("rootnAndRemainder", bits.mapToObj(x -> BigInteger.valueOf(x)).collect(Collectors.summingInt(g))); } @@ -1472,8 +1472,8 @@ public class BigIntegerTest { squareRoot(); squareRootAndRemainder(); - nthRoot(); - nthRootAndRemainder(); + rootn(); + rootnAndRemainder(); } if (failure) From 91e1dcb1083cc8c451d2d169d7f2fdb51c1a158e Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 27 Oct 2025 10:07:55 +0000 Subject: [PATCH 0023/1004] 8366781: Parallel: Include OS free memory in GC selection heuristics Reviewed-by: gli, iwalulya --- src/hotspot/share/gc/parallel/psScavenge.cpp | 47 +++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index f633f40ef7f..e738a13d464 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -521,31 +521,56 @@ void PSScavenge::clean_up_failed_promotion() { } bool PSScavenge::should_attempt_scavenge() { - ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); + const bool ShouldRunYoungGC = true; + const bool ShouldRunFullGC = false; + ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); PSYoungGen* young_gen = heap->young_gen(); PSOldGen* old_gen = heap->old_gen(); if (!young_gen->to_space()->is_empty()) { - log_debug(gc, ergo)("To-space is not empty; should run full-gc instead."); - return false; + log_debug(gc, ergo)("To-space is not empty; run full-gc instead."); + return ShouldRunFullGC; } - // Test to see if the scavenge will likely fail. + // Check if the predicted promoted bytes will overflow free space in old-gen. PSAdaptiveSizePolicy* policy = heap->size_policy(); size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes(); size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes()); // Total free size after possible old gen expansion - size_t free_in_old_gen = old_gen->max_gen_size() - old_gen->used_in_bytes(); - bool result = promotion_estimate < free_in_old_gen; + size_t free_in_old_gen_with_expansion = old_gen->max_gen_size() - old_gen->used_in_bytes(); - log_trace(gc, ergo)("%s scavenge: average_promoted %zu padded_average_promoted %zu free in old gen %zu", - result ? "Do" : "Skip", (size_t) policy->average_promoted_in_bytes(), - (size_t) policy->padded_average_promoted_in_bytes(), - free_in_old_gen); + log_trace(gc, ergo)("average_promoted %zu; padded_average_promoted %zu", + (size_t) policy->average_promoted_in_bytes(), + (size_t) policy->padded_average_promoted_in_bytes()); - return result; + if (promotion_estimate >= free_in_old_gen_with_expansion) { + log_debug(gc, ergo)("Run full-gc; predicted promotion size >= max free space in old-gen: %zu >= %zu", + promotion_estimate, free_in_old_gen_with_expansion); + return ShouldRunFullGC; + } + + if (UseAdaptiveSizePolicy) { + // Also checking OS has enough free memory to commit and expand old-gen. + // Otherwise, the recorded gc-pause-time might be inflated to include time + // of OS preparing free memory, resulting in inaccurate young-gen resizing. + assert(old_gen->committed().byte_size() >= old_gen->used_in_bytes(), "inv"); + // Use uint64_t instead of size_t for 32bit compatibility. + uint64_t free_mem_in_os; + if (os::free_memory(free_mem_in_os)) { + size_t actual_free = (size_t)MIN2(old_gen->committed().byte_size() - old_gen->used_in_bytes() + free_mem_in_os, + (uint64_t)SIZE_MAX); + if (promotion_estimate > actual_free) { + log_debug(gc, ergo)("Run full-gc; predicted promotion size > free space in old-gen and OS: %zu > %zu", + promotion_estimate, actual_free); + return ShouldRunFullGC; + } + } + } + + // No particular reasons to run full-gc, so young-gc. + return ShouldRunYoungGC; } // Adaptive size policy support. From 6f8d07ae21e49f87f64a5d4e10c930c4447ec8b6 Mon Sep 17 00:00:00 2001 From: Johny Jose Date: Mon, 27 Oct 2025 10:23:48 +0000 Subject: [PATCH 0024/1004] 8368500: ContextClassLoader cannot be reset on threads in ForkJoinPool.commonPool() Reviewed-by: vklang, alanb --- .../util/concurrent/ForkJoinWorkerThread.java | 6 +- .../forkjoin/ContextClassLoaderTest.java | 56 +++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 test/jdk/java/util/concurrent/forkjoin/ContextClassLoaderTest.java diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java index b942d3ecd09..566fc417952 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java @@ -267,10 +267,8 @@ public class ForkJoinWorkerThread extends Thread { @Override // to record changes public void setContextClassLoader(ClassLoader cl) { - if (ClassLoader.getSystemClassLoader() != cl) { - resetCCL = true; - super.setContextClassLoader(cl); - } + resetCCL = ClassLoader.getSystemClassLoader() != cl; + super.setContextClassLoader(cl); } @Override // to re-establish CCL if necessary diff --git a/test/jdk/java/util/concurrent/forkjoin/ContextClassLoaderTest.java b/test/jdk/java/util/concurrent/forkjoin/ContextClassLoaderTest.java new file mode 100644 index 00000000000..eb6465e9619 --- /dev/null +++ b/test/jdk/java/util/concurrent/forkjoin/ContextClassLoaderTest.java @@ -0,0 +1,56 @@ +/* + * 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 8368500 + * @run junit/othervm ContextClassLoaderTest + * @summary Check the context classloader is reset + */ +import java.net.URL; +import java.net.URLClassLoader; +import java.util.concurrent.Future; +import java.util.concurrent.ForkJoinPool; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class ContextClassLoaderTest { + + @Test + void testContextClassLoaderIsSetAndRestored() throws Exception { + Future future = ForkJoinPool.commonPool().submit(() -> { + Thread thread = Thread.currentThread(); + ClassLoader originalCCL = thread.getContextClassLoader(); + ClassLoader customCCL = new URLClassLoader(new URL[0], originalCCL); + // Set custom context classloader and verify it + thread.setContextClassLoader(customCCL); + assertSame(customCCL, thread.getContextClassLoader(), "Custom context class loader not set"); + + // Reset to original and verify restoration + thread.setContextClassLoader(originalCCL); + assertSame(originalCCL, thread.getContextClassLoader(), "Original context class loader not restored"); + }); + future.get(); + } +} + From 7bb490c4bf7ae55547e4468da0795dac0a873d2b Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 27 Oct 2025 10:35:02 +0000 Subject: [PATCH 0025/1004] 8370318: AES-GCM vector intrinsic may read out of bounds (x86_64, AVX-512) Reviewed-by: kvn, roland --- src/hotspot/cpu/x86/stubGenerator_x86_64.hpp | 2 ++ .../cpu/x86/stubGenerator_x86_64_aes.cpp | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 44d13bbbf31..0887225d3e8 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -393,6 +393,8 @@ class StubGenerator: public StubCodeGenerator { XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, XMMRegister xmm8); void ghash_last_8_avx2(Register subkeyHtbl); + void check_key_offset(Register key, int offset, int load_size); + // Load key and shuffle operation void ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask); void ev_load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 6f698b954ad..f0726ded7e5 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -1759,25 +1759,43 @@ void StubGenerator::roundDeclast(XMMRegister xmm_reg) { __ vaesdeclast(xmm8, xmm8, xmm_reg, Assembler::AVX_512bit); } +// Check incoming byte offset against the int[] len. key is the pointer to the int[0]. +// This check happens often, so it is important for it to be very compact. +void StubGenerator::check_key_offset(Register key, int offset, int load_size) { +#ifdef ASSERT + Address key_length(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)); + assert((offset + load_size) % 4 == 0, "Alignment is good: %d + %d", offset, load_size); + int end_offset = (offset + load_size) / 4; + Label L_good; + __ cmpl(key_length, end_offset); + __ jccb(Assembler::greaterEqual, L_good); + __ hlt(); + __ bind(L_good); +#endif +} // Utility routine for loading a 128-bit key word in little endian format void StubGenerator::load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, xmm_shuf_mask); } void StubGenerator::load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, ExternalAddress(key_shuffle_mask_addr()), rscratch); } void StubGenerator::ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, xmm_shuf_mask); __ evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit); } void StubGenerator::ev_load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch) { + check_key_offset(key, offset, 16); __ movdqu(xmmdst, Address(key, offset)); __ pshufb(xmmdst, ExternalAddress(key_shuffle_mask_addr()), rscratch); __ evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit); @@ -3205,12 +3223,12 @@ void StubGenerator::ghash16_encrypt_parallel16_avx512(Register in, Register out, //AES round 9 roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); - ev_load_key(AESKEY2, key, 11 * 16, rbx); //AES rounds up to 11 (AES192) or 13 (AES256) //AES128 is done __ cmpl(NROUNDS, 52); __ jcc(Assembler::less, last_aes_rnd); __ bind(aes_192); + ev_load_key(AESKEY2, key, 11 * 16, rbx); roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); ev_load_key(AESKEY1, key, 12 * 16, rbx); roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); From 5ed6c201ba0a9dc78960f2f3a5afce268e84a82d Mon Sep 17 00:00:00 2001 From: Johannes Bechberger Date: Mon, 27 Oct 2025 12:29:22 +0000 Subject: [PATCH 0026/1004] 8370681: [BACKOUT] Improve memory ordering in new CPU Time Profiler Reviewed-by: mdoerr --- .../sampling/jfrCPUTimeThreadSampler.cpp | 38 +++++++++++-------- .../sampling/jfrCPUTimeThreadSampler.hpp | 20 ++++------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp index 031dfb7e8ad..7507b9c994e 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp @@ -82,7 +82,14 @@ JfrCPUTimeTraceQueue::~JfrCPUTimeTraceQueue() { bool JfrCPUTimeTraceQueue::enqueue(JfrCPUTimeSampleRequest& request) { assert(JavaThread::current()->jfr_thread_local()->is_cpu_time_jfr_enqueue_locked(), "invariant"); assert(&JavaThread::current()->jfr_thread_local()->cpu_time_jfr_queue() == this, "invariant"); - _data[_head++] = request; + u4 elementIndex; + do { + elementIndex = AtomicAccess::load_acquire(&_head); + if (elementIndex >= _capacity) { + return false; + } + } while (AtomicAccess::cmpxchg(&_head, elementIndex, elementIndex + 1) != elementIndex); + _data[elementIndex] = request; return true; } @@ -94,19 +101,19 @@ JfrCPUTimeSampleRequest& JfrCPUTimeTraceQueue::at(u4 index) { static volatile u4 _lost_samples_sum = 0; u4 JfrCPUTimeTraceQueue::size() const { - return _head; + return AtomicAccess::load_acquire(&_head); } void JfrCPUTimeTraceQueue::set_size(u4 size) { - _head = size; + AtomicAccess::release_store(&_head, size); } u4 JfrCPUTimeTraceQueue::capacity() const { - return _capacity; + return AtomicAccess::load_acquire(&_capacity); } void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) { - if (capacity == _capacity) { + if (capacity == AtomicAccess::load(&_capacity)) { return; } _head = 0; @@ -119,15 +126,15 @@ void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) { } else { _data = nullptr; } - _capacity = capacity; + AtomicAccess::release_store(&_capacity, capacity); } bool JfrCPUTimeTraceQueue::is_empty() const { - return _head == 0; + return AtomicAccess::load_acquire(&_head) == 0; } u4 JfrCPUTimeTraceQueue::lost_samples() const { - return _lost_samples; + return AtomicAccess::load(&_lost_samples); } void JfrCPUTimeTraceQueue::increment_lost_samples() { @@ -136,7 +143,7 @@ void JfrCPUTimeTraceQueue::increment_lost_samples() { } void JfrCPUTimeTraceQueue::increment_lost_samples_due_to_queue_full() { - _lost_samples_due_to_queue_full++; + AtomicAccess::inc(&_lost_samples_due_to_queue_full); } u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples() { @@ -144,9 +151,7 @@ u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples() { } u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples_due_to_queue_full() { - u4 lost = _lost_samples_due_to_queue_full; - _lost_samples_due_to_queue_full = 0; - return lost; + return AtomicAccess::xchg(&_lost_samples_due_to_queue_full, (u4)0); } void JfrCPUTimeTraceQueue::init() { @@ -154,7 +159,7 @@ void JfrCPUTimeTraceQueue::init() { } void JfrCPUTimeTraceQueue::clear() { - _head = 0; + AtomicAccess::release_store(&_head, (u4)0); } void JfrCPUTimeTraceQueue::resize_if_needed() { @@ -162,8 +167,9 @@ void JfrCPUTimeTraceQueue::resize_if_needed() { if (lost_samples_due_to_queue_full == 0) { return; } - if (_capacity < CPU_TIME_QUEUE_MAX_CAPACITY) { - float ratio = (float)lost_samples_due_to_queue_full / (float)_capacity; + u4 capacity = AtomicAccess::load(&_capacity); + if (capacity < CPU_TIME_QUEUE_MAX_CAPACITY) { + float ratio = (float)lost_samples_due_to_queue_full / (float)capacity; int factor = 1; if (ratio > 8) { // idea is to quickly scale the queue in the worst case factor = ratio; @@ -175,7 +181,7 @@ void JfrCPUTimeTraceQueue::resize_if_needed() { factor = 2; } if (factor > 1) { - u4 new_capacity = MIN2(CPU_TIME_QUEUE_MAX_CAPACITY, _capacity * factor); + u4 new_capacity = MIN2(CPU_TIME_QUEUE_MAX_CAPACITY, capacity * factor); set_capacity(new_capacity); } } diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp index 48fe28d22f0..e7c915fc8be 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp @@ -43,24 +43,19 @@ struct JfrCPUTimeSampleRequest { // Fixed size async-signal-safe SPSC linear queue backed by an array. // Designed to be only used under lock and read linearly -// The lock in question is the tri-state CPU time JFR lock in JfrThreadLocal -// This allows us to skip most of the atomic accesses and memory barriers, -// holding a lock acts as a memory barrier -// Only the _lost_samples property is atomic, as it can be accessed even after -// acquiring the lock failed. -// Important to note is that the queue is also only accessed under lock in signal -// handlers. class JfrCPUTimeTraceQueue { + // the default queue capacity, scaled if the sampling period is smaller than 10ms + // when the thread is started + static const u4 CPU_TIME_QUEUE_CAPACITY = 500; + JfrCPUTimeSampleRequest* _data; - u4 _capacity; + volatile u4 _capacity; // next unfilled index - u4 _head; + volatile u4 _head; - // the only property accessible without a lock volatile u4 _lost_samples; - - u4 _lost_samples_due_to_queue_full; + volatile u4 _lost_samples_due_to_queue_full; static const u4 CPU_TIME_QUEUE_INITIAL_CAPACITY = 20; static const u4 CPU_TIME_QUEUE_MAX_CAPACITY = 2000; @@ -87,7 +82,6 @@ public: u4 lost_samples() const; - // the only method callable without holding a lock void increment_lost_samples(); void increment_lost_samples_due_to_queue_full(); From 1e49376ece39e8f9b5c72b58688b1e195a0014be Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Mon, 27 Oct 2025 15:09:59 +0000 Subject: [PATCH 0027/1004] 8368321: Rethink compilation delay strategy for lukewarm methods Reviewed-by: kvn, vlivanov --- .../share/compiler/compilationPolicy.cpp | 65 ++++++++++++------- .../share/compiler/compilationPolicy.hpp | 9 +-- .../share/compiler/compiler_globals.hpp | 7 -- src/hotspot/share/oops/trainingData.cpp | 3 + src/hotspot/share/oops/trainingData.hpp | 7 ++ 5 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 177fd04fbbc..1cc44602186 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -423,13 +423,16 @@ void CompilationPolicy::print_counters_on(outputStream* st, const char* prefix, st->print(" %smax levels=%d,%d", prefix, m->highest_comp_level(), m->highest_osr_comp_level()); } -void CompilationPolicy::print_training_data_on(outputStream* st, const char* prefix, Method* method) { +void CompilationPolicy::print_training_data_on(outputStream* st, const char* prefix, Method* method, CompLevel cur_level) { methodHandle m(Thread::current(), method); st->print(" %smtd: ", prefix); MethodTrainingData* mtd = MethodTrainingData::find(m); if (mtd == nullptr) { st->print("null"); } else { + if (should_delay_standard_transition(m, cur_level, mtd)) { + st->print("delayed, "); + } MethodData* md = mtd->final_profile(); st->print("mdo="); if (md == nullptr) { @@ -536,9 +539,9 @@ void CompilationPolicy::print_event_on(outputStream *st, EventType type, Method* st->print("in-queue"); } else st->print("idle"); - print_training_data_on(st, "", m); + print_training_data_on(st, "", m, level); if (inlinee_event) { - print_training_data_on(st, "inlinee ", im); + print_training_data_on(st, "inlinee ", im, level); } } st->print_cr("]"); @@ -1153,7 +1156,7 @@ CompLevel CompilationPolicy::trained_transition_from_none(const methodHandle& me // Now handle the case of level 4. assert(highest_training_level == CompLevel_full_optimization, "Unexpected compilation level: %d", highest_training_level); if (!training_has_profile) { - // The method was a part of a level 4 compile, but don't have a stored profile, + // The method was a part of a level 4 compile, but doesn't have a stored profile, // we need to profile it. return CompLevel_full_profile; } @@ -1308,33 +1311,53 @@ CompLevel CompilationPolicy::common(const methodHandle& method, CompLevel cur_le if (mtd == nullptr) { // We haven't see compilations of this method in training. It's either very cold or the behavior changed. // Feed it to the standard TF with no profiling delay. - next_level = standard_transition(method, cur_level, false /*delay_profiling*/, disable_feedback); + next_level = standard_transition(method, cur_level, disable_feedback); } else { next_level = trained_transition(method, cur_level, mtd, THREAD); - if (cur_level == next_level) { + if (cur_level == next_level && !should_delay_standard_transition(method, cur_level, mtd)) { // trained_transtion() is going to return the same level if no startup/warmup optimizations apply. // In order to catch possible pathologies due to behavior change we feed the event to the regular // TF but with profiling delay. - next_level = standard_transition(method, cur_level, true /*delay_profiling*/, disable_feedback); + next_level = standard_transition(method, cur_level, disable_feedback); } } } else { - next_level = standard_transition(method, cur_level, false /*delay_profiling*/, disable_feedback); + next_level = standard_transition(method, cur_level, disable_feedback); } return (next_level != cur_level) ? limit_level(next_level) : next_level; } +bool CompilationPolicy::should_delay_standard_transition(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd) { + precond(mtd != nullptr); + CompLevel highest_training_level = static_cast(mtd->highest_top_level()); + if (highest_training_level != CompLevel_full_optimization && cur_level == CompLevel_limited_profile) { + // This is a lukewarm method - it hasn't been compiled with C2 during the tranining run and is currently + // running at level 2. Delay any further state changes until its counters exceed the training run counts. + MethodCounters* mc = method->method_counters(); + if (mc == nullptr) { + return false; + } + if (mc->invocation_counter()->carry() || mc->backedge_counter()->carry()) { + return false; + } + if (static_cast(mc->invocation_counter()->count()) <= mtd->invocation_count() && + static_cast(mc->backedge_counter()->count()) <= mtd->backedge_count()) { + return true; + } + } + return false; +} template -CompLevel CompilationPolicy::standard_transition(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) { +CompLevel CompilationPolicy::standard_transition(const methodHandle& method, CompLevel cur_level, bool disable_feedback) { CompLevel next_level = cur_level; switch(cur_level) { default: break; case CompLevel_none: - next_level = transition_from_none(method, cur_level, delay_profiling, disable_feedback); + next_level = transition_from_none(method, cur_level, disable_feedback); break; case CompLevel_limited_profile: - next_level = transition_from_limited_profile(method, cur_level, delay_profiling, disable_feedback); + next_level = transition_from_limited_profile(method, cur_level, disable_feedback); break; case CompLevel_full_profile: next_level = transition_from_full_profile(method, cur_level); @@ -1343,16 +1366,8 @@ CompLevel CompilationPolicy::standard_transition(const methodHandle& method, Com return next_level; } -template static inline bool apply_predicate(const methodHandle& method, CompLevel cur_level, int i, int b, bool delay_profiling, double delay_profiling_scale) { - if (delay_profiling) { - return Predicate::apply_scaled(method, cur_level, i, b, delay_profiling_scale); - } else { - return Predicate::apply(method, cur_level, i, b); - } -} - template -CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) { +CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, CompLevel cur_level, bool disable_feedback) { precond(cur_level == CompLevel_none); CompLevel next_level = cur_level; int i = method->invocation_count(); @@ -1360,7 +1375,7 @@ CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, Co // If we were at full profile level, would we switch to full opt? if (transition_from_full_profile(method, CompLevel_full_profile) == CompLevel_full_optimization) { next_level = CompLevel_full_optimization; - } else if (!CompilationModeFlag::disable_intermediate() && apply_predicate(method, cur_level, i, b, delay_profiling, Tier0ProfileDelayFactor)) { + } else if (!CompilationModeFlag::disable_intermediate() && Predicate::apply(method, cur_level, i, b)) { // C1-generated fully profiled code is about 30% slower than the limited profile // code that has only invocation and backedge counters. The observation is that // if C2 queue is large enough we can spend too much time in the fully profiled code @@ -1368,7 +1383,7 @@ CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, Co // we introduce a feedback on the C2 queue size. If the C2 queue is sufficiently long // we choose to compile a limited profiled version and then recompile with full profiling // when the load on C2 goes down. - if (delay_profiling || (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) > Tier3DelayOn * compiler_count(CompLevel_full_optimization))) { + if (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) > Tier3DelayOn * compiler_count(CompLevel_full_optimization)) { next_level = CompLevel_limited_profile; } else { next_level = CompLevel_full_profile; @@ -1397,7 +1412,7 @@ CompLevel CompilationPolicy::transition_from_full_profile(const methodHandle& me } template -CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) { +CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool disable_feedback) { precond(cur_level == CompLevel_limited_profile); CompLevel next_level = cur_level; int i = method->invocation_count(); @@ -1407,7 +1422,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& if (mdo->would_profile()) { if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <= Tier3DelayOff * compiler_count(CompLevel_full_optimization) && - apply_predicate(method, cur_level, i, b, delay_profiling, Tier2ProfileDelayFactor))) { + Predicate::apply(method, cur_level, i, b))) { next_level = CompLevel_full_profile; } } else { @@ -1417,7 +1432,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle& // If there is no MDO we need to profile if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <= Tier3DelayOff * compiler_count(CompLevel_full_optimization) && - apply_predicate(method, cur_level, i, b, delay_profiling, Tier2ProfileDelayFactor))) { + Predicate::apply(method, cur_level, i, b))) { next_level = CompLevel_full_profile; } } diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index d950ba418f9..3efc374d998 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -263,14 +263,15 @@ class CompilationPolicy : AllStatic { static CompLevel common(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD, bool disable_feedback = false); template - static CompLevel transition_from_none(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback); + static CompLevel transition_from_none(const methodHandle& method, CompLevel cur_level, bool disable_feedback); template - static CompLevel transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback); + static CompLevel transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, bool disable_feedback); template static CompLevel transition_from_full_profile(const methodHandle& method, CompLevel cur_level); template - static CompLevel standard_transition(const methodHandle& method, CompLevel cur_level, bool delayprof, bool disable_feedback); + static CompLevel standard_transition(const methodHandle& method, CompLevel cur_level, bool disable_feedback); + static bool should_delay_standard_transition(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd); static CompLevel trained_transition_from_none(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD); static CompLevel trained_transition_from_limited_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD); static CompLevel trained_transition_from_full_profile(const methodHandle& method, CompLevel cur_level, MethodTrainingData* mtd, JavaThread* THREAD); @@ -284,7 +285,7 @@ class CompilationPolicy : AllStatic { // level. static CompLevel loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD); static void print_counters_on(outputStream* st, const char* prefix, Method* m); - static void print_training_data_on(outputStream* st, const char* prefix, Method* method); + static void print_training_data_on(outputStream* st, const char* prefix, Method* method, CompLevel cur_level); // Has a method been long around? // We don't remove old methods from the compile queue even if they have // very low activity (see select_task()). diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp index 605c0c58869..3919c596d74 100644 --- a/src/hotspot/share/compiler/compiler_globals.hpp +++ b/src/hotspot/share/compiler/compiler_globals.hpp @@ -271,13 +271,6 @@ "Maximum rate sampling interval (in milliseconds)") \ range(1, max_intx) \ \ - product(double, Tier0ProfileDelayFactor, 100.0, DIAGNOSTIC, \ - "Delay profiling/compiling of methods that were " \ - "observed to be lukewarm") \ - \ - product(double, Tier2ProfileDelayFactor, 250.0, DIAGNOSTIC, \ - "Delay profiling of methods that were observed to be lukewarm") \ - \ product(bool, SkipTier2IfPossible, false, DIAGNOSTIC, \ "Compile at tier 4 instead of tier 2 in training replay " \ "mode if posssible") \ diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp index 24f82a53843..27ae3404f41 100644 --- a/src/hotspot/share/oops/trainingData.cpp +++ b/src/hotspot/share/oops/trainingData.cpp @@ -35,6 +35,7 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/method.hpp" +#include "oops/method.inline.hpp" #include "oops/methodCounters.hpp" #include "oops/trainingData.hpp" #include "runtime/arguments.hpp" @@ -352,6 +353,8 @@ void MethodTrainingData::prepare(Visitor& visitor) { _final_counters = holder()->method_counters(); _final_profile = holder()->method_data(); assert(_final_profile == nullptr || _final_profile->method() == holder(), ""); + _invocation_count = holder()->invocation_count(); + _backedge_count = holder()->backedge_count(); } for (int i = 0; i < CompLevel_count - 1; i++) { CompileTrainingData* ctd = _last_toplevel_compiles[i]; diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp index 7ab5e179eea..c549004e76e 100644 --- a/src/hotspot/share/oops/trainingData.hpp +++ b/src/hotspot/share/oops/trainingData.hpp @@ -748,6 +748,9 @@ class MethodTrainingData : public TrainingData { MethodCounters* _final_counters; MethodData* _final_profile; + int _invocation_count; + int _backedge_count; + MethodTrainingData(); MethodTrainingData(Method* method, KlassTrainingData* ktd) : TrainingData(method) { _klass = ktd; @@ -758,6 +761,8 @@ class MethodTrainingData : public TrainingData { _highest_top_level = CompLevel_none; _level_mask = 0; _was_toplevel = false; + _invocation_count = 0; + _backedge_count = 0; } static int level_mask(int level) { @@ -772,6 +777,8 @@ class MethodTrainingData : public TrainingData { bool saw_level(CompLevel l) const { return (_level_mask & level_mask(l)) != 0; } int highest_top_level() const { return _highest_top_level; } MethodData* final_profile() const { return _final_profile; } + int invocation_count() const { return _invocation_count; } + int backedge_count() const { return _backedge_count; } Symbol* name() const { precond(has_holder()); From 583ff202b1cc1f018d798a34d93359301840cf06 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Mon, 27 Oct 2025 16:15:10 +0000 Subject: [PATCH 0028/1004] 8370251: C2: Inlining checks for method handle intrinsics are too strict Reviewed-by: kvn, roland --- src/hotspot/share/opto/doCall.cpp | 12 +- .../jtreg/compiler/jsr292/MHInlineTest.java | 157 +++++++++++++----- 2 files changed, 126 insertions(+), 43 deletions(-) diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index ad7b64f93f5..754b0fa8d1c 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -102,6 +102,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool (orig_callee->intrinsic_id() == vmIntrinsics::_linkToVirtual) || (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface); + const bool check_access = !orig_callee->is_method_handle_intrinsic(); // method handle intrinsics don't perform access checks + // Dtrace currently doesn't work unless all calls are vanilla if (env()->dtrace_method_probes()) { allow_inline = false; @@ -239,7 +241,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool // this invoke because it may lead to bimorphic inlining which // a speculative type should help us avoid. receiver_method = callee->resolve_invoke(jvms->method()->holder(), - speculative_receiver_type); + speculative_receiver_type, + check_access); if (receiver_method == nullptr) { speculative_receiver_type = nullptr; } else { @@ -256,8 +259,9 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool (morphism == 2 && UseBimorphicInlining))) { // receiver_method = profile.method(); // Profiles do not suggest methods now. Look it up in the major receiver. + assert(check_access, "required"); receiver_method = callee->resolve_invoke(jvms->method()->holder(), - profile.receiver(0)); + profile.receiver(0)); } if (receiver_method != nullptr) { // The single majority receiver sufficiently outweighs the minority. @@ -268,8 +272,9 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool CallGenerator* next_hit_cg = nullptr; ciMethod* next_receiver_method = nullptr; if (morphism == 2 && UseBimorphicInlining) { + assert(check_access, "required"); next_receiver_method = callee->resolve_invoke(jvms->method()->holder(), - profile.receiver(1)); + profile.receiver(1)); if (next_receiver_method != nullptr) { next_hit_cg = this->call_generator(next_receiver_method, vtable_index, !call_does_dispatch, jvms, @@ -342,6 +347,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool if (singleton != nullptr) { assert(singleton != declared_interface, "not a unique implementor"); + assert(check_access, "required"); ciMethod* cha_monomorphic_target = callee->find_monomorphic_target(caller->holder(), declared_interface, singleton); diff --git a/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java b/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java index a8e76f5797d..4638c27fe59 100644 --- a/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java +++ b/test/hotspot/jtreg/compiler/jsr292/MHInlineTest.java @@ -51,7 +51,8 @@ public class MHInlineTest { "-XX:+IgnoreUnrecognizedVMOptions", "-showversion", "-XX:-TieredCompilation", "-Xbatch", "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining", - "-XX:CompileCommand=dontinline,compiler.jsr292.MHInlineTest::test*", + "-XX:CompileCommand=quiet", + "-XX:CompileCommand=compileonly,compiler.jsr292.MHInlineTest::test*", Launcher.class.getName()); OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); @@ -60,13 +61,17 @@ public class MHInlineTest { // The test is applicable only to C2 (present in Server VM). if (analyzer.getStderr().contains("Server VM")) { + analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_final_x (3 bytes) inline (hot)"); + analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::private_x (3 bytes) inline (hot)"); + analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_static_x (3 bytes) inline (hot)"); + analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::public_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::protected_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::package_x (3 bytes) inline (hot)"); - analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_final_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::private_x (3 bytes) inline (hot)"); analyzer.shouldContain("compiler.jsr292.MHInlineTest$B::private_static_x (3 bytes) inline (hot)"); - analyzer.shouldContain("compiler.jsr292.MHInlineTest$A::package_static_x (3 bytes) inline (hot)"); + + analyzer.shouldNotContain("failed to inline"); } else { throw new SkippedException("The test is applicable only to C2 (present in Server VM)"); } @@ -75,23 +80,24 @@ public class MHInlineTest { static class A { public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - public Class public_x() { return A.class; } - protected Class protected_x() { return A.class; } - Class package_x() { return A.class; } - final Class package_final_x() { return A.class; } + public Class public_x() { return A.class; } + protected Class protected_x() { return A.class; } + /*package*/ Class package_x() { return A.class; } + /*package*/ final Class package_final_x() { return A.class; } + private Class private_x() { return A.class; } - static Class package_static_x() { return A.class; } + /*package*/ static Class package_static_x() { return A.class; } } static class B extends A { public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - @Override public Class public_x() { return B.class; } - @Override protected Class protected_x() { return B.class; } - @Override Class package_x() { return B.class; } + @Override public Class public_x() { return B.class; } + @Override protected Class protected_x() { return B.class; } + @Override /*package*/ Class package_x() { return B.class; } - private Class private_x() { return B.class; } - static Class private_static_x() { return B.class; } + private Class private_x() { return B.class; } + private static Class private_static_x() { return B.class; } } static final MethodHandle A_PUBLIC_X; @@ -99,6 +105,7 @@ public class MHInlineTest { static final MethodHandle A_PACKAGE_X; static final MethodHandle A_PACKAGE_STATIC_X; static final MethodHandle A_PACKAGE_FINAL_X; + static final MethodHandle A_PRIVATE_X; static final MethodHandle B_PRIVATE_X; static final MethodHandle B_PRIVATE_STATIC_X; @@ -117,6 +124,8 @@ public class MHInlineTest { A.class, "package_final_x", MethodType.methodType(Class.class)); A_PACKAGE_STATIC_X = LOOKUP.findStatic( A.class, "package_static_x", MethodType.methodType(Class.class)); + A_PRIVATE_X = LOOKUP.findVirtual( + A.class, "private_x", MethodType.methodType(Class.class)); B_PRIVATE_X = B.LOOKUP.findVirtual( B.class, "private_x", MethodType.methodType(Class.class)); @@ -129,7 +138,18 @@ public class MHInlineTest { static final A a = new B(); - private static void testPublicMH() { + /* ============== public Class public_x() ============== */ + + private static void testPublicMH(A recv) { + try { + Class r = (Class)A_PUBLIC_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPublicMHConst() { try { Class r = (Class)A_PUBLIC_X.invokeExact(a); assertEquals(r, B.class); @@ -138,7 +158,18 @@ public class MHInlineTest { } } - private static void testProtectedMH() { + /* ============== protected Class protected_x() ============== */ + + private static void testProtectedMH(A recv) { + try { + Class r = (Class)A_PROTECTED_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testProtectedMHConst() { try { Class r = (Class)A_PROTECTED_X.invokeExact(a); assertEquals(r, B.class); @@ -147,7 +178,18 @@ public class MHInlineTest { } } - private static void testPackageMH() { + /* ============== Class package_x() ============== */ + + private static void testPackageMH(A recv) { + try { + Class r = (Class)A_PACKAGE_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPackageMHConst() { try { Class r = (Class)A_PACKAGE_X.invokeExact(a); assertEquals(r, B.class); @@ -156,7 +198,9 @@ public class MHInlineTest { } } - private static void testPackageFinalMH() { + /* ============== final Class package_final_x() ============== */ + + private static void testPackageFinalMH(A recv) { try { Class r = (Class)A_PACKAGE_FINAL_X.invokeExact(a); assertEquals(r, A.class); @@ -165,16 +209,45 @@ public class MHInlineTest { } } - private static void testPackageStaticMH() { + private static void testPackageFinalMHConst() { try { - Class r = (Class)A_PACKAGE_STATIC_X.invokeExact(); + Class r = (Class)A_PACKAGE_FINAL_X.invokeExact(a); assertEquals(r, A.class); } catch (Throwable throwable) { throw new Error(throwable); } } - private static void testPrivateMH() { + /* ============== private Class private_x() ============== */ + + private static void testPrivateA_MH(A recv) { + try { + Class r = (Class)A_PRIVATE_X.invokeExact(recv); + assertEquals(r, A.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPrivateA_MHConst() { + try { + Class r = (Class)A_PRIVATE_X.invokeExact(a); + assertEquals(r, A.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPrivateB_MH(B recv) { + try { + Class r = (Class)B_PRIVATE_X.invokeExact(recv); + assertEquals(r, B.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + private static void testPrivateB_MHConst() { try { Class r = (Class)B_PRIVATE_X.invokeExact((B)a); assertEquals(r, B.class); @@ -183,7 +256,19 @@ public class MHInlineTest { } } - private static void testPrivateStaticMH() { + /* ============== static ============== */ + + private static void testPackageStaticMHConst() { + try { + Class r = (Class)A_PACKAGE_STATIC_X.invokeExact(); + assertEquals(r, A.class); + } catch (Throwable throwable) { + throw new Error(throwable); + } + } + + + private static void testPrivateStaticMHConst() { try { Class r = (Class)B_PRIVATE_STATIC_X.invokeExact(); assertEquals(r, B.class); @@ -192,28 +277,20 @@ public class MHInlineTest { } } + /* ====================================================================== */ + static class Launcher { public static void main(String[] args) throws Exception { for (int i = 0; i < 20_000; i++) { - testPublicMH(); - } - for (int i = 0; i < 20_000; i++) { - testProtectedMH(); - } - for (int i = 0; i < 20_000; i++) { - testPackageMH(); - } - for (int i = 0; i < 20_000; i++) { - testPackageFinalMH(); - } - for (int i = 0; i < 20_000; i++) { - testPackageStaticMH(); - } - for (int i = 0; i < 20_000; i++) { - testPrivateMH(); - } - for (int i = 0; i < 20_000; i++) { - testPrivateStaticMH(); + testPublicMH(a); testPublicMHConst(); + testProtectedMH(a); testProtectedMHConst(); + testPackageMH(a); testPackageMHConst(); + testPackageFinalMH(a); testPackageFinalMHConst(); + testPrivateA_MH(a); testPrivateA_MHConst(); + testPrivateB_MH((B)a); testPrivateB_MHConst(); + + testPackageStaticMHConst(); + testPrivateStaticMHConst(); } } } From ebf9c5bfc1b2e8e9210cc37283a29d471f913916 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Mon, 27 Oct 2025 16:40:17 +0000 Subject: [PATCH 0029/1004] 8370250: Locale should mention the behavior for duplicate subtags Reviewed-by: naoto --- .../share/classes/java/util/Locale.java | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index a55ddee648e..452b621ea05 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -204,15 +204,18 @@ import sun.util.locale.provider.TimeZoneNameUtility; * key="x"/value="java-1-7" * * - * BCP 47 deviation: Although BCP 47 requires field values to be registered - * in the IANA Language Subtag Registry, the {@code Locale} class - * does not validate this requirement. For example, the variant code "foobar" - * is well-formed since it is composed of 5 to 8 alphanumerics, but is not defined - * the IANA Language Subtag Registry. The {@link Builder} - * only checks if an individual field satisfies the syntactic - * requirement (is well-formed), but does not validate the value - * itself. Conversely, {@link #of(String, String, String) Locale::of} and its - * overloads do not make any syntactic checks on the input. + * BCP 47 deviation: BCP47 defines the following two levels of + * conformance, + * "valid" and "well-formed". A valid tag requires that it is well-formed, its + * subtag values are registered in the IANA Language Subtag Registry, and it does not + * contain duplicate variant or extension singleton subtags. The {@code Locale} + * class does not enforce that subtags are registered in the Subtag Registry. + * {@link Builder} only checks if an individual field satisfies the syntactic + * requirement (is well-formed). When passed duplicate variants, {@code Builder} + * accepts and includes them. When passed duplicate extension singletons, {@code + * Builder} accepts but ignores the duplicate key and its associated value. + * Conversely, {@link #of(String, String, String) Locale::of} and its + * overloads do not check if the input is well-formed at all. * *

Unicode BCP 47 U Extension

* @@ -246,7 +249,11 @@ import sun.util.locale.provider.TimeZoneNameUtility; * can be empty, or a series of subtags 3-8 alphanums in length). A * well-formed locale attribute has the form * {@code [0-9a-zA-Z]{3,8}} (it is a single subtag with the same - * form as a locale type subtag). + * form as a locale type subtag). Duplicate locale attributes as well + * as locale keys do not convey meaning. For methods in {@code Locale} and + * {@code Locale.Builder} that accept extensions, occurrences of duplicate + * locale attributes as well as locale keys and their associated type are accepted + * but ignored. * *

The Unicode locale extension specifies optional behavior in * locale-sensitive services. Although the LDML specification defines @@ -561,6 +568,8 @@ import sun.util.locale.provider.TimeZoneNameUtility; * RFC 4647: Matching of Language Tags * @spec https://www.rfc-editor.org/info/rfc5646 * RFC 5646: Tags for Identifying Languages + * @spec https://www.rfc-editor.org/info/rfc6067 + * RFC 6067: BCP 47 Extension U * @spec https://www.unicode.org/reports/tr35 * Unicode Locale Data Markup Language (LDML) * @see Builder @@ -1743,6 +1752,12 @@ public final class Locale implements Cloneable, Serializable { * to {@link Locale.Builder#setLanguageTag(String)} which throws an exception * in this case. * + *

Duplicate variants are accepted and included by the builder. + * However, duplicate extension singleton keys and their associated type + * are accepted but ignored. The same behavior applies to duplicate locale + * keys and attributes within a U extension. Note that subsequent subtags after + * the occurrence of a duplicate are not ignored. + * *

The following conversions are performed: