From 9593730a23f465d26ba7b310d5b0c5d3b4ee4326 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 11 Aug 2025 23:45:24 +0000 Subject: [PATCH 001/112] 8362376: Use @Stable annotation in Java FDLIBM implementation Reviewed-by: liach, rgiulietti --- .../share/classes/java/lang/FdLibm.java | 51 +++++++++++-------- test/jdk/java/lang/StrictMath/ExpTests.java | 8 ++- test/jdk/java/lang/StrictMath/PowTests.java | 19 ++++++- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/java.base/share/classes/java/lang/FdLibm.java b/src/java.base/share/classes/java/lang/FdLibm.java index 70d728a16db..73e1da46af4 100644 --- a/src/java.base/share/classes/java/lang/FdLibm.java +++ b/src/java.base/share/classes/java/lang/FdLibm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.lang; +import jdk.internal.vm.annotation.Stable; + /** * Port of the "Freely Distributable Math Library", version 5.3, from * C to Java. @@ -451,8 +453,10 @@ final class FdLibm { */ private static final double pio4 = 0x1.921fb54442d18p-1, // 7.85398163397448278999e-01 - pio4lo= 0x1.1a62633145c07p-55, // 3.06161699786838301793e-17 - T[] = { + pio4lo= 0x1.1a62633145c07p-55; // 3.06161699786838301793e-17 + @Stable + private static final double[] + T = { 0x1.5555555555563p-2, // 3.33333333333334091986e-01 0x1.111111110fe7ap-3, // 1.33333333333201242699e-01 0x1.ba1ba1bb341fep-5, // 5.39682539762260521377e-02 @@ -546,6 +550,7 @@ final class FdLibm { /* * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi */ + @Stable private static final int[] two_over_pi = { 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, @@ -560,6 +565,7 @@ final class FdLibm { 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, }; + @Stable private static final int[] npio2_hw = { 0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C, 0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C, @@ -807,8 +813,10 @@ final class FdLibm { * to produce the hexadecimal values shown. */ + @Stable private static final int init_jk[] = {2, 3, 4, 6}; // initial value for jk + @Stable private static final double PIo2[] = { 0x1.921fb4p0, // 1.57079625129699707031e+00 0x1.4442dp-24, // 7.54978941586159635335e-08 @@ -1232,6 +1240,7 @@ final class FdLibm { static final class Atan { private Atan() {throw new UnsupportedOperationException();} + @Stable private static final double atanhi[] = { 0x1.dac670561bb4fp-2, // atan(0.5)hi 4.63647609000806093515e-01 0x1.921fb54442d18p-1, // atan(1.0)hi 7.85398163397448278999e-01 @@ -1239,6 +1248,7 @@ final class FdLibm { 0x1.921fb54442d18p0, // atan(inf)hi 1.57079632679489655800e+00 }; + @Stable private static final double atanlo[] = { 0x1.a2b7f222f65e2p-56, // atan(0.5)lo 2.26987774529616870924e-17 0x1.1a62633145c07p-55, // atan(1.0)lo 3.06161699786838301793e-17 @@ -1246,6 +1256,7 @@ final class FdLibm { 0x1.1a62633145c07p-54, // atan(inf)lo 6.12323399573676603587e-17 }; + @Stable private static final double aT[] = { 0x1.555555555550dp-2, // 3.33333333333329318027e-01 -0x1.999999998ebc4p-3, // -1.99999999998764832476e-01 @@ -2245,12 +2256,8 @@ final class FdLibm { // Compute ss = s_h + s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) - final double BP[] = {1.0, - 1.5}; - final double DP_H[] = {0.0, - 0x1.2b80_34p-1}; // 5.84962487220764160156e-01 - final double DP_L[] = {0.0, - 0x1.cfde_b43c_fd006p-27};// 1.35003920212974897128e-08 + final double DP_H = 0x1.2b80_34p-1; // 5.84962487220764160156e-01 + final double DP_L = 0x1.cfde_b43c_fd006p-27; // 1.35003920212974897128e-08 // Poly coefs for (3/2)*(log(x)-2s-2/3*s**3 final double L1 = 0x1.3333_3333_33303p-1; // 5.99999999999994648725e-01 @@ -2259,15 +2266,17 @@ final class FdLibm { final double L4 = 0x1.1746_0a91_d4101p-2; // 2.72728123808534006489e-01 final double L5 = 0x1.d864_a93c_9db65p-3; // 2.30660745775561754067e-01 final double L6 = 0x1.a7e2_84a4_54eefp-3; // 2.06975017800338417784e-01 - u = x_abs - BP[k]; // BP[0]=1.0, BP[1]=1.5 - v = 1.0 / (x_abs + BP[k]); + + double BP_k = 1.0 + 0.5*k; // BP[0]=1.0, BP[1]=1.5 + u = x_abs - BP_k; + v = 1.0 / (x_abs + BP_k); ss = u * v; s_h = ss; s_h = __LO(s_h, 0); // t_h=x_abs + BP[k] High t_h = 0.0; t_h = __HI(t_h, ((ix >> 1) | 0x20000000) + 0x00080000 + (k << 18) ); - t_l = x_abs - (t_h - BP[k]); + t_l = x_abs - (t_h - BP_k); s_l = v * ((u - s_h * t_h) - s_h * t_l); // Compute log(x_abs) s2 = ss * ss; @@ -2285,12 +2294,12 @@ final class FdLibm { p_h = __LO(p_h, 0); p_l = v - (p_h - u); z_h = CP_H * p_h; // CP_H + CP_L = 2/(3*log2) - z_l = CP_L * p_h + p_l * CP + DP_L[k]; + z_l = CP_L * p_h + p_l * CP + DP_L*k; // log2(x_abs) = (ss + ..)*2/(3*log2) = n + DP_H + z_h + z_l t = (double)n; - t1 = (((z_h + z_l) + DP_H[k]) + t); + t1 = (((z_h + z_l) + DP_H*k) + t); t1 = __LO(t1, 0); - t2 = z_l - (((t1 - t) - DP_H[k]) - z_h); + t2 = z_l - (((t1 - t) - DP_H*k) - z_h); } // Split up y into (y1 + y2) and compute (y1 + y2) * (t1 + t2) @@ -2430,13 +2439,13 @@ final class FdLibm { static final class Exp { private Exp() {throw new UnsupportedOperationException();} - private static final double[] half = {0.5, -0.5,}; private static final double huge = 1.0e+300; private static final double twom1000= 0x1.0p-1000; // 9.33263618503218878990e-302 = 2^-1000 private static final double o_threshold= 0x1.62e42fefa39efp9; // 7.09782712893383973096e+02 private static final double u_threshold= -0x1.74910d52d3051p9; // -7.45133219101941108420e+02; - private static final double[] ln2HI ={ 0x1.62e42feep-1, // 6.93147180369123816490e-01 - -0x1.62e42feep-1}; // -6.93147180369123816490e-01 + private static final double ln2HI = 0x1.62e42feep-1; // 6.93147180369123816490e-01 + + @Stable private static final double[] ln2LO ={ 0x1.a39ef35793c76p-33, // 1.90821492927058770002e-10 -0x1.a39ef35793c76p-33}; // -1.90821492927058770002e-10 private static final double invln2 = 0x1.71547652b82fep0; // 1.44269504088896338700e+00 @@ -2478,13 +2487,13 @@ final class FdLibm { /* argument reduction */ if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ - hi = x - ln2HI[xsb]; + hi = x - ln2HI*(1 - 2*xsb); /* +/- ln2HI */ lo=ln2LO[xsb]; k = 1 - xsb - xsb; } else { - k = (int)(invln2 * x + half[xsb]); + k = (int)(invln2 * x + 0.5 * (1 - 2*xsb) /* +/- 0.5 */ ); t = k; - hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + hi = x - t*ln2HI; /* t*ln2HI is exact here */ lo = t*ln2LO[0]; } x = hi - lo; diff --git a/test/jdk/java/lang/StrictMath/ExpTests.java b/test/jdk/java/lang/StrictMath/ExpTests.java index 9f7d2d1ffd0..c36314a48fa 100644 --- a/test/jdk/java/lang/StrictMath/ExpTests.java +++ b/test/jdk/java/lang/StrictMath/ExpTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8139688 + * @bug 8139688 8362376 * @key randomness * @library /test/lib * @build jdk.test.lib.RandomFactory @@ -87,6 +87,10 @@ public class ExpTests { {-0x1.49f33ad2c1c58p+9, 0x1.f3ccc815431b6p-953}, {+0x1.fce66609f7428p+5, 0x1.b59724cb0bc4cp91}, {-0x1.49f33ad2c1c58p+9, 0x1.f3ccc815431b6p-953}, + + // Test values near 0.5*ln*(2) and 1.5*ln(2) to check refactoring + {0.34657359027997275, 1.4142135623730951}, + {1.0397207708399183, 2.828427124746191}, }; for(double[] testCase: testCases) diff --git a/test/jdk/java/lang/StrictMath/PowTests.java b/test/jdk/java/lang/StrictMath/PowTests.java index 2936dfdf247..4b68c5bdcd6 100644 --- a/test/jdk/java/lang/StrictMath/PowTests.java +++ b/test/jdk/java/lang/StrictMath/PowTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8136874 + * @bug 8136874 8362376 * @summary Tests for StrictMath.pow */ @@ -283,6 +283,21 @@ public class PowTests { 0x1.ffffffffffd9fp1023, // 1.7976931348621944E308 }, + // Check refactoring, abs(y) < 2^31, x < sqrt(3/2), x < sqrt(3) + {1.2, // x < sqrt(3/2) + 5.0, + 2.4883199999999994 + }, + + {1.4142135623730951, // sqrt(3/2) < x < sqrt(3) + 5.0, + 5.656854249492382 + }, + + {2.23606797749979, // x > sqrt(3) + 5.0, + 55.901699437494756 + }, }; for (double[] testCase: testCases) From 6927fc3904eb239bd43ab7c581d479c00a6a4af2 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 12 Aug 2025 01:25:35 +0000 Subject: [PATCH 002/112] 8365200: RISC-V: compiler/loopopts/superword/TestGeneralizedReductions.java fails with Zvbb and vlen=128 Reviewed-by: fyang, fjiang --- .../loopopts/superword/TestGeneralizedReductions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java index bda0979a70b..6c2f0d825b9 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java @@ -160,13 +160,13 @@ public class TestGeneralizedReductions { @Test @IR(applyIfCPUFeatureOr = {"avx2", "true"}, - applyIfAnd = {"SuperWordReductions", "true","UsePopCountInstruction", "true"}, + applyIfAnd = {"SuperWordReductions", "true", "UsePopCountInstruction", "true"}, applyIfPlatform = {"64-bit", "true"}, counts = {IRNode.ADD_REDUCTION_VI, ">= 1", IRNode.POPCOUNT_VL, ">= 1"}) @IR(applyIfPlatform = {"riscv64", "true"}, applyIfCPUFeatureOr = {"zvbb", "true"}, - applyIfAnd = {"SuperWordReductions", "true","UsePopCountInstruction", "true"}, + applyIfAnd = {"SuperWordReductions", "true", "UsePopCountInstruction", "true", "MaxVectorSize", ">=32"}, counts = {IRNode.ADD_REDUCTION_VI, ">= 1", IRNode.POPCOUNT_VL, ">= 1"}) private static long testMapReductionOnGlobalAccumulator(long[] array) { From 72d3a2a9773b2a3fe0351e0acb7b10c0751d23d8 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 12 Aug 2025 03:15:49 +0000 Subject: [PATCH 003/112] 8308349: missing working directory option for launcher when invoked from shortcuts Reviewed-by: almatvee --- .../jpackage/internal/DesktopIntegration.java | 18 ++ .../jpackage/internal/LinuxFromParams.java | 8 +- .../internal/resources/template.desktop | 1 + .../jdk/jpackage/internal/Arguments.java | 41 ++- .../jdk/jpackage/internal/FromParams.java | 30 ++- .../LauncherShortcutStartupDirectory.java | 9 +- .../jpackage/internal/model/ParseUtils.java | 59 +++++ .../resources/MainResources.properties | 2 + .../jdk/jpackage/internal/WinFromParams.java | 4 +- .../internal/WixAppImageFragmentBuilder.java | 3 + .../jdk/jpackage/test/AdditionalLauncher.java | 4 + .../jdk/jpackage/test/LauncherShortcut.java | 12 +- .../jdk/jpackage/test/LinuxHelper.java | 3 + .../jpackage/test/WinShortcutVerifier.java | 12 +- .../jpackage/share/AddLShortcutTest.java | 242 ++++++++++++------ test/jdk/tools/jpackage/share/ErrorTest.java | 12 + 16 files changed, 352 insertions(+), 108 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ParseUtils.java diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java index 476ca3201ce..dad9917c48f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -46,6 +46,7 @@ import javax.imageio.ImageIO; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import jdk.jpackage.internal.model.FileAssociation; +import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LinuxLauncher; import jdk.jpackage.internal.model.LinuxPackage; import jdk.jpackage.internal.model.Package; @@ -237,6 +238,23 @@ final class DesktopIntegration extends ShellCustomAction { data.put("DEPLOY_BUNDLE_CATEGORY", pkg.menuGroupName()); data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo( installedLayout.launchersDirectory().resolve(launcher.executableNameWithSuffix()).toString())); + data.put("STARTUP_DIRECTORY", launcher.shortcut() + .flatMap(LauncherShortcut::startupDirectory) + .map(startupDirectory -> { + switch (startupDirectory) { + case DEFAULT -> { + return (Path)null; + } + case APP_DIR -> { + return installedLayout.appDirectory(); + } + default -> { + throw new AssertionError(); + } + } + }).map(str -> { + return "Path=" + str; + }).orElse(null)); return data; } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java index 6967dea111e..ced77b1aa68 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java @@ -109,12 +109,8 @@ final class LinuxFromParams { static final BundlerParamInfo DEB_PACKAGE = createPackageBundlerParam( LinuxFromParams::createLinuxDebPackage); - private static final BundlerParamInfo LINUX_SHORTCUT_HINT = new BundlerParamInfo<>( - Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId(), - Boolean.class, - params -> false, - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? false : Boolean.valueOf(s) - ); + private static final BundlerParamInfo LINUX_SHORTCUT_HINT = createStringBundlerParam( + Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId()); private static final BundlerParamInfo LINUX_CATEGORY = createStringBundlerParam( Arguments.CLIOptions.LINUX_CATEGORY.getId()); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop index bd645b77669..de7df568454 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop @@ -2,6 +2,7 @@ Name=APPLICATION_NAME Comment=APPLICATION_DESCRIPTION Exec=APPLICATION_LAUNCHER +STARTUP_DIRECTORY Icon=APPLICATION_ICON Terminal=false Type=Application diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java index 4700231a162..f9a5429a8bf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java @@ -37,6 +37,7 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.ResourceBundle; @@ -348,16 +349,13 @@ public class Arguments { WIN_UPDATE_URL ("win-update-url", OptionCategories.PLATFORM_WIN), - WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-menu", true); - }), + WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, + createArgumentWithOptionalValueAction("win-menu")), WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN), - WIN_SHORTCUT_HINT ("win-shortcut", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-shortcut", true); - }), + WIN_SHORTCUT_HINT ("win-shortcut", OptionCategories.PLATFORM_WIN, + createArgumentWithOptionalValueAction("win-shortcut")), WIN_SHORTCUT_PROMPT ("win-shortcut-prompt", OptionCategories.PLATFORM_WIN, () -> { @@ -396,10 +394,8 @@ public class Arguments { LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps", OptionCategories.PLATFORM_LINUX), - LINUX_SHORTCUT_HINT ("linux-shortcut", - OptionCategories.PLATFORM_LINUX, () -> { - setOptionValue("linux-shortcut", true); - }), + LINUX_SHORTCUT_HINT ("linux-shortcut", OptionCategories.PLATFORM_LINUX, + createArgumentWithOptionalValueAction("linux-shortcut")), LINUX_MENU_GROUP ("linux-menu-group", OptionCategories.PLATFORM_LINUX); @@ -478,9 +474,32 @@ public class Arguments { context().pos++; } + private static void prevArg() { + Objects.checkIndex(context().pos, context().argList.size()); + context().pos--; + } + private static boolean hasNextArg() { return context().pos < context().argList.size(); } + + private static Runnable createArgumentWithOptionalValueAction(String option) { + Objects.requireNonNull(option); + return () -> { + nextArg(); + if (hasNextArg()) { + var value = getArg(); + if (value.startsWith("-")) { + prevArg(); + setOptionValue(option, true); + } else { + setOptionValue(option, value); + } + } else { + setOptionValue(option, true); + } + }; + } } enum OptionCategories { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java index 34818fafc94..d4ea595969a 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java @@ -52,6 +52,7 @@ import static jdk.jpackage.internal.StandardBundlerParam.VENDOR; import static jdk.jpackage.internal.StandardBundlerParam.VERSION; import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage; import static jdk.jpackage.internal.StandardBundlerParam.isRuntimeInstaller; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; import java.nio.file.Path; @@ -69,6 +70,7 @@ import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.ParseUtils; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.function.ThrowingFunction; @@ -171,11 +173,11 @@ final class FromParams { } static Optional findLauncherShortcut( - BundlerParamInfo shortcutParam, + BundlerParamInfo shortcutParam, Map mainParams, Map launcherParams) { - Optional launcherValue; + Optional launcherValue; if (launcherParams == mainParams) { // The main launcher launcherValue = Optional.empty(); @@ -183,17 +185,19 @@ final class FromParams { launcherValue = shortcutParam.findIn(launcherParams); } - return launcherValue.map(withShortcut -> { - if (withShortcut) { - return Optional.of(LauncherShortcutStartupDirectory.DEFAULT); - } else { - return Optional.empty(); - } - }).or(() -> { - return shortcutParam.findIn(mainParams).map(_ -> { - return Optional.of(LauncherShortcutStartupDirectory.DEFAULT); - }); - }).map(LauncherShortcut::new); + return launcherValue.map(ParseUtils::parseLauncherShortcutForAddLauncher).or(() -> { + return Optional.ofNullable(mainParams.get(shortcutParam.getID())).map(toFunction(value -> { + if (value instanceof Boolean) { + return new LauncherShortcut(LauncherShortcutStartupDirectory.DEFAULT); + } else { + try { + return ParseUtils.parseLauncherShortcutForMainLauncher((String)value); + } catch (IllegalArgumentException ex) { + throw I18N.buildConfigException("error.invalid-option-value", value, "--" + shortcutParam.getID()).create(); + } + } + })); + }); } private static ApplicationLaunchers createLaunchers( diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java index c604b00c3e2..c6ceb1af40d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java @@ -41,7 +41,14 @@ public enum LauncherShortcutStartupDirectory { * On Linux, it indicates that a shortcut doesn't have the startup directory * configured explicitly. */ - DEFAULT("true"); + DEFAULT("true"), + + /** + * The 'app' directory in the installed application app image. This is the + * directory that is referenced with {@link ApplicationLayout#appDirectory()} + * method. + */ + APP_DIR("app-dir"); LauncherShortcutStartupDirectory(String stringValue) { this.stringValue = Objects.requireNonNull(stringValue); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ParseUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ParseUtils.java new file mode 100644 index 00000000000..411e487d9e0 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ParseUtils.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.model; + +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Collection of functions to create instances of types defined in this package from strings. + */ +public final class ParseUtils { + + private ParseUtils() { + } + + public static LauncherShortcut parseLauncherShortcutForMainLauncher(String str) { + return parse(str, LauncherShortcutStartupDirectory.APP_DIR).map(LauncherShortcut::new).orElseThrow(IllegalArgumentException::new); + } + + public static LauncherShortcut parseLauncherShortcutForAddLauncher(String str) { + return parse(str, LauncherShortcutStartupDirectory.values()).map(LauncherShortcut::new).orElseGet(() -> { + if (Boolean.valueOf(str)) { + return new LauncherShortcut(LauncherShortcutStartupDirectory.DEFAULT); + } else { + return new LauncherShortcut(); + } + }); + } + + private static Optional parse(String str, LauncherShortcutStartupDirectory... recognizedValues) { + Objects.requireNonNull(str); + return Stream.of(recognizedValues).filter(v -> { + return str.equals(v.asStringValue()); + }).findFirst(); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index ae225e15ea2..684a97bc1bd 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -82,6 +82,8 @@ error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage error.invalid-install-dir=Invalid installation directory "{0}" +error.invalid-option-value=Invalid value "{0}" of option {1} + MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1} \n\ Advice to fix: {2} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java index 15d8d2f83b0..e2259535058 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java @@ -105,10 +105,10 @@ final class WinFromParams { static final BundlerParamInfo MSI_PACKAGE = createPackageBundlerParam( WinFromParams::createWinMsiPackage); - private static final BundlerParamInfo WIN_MENU_HINT = createBooleanBundlerParam( + private static final BundlerParamInfo WIN_MENU_HINT = createStringBundlerParam( Arguments.CLIOptions.WIN_MENU_HINT.getId()); - private static final BundlerParamInfo WIN_SHORTCUT_HINT = createBooleanBundlerParam( + private static final BundlerParamInfo WIN_SHORTCUT_HINT = createStringBundlerParam( Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId()); public static final BundlerParamInfo CONSOLE_HINT = createBooleanBundlerParam( diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java index ea4d9eee19a..63be18a5ee8 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java @@ -474,6 +474,9 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { case DEFAULT -> { return INSTALLDIR; } + case APP_DIR -> { + return installedAppImage.appDirectory(); + } default -> { throw new AssertionError(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java index 07c8e06856f..50222d89ceb 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java @@ -54,6 +54,10 @@ public final class AdditionalLauncher { setPersistenceHandler(null); } + public String name() { + return name; + } + public AdditionalLauncher withVerifyActions(Action... actions) { verifyActions.addAll(List.of(actions)); return this; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java index 15bb3ea0333..1ee3b8d47b0 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java @@ -44,6 +44,7 @@ public enum LauncherShortcut { public enum StartupDirectory { DEFAULT("true"), + APP_DIR("app-dir"), ; StartupDirectory(String stringValue) { @@ -79,7 +80,7 @@ public enum LauncherShortcut { private final String stringValue; - private final static Map VALUE_MAP = + private static final Map VALUE_MAP = Stream.of(values()).collect(toMap(StartupDirectory::asStringValue, x -> x)); } @@ -147,7 +148,14 @@ public enum LauncherShortcut { private Optional findMainLauncherShortcut(JPackageCommand cmd) { if (cmd.hasArgument(optionName())) { - return Optional.of(StartupDirectory.DEFAULT); + var value = Optional.ofNullable(cmd.getArgumentValue(optionName())).filter(optionValue -> { + return !optionValue.startsWith("-"); + }); + if (value.isPresent()) { + return value.flatMap(StartupDirectory::parse); + } else { + return Optional.of(StartupDirectory.DEFAULT); + } } else { return Optional.empty(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index e8f6273b18b..caec0e315c4 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -514,6 +514,9 @@ public final class LinuxHelper { case DEFAULT -> { return (Path)null; } + case APP_DIR -> { + return cmd.pathToPackageFile(appLayout.appDirectory()); + } default -> { throw new AssertionError(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java index cca904e017e..27e94366e69 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java @@ -213,7 +213,17 @@ public final class WinShortcutVerifier { final var installDir = Path.of(installRoot.getMsiPropertyName()).resolve(getInstallationSubDirectory(cmd)); final Function workDir = startupDirectory -> { - return installDir; + switch (startupDirectory) { + case DEFAULT -> { + return installDir; + } + case APP_DIR -> { + return ApplicationLayout.windowsAppImage().resolveAt(installDir).appDirectory(); + } + default -> { + throw new IllegalArgumentException(); + } + } }; if (winMenu.isPresent()) { diff --git a/test/jdk/tools/jpackage/share/AddLShortcutTest.java b/test/jdk/tools/jpackage/share/AddLShortcutTest.java index 7d7d8b50c1d..9c50c6ffc98 100644 --- a/test/jdk/tools/jpackage/share/AddLShortcutTest.java +++ b/test/jdk/tools/jpackage/share/AddLShortcutTest.java @@ -28,16 +28,21 @@ import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import jdk.internal.util.OperatingSystem; import jdk.jpackage.test.AdditionalLauncher; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.FileAssociations; +import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.LauncherShortcut; import jdk.jpackage.test.LauncherShortcut.InvokeShortcutSpec; import jdk.jpackage.test.LauncherShortcut.StartupDirectory; @@ -63,6 +68,7 @@ import jdk.jpackage.test.WinShortcutVerifier; * @key jpackagePlatformPackage * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* + * @requires (os.family != "mac") * @requires (jpackage.test.SQETest != null) * @compile -Xlint:all -Werror AddLShortcutTest.java * @run main/othervm/timeout=540 -Xmx512m @@ -76,6 +82,7 @@ import jdk.jpackage.test.WinShortcutVerifier; * @key jpackagePlatformPackage * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* + * @requires (os.family != "mac") * @requires (jpackage.test.SQETest == null) * @compile -Xlint:all -Werror AddLShortcutTest.java * @run main/othervm/timeout=1080 -Xmx512m @@ -85,7 +92,7 @@ import jdk.jpackage.test.WinShortcutVerifier; public class AddLShortcutTest { - @Test + @Test(ifNotOS = OperatingSystem.MACOS) public void test() { // Configure several additional launchers with each combination of // possible shortcut hints in add-launcher property file. @@ -93,6 +100,8 @@ public class AddLShortcutTest { // will have shortcuts while other launchers with some properties set // to "false" will have none. + final var packageName = MethodHandles.lookup().lookupClass().getSimpleName(); + PackageTest packageTest = new PackageTest().configureHelloApp(); packageTest.addInitializer(cmd -> { cmd.addArguments("--arguments", "Duke", "--arguments", "is", @@ -102,11 +111,14 @@ public class AddLShortcutTest { } else if (TKit.isLinux()) { cmd.addArguments("--linux-shortcut"); } + + cmd.setArgumentValue("--name", packageName); + + var addLauncherApp = TKit.TEST_SRC_ROOT.resolve("apps/PrintEnv.java"); + HelloApp.createBundle(JavaAppDesc.parse(addLauncherApp + "*another.jar:Welcome"), cmd.inputDir()); }); - new FileAssociations( - MethodHandles.lookup().lookupClass().getSimpleName()).applyTo( - packageTest); + new FileAssociations(packageName).applyTo(packageTest); new AdditionalLauncher("Foo") .setDefaultArguments("yep!") @@ -131,11 +143,16 @@ public class AddLShortcutTest { .setShortcuts(true, false) .applyTo(packageTest); - new AdditionalLauncher("Launcher5") - .setDefaultArguments() + var launcher5 = new AdditionalLauncher("Launcher5") + .setDefaultArguments("--print-workdir") .setIcon(GOLDEN_ICON) - .setShortcuts(false, true) - .applyTo(packageTest); + .setShortcut(LauncherShortcut.LINUX_SHORTCUT, StartupDirectory.APP_DIR) + .setShortcut(LauncherShortcut.WIN_DESKTOP_SHORTCUT, StartupDirectory.APP_DIR) + .setShortcut(LauncherShortcut.WIN_START_MENU_SHORTCUT, null) + .setProperty("main-jar", "another.jar") + .setProperty("main-class", "Welcome"); + + new ShortcutStartupDirectoryVerifier(packageName).add(launcher5).applyTo(packageTest); packageTest.run(); } @@ -190,10 +207,50 @@ public class AddLShortcutTest { predefinedAppImage[0] = cmd.outputBundle(); }).addInitializer(cmd -> { + cfgs[0].applyToMainLauncher(cmd); cmd.removeArgumentWithValue("--input"); cmd.setArgumentValue("--name", "AddLShortcutDir2Test"); cmd.addArguments("--app-image", predefinedAppImage[0]); - cfgs[0].applyToMainLauncher(cmd); + }).run(RunnablePackageTest.Action.CREATE_AND_UNPACK); + } + + @Test(ifNotOS = OperatingSystem.MACOS) + @Parameter(value = "DEFAULT") + @Parameter(value = "APP_DIR") + public void testLastArg(StartupDirectory startupDirectory) { + final List shortcutArgs = new ArrayList<>(); + if (TKit.isLinux()) { + shortcutArgs.add("--linux-shortcut"); + } else if (TKit.isWindows()) { + shortcutArgs.add("--win-shortcut"); + } else { + TKit.assertUnexpected("Unsupported platform"); + } + + if (startupDirectory == StartupDirectory.APP_DIR) { + shortcutArgs.add(startupDirectory.asStringValue()); + } + + Path[] predefinedAppImage = new Path[1]; + + new PackageTest().addRunOnceInitializer(() -> { + var cmd = JPackageCommand.helloAppImage() + .setArgumentValue("--name", "foo") + .setFakeRuntime(); + + cmd.execute(); + + predefinedAppImage[0] = cmd.outputBundle(); + }).addInitializer(cmd -> { + cmd.removeArgumentWithValue("--input"); + cmd.setArgumentValue("--name", "AddLShortcutDir3Test"); + cmd.addArguments("--app-image", predefinedAppImage[0]); + cmd.ignoreDefaultVerbose(true); + }).addInitializer(cmd -> { + cmd.addArguments(shortcutArgs); + }).addBundleVerifier(cmd -> { + TKit.assertEquals(shortcutArgs.getLast(), cmd.getAllArguments().getLast(), + "Check the last argument of jpackage command line"); }).run(RunnablePackageTest.Action.CREATE_AND_UNPACK); } @@ -207,6 +264,7 @@ public class AddLShortcutTest { @Test(ifNotOS = OperatingSystem.MACOS) @Parameter(value = "DEFAULT") + @Parameter(value = "APP_DIR") public void testInvokeShortcuts(StartupDirectory startupDirectory) { var testApp = TKit.TEST_SRC_ROOT.resolve("apps/PrintEnv.java"); @@ -219,82 +277,118 @@ public class AddLShortcutTest { cmd.addArguments("--arguments", "--print-workdir"); }).addInitializer(JPackageCommand::ignoreFakeRuntime).addHelloAppInitializer(testApp + "*Hello"); - var shortcutStartupDirectoryVerifier = new ShortcutStartupDirectoryVerifier(name, "a"); - - shortcutStartupDirectoryVerifier.applyTo(test, startupDirectory); - - test.addInstallVerifier(cmd -> { - if (!cmd.isPackageUnpacked("Not invoking launcher shortcuts")) { - Collection invokeShortcutSpecs; - if (TKit.isLinux()) { - invokeShortcutSpecs = LinuxHelper.getInvokeShortcutSpecs(cmd); - } else if (TKit.isWindows()) { - invokeShortcutSpecs = WinShortcutVerifier.getInvokeShortcutSpecs(cmd); - } else { - throw new UnsupportedOperationException(); - } - shortcutStartupDirectoryVerifier.verify(invokeShortcutSpecs); - } - }); + new ShortcutStartupDirectoryVerifier(name).add("a", startupDirectory).applyTo(test); test.run(); } - private record ShortcutStartupDirectoryVerifier(String packageName, String launcherName) { - ShortcutStartupDirectoryVerifier { - Objects.requireNonNull(packageName); - Objects.requireNonNull(launcherName); + private static final class ShortcutStartupDirectoryVerifier { + + ShortcutStartupDirectoryVerifier(String packageName) { + this.packageName = Objects.requireNonNull(packageName); } - void applyTo(PackageTest test, StartupDirectory startupDirectory) { - var al = new AdditionalLauncher(launcherName); - al.setShortcut(shortcut(), Objects.requireNonNull(startupDirectory)); - al.addJavaOptions(String.format("-Djpackage.test.appOutput=${%s}/%s", - outputDirVarName(), expectedOutputFilename())); - al.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test); - } + void applyTo(PackageTest test) { + verifiers.values().forEach(verifier -> { + verifier.applyTo(test); + }); + test.addInstallVerifier(cmd -> { + if (!cmd.isPackageUnpacked("Not invoking launcher shortcuts")) { + Collection invokeShortcutSpecs; + if (TKit.isLinux()) { + invokeShortcutSpecs = LinuxHelper.getInvokeShortcutSpecs(cmd); + } else if (TKit.isWindows()) { + invokeShortcutSpecs = WinShortcutVerifier.getInvokeShortcutSpecs(cmd); + } else { + throw new UnsupportedOperationException(); + } - void verify(Collection invokeShortcutSpecs) throws IOException { + var invokeShortcutSpecsMap = invokeShortcutSpecs.stream().collect(Collectors.groupingBy(InvokeShortcutSpec::launcherName)); - TKit.trace(String.format("Verify shortcut [%s]", launcherName)); - - var expectedOutputFile = Path.of(System.getenv(outputDirVarName())).resolve(expectedOutputFilename()); - - TKit.deleteIfExists(expectedOutputFile); - - var invokeShortcutSpec = invokeShortcutSpecs.stream().filter(v -> { - return launcherName.equals(v.launcherName()); - }).findAny().orElseThrow(); - - invokeShortcutSpec.execute(); - - // On Linux, "gtk-launch" is used to launch a .desktop file. It is async and there is no - // way to make it wait for exit of a process it triggers. - TKit.waitForFileCreated(expectedOutputFile, Duration.ofSeconds(10), Duration.ofSeconds(3)); - - TKit.assertFileExists(expectedOutputFile); - var actualStr = Files.readAllLines(expectedOutputFile).getFirst(); - - var outputPrefix = "$CD="; - - TKit.assertTrue(actualStr.startsWith(outputPrefix), "Check output starts with '" + outputPrefix+ "' string"); - - invokeShortcutSpec.expectedWorkDirectory().ifPresent(expectedWorkDirectory -> { - TKit.assertEquals( - expectedWorkDirectory, - Path.of(actualStr.substring(outputPrefix.length())), - String.format("Check work directory of %s of launcher [%s]", - invokeShortcutSpec.shortcut().propertyName(), - invokeShortcutSpec.launcherName())); + for (var e : verifiers.entrySet()) { + e.getValue().verify(invokeShortcutSpecsMap.get(e.getKey())); + } + } }); } - private String expectedOutputFilename() { - return String.format("%s-%s.out", packageName, launcherName); + ShortcutStartupDirectoryVerifier add(String launcherName, StartupDirectory startupDirectory) { + return add(new AdditionalLauncher(launcherName) + .setShortcut(shortcut(), Objects.requireNonNull(Objects.requireNonNull(startupDirectory)))); } - private String outputDirVarName() { + ShortcutStartupDirectoryVerifier add(AdditionalLauncher addLauncher) { + var launcherVerifier = new LauncherVerifier(addLauncher); + verifiers.put(launcherVerifier.launcherName(), launcherVerifier); + return this; + } + + + private final class LauncherVerifier { + + private LauncherVerifier(AdditionalLauncher addLauncher) { + this.addLauncher = Objects.requireNonNull(addLauncher); + } + + private String launcherName() { + return addLauncher.name(); + } + + private void applyTo(PackageTest test) { + addLauncher.addJavaOptions(String.format("-Djpackage.test.appOutput=${%s}/%s", + outputDirVarName(), expectedOutputFilename())); + addLauncher.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test); + } + + private void verify(Collection invokeShortcutSpecs) throws IOException { + Objects.requireNonNull(invokeShortcutSpecs); + if (invokeShortcutSpecs.isEmpty()) { + throw new IllegalArgumentException(); + } + + TKit.trace(String.format("Verify shortcut [%s]", launcherName())); + + var expectedOutputFile = Path.of(System.getenv(outputDirVarName())).resolve(expectedOutputFilename()); + + TKit.deleteIfExists(expectedOutputFile); + + var invokeShortcutSpec = invokeShortcutSpecs.stream().filter(v -> { + return launcherName().equals(v.launcherName()); + }).findAny().orElseThrow(); + + invokeShortcutSpec.execute(); + + // On Linux, "gtk-launch" is used to launch a .desktop file. It is async and there is no + // way to make it wait for exit of a process it triggers. + TKit.waitForFileCreated(expectedOutputFile, Duration.ofSeconds(10), Duration.ofSeconds(3)); + + TKit.assertFileExists(expectedOutputFile); + var actualStr = Files.readAllLines(expectedOutputFile).getFirst(); + + var outputPrefix = "$CD="; + + TKit.assertTrue(actualStr.startsWith(outputPrefix), "Check output starts with '" + outputPrefix+ "' string"); + + invokeShortcutSpec.expectedWorkDirectory().ifPresent(expectedWorkDirectory -> { + TKit.assertEquals( + expectedWorkDirectory, + Path.of(actualStr.substring(outputPrefix.length())), + String.format("Check work directory of %s of launcher [%s]", + invokeShortcutSpec.shortcut().propertyName(), + invokeShortcutSpec.launcherName())); + }); + } + + private String expectedOutputFilename() { + return String.format("%s-%s.out", packageName, launcherName()); + } + + private final AdditionalLauncher addLauncher; + } + + + private static String outputDirVarName() { if (TKit.isLinux()) { return "HOME"; } else if (TKit.isWindows()) { @@ -304,7 +398,7 @@ public class AddLShortcutTest { } } - private LauncherShortcut shortcut() { + private static LauncherShortcut shortcut() { if (TKit.isLinux()) { return LauncherShortcut.LINUX_SHORTCUT; } else if (TKit.isWindows()) { @@ -313,6 +407,10 @@ public class AddLShortcutTest { throw new UnsupportedOperationException(); } } + + private final String packageName; + // Keep the order + private final Map verifiers = new LinkedHashMap<>(); } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index c352decc0f3..2d3d61de864 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -576,6 +576,9 @@ public final class ErrorTest { ); }).flatMap(x -> x).map(TestSpec.Builder::create).toList()); + invalidShortcut(testCases::add, "--win-menu"); + invalidShortcut(testCases::add, "--win-shortcut"); + return toTestArgs(testCases.stream()); } @@ -642,6 +645,8 @@ public final class ErrorTest { .error("error.rpm-invalid-value-for-package-name.advice") ).map(TestSpec.Builder::create).toList()); + invalidShortcut(testCases::add, "--linux-shortcut"); + return toTestArgs(testCases.stream()); } @@ -697,6 +702,13 @@ public final class ErrorTest { duplicateAddArgs(builder, accumulator, "--mac-sign"); } + private static void invalidShortcut(Consumer accumulator, String shortcutOption) { + Objects.requireNonNull(shortcutOption); + Stream.of("true", "false", "").map(value -> { + return testSpec().nativeType().addArgs(shortcutOption, value).error("error.invalid-option-value", value, shortcutOption).create(); + }).forEach(accumulator); + } + private record UnsupportedPlatformOption(String name, Optional value) { UnsupportedPlatformOption { Objects.requireNonNull(name); From d78fa5a9f6254e2e93e75c693efba75e09736749 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 12 Aug 2025 07:16:57 +0000 Subject: [PATCH 004/112] 8365240: [asan] exclude some tests when using asan enabled binaries Reviewed-by: lmesnik, sspitsyn --- .../vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java | 4 +++- test/jdk/tools/launcher/TooSmallStackSize.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java index eafbbf8b05f..e9f4ee7c4f1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, 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 @@ -43,6 +43,8 @@ * * @comment Not run on AIX as it does not support ulimit -v * @requires os.family != "aix" + * @comment Do not run with asan enabled because asan has issues with ulimit + * @requires !vm.asan * @run main/native nsk.jvmti.Allocate.alloc001.alloc001 */ diff --git a/test/jdk/tools/launcher/TooSmallStackSize.java b/test/jdk/tools/launcher/TooSmallStackSize.java index 8485f115d6e..8133dbf4550 100644 --- a/test/jdk/tools/launcher/TooSmallStackSize.java +++ b/test/jdk/tools/launcher/TooSmallStackSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ * @bug 6762191 8222334 * @summary Setting stack size to 16K causes segmentation fault * @compile TooSmallStackSize.java + * @comment VM fails to launch with minimum allowed stack size of 136k when asan is enabled + * @requires !vm.asan * @run main TooSmallStackSize */ From db12f1934a659843d9cc77f4f21e67ebf9fa94e6 Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Tue, 12 Aug 2025 08:03:18 +0000 Subject: [PATCH 005/112] 8364280: NMTCommittedVirtualMemoryTracker.test_committed_virtualmemory_region_vm fails with assertion "negative distance" Reviewed-by: gziemski, jsjolen --- .../runtime/test_committed_virtualmemory.cpp | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp index cd47e3a4e17..caf685f587b 100644 --- a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp +++ b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp @@ -43,10 +43,16 @@ public: // snapshot current stack usage VirtualMemoryTracker::Instance::snapshot_thread_stacks(); - ReservedMemoryRegion rmr_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region(stack_end); + ReservedMemoryRegion rmr_found; + { + MemTracker::NmtVirtualMemoryLocker vml; + rmr_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region(stack_end); + } + ASSERT_TRUE(rmr_found.is_valid()); ASSERT_EQ(rmr_found.base(), stack_end); + int i = 0; address i_addr = (address)&i; bool found_i_addr = false; @@ -54,18 +60,20 @@ public: // stack grows downward address stack_top = stack_end + stack_size; bool found_stack_top = false; - VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr_found, [&](const CommittedMemoryRegion& cmr) { - if (cmr.base() + cmr.size() == stack_top) { - EXPECT_TRUE(cmr.size() <= stack_size); - found_stack_top = true; - } - if(i_addr < stack_top && i_addr >= cmr.base()) { - found_i_addr = true; - } - i++; - return true; - }); - + { + MemTracker::NmtVirtualMemoryLocker vml; + VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr_found, [&](const CommittedMemoryRegion& cmr) { + if (cmr.base() + cmr.size() == stack_top) { + EXPECT_TRUE(cmr.size() <= stack_size); + found_stack_top = true; + } + if (i_addr < stack_top && i_addr >= cmr.base()) { + found_i_addr = true; + } + i++; + return true; + }); + } // stack and guard pages may be contiguous as one region ASSERT_TRUE(i >= 1); From 5a442197d21e1dfb89cdbf5f0ad5596869ab333a Mon Sep 17 00:00:00 2001 From: Johny Jose Date: Tue, 12 Aug 2025 08:26:42 +0000 Subject: [PATCH 006/112] 7191877: TEST_BUG: java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java failing intermittently Reviewed-by: smarks, coffeys --- test/jdk/ProblemList.txt | 2 - .../checkLeaseInfoLeak/CheckLeaseLeak.java | 98 ++++++++----------- .../checkLeaseInfoLeak/LeaseLeakClient.java | 3 +- 3 files changed, 44 insertions(+), 59 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 84555a6edfb..02eef5888e0 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -615,8 +615,6 @@ java/rmi/server/Unreferenced/finiteGCLatency/FiniteGCLatency.java 7140992 generi java/rmi/transport/rapidExportUnexport/RapidExportUnexport.java 7146541 linux-all -java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java 7191877 generic-all - java/rmi/registry/readTest/CodebaseTest.java 8173324 windows-all java/rmi/registry/multipleRegistries/MultipleRegistries.java 8268182 macosx-all diff --git a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java index 2370dcd35c4..4de6598a0f4 100644 --- a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java +++ b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,12 +54,11 @@ import java.rmi.*; import java.rmi.server.*; -import sun.rmi.transport.*; -import sun.rmi.*; import java.util.Map; import java.io.*; import java.lang.reflect.*; import java.rmi.registry.*; +import sun.rmi.transport.*; public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { public CheckLeaseLeak() throws RemoteException { } @@ -102,8 +101,6 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { System.err.println("Created client: " + i); JavaVM jvm = new JavaVM("LeaseLeakClient", - " -Djava.security.policy=" + - TestParams.defaultPolicy + " -Drmi.registry.port=" + registryPort, ""); @@ -128,8 +125,8 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { } } - /* numLeft should be 2 - if 11 there is a problem. */ - if (numLeft > 2) { + /* numLeft should be 4 - if 11 there is a problem. */ + if (numLeft > 4) { TestLibrary.bomb("Too many objects in DGCImpl.leaseTable: "+ numLeft); } else { @@ -156,57 +153,51 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { Field f; try { - f = (Field) java.security.AccessController.doPrivileged - (new java.security.PrivilegedExceptionAction() { - public Object run() throws Exception { + ObjID dgcID = new ObjID(DGC_ID); + /* + * Construct an ObjectEndpoint containing DGC's + * ObjID. + */ + Class oeClass = + Class.forName("sun.rmi.transport.ObjectEndpoint"); + Class[] constrParams = + new Class[]{ ObjID.class, Transport.class }; + Constructor oeConstructor = + oeClass.getDeclaredConstructor(constrParams); + oeConstructor.setAccessible(true); + Object oe = + oeConstructor.newInstance( + new Object[]{ dgcID, null }); - ObjID dgcID = new ObjID(DGC_ID); + /* + * Get Target that contains DGCImpl in ObjectTable + */ + Class objTableClass = + Class.forName("sun.rmi.transport.ObjectTable"); + Class getTargetParams[] = new Class[] { oeClass }; + Method objTableGetTarget = + objTableClass.getDeclaredMethod("getTarget", + getTargetParams); + objTableGetTarget.setAccessible(true); + Target dgcTarget = (Target) + objTableGetTarget.invoke(null, new Object[]{ oe }); - /* - * Construct an ObjectEndpoint containing DGC's - * ObjID. - */ - Class oeClass = - Class.forName("sun.rmi.transport.ObjectEndpoint"); - Class[] constrParams = - new Class[]{ ObjID.class, Transport.class }; - Constructor oeConstructor = - oeClass.getDeclaredConstructor(constrParams); - oeConstructor.setAccessible(true); - Object oe = - oeConstructor.newInstance( - new Object[]{ dgcID, null }); - - /* - * Get Target that contains DGCImpl in ObjectTable - */ - Class objTableClass = - Class.forName("sun.rmi.transport.ObjectTable"); - Class getTargetParams[] = new Class[] { oeClass }; - Method objTableGetTarget = - objTableClass.getDeclaredMethod("getTarget", - getTargetParams); - objTableGetTarget.setAccessible(true); - Target dgcTarget = (Target) - objTableGetTarget.invoke(null, new Object[]{ oe }); - - /* get the DGCImpl from its Target */ - Method targetGetImpl = - dgcTarget.getClass().getDeclaredMethod + /* get the DGCImpl from its Target */ + Method targetGetImpl = + dgcTarget.getClass().getDeclaredMethod ("getImpl", null); - targetGetImpl.setAccessible(true); - dgcImpl[0] = - (Remote) targetGetImpl.invoke(dgcTarget, null); + targetGetImpl.setAccessible(true); + dgcImpl[0] = + (Remote) targetGetImpl.invoke(dgcTarget, null); - /* Get the lease table from the DGCImpl. */ - Field reflectedLeaseTable = - dgcImpl[0].getClass().getDeclaredField + /* Get the lease table from the DGCImpl. */ + Field reflectedLeaseTable = + dgcImpl[0].getClass().getDeclaredField ("leaseTable"); - reflectedLeaseTable.setAccessible(true); + reflectedLeaseTable.setAccessible(true); + + f = reflectedLeaseTable; - return reflectedLeaseTable; - } - }); /** * This is the leaseTable that will fill up with LeaseInfo @@ -217,9 +208,6 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { numLeaseInfosLeft = leaseTable.size(); } catch(Exception e) { - if (e instanceof java.security.PrivilegedActionException) - e = ((java.security.PrivilegedActionException) e). - getException(); TestLibrary.bomb(e); } diff --git a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java index d9cf9fa9e96..dd24b819146 100644 --- a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java +++ b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ import java.rmi.registry.*; public class LeaseLeakClient { public static void main(String args[]) { - TestLibrary.suggestSecurityManager("java.rmi.RMISecurityManager"); try { LeaseLeak leaseLeak = null; From b81f4faed7180e51aa966a9bf2f84ba755c6736d Mon Sep 17 00:00:00 2001 From: David Beaumont Date: Tue, 12 Aug 2025 08:34:26 +0000 Subject: [PATCH 007/112] 8360037: Refactor ImageReader in preparation for Valhalla support Reviewed-by: alanb, rriggs, jpai --- .../jdk/internal/jimage/ImageReader.java | 1019 ++++++++--------- .../jdk/internal/jrtfs/ExplodedImage.java | 6 +- .../jdk/internal/jrtfs/JrtFileAttributes.java | 8 +- .../jdk/internal/jrtfs/JrtFileSystem.java | 26 +- .../jdk/internal/jrtfs/SystemImage.java | 1 - .../internal/module/SystemModuleFinders.java | 115 +- .../jdk/internal/jimage/ImageReaderTest.java | 279 +++++ .../jdk/internal/jimage/JImageReadTest.java | 38 +- .../ImageReaderDuplicateChildNodesTest.java | 10 +- .../internal/jrtfs/ImageReaderBenchmark.java | 4 +- 10 files changed, 880 insertions(+), 626 deletions(-) create mode 100644 test/jdk/jdk/internal/jimage/ImageReaderTest.java diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java index f12c39f3e81..79e718c76e5 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,16 +25,14 @@ package jdk.internal.jimage; import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.file.Files; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -42,9 +40,36 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; /** + * A view over the entries of a jimage file with a unified namespace suitable + * for file system use. The jimage entries (resources, module and package + * information) are mapped into a unified hierarchy of named nodes, which serve + * as the underlying structure for {@code JrtFileSystem} and other utilities. + * + *

Entries in jimage are expressed as one of three {@link Node} types; + * resource nodes, directory nodes and link nodes. + * + *

When remapping jimage entries, jimage location names (e.g. {@code + * "/java.base/java/lang/Integer.class"}) are prefixed with {@code "/modules"} + * to form the names of resource nodes. This aligns with the naming of module + * entries in jimage (e.g. "/modules/java.base/java/lang"), which appear as + * directory nodes in {@code ImageReader}. + * + *

Package entries (e.g. {@code "/packages/java.lang"} appear as directory + * nodes containing link nodes, which resolve back to the root directory of the + * module in which that package exists (e.g. {@code "/modules/java.base"}). + * Unlike other nodes, the jimage file does not contain explicit entries for + * link nodes, and their existence is derived only from the contents of the + * parent directory. + * + *

While similar to {@code BasicImageReader}, this class is not a conceptual + * subtype of it, and deliberately hides types such as {@code ImageLocation} to + * give a focused API based only on nodes. + * * @implNote This class needs to maintain JDK 8 source compatibility. * * It is used internally in the JDK to implement jimage/jrtfs access, @@ -60,6 +85,10 @@ public final class ImageReader implements AutoCloseable { this.reader = reader; } + /** + * Opens an image reader for a jimage file at the specified path, using the + * given byte order. + */ public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { Objects.requireNonNull(imagePath); Objects.requireNonNull(byteOrder); @@ -67,6 +96,10 @@ public final class ImageReader implements AutoCloseable { return SharedImageReader.open(imagePath, byteOrder); } + /** + * Opens an image reader for a jimage file at the specified path, using the + * platform native byte order. + */ public static ImageReader open(Path imagePath) throws IOException { return open(imagePath, ByteOrder.nativeOrder()); } @@ -92,146 +125,113 @@ public final class ImageReader implements AutoCloseable { } } - // directory management interface - public Directory getRootDirectory() throws IOException { - ensureOpen(); - return reader.getRootDirectory(); - } - - + /** + * Finds the node with the given name. + * + * @param name a node name of the form {@code "/modules//...} or + * {@code "/packages//...}. + * @return a node representing a resource, directory or symbolic link. + */ public Node findNode(String name) throws IOException { ensureOpen(); return reader.findNode(name); } + /** + * Returns a copy of the content of a resource node. The buffer returned by + * this method is not cached by the node, and each call returns a new array + * instance. + * + * @throws IOException if the content cannot be returned (including if the + * given node is not a resource node). + */ public byte[] getResource(Node node) throws IOException { ensureOpen(); return reader.getResource(node); } - public byte[] getResource(Resource rs) throws IOException { - ensureOpen(); - return reader.getResource(rs); - } - - public ImageHeader getHeader() { - requireOpen(); - return reader.getHeader(); - } - + /** + * Releases a (possibly cached) {@link ByteBuffer} obtained via + * {@link #getResourceBuffer(Node)}. + * + *

Note that no testing is performed to check whether the buffer about + * to be released actually came from a call to {@code getResourceBuffer()}. + */ public static void releaseByteBuffer(ByteBuffer buffer) { BasicImageReader.releaseByteBuffer(buffer); } - public String getName() { + /** + * Returns the content of a resource node in a possibly cached byte buffer. + * Callers of this method must call {@link #releaseByteBuffer(ByteBuffer)} + * when they are finished with it. + */ + public ByteBuffer getResourceBuffer(Node node) { requireOpen(); - return reader.getName(); - } - - public ByteOrder getByteOrder() { - requireOpen(); - return reader.getByteOrder(); - } - - public Path getImagePath() { - requireOpen(); - return reader.getImagePath(); - } - - public ImageStringsReader getStrings() { - requireOpen(); - return reader.getStrings(); - } - - public ImageLocation findLocation(String mn, String rn) { - requireOpen(); - return reader.findLocation(mn, rn); - } - - public boolean verifyLocation(String mn, String rn) { - requireOpen(); - return reader.verifyLocation(mn, rn); - } - - public ImageLocation findLocation(String name) { - requireOpen(); - return reader.findLocation(name); - } - - public String[] getEntryNames() { - requireOpen(); - return reader.getEntryNames(); - } - - public String[] getModuleNames() { - requireOpen(); - int off = "/modules/".length(); - return reader.findNode("/modules") - .getChildren() - .stream() - .map(Node::getNameString) - .map(s -> s.substring(off, s.length())) - .toArray(String[]::new); - } - - public long[] getAttributes(int offset) { - requireOpen(); - return reader.getAttributes(offset); - } - - public String getString(int offset) { - requireOpen(); - return reader.getString(offset); - } - - public byte[] getResource(String name) { - requireOpen(); - return reader.getResource(name); - } - - public byte[] getResource(ImageLocation loc) { - requireOpen(); - return reader.getResource(loc); - } - - public ByteBuffer getResourceBuffer(ImageLocation loc) { - requireOpen(); - return reader.getResourceBuffer(loc); - } - - public InputStream getResourceStream(ImageLocation loc) { - requireOpen(); - return reader.getResourceStream(loc); + if (!node.isResource()) { + throw new IllegalArgumentException("Not a resource node: " + node); + } + return reader.getResourceBuffer(node.getLocation()); } private static final class SharedImageReader extends BasicImageReader { - static final int SIZE_OF_OFFSET = Integer.BYTES; - - static final Map OPEN_FILES = new HashMap<>(); + private static final Map OPEN_FILES = new HashMap<>(); + private static final String MODULES_ROOT = "/modules"; + private static final String PACKAGES_ROOT = "/packages"; + // There are >30,000 nodes in a complete jimage tree, and even relatively + // common tasks (e.g. starting up javac) load somewhere in the region of + // 1000 classes. Thus, an initial capacity of 2000 is a reasonable guess. + private static final int INITIAL_NODE_CACHE_CAPACITY = 2000; // List of openers for this shared image. - final Set openers; + private final Set openers = new HashSet<>(); - // attributes of the .jimage file. jimage file does not contain + // Attributes of the jimage file. The jimage file does not contain // attributes for the individual resources (yet). We use attributes // of the jimage file itself (creation, modification, access times). - // Iniitalized lazily, see {@link #imageFileAttributes()}. - BasicFileAttributes imageFileAttributes; + private final BasicFileAttributes imageFileAttributes; - // directory management implementation - final HashMap nodes; - volatile Directory rootDir; - - Directory packagesDir; - Directory modulesDir; + // Cache of all user visible nodes, guarded by synchronizing 'this' instance. + private final Map nodes; + // Used to classify ImageLocation instances without string comparison. + private final int modulesStringOffset; + private final int packagesStringOffset; private SharedImageReader(Path imagePath, ByteOrder byteOrder) throws IOException { super(imagePath, byteOrder); - this.openers = new HashSet<>(); - this.nodes = new HashMap<>(); + this.imageFileAttributes = Files.readAttributes(imagePath, BasicFileAttributes.class); + this.nodes = new HashMap<>(INITIAL_NODE_CACHE_CAPACITY); + // Pick stable jimage names from which to extract string offsets (we cannot + // use "/modules" or "/packages", since those have a module offset of zero). + this.modulesStringOffset = getModuleOffset("/modules/java.base"); + this.packagesStringOffset = getModuleOffset("/packages/java.lang"); + + // Node creation is very lazy, so we can just make the top-level directories + // now without the risk of triggering the building of lots of other nodes. + Directory packages = newDirectory(PACKAGES_ROOT); + nodes.put(packages.getName(), packages); + Directory modules = newDirectory(MODULES_ROOT); + nodes.put(modules.getName(), modules); + + Directory root = newDirectory("/"); + root.setChildren(Arrays.asList(packages, modules)); + nodes.put(root.getName(), root); } - public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { + /** + * Returns the offset of the string denoting the leading "module" segment in + * the given path (e.g. {@code /}). We can't just pass in the + * {@code /} string here because that has a module offset of zero. + */ + private int getModuleOffset(String path) { + ImageLocation location = findLocation(path); + assert location != null : "Cannot find expected jimage location: " + path; + int offset = location.getModuleOffset(); + assert offset != 0 : "Invalid module offset for jimage location: " + path; + return offset; + } + + private static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { Objects.requireNonNull(imagePath); Objects.requireNonNull(byteOrder); @@ -264,7 +264,6 @@ public final class ImageReader implements AutoCloseable { if (openers.isEmpty()) { close(); nodes.clear(); - rootDir = null; if (!OPEN_FILES.remove(this.getImagePath(), this)) { throw new IOException("image file not found in open list"); @@ -273,448 +272,412 @@ public final class ImageReader implements AutoCloseable { } } - void addOpener(ImageReader reader) { - synchronized (OPEN_FILES) { - openers.add(reader); + /** + * Returns a node with the given name, or null if no resource or directory of + * that name exists. + * + *

This is the only public API by which anything outside this class can access + * {@code Node} instances either directly, or by resolving symbolic links. + * + *

Note also that there is no reentrant calling back to this method from within + * the node handling code. + * + * @param name an absolute, {@code /}-separated path string, prefixed with either + * "/modules" or "/packages". + */ + synchronized Node findNode(String name) { + Node node = nodes.get(name); + if (node == null) { + // We cannot get the root paths ("/modules" or "/packages") here + // because those nodes are already in the nodes cache. + if (name.startsWith(MODULES_ROOT + "/")) { + node = buildModulesNode(name); + } else if (name.startsWith(PACKAGES_ROOT + "/")) { + node = buildPackagesNode(name); + } + if (node != null) { + nodes.put(node.getName(), node); + } + } else if (!node.isCompleted()) { + // Only directories can be incomplete. + assert node instanceof Directory : "Invalid incomplete node: " + node; + completeDirectory((Directory) node); } - } - - boolean removeOpener(ImageReader reader) { - synchronized (OPEN_FILES) { - return openers.remove(reader); - } - } - - // directory management interface - Directory getRootDirectory() { - return buildRootDirectory(); + assert node == null || node.isCompleted() : "Incomplete node: " + node; + return node; } /** - * Lazily build a node from a name. + * Builds a node in the "/modules/..." namespace. + * + *

Called by {@link #findNode(String)} if a {@code /modules/...} node + * is not present in the cache. */ - synchronized Node buildNode(String name) { - Node n; - boolean isPackages = name.startsWith("/packages"); - boolean isModules = !isPackages && name.startsWith("/modules"); - - if (!(isModules || isPackages)) { - return null; - } - + private Node buildModulesNode(String name) { + assert name.startsWith(MODULES_ROOT + "/") : "Invalid module node name: " + name; + // Returns null for non-directory resources, since the jimage name does not + // start with "/modules" (e.g. "/java.base/java/lang/Object.class"). ImageLocation loc = findLocation(name); - - if (loc != null) { // A sub tree node - if (isPackages) { - n = handlePackages(name, loc); - } else { // modules sub tree - n = handleModulesSubTree(name, loc); - } - } else { // Asking for a resource? /modules/java.base/java/lang/Object.class - if (isModules) { - n = handleResource(name); - } else { - // Possibly ask for /packages/java.lang/java.base - // although /packages/java.base not created - n = handleModuleLink(name); - } + if (loc != null) { + assert name.equals(loc.getFullName()) : "Mismatched location for directory: " + name; + assert isModulesSubdirectory(loc) : "Invalid modules directory: " + name; + return completeModuleDirectory(newDirectory(name), loc); } - return n; - } - - synchronized Directory buildRootDirectory() { - Directory root = rootDir; // volatile read - if (root != null) { - return root; - } - - root = newDirectory(null, "/"); - root.setIsRootDir(); - - // /packages dir - packagesDir = newDirectory(root, "/packages"); - packagesDir.setIsPackagesDir(); - - // /modules dir - modulesDir = newDirectory(root, "/modules"); - modulesDir.setIsModulesDir(); - - root.setCompleted(true); - return rootDir = root; + // Now try the non-prefixed resource name, but be careful to avoid false + // positives for names like "/modules/modules/xxx" which could return a + // location of a directory entry. + loc = findLocation(name.substring(MODULES_ROOT.length())); + return loc != null && isResource(loc) ? newResource(name, loc) : null; } /** - * To visit sub tree resources. + * Builds a node in the "/packages/..." namespace. + * + *

Called by {@link #findNode(String)} if a {@code /packages/...} node + * is not present in the cache. */ - interface LocationVisitor { - void visit(ImageLocation loc); - } - - void visitLocation(ImageLocation loc, LocationVisitor visitor) { - byte[] offsets = getResource(loc); - ByteBuffer buffer = ByteBuffer.wrap(offsets); - buffer.order(getByteOrder()); - IntBuffer intBuffer = buffer.asIntBuffer(); - for (int i = 0; i < offsets.length / SIZE_OF_OFFSET; i++) { - int offset = intBuffer.get(i); - ImageLocation pkgLoc = getLocation(offset); - visitor.visit(pkgLoc); - } - } - - void visitPackageLocation(ImageLocation loc) { - // Retrieve package name - String pkgName = getBaseExt(loc); - // Content is array of offsets in Strings table - byte[] stringsOffsets = getResource(loc); - ByteBuffer buffer = ByteBuffer.wrap(stringsOffsets); - buffer.order(getByteOrder()); - IntBuffer intBuffer = buffer.asIntBuffer(); - // For each module, create a link node. - for (int i = 0; i < stringsOffsets.length / SIZE_OF_OFFSET; i++) { - // skip empty state, useless. - intBuffer.get(i); - i++; - int offset = intBuffer.get(i); - String moduleName = getString(offset); - Node targetNode = findNode("/modules/" + moduleName); - if (targetNode != null) { - String pkgDirName = packagesDir.getName() + "/" + pkgName; - Directory pkgDir = (Directory) nodes.get(pkgDirName); - newLinkNode(pkgDir, pkgDir.getName() + "/" + moduleName, targetNode); - } - } - } - - Node handlePackages(String name, ImageLocation loc) { - long size = loc.getUncompressedSize(); - Node n = null; - // Only possibilities are /packages, /packages/package/module - if (name.equals("/packages")) { - visitLocation(loc, (childloc) -> { - findNode(childloc.getFullName()); - }); - packagesDir.setCompleted(true); - n = packagesDir; + private Node buildPackagesNode(String name) { + // There are only locations for the root "/packages" or "/packages/xxx" + // directories, but not the symbolic links below them (the links can be + // entirely derived from the name information in the parent directory). + // However, unlike resources this means that we do not have a constant + // time lookup for link nodes when creating them. + int packageStart = PACKAGES_ROOT.length() + 1; + int packageEnd = name.indexOf('/', packageStart); + if (packageEnd == -1) { + ImageLocation loc = findLocation(name); + return loc != null ? completePackageDirectory(newDirectory(name), loc) : null; } else { - if (size != 0) { // children are offsets to module in StringsTable - String pkgName = getBaseExt(loc); - Directory pkgDir = newDirectory(packagesDir, packagesDir.getName() + "/" + pkgName); - visitPackageLocation(loc); - pkgDir.setCompleted(true); - n = pkgDir; - } else { // Link to module - String pkgName = loc.getParent(); - String modName = getBaseExt(loc); - Node targetNode = findNode("/modules/" + modName); - if (targetNode != null) { - String pkgDirName = packagesDir.getName() + "/" + pkgName; - Directory pkgDir = (Directory) nodes.get(pkgDirName); - Node linkNode = newLinkNode(pkgDir, pkgDir.getName() + "/" + modName, targetNode); - n = linkNode; + // We cannot assume that the parent directory exists for a link node, since + // the given name is untrusted and could reference a non-existent link. + // However, if the parent directory is present, we can conclude that the + // given name was not a valid link (or else it would already be cached). + String dirName = name.substring(0, packageEnd); + if (!nodes.containsKey(dirName)) { + ImageLocation loc = findLocation(dirName); + // If the parent location doesn't exist, the link node cannot exist. + if (loc != null) { + nodes.put(dirName, completePackageDirectory(newDirectory(dirName), loc)); + // When the parent is created its child nodes are created and cached, + // but this can still return null if given name wasn't a valid link. + return nodes.get(name); } } } - return n; + return null; } - // Asking for /packages/package/module although - // /packages// not yet created, need to create it - // prior to return the link to module node. - Node handleModuleLink(String name) { - // eg: unresolved /packages/package/module - // Build /packages/package node - Node ret = null; - String radical = "/packages/"; - String path = name; - if (path.startsWith(radical)) { - int start = radical.length(); - int pkgEnd = path.indexOf('/', start); - if (pkgEnd != -1) { - String pkg = path.substring(start, pkgEnd); - String pkgPath = radical + pkg; - Node n = findNode(pkgPath); - // If not found means that this is a symbolic link such as: - // /packages/java.util/java.base/java/util/Vector.class - // and will be done by a retry of the filesystem - for (Node child : n.getChildren()) { - if (child.name.equals(name)) { - ret = child; - break; - } - } - } + /** Completes a directory by ensuring its child list is populated correctly. */ + private void completeDirectory(Directory dir) { + String name = dir.getName(); + // Since the node exists, we can assert that its name starts with + // either "/modules" or "/packages", making differentiation easy. + // It also means that the name is valid, so it must yield a location. + assert name.startsWith(MODULES_ROOT) || name.startsWith(PACKAGES_ROOT); + ImageLocation loc = findLocation(name); + assert loc != null && name.equals(loc.getFullName()) : "Invalid location for name: " + name; + // We cannot use 'isXxxSubdirectory()' methods here since we could + // be given a top-level directory (for which that test doesn't work). + // The string MUST start "/modules" or "/packages" here. + if (name.charAt(1) == 'm') { + completeModuleDirectory(dir, loc); + } else { + completePackageDirectory(dir, loc); } - return ret; + assert dir.isCompleted() : "Directory must be complete by now: " + dir; } - Node handleModulesSubTree(String name, ImageLocation loc) { - Node n; - assert (name.equals(loc.getFullName())); - Directory dir = makeDirectories(name); - visitLocation(loc, (childloc) -> { - String path = childloc.getFullName(); - if (path.startsWith("/modules")) { // a package - makeDirectories(path); - } else { // a resource - makeDirectories(childloc.buildName(true, true, false)); - // if we have already created a resource for this name previously, then don't - // recreate it - if (!nodes.containsKey(childloc.getFullName(true))) { - newResource(dir, childloc); - } + /** + * Completes a modules directory by setting the list of child nodes. + * + *

The given directory can be the top level {@code /modules} directory, + * so it is NOT safe to use {@code isModulesSubdirectory(loc)} here. + */ + private Directory completeModuleDirectory(Directory dir, ImageLocation loc) { + assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; + List children = createChildNodes(loc, childLoc -> { + if (isModulesSubdirectory(childLoc)) { + return nodes.computeIfAbsent(childLoc.getFullName(), this::newDirectory); + } else { + // Add "/modules" prefix to image location paths to get node names. + String resourceName = childLoc.getFullName(true); + return nodes.computeIfAbsent(resourceName, n -> newResource(n, childLoc)); } }); - dir.setCompleted(true); - n = dir; - return n; - } - - Node handleResource(String name) { - Node n = null; - if (!name.startsWith("/modules/")) { - return null; - } - // Make sure that the thing that follows "/modules/" is a module name. - int moduleEndIndex = name.indexOf('/', "/modules/".length()); - if (moduleEndIndex == -1) { - return null; - } - ImageLocation moduleLoc = findLocation(name.substring(0, moduleEndIndex)); - if (moduleLoc == null || moduleLoc.getModuleOffset() == 0) { - return null; - } - - String locationPath = name.substring("/modules".length()); - ImageLocation resourceLoc = findLocation(locationPath); - if (resourceLoc != null) { - Directory dir = makeDirectories(resourceLoc.buildName(true, true, false)); - Resource res = newResource(dir, resourceLoc); - n = res; - } - return n; - } - - String getBaseExt(ImageLocation loc) { - String base = loc.getBase(); - String ext = loc.getExtension(); - if (ext != null && !ext.isEmpty()) { - base = base + "." + ext; - } - return base; - } - - synchronized Node findNode(String name) { - buildRootDirectory(); - Node n = nodes.get(name); - if (n == null || !n.isCompleted()) { - n = buildNode(name); - } - return n; - } - - /** - * Returns the file attributes of the image file. - */ - BasicFileAttributes imageFileAttributes() { - BasicFileAttributes attrs = imageFileAttributes; - if (attrs == null) { - try { - Path file = getImagePath(); - attrs = Files.readAttributes(file, BasicFileAttributes.class); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - imageFileAttributes = attrs; - } - return attrs; - } - - Directory newDirectory(Directory parent, String name) { - Directory dir = Directory.create(parent, name, imageFileAttributes()); - nodes.put(dir.getName(), dir); + dir.setChildren(children); return dir; } - Resource newResource(Directory parent, ImageLocation loc) { - Resource res = Resource.create(parent, loc, imageFileAttributes()); - nodes.put(res.getName(), res); - return res; - } - - LinkNode newLinkNode(Directory dir, String name, Node link) { - LinkNode linkNode = LinkNode.create(dir, name, link); - nodes.put(linkNode.getName(), linkNode); - return linkNode; - } - - Directory makeDirectories(String parent) { - Directory last = rootDir; - for (int offset = parent.indexOf('/', 1); - offset != -1; - offset = parent.indexOf('/', offset + 1)) { - String dir = parent.substring(0, offset); - last = makeDirectory(dir, last); + /** + * Completes a package directory by setting the list of child nodes. + * + *

The given directory can be the top level {@code /packages} directory, + * so it is NOT safe to use {@code isPackagesSubdirectory(loc)} here. + */ + private Directory completePackageDirectory(Directory dir, ImageLocation loc) { + assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; + // The only directories in the "/packages" namespace are "/packages" or + // "/packages/". However, unlike "/modules" directories, the + // location offsets mean different things. + List children; + if (dir.getName().equals(PACKAGES_ROOT)) { + // Top-level directory just contains a list of subdirectories. + children = createChildNodes(loc, c -> nodes.computeIfAbsent(c.getFullName(), this::newDirectory)); + } else { + // A package directory's content is array of offset PAIRS in the + // Strings table, but we only need the 2nd value of each pair. + IntBuffer intBuffer = getOffsetBuffer(loc); + int offsetCount = intBuffer.capacity(); + assert (offsetCount & 0x1) == 0 : "Offset count must be even: " + offsetCount; + children = new ArrayList<>(offsetCount / 2); + // Iterate the 2nd offset in each pair (odd indices). + for (int i = 1; i < offsetCount; i += 2) { + String moduleName = getString(intBuffer.get(i)); + children.add(nodes.computeIfAbsent( + dir.getName() + "/" + moduleName, + n -> newLinkNode(n, MODULES_ROOT + "/" + moduleName))); + } } - return makeDirectory(parent, last); - + // This only happens once and "completes" the directory. + dir.setChildren(children); + return dir; } - Directory makeDirectory(String dir, Directory last) { - Directory nextDir = (Directory) nodes.get(dir); - if (nextDir == null) { - nextDir = newDirectory(last, dir); + /** + * Creates the list of child nodes for a {@code Directory} based on a given + * + *

Note: This cannot be used for package subdirectories as they have + * child offsets stored differently to other directories. + */ + private List createChildNodes(ImageLocation loc, Function newChildFn) { + IntBuffer offsets = getOffsetBuffer(loc); + int childCount = offsets.capacity(); + List children = new ArrayList<>(childCount); + for (int i = 0; i < childCount; i++) { + children.add(newChildFn.apply(getLocation(offsets.get(i)))); } - return nextDir; + return children; } - byte[] getResource(Node node) throws IOException { + /** Helper to extract the integer offset buffer from a directory location. */ + private IntBuffer getOffsetBuffer(ImageLocation dir) { + assert !isResource(dir) : "Not a directory: " + dir.getFullName(); + byte[] offsets = getResource(dir); + ByteBuffer buffer = ByteBuffer.wrap(offsets); + buffer.order(getByteOrder()); + return buffer.asIntBuffer(); + } + + /** + * Efficiently determines if an image location is a resource. + * + *

A resource must have a valid module associated with it, so its + * module offset must be non-zero, and not equal to the offsets for + * "/modules/..." or "/packages/..." entries. + */ + private boolean isResource(ImageLocation loc) { + int moduleOffset = loc.getModuleOffset(); + return moduleOffset != 0 + && moduleOffset != modulesStringOffset + && moduleOffset != packagesStringOffset; + } + + /** + * Determines if an image location is a directory in the {@code /modules} + * namespace (if so, the location name is the node name). + * + *

In jimage, every {@code ImageLocation} under {@code /modules/} is a + * directory and has the same value for {@code getModule()}, and {@code + * getModuleOffset()}. + */ + private boolean isModulesSubdirectory(ImageLocation loc) { + return loc.getModuleOffset() == modulesStringOffset; + } + + /** + * Creates an "incomplete" directory node with no child nodes set. + * Directories need to be "completed" before they are returned by + * {@link #findNode(String)}. + */ + private Directory newDirectory(String name) { + return new Directory(name, imageFileAttributes); + } + + /** + * Creates a new resource from an image location. This is the only case + * where the image location name does not match the requested node name. + * In image files, resource locations are NOT prefixed by {@code /modules}. + */ + private Resource newResource(String name, ImageLocation loc) { + assert name.equals(loc.getFullName(true)) : "Mismatched location for resource: " + name; + return new Resource(name, loc, imageFileAttributes); + } + + /** + * Creates a new link node pointing at the given target name. + * + *

Note that target node is resolved each time {@code resolve()} is called, + * so if a link node is retained after its reader is closed, it will fail. + */ + private LinkNode newLinkNode(String name, String targetName) { + return new LinkNode(name, () -> findNode(targetName), imageFileAttributes); + } + + /** Returns the content of a resource node. */ + private byte[] getResource(Node node) throws IOException { + // We could have been given a non-resource node here. if (node.isResource()) { return super.getResource(node.getLocation()); } throw new IOException("Not a resource: " + node); } - - byte[] getResource(Resource rs) throws IOException { - return super.getResource(rs.getLocation()); - } } - // jimage file does not store directory structure. We build nodes - // using the "path" strings found in the jimage file. - // Node can be a directory or a resource + /** + * A directory, resource or symbolic link. + * + *

Node Equality

+ * + * Nodes are identified solely by their name, and it is not valid to attempt + * to compare nodes from different reader instances. Different readers may + * produce nodes with the same names, but different contents. + * + *

Furthermore, since a {@link ImageReader} provides "perfect" caching of + * nodes, equality of nodes from the same reader is equivalent to instance + * identity. + */ public abstract static class Node { - private static final int ROOT_DIR = 0b0000_0000_0000_0001; - private static final int PACKAGES_DIR = 0b0000_0000_0000_0010; - private static final int MODULES_DIR = 0b0000_0000_0000_0100; - - private int flags; private final String name; private final BasicFileAttributes fileAttrs; - private boolean completed; + /** + * Creates an abstract {@code Node}, which is either a resource, directory + * or symbolic link. + * + *

This constructor is only non-private so it can be used by the + * {@code ExplodedImage} class, and must not be used otherwise. + */ protected Node(String name, BasicFileAttributes fileAttrs) { this.name = Objects.requireNonNull(name); this.fileAttrs = Objects.requireNonNull(fileAttrs); } + // A node is completed when all its direct children have been built. + // As such, non-directory nodes are always complete. + boolean isCompleted() { + return true; + } + + // Only resources can return a location. + ImageLocation getLocation() { + throw new IllegalStateException("not a resource: " + getName()); + } + /** - * A node is completed when all its direct children have been built. + * Returns the name of this node (e.g. {@code + * "/modules/java.base/java/lang/Object.class"} or {@code + * "/packages/java.lang"}). * - * @return + *

Note that for resource nodes this is NOT the underlying jimage + * resource name (it is prefixed with {@code "/modules"}). */ - public boolean isCompleted() { - return completed; - } - - public void setCompleted(boolean completed) { - this.completed = completed; - } - - public final void setIsRootDir() { - flags |= ROOT_DIR; - } - - public final boolean isRootDir() { - return (flags & ROOT_DIR) != 0; - } - - public final void setIsPackagesDir() { - flags |= PACKAGES_DIR; - } - - public final boolean isPackagesDir() { - return (flags & PACKAGES_DIR) != 0; - } - - public final void setIsModulesDir() { - flags |= MODULES_DIR; - } - - public final boolean isModulesDir() { - return (flags & MODULES_DIR) != 0; - } - public final String getName() { return name; } + /** + * Returns file attributes for this node. The value returned may be the + * same for all nodes, and should not be relied upon for accuracy. + */ public final BasicFileAttributes getFileAttributes() { return fileAttrs; } - // resolve this Node (if this is a soft link, get underlying Node) + /** + * Resolves a symbolic link to its target node. If this code is not a + * symbolic link, then it resolves to itself. + */ public final Node resolveLink() { return resolveLink(false); } + /** + * Resolves a symbolic link to its target node. If this code is not a + * symbolic link, then it resolves to itself. + */ public Node resolveLink(boolean recursive) { return this; } - // is this a soft link Node? + /** Returns whether this node is a symbolic link. */ public boolean isLink() { return false; } + /** + * Returns whether this node is a directory. Directory nodes can have + * {@link #getChildNames()} invoked to get the fully qualified names + * of any child nodes. + */ public boolean isDirectory() { return false; } - public List getChildren() { - throw new IllegalArgumentException("not a directory: " + getNameString()); - } - + /** + * Returns whether this node is a resource. Resource nodes can have + * their contents obtained via {@link ImageReader#getResource(Node)} + * or {@link ImageReader#getResourceBuffer(Node)}. + */ public boolean isResource() { return false; } - public ImageLocation getLocation() { - throw new IllegalArgumentException("not a resource: " + getNameString()); + /** + * Returns the fully qualified names of any child nodes for a directory. + * + *

By default, this method throws {@link IllegalStateException} and + * is overridden for directories. + */ + public Stream getChildNames() { + throw new IllegalStateException("not a directory: " + getName()); } + /** + * Returns the uncompressed size of this node's content. If this node is + * not a resource, this method returns zero. + */ public long size() { return 0L; } + /** + * Returns the compressed size of this node's content. If this node is + * not a resource, this method returns zero. + */ public long compressedSize() { return 0L; } + /** + * Returns the extension string of a resource node. If this node is not + * a resource, this method returns null. + */ public String extension() { return null; } - public long contentOffset() { - return 0L; - } - - public final FileTime creationTime() { - return fileAttrs.creationTime(); - } - - public final FileTime lastAccessTime() { - return fileAttrs.lastAccessTime(); - } - - public final FileTime lastModifiedTime() { - return fileAttrs.lastModifiedTime(); - } - - public final String getNameString() { - return name; - } - @Override public final String toString() { - return getNameString(); + return getName(); } + /** See Node Equality. */ @Override public final int hashCode() { return name.hashCode(); } + /** See Node Equality. */ @Override public final boolean equals(Object other) { if (this == other) { @@ -729,21 +692,40 @@ public final class ImageReader implements AutoCloseable { } } - // directory node - directory has full path name without '/' at end. - static final class Directory extends Node { - private final List children; + /** + * Directory node (referenced from a full path, without a trailing '/'). + * + *

Directory nodes have two distinct states: + *

    + *
  • Incomplete: The child list has not been set. + *
  • Complete: The child list has been set. + *
+ * + *

When a directory node is returned by {@link ImageReader#findNode(String)} + * it is always complete, but this DOES NOT mean that its child nodes are + * complete yet. + * + *

To avoid users being able to access incomplete child nodes, the + * {@code Node} API offers only a way to obtain child node names, forcing + * callers to invoke {@code findNode()} if they need to access the child + * node itself. + * + *

This approach allows directories to be implemented lazily with respect + * to child nodes, while retaining efficiency when child nodes are accessed + * (since any incomplete nodes will be created and placed in the node cache + * when the parent was first returned to the user). + */ + private static final class Directory extends Node { + // Monotonic reference, will be set to the unmodifiable child list exactly once. + private List children = null; private Directory(String name, BasicFileAttributes fileAttrs) { super(name, fileAttrs); - children = new ArrayList<>(); } - static Directory create(Directory parent, String name, BasicFileAttributes fileAttrs) { - Directory d = new Directory(name, fileAttrs); - if (parent != null) { - parent.addChild(d); - } - return d; + @Override + boolean isCompleted() { + return children != null; } @Override @@ -752,46 +734,41 @@ public final class ImageReader implements AutoCloseable { } @Override - public List getChildren() { - return Collections.unmodifiableList(children); - } - - void addChild(Node node) { - assert !children.contains(node) : "Child " + node + " already added"; - children.add(node); - } - - public void walk(Consumer consumer) { - consumer.accept(this); - for (Node child : children) { - if (child.isDirectory()) { - ((Directory)child).walk(consumer); - } else { - consumer.accept(child); - } + public Stream getChildNames() { + if (children != null) { + return children.stream().map(Node::getName); } + throw new IllegalStateException("Cannot get child nodes of an incomplete directory: " + getName()); + } + + private void setChildren(List children) { + assert this.children == null : this + ": Cannot set child nodes twice!"; + this.children = Collections.unmodifiableList(children); } } - - // "resource" is .class or any other resource (compressed/uncompressed) in a jimage. - // full path of the resource is the "name" of the resource. - static class Resource extends Node { + /** + * Resource node (e.g. a ".class" entry, or any other data resource). + * + *

Resources are leaf nodes referencing an underlying image location. They + * are lightweight, and do not cache their contents. + * + *

Unlike directories (where the node name matches the jimage path for the + * corresponding {@code ImageLocation}), resource node names are NOT the same + * as the corresponding jimage path. The difference is that node names for + * resources are prefixed with "/modules", which is missing from the + * equivalent jimage path. + */ + private static class Resource extends Node { private final ImageLocation loc; - private Resource(ImageLocation loc, BasicFileAttributes fileAttrs) { - super(loc.getFullName(true), fileAttrs); + private Resource(String name, ImageLocation loc, BasicFileAttributes fileAttrs) { + super(name, fileAttrs); this.loc = loc; } - static Resource create(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) { - Resource rs = new Resource(loc, fileAttrs); - parent.addChild(rs); - return rs; - } - @Override - public boolean isCompleted() { - return true; + ImageLocation getLocation() { + return loc; } @Override @@ -799,11 +776,6 @@ public final class ImageReader implements AutoCloseable { return true; } - @Override - public ImageLocation getLocation() { - return loc; - } - @Override public long size() { return loc.getUncompressedSize(); @@ -818,36 +790,29 @@ public final class ImageReader implements AutoCloseable { public String extension() { return loc.getExtension(); } - - @Override - public long contentOffset() { - return loc.getContentOffset(); - } } - // represents a soft link to another Node - static class LinkNode extends Node { - private final Node link; + /** + * Link node (a symbolic link to a top-level modules directory). + * + *

Link nodes resolve their target by invoking a given supplier, and do + * not cache the result. Since nodes are cached by the {@code ImageReader}, + * this means that only the first call to {@link #resolveLink(boolean)} + * could do any significant work. + */ + private static class LinkNode extends Node { + private final Supplier link; - private LinkNode(String name, Node link) { - super(name, link.getFileAttributes()); + private LinkNode(String name, Supplier link, BasicFileAttributes fileAttrs) { + super(name, fileAttrs); this.link = link; } - static LinkNode create(Directory parent, String name, Node link) { - LinkNode ln = new LinkNode(name, link); - parent.addChild(ln); - return ln; - } - - @Override - public boolean isCompleted() { - return true; - } - @Override public Node resolveLink(boolean recursive) { - return (recursive && link instanceof LinkNode) ? ((LinkNode)link).resolveLink(true) : link; + // No need to use or propagate the recursive flag, since the target + // cannot possibly be a link node (links only point to directories). + return link.get(); } @Override diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java b/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java index e2c17f8ca25..87a00da4393 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java @@ -119,9 +119,9 @@ class ExplodedImage extends SystemImage { } @Override - public List getChildren() { + public Stream getChildNames() { if (!isDirectory()) - throw new IllegalArgumentException("not a directory: " + getNameString()); + throw new IllegalArgumentException("not a directory: " + getName()); if (children == null) { List list = new ArrayList<>(); try (DirectoryStream stream = Files.newDirectoryStream(path)) { @@ -138,7 +138,7 @@ class ExplodedImage extends SystemImage { } children = list; } - return children; + return children.stream().map(Node::getName); } @Override diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java index f0804b58c1c..e1eb1115260 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ final class JrtFileAttributes implements BasicFileAttributes { //-------- basic attributes -------- @Override public FileTime creationTime() { - return node.creationTime(); + return node.getFileAttributes().creationTime(); } @Override @@ -69,12 +69,12 @@ final class JrtFileAttributes implements BasicFileAttributes { @Override public FileTime lastAccessTime() { - return node.lastAccessTime(); + return node.getFileAttributes().lastAccessTime(); } @Override public FileTime lastModifiedTime() { - return node.lastModifiedTime(); + return node.getFileAttributes().lastModifiedTime(); } @Override diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java index 9a8d9d22dfa..6530bd1f90a 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java @@ -54,7 +54,6 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.UserPrincipalLookupService; import java.nio.file.spi.FileSystemProvider; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -64,7 +63,6 @@ import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; import jdk.internal.jimage.ImageReader.Node; -import static java.util.stream.Collectors.toList; /** * jrt file system implementation built on System jimage files. @@ -225,19 +223,19 @@ class JrtFileSystem extends FileSystem { throw new NotDirectoryException(path.getName()); } if (filter == null) { - return node.getChildren() - .stream() - .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) - .iterator(); + return node.getChildNames() + .map(child -> (Path) (path.resolve(new JrtPath(this, child).getFileName()))) + .iterator(); } - return node.getChildren() - .stream() - .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) - .filter(p -> { try { return filter.accept(p); - } catch (IOException x) {} - return false; - }) - .iterator(); + return node.getChildNames() + .map(child -> (Path) (path.resolve(new JrtPath(this, child).getFileName()))) + .filter(p -> { + try { + return filter.accept(p); + } catch (IOException x) {} + return false; + }) + .iterator(); } // returns the content of the file resource specified by the path diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java b/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java index 88cdf724e7d..6813c7e627f 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java @@ -58,7 +58,6 @@ abstract class SystemImage { if (modulesImageExists) { // open a .jimage and build directory structure final ImageReader image = ImageReader.open(moduleImageFile); - image.getRootDirectory(); return new SystemImage() { @Override Node findNode(String path) throws IOException { diff --git a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java index c520e6e636a..09ad3dc456d 100644 --- a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java +++ b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.reflect.Constructor; import java.net.URI; -import java.net.URLConnection; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; @@ -54,7 +53,6 @@ import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import jdk.internal.jimage.ImageLocation; import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReaderFactory; import jdk.internal.access.JavaNetUriAccess; @@ -210,7 +208,7 @@ public final class SystemModuleFinders { } /** - * Parses the module-info.class of all module in the runtime image and + * Parses the {@code module-info.class} of all modules in the runtime image and * returns a ModuleFinder to find the modules. * * @apiNote The returned ModuleFinder is thread safe. @@ -219,20 +217,16 @@ public final class SystemModuleFinders { // parse the module-info.class in every module Map nameToAttributes = new HashMap<>(); Map nameToHash = new HashMap<>(); - ImageReader reader = SystemImage.reader(); - for (String mn : reader.getModuleNames()) { - ImageLocation loc = reader.findLocation(mn, "module-info.class"); - ModuleInfo.Attributes attrs - = ModuleInfo.read(reader.getResourceBuffer(loc), null); - nameToAttributes.put(mn, attrs); + allModuleAttributes().forEach(attrs -> { + nameToAttributes.put(attrs.descriptor().name(), attrs); ModuleHashes hashes = attrs.recordedHashes(); if (hashes != null) { for (String name : hashes.names()) { nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name)); } } - } + }); // create a ModuleReference for each module Set mrefs = new HashSet<>(); @@ -253,6 +247,40 @@ public final class SystemModuleFinders { return new SystemModuleFinder(mrefs, nameToModule); } + /** + * Parses the {@code module-info.class} of all modules in the runtime image and + * returns a stream of {@link ModuleInfo.Attributes Attributes} for them. The + * returned attributes are in no specific order. + */ + private static Stream allModuleAttributes() { + // System-wide image reader. + ImageReader reader = SystemImage.reader(); + try { + return reader.findNode("/modules") + .getChildNames() + .map(mn -> readModuleAttributes(reader, mn)); + } catch (IOException e) { + throw new Error("Error reading root /modules entry", e); + } + } + + /** + * Returns the module's "module-info", returning a holder for its class file + * attributes. Every module is required to have a valid {@code module-info.class}. + */ + private static ModuleInfo.Attributes readModuleAttributes(ImageReader reader, String moduleName) { + Exception err = null; + try { + ImageReader.Node node = reader.findNode(moduleName + "/module-info.class"); + if (node != null && node.isResource()) { + return ModuleInfo.read(reader.getResourceBuffer(node), null); + } + } catch (IOException | UncheckedIOException e) { + err = e; + } + throw new Error("Missing or invalid module-info.class for module: " + moduleName, err); + } + /** * A ModuleFinder that finds module in an array or set of modules. */ @@ -382,34 +410,18 @@ public final class SystemModuleFinders { this.module = module; } - /** - * Returns the ImageLocation for the given resource, {@code null} - * if not found. - */ - private ImageLocation findImageLocation(String name) throws IOException { - Objects.requireNonNull(name); - if (closed) - throw new IOException("ModuleReader is closed"); - ImageReader imageReader = SystemImage.reader(); - if (imageReader != null) { - return imageReader.findLocation(module, name); - } else { - // not an images build - return null; - } - } - /** * Returns {@code true} if the given resource exists, {@code false} * if not found. */ - private boolean containsImageLocation(String name) throws IOException { - Objects.requireNonNull(name); + private boolean containsResource(String resourcePath) throws IOException { + Objects.requireNonNull(resourcePath); if (closed) throw new IOException("ModuleReader is closed"); ImageReader imageReader = SystemImage.reader(); if (imageReader != null) { - return imageReader.verifyLocation(module, name); + ImageReader.Node node = imageReader.findNode("/modules" + resourcePath); + return node != null && node.isResource(); } else { // not an images build return false; @@ -418,8 +430,9 @@ public final class SystemModuleFinders { @Override public Optional find(String name) throws IOException { - if (containsImageLocation(name)) { - URI u = JNUA.create("jrt", "/" + module + "/" + name); + String resourcePath = "/" + module + "/" + name; + if (containsResource(resourcePath)) { + URI u = JNUA.create("jrt", resourcePath); return Optional.of(u); } else { return Optional.empty(); @@ -442,14 +455,25 @@ public final class SystemModuleFinders { } } + /** + * Returns the node for the given resource if found. If the name references + * a non-resource node, then {@code null} is returned. + */ + private ImageReader.Node findResource(ImageReader reader, String name) throws IOException { + Objects.requireNonNull(name); + if (closed) { + throw new IOException("ModuleReader is closed"); + } + String nodeName = "/modules/" + module + "/" + name; + ImageReader.Node node = reader.findNode(nodeName); + return (node != null && node.isResource()) ? node : null; + } + @Override public Optional read(String name) throws IOException { - ImageLocation location = findImageLocation(name); - if (location != null) { - return Optional.of(SystemImage.reader().getResourceBuffer(location)); - } else { - return Optional.empty(); - } + ImageReader reader = SystemImage.reader(); + return Optional.ofNullable(findResource(reader, name)) + .map(reader::getResourceBuffer); } @Override @@ -481,7 +505,7 @@ public final class SystemModuleFinders { private static class ModuleContentSpliterator implements Spliterator { final String moduleRoot; final Deque stack; - Iterator iterator; + Iterator iterator; ModuleContentSpliterator(String module) throws IOException { moduleRoot = "/modules/" + module; @@ -502,13 +526,10 @@ public final class SystemModuleFinders { private String next() throws IOException { for (;;) { while (iterator.hasNext()) { - ImageReader.Node node = iterator.next(); - String name = node.getName(); + String name = iterator.next(); + ImageReader.Node node = SystemImage.reader().findNode(name); if (node.isDirectory()) { - // build node - ImageReader.Node dir = SystemImage.reader().findNode(name); - assert dir.isDirectory(); - stack.push(dir); + stack.push(node); } else { // strip /modules/$MODULE/ prefix return name.substring(moduleRoot.length() + 1); @@ -520,7 +541,7 @@ public final class SystemModuleFinders { } else { ImageReader.Node dir = stack.poll(); assert dir.isDirectory(); - iterator = dir.getChildren().iterator(); + iterator = dir.getChildNames().iterator(); } } } diff --git a/test/jdk/jdk/internal/jimage/ImageReaderTest.java b/test/jdk/jdk/internal/jimage/ImageReaderTest.java new file mode 100644 index 00000000000..6bdf0cf479a --- /dev/null +++ b/test/jdk/jdk/internal/jimage/ImageReaderTest.java @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.internal.jimage.ImageReader; +import jdk.internal.jimage.ImageReader.Node; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.util.JarBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.opentest4j.TestSkippedException; +import tests.Helper; +import tests.JImageGenerator; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; + +/* + * @test + * @summary Tests for ImageReader. + * @modules java.base/jdk.internal.jimage + * jdk.jlink/jdk.tools.jimage + * @library /test/jdk/tools/lib + * /test/lib + * @build tests.* + * @run junit/othervm ImageReaderTest + */ + +/// Using PER_CLASS lifecycle means the (expensive) image file is only build once. +/// There is no mutable test instance state to worry about. +@TestInstance(PER_CLASS) +public class ImageReaderTest { + + private static final Map> IMAGE_ENTRIES = Map.of( + "modfoo", Arrays.asList( + "com.foo.Alpha", + "com.foo.Beta", + "com.foo.bar.Gamma"), + "modbar", Arrays.asList( + "com.bar.One", + "com.bar.Two")); + private final Path image = buildJImage(IMAGE_ENTRIES); + + @ParameterizedTest + @ValueSource(strings = { + "/", + "/modules", + "/modules/modfoo", + "/modules/modbar", + "/modules/modfoo/com", + "/modules/modfoo/com/foo", + "/modules/modfoo/com/foo/bar"}) + public void testModuleDirectories_expected(String name) throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + assertDir(reader, name); + } + } + + @ParameterizedTest + @ValueSource(strings = { + "", + "//", + "/modules/", + "/modules/unknown", + "/modules/modbar/", + "/modules/modfoo//com", + "/modules/modfoo/com/"}) + public void testModuleNodes_absent(String name) throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + assertAbsent(reader, name); + } + } + + @Test + public void testModuleResources() throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + assertNode(reader, "/modules/modfoo/com/foo/Alpha.class"); + assertNode(reader, "/modules/modbar/com/bar/One.class"); + + ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet()); + assertEquals("Class: com.foo.Alpha", loader.loadAndGetToString("modfoo", "com.foo.Alpha")); + assertEquals("Class: com.foo.Beta", loader.loadAndGetToString("modfoo", "com.foo.Beta")); + assertEquals("Class: com.foo.bar.Gamma", loader.loadAndGetToString("modfoo", "com.foo.bar.Gamma")); + assertEquals("Class: com.bar.One", loader.loadAndGetToString("modbar", "com.bar.One")); + } + } + + @Test + public void testPackageDirectories() throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + Node root = assertDir(reader, "/packages"); + Set pkgNames = root.getChildNames().collect(Collectors.toSet()); + assertTrue(pkgNames.contains("/packages/com")); + assertTrue(pkgNames.contains("/packages/com.foo")); + assertTrue(pkgNames.contains("/packages/com.bar")); + + // Even though no classes exist directly in the "com" package, it still + // creates a directory with links back to all the modules which contain it. + Set comLinks = assertDir(reader, "/packages/com").getChildNames().collect(Collectors.toSet()); + assertTrue(comLinks.contains("/packages/com/modfoo")); + assertTrue(comLinks.contains("/packages/com/modbar")); + } + } + + @Test + public void testPackageLinks() throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + Node moduleFoo = assertDir(reader, "/modules/modfoo"); + Node moduleBar = assertDir(reader, "/modules/modbar"); + assertSame(assertLink(reader, "/packages/com.foo/modfoo").resolveLink(), moduleFoo); + assertSame(assertLink(reader, "/packages/com.bar/modbar").resolveLink(), moduleBar); + } + } + + private static ImageReader.Node assertNode(ImageReader reader, String name) throws IOException { + ImageReader.Node node = reader.findNode(name); + assertNotNull(node, "Could not find node: " + name); + return node; + } + + private static ImageReader.Node assertDir(ImageReader reader, String name) throws IOException { + ImageReader.Node dir = assertNode(reader, name); + assertTrue(dir.isDirectory(), "Node was not a directory: " + name); + return dir; + } + + private static ImageReader.Node assertLink(ImageReader reader, String name) throws IOException { + ImageReader.Node link = assertNode(reader, name); + assertTrue(link.isLink(), "Node was not a symbolic link: " + name); + return link; + } + + private static void assertAbsent(ImageReader reader, String name) throws IOException { + assertNull(reader.findNode(name), "Should not be able to find node: " + name); + } + + /// Builds a jimage file with the specified class entries. The classes in the built + /// image can be loaded and executed to return their names via `toString()` to confirm + /// the correct bytes were returned. + public static Path buildJImage(Map> entries) { + Helper helper = getHelper(); + Path outDir = helper.createNewImageDir("test"); + JImageGenerator.JLinkTask jlink = JImageGenerator.getJLinkTask() + .modulePath(helper.defaultModulePath()) + .output(outDir); + + Path jarDir = helper.getJarDir(); + entries.forEach((module, classes) -> { + JarBuilder jar = new JarBuilder(jarDir.resolve(module + ".jar").toString()); + String moduleInfo = "module " + module + " {}"; + jar.addEntry("module-info.class", InMemoryJavaCompiler.compile("module-info", moduleInfo)); + + classes.forEach(fqn -> { + int lastDot = fqn.lastIndexOf('.'); + String pkg = fqn.substring(0, lastDot); + String cls = fqn.substring(lastDot + 1); + + String path = fqn.replace('.', '/') + ".class"; + String source = String.format( + """ + package %s; + public class %s { + public String toString() { + return "Class: %s"; + } + } + """, pkg, cls, fqn); + jar.addEntry(path, InMemoryJavaCompiler.compile(fqn, source)); + }); + try { + jar.build(); + } catch (IOException e) { + throw new RuntimeException(e); + } + jlink.addMods(module); + }); + return jlink.call().assertSuccess().resolve("lib", "modules"); + } + + /// Returns the helper for building JAR and jimage files. + private static Helper getHelper() { + try { + Helper helper = Helper.newHelper(); + if (helper == null) { + throw new TestSkippedException("Cannot create test helper (exploded image?)"); + } + return helper; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /// Loads and performs actions on classes stored in a given `ImageReader`. + private static class ImageClassLoader extends ClassLoader { + private final ImageReader reader; + private final Set testModules; + + private ImageClassLoader(ImageReader reader, Set testModules) { + this.reader = reader; + this.testModules = testModules; + } + + @FunctionalInterface + public interface ClassAction { + R call(Class cls) throws T; + } + + String loadAndGetToString(String module, String fqn) { + return loadAndCall(module, fqn, c -> c.getDeclaredConstructor().newInstance().toString()); + } + + R loadAndCall(String module, String fqn, ClassAction action) { + Class cls = findClass(module, fqn); + assertNotNull(cls, "Could not load class: " + module + "/" + fqn); + try { + return action.call(cls); + } catch (Exception e) { + fail("Class loading failed", e); + return null; + } + } + + @Override + protected Class findClass(String module, String fqn) { + assumeTrue(testModules.contains(module), "Can only load classes in modules: " + testModules); + String name = "/modules/" + module + "/" + fqn.replace('.', '/') + ".class"; + Class cls = findLoadedClass(fqn); + if (cls == null) { + try { + ImageReader.Node node = reader.findNode(name); + if (node != null && node.isResource()) { + byte[] classBytes = reader.getResource(node); + cls = defineClass(fqn, classBytes, 0, classBytes.length); + resolveClass(cls); + return cls; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return null; + } + } +} diff --git a/test/jdk/jdk/internal/jimage/JImageReadTest.java b/test/jdk/jdk/internal/jimage/JImageReadTest.java index ea700d03a4f..35fb2adb687 100644 --- a/test/jdk/jdk/internal/jimage/JImageReadTest.java +++ b/test/jdk/jdk/internal/jimage/JImageReadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,9 @@ import org.testng.annotations.Test; import org.testng.Assert; import org.testng.TestNG; +import static java.nio.ByteOrder.BIG_ENDIAN; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + @Test public class JImageReadTest { @@ -333,32 +336,21 @@ public class JImageReadTest { */ @Test static void test5_imageReaderEndianness() throws IOException { - ImageReader nativeReader = ImageReader.open(imageFile); - Assert.assertEquals(nativeReader.getByteOrder(), ByteOrder.nativeOrder()); - - try { - ImageReader leReader = ImageReader.open(imageFile, ByteOrder.LITTLE_ENDIAN); - Assert.assertEquals(leReader.getByteOrder(), ByteOrder.LITTLE_ENDIAN); - leReader.close(); - } catch (IOException io) { - // IOException expected if LITTLE_ENDIAN not the nativeOrder() - Assert.assertNotEquals(ByteOrder.nativeOrder(), ByteOrder.LITTLE_ENDIAN); + // Will be opened with native byte order. + try (ImageReader nativeReader = ImageReader.open(imageFile)) { + // Just ensure something works as expected. + Assert.assertNotNull(nativeReader.findNode("/")); + } catch (IOException expected) { + Assert.fail("Reader should be openable with native byte order."); } - try { - ImageReader beReader = ImageReader.open(imageFile, ByteOrder.BIG_ENDIAN); - Assert.assertEquals(beReader.getByteOrder(), ByteOrder.BIG_ENDIAN); - beReader.close(); - } catch (IOException io) { - // IOException expected if LITTLE_ENDIAN not the nativeOrder() - Assert.assertNotEquals(ByteOrder.nativeOrder(), ByteOrder.BIG_ENDIAN); - } - - nativeReader.close(); + // Reader should not be openable with the wrong byte order. + ByteOrder otherOrder = ByteOrder.nativeOrder() == BIG_ENDIAN ? LITTLE_ENDIAN : BIG_ENDIAN; + Assert.assertThrows(IOException.class, () -> ImageReader.open(imageFile, otherOrder)); } - // main method to run standalone from jtreg - @Test(enabled=false) + // main method to run standalone from jtreg + @Test(enabled = false) @Parameters({"x"}) @SuppressWarnings("raw_types") public static void main(@Optional String[] args) { diff --git a/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java b/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java index bec32bee0f8..8656a4a3d00 100644 --- a/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java +++ b/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java @@ -68,17 +68,17 @@ public class ImageReaderDuplicateChildNodesTest { + " in " + imagePath); } // now verify that the parent node which is a directory, doesn't have duplicate children - final List children = parent.getChildren(); - if (children == null || children.isEmpty()) { + final List childNames = parent.getChildNames().toList(); + if (childNames.isEmpty()) { throw new RuntimeException("ImageReader did not return any child resources under " + integersParentResource + " in " + imagePath); } final Set uniqueChildren = new HashSet<>(); - for (final ImageReader.Node child : children) { - final boolean unique = uniqueChildren.add(child); + for (final String childName : childNames) { + final boolean unique = uniqueChildren.add(reader.findNode(childName)); if (!unique) { throw new RuntimeException("ImageReader returned duplicate child resource " - + child + " under " + parent + " from image " + imagePath); + + childName + " under " + parent + " from image " + imagePath); } } } diff --git a/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java b/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java index c58a4dc33b2..4f97e12171f 100644 --- a/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java @@ -195,9 +195,9 @@ public class ImageReaderBenchmark { static long countAllNodes(ImageReader reader, Node node) { long count = 1; if (node.isDirectory()) { - count += node.getChildren().stream().mapToLong(n -> { + count += node.getChildNames().mapToLong(n -> { try { - return countAllNodes(reader, reader.findNode(n.getName())); + return countAllNodes(reader, reader.findNode(n)); } catch (IOException e) { throw new RuntimeException(e); } From f155f7d6e50c702f65858774cfd02ef60aa9cad5 Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Tue, 12 Aug 2025 08:45:02 +0000 Subject: [PATCH 008/112] 8364141: Remove LockingMode related code from x86 Reviewed-by: aboldtch, dholmes, coleenp --- src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp | 16 +- src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp | 2 +- src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp | 79 +---- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 272 +++--------------- src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp | 7 +- src/hotspot/cpu/x86/interp_masm_x86.cpp | 169 ++--------- src/hotspot/cpu/x86/sharedRuntime_x86.cpp | 15 +- src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 79 +---- .../x86/templateInterpreterGenerator_x86.cpp | 25 +- src/hotspot/cpu/x86/x86_64.ad | 27 -- 10 files changed, 91 insertions(+), 600 deletions(-) diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 0176ff967ce..a30bbe08c55 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -413,11 +413,7 @@ int LIR_Assembler::emit_unwind_handler() { if (method()->is_synchronized()) { monitor_address(0, FrameMap::rax_opr); stub = new MonitorExitStub(FrameMap::rax_opr, true, 0); - if (LockingMode == LM_MONITOR) { - __ jmp(*stub->entry()); - } else { - __ unlock_object(rdi, rsi, rax, *stub->entry()); - } + __ unlock_object(rdi, rsi, rax, *stub->entry()); __ bind(*stub->continuation()); } @@ -2733,15 +2729,9 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register obj = op->obj_opr()->as_register(); // may not be an oop Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); - if (LockingMode == LM_MONITOR) { - if (op->info() != nullptr) { - add_debug_info_for_null_check_here(op->info()); - __ null_check(obj); - } - __ jmp(*op->stub()->entry()); - } else if (op->code() == lir_lock) { + if (op->code() == lir_lock) { assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); - Register tmp = LockingMode == LM_LIGHTWEIGHT ? op->scratch_opr()->as_register() : noreg; + Register tmp = op->scratch_opr()->as_register(); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, tmp, *op->stub()->entry()); if (op->info() != nullptr) { diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 3bd86d9b7e5..1fdfcc4f9cd 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -289,7 +289,7 @@ void LIRGenerator::do_MonitorEnter(MonitorEnter* x) { // this CodeEmitInfo must not have the xhandlers because here the // object is already locked (xhandlers expect object to be unlocked) CodeEmitInfo* info = state_for(x, x->state(), true); - LIR_Opr tmp = LockingMode == LM_LIGHTWEIGHT ? new_register(T_ADDRESS) : LIR_OprFact::illegalOpr; + LIR_Opr tmp = new_register(T_ADDRESS); monitor_enter(obj.result(), lock, syncTempOpr(), tmp, x->monitor_no(), info_for_exception, info); } diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp index c9d103768b8..36efeafa940 100644 --- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp @@ -42,8 +42,6 @@ #include "utilities/globalDefinitions.hpp" int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register tmp, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert(hdr == rax, "hdr must be rax, for the cmpxchg instruction"); assert_different_registers(hdr, obj, disp_hdr, tmp); int null_check_offset = -1; @@ -55,93 +53,20 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr null_check_offset = offset(); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(disp_hdr, obj, hdr, tmp, slow_case); - } else if (LockingMode == LM_LEGACY) { - Label done; - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(hdr, obj, rscratch1); - testb(Address(hdr, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - jcc(Assembler::notZero, slow_case); - } - - // Load object header - movptr(hdr, Address(obj, hdr_offset)); - // and mark it as unlocked - orptr(hdr, markWord::unlocked_value); - // save unlocked object header into the displaced header location on the stack - movptr(Address(disp_hdr, 0), hdr); - // test if object header is still the same (i.e. unlocked), and if so, store the - // displaced header address in the object header - if it is not the same, get the - // object header instead - MacroAssembler::lock(); // must be immediately before cmpxchg! - cmpxchgptr(disp_hdr, Address(obj, hdr_offset)); - // if the object header was the same, we're done - jcc(Assembler::equal, done); - // if the object header was not the same, it is now in the hdr register - // => test if it is a stack pointer into the same stack (recursive locking), i.e.: - // - // 1) (hdr & aligned_mask) == 0 - // 2) rsp <= hdr - // 3) hdr <= rsp + page_size - // - // these 3 tests can be done by evaluating the following expression: - // - // (hdr - rsp) & (aligned_mask - page_size) - // - // assuming both the stack pointer and page_size have their least - // significant 2 bits cleared and page_size is a power of 2 - subptr(hdr, rsp); - andptr(hdr, aligned_mask - (int)os::vm_page_size()); - // for recursive locking, the result is zero => save it in the displaced header - // location (null in the displaced hdr location indicates recursive locking) - movptr(Address(disp_hdr, 0), hdr); - // otherwise we don't care about the result and handle locking via runtime call - jcc(Assembler::notZero, slow_case); - // done - bind(done); - inc_held_monitor_count(); - } + lightweight_lock(disp_hdr, obj, hdr, tmp, slow_case); return null_check_offset; } void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert(disp_hdr == rax, "disp_hdr must be rax, for the cmpxchg instruction"); assert(hdr != obj && hdr != disp_hdr && obj != disp_hdr, "registers must be different"); - Label done; - - if (LockingMode != LM_LIGHTWEIGHT) { - // load displaced header - movptr(hdr, Address(disp_hdr, 0)); - // if the loaded hdr is null we had recursive locking - testptr(hdr, hdr); - // if we had recursive locking, we are done - jcc(Assembler::zero, done); - } // load object movptr(obj, Address(disp_hdr, BasicObjectLock::obj_offset())); verify_oop(obj); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj, disp_hdr, hdr, slow_case); - } else if (LockingMode == LM_LEGACY) { - // test if object header is pointing to the displaced header, and if so, restore - // the displaced header in the object - if the object header is not pointing to - // the displaced header, get the object header instead - MacroAssembler::lock(); // must be immediately before cmpxchg! - cmpxchgptr(hdr, Address(obj, hdr_offset)); - // if the object header was not pointing to the displaced header, - // we do unlocking via runtime call - jcc(Assembler::notEqual, slow_case); - // done - bind(done); - dec_held_monitor_count(); - } + lightweight_unlock(obj, disp_hdr, hdr, slow_case); } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index b5b65171e32..8c3f33e0aca 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -219,244 +219,11 @@ inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vle // obj: object to lock -// box: on-stack box address (displaced header location) - KILLED -// rax,: tmp -- KILLED -// scr: tmp -- KILLED -void C2_MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg, - Register scrReg, Register cx1Reg, Register cx2Reg, Register thread, - Metadata* method_data) { - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight"); - // Ensure the register assignments are disjoint - assert(tmpReg == rax, ""); - assert(cx1Reg == noreg, ""); - assert(cx2Reg == noreg, ""); - assert_different_registers(objReg, boxReg, tmpReg, scrReg); - - // Possible cases that we'll encounter in fast_lock - // ------------------------------------------------ - // * Inflated - // -- unlocked - // -- Locked - // = by self - // = by other - // * neutral - // * stack-locked - // -- by self - // = sp-proximity test hits - // = sp-proximity test generates false-negative - // -- by other - // - - Label IsInflated, DONE_LABEL, NO_COUNT, COUNT; - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmpReg, objReg, scrReg); - testb(Address(tmpReg, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - jcc(Assembler::notZero, DONE_LABEL); - } - - movptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // [FETCH] - testptr(tmpReg, markWord::monitor_value); // inflated vs stack-locked|neutral - jcc(Assembler::notZero, IsInflated); - - if (LockingMode == LM_MONITOR) { - // Clear ZF so that we take the slow path at the DONE label. objReg is known to be not 0. - testptr(objReg, objReg); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Attempt stack-locking ... - orptr (tmpReg, markWord::unlocked_value); - movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS - lock(); - cmpxchgptr(boxReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Updates tmpReg - jcc(Assembler::equal, COUNT); // Success - - // Recursive locking. - // The object is stack-locked: markword contains stack pointer to BasicLock. - // Locked by current thread if difference with current SP is less than one page. - subptr(tmpReg, rsp); - // Next instruction set ZFlag == 1 (Success) if difference is less then one page. - andptr(tmpReg, (int32_t) (7 - (int)os::vm_page_size()) ); - movptr(Address(boxReg, 0), tmpReg); - } - jmp(DONE_LABEL); - - bind(IsInflated); - // The object is inflated. tmpReg contains pointer to ObjectMonitor* + markWord::monitor_value - - // Unconditionally set box->_displaced_header = markWord::unused_mark(). - // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. - movptr(Address(boxReg, 0), checked_cast(markWord::unused_mark().value())); - - // It's inflated and we use scrReg for ObjectMonitor* in this section. - movptr(boxReg, Address(r15_thread, JavaThread::monitor_owner_id_offset())); - movq(scrReg, tmpReg); - xorq(tmpReg, tmpReg); - lock(); - cmpxchgptr(boxReg, Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); - - // Propagate ICC.ZF from CAS above into DONE_LABEL. - jccb(Assembler::equal, COUNT); // CAS above succeeded; propagate ZF = 1 (success) - - cmpptr(boxReg, rax); // Check if we are already the owner (recursive lock) - jccb(Assembler::notEqual, NO_COUNT); // If not recursive, ZF = 0 at this point (fail) - incq(Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - xorq(rax, rax); // Set ZF = 1 (success) for recursive lock, denoting locking success - bind(DONE_LABEL); - - // ZFlag == 1 count in fast path - // ZFlag == 0 count in slow path - jccb(Assembler::notZero, NO_COUNT); // jump if ZFlag == 0 - - bind(COUNT); - if (LockingMode == LM_LEGACY) { - // Count monitors in fast path - increment(Address(thread, JavaThread::held_monitor_count_offset())); - } - xorl(tmpReg, tmpReg); // Set ZF == 1 - - bind(NO_COUNT); - - // At NO_COUNT the icc ZFlag is set as follows ... - // fast_unlock uses the same protocol. - // ZFlag == 1 -> Success - // ZFlag == 0 -> Failure - force control through the slow path -} - -// obj: object to unlock -// box: box address (displaced header location), killed. Must be EAX. -// tmp: killed, cannot be obj nor box. -// -// Some commentary on balanced locking: -// -// fast_lock and fast_unlock are emitted only for provably balanced lock sites. -// Methods that don't have provably balanced locking are forced to run in the -// interpreter - such methods won't be compiled to use fast_lock and fast_unlock. -// The interpreter provides two properties: -// I1: At return-time the interpreter automatically and quietly unlocks any -// objects acquired the current activation (frame). Recall that the -// interpreter maintains an on-stack list of locks currently held by -// a frame. -// I2: If a method attempts to unlock an object that is not held by the -// the frame the interpreter throws IMSX. -// -// Lets say A(), which has provably balanced locking, acquires O and then calls B(). -// B() doesn't have provably balanced locking so it runs in the interpreter. -// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O -// is still locked by A(). -// -// The only other source of unbalanced locking would be JNI. The "Java Native Interface: -// Programmer's Guide and Specification" claims that an object locked by jni_monitorenter -// should not be unlocked by "normal" java-level locking and vice-versa. The specification -// doesn't specify what will occur if a program engages in such mixed-mode locking, however. -// Arguably given that the spec legislates the JNI case as undefined our implementation -// could reasonably *avoid* checking owner in fast_unlock(). -// In the interest of performance we elide m->Owner==Self check in unlock. -// A perfectly viable alternative is to elide the owner check except when -// Xcheck:jni is enabled. - -void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpReg) { - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight"); - assert(boxReg == rax, ""); - assert_different_registers(objReg, boxReg, tmpReg); - - Label DONE_LABEL, Stacked, COUNT, NO_COUNT; - - if (LockingMode == LM_LEGACY) { - cmpptr(Address(boxReg, 0), NULL_WORD); // Examine the displaced header - jcc (Assembler::zero, COUNT); // 0 indicates recursive stack-lock - } - movptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Examine the object's markword - if (LockingMode != LM_MONITOR) { - testptr(tmpReg, markWord::monitor_value); // Inflated? - jcc(Assembler::zero, Stacked); - } - - // It's inflated. - - // Despite our balanced locking property we still check that m->_owner == Self - // as java routines or native JNI code called by this thread might - // have released the lock. - // - // If there's no contention try a 1-0 exit. That is, exit without - // a costly MEMBAR or CAS. See synchronizer.cpp for details on how - // we detect and recover from the race that the 1-0 exit admits. - // - // Conceptually fast_unlock() must execute a STST|LDST "release" barrier - // before it STs null into _owner, releasing the lock. Updates - // to data protected by the critical section must be visible before - // we drop the lock (and thus before any other thread could acquire - // the lock and observe the fields protected by the lock). - // IA32's memory-model is SPO, so STs are ordered with respect to - // each other and there's no need for an explicit barrier (fence). - // See also http://gee.cs.oswego.edu/dl/jmm/cookbook.html. - Label LSuccess, LNotRecursive; - - cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0); - jccb(Assembler::equal, LNotRecursive); - - // Recursive inflated unlock - decrement(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - jmpb(LSuccess); - - bind(LNotRecursive); - - // Set owner to null. - // Release to satisfy the JMM - movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD); - // We need a full fence after clearing owner to avoid stranding. - // StoreLoad achieves this. - membar(StoreLoad); - - // Check if the entry_list is empty. - cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(entry_list)), NULL_WORD); - jccb(Assembler::zero, LSuccess); // If so we are done. - - // Check if there is a successor. - cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD); - jccb(Assembler::notZero, LSuccess); // If so we are done. - - // Save the monitor pointer in the current thread, so we can try to - // reacquire the lock in SharedRuntime::monitor_exit_helper(). - andptr(tmpReg, ~(int32_t)markWord::monitor_value); - movptr(Address(r15_thread, JavaThread::unlocked_inflated_monitor_offset()), tmpReg); - - orl (boxReg, 1); // set ICC.ZF=0 to indicate failure - jmpb (DONE_LABEL); - - bind (LSuccess); - testl (boxReg, 0); // set ICC.ZF=1 to indicate success - jmpb (DONE_LABEL); - - if (LockingMode == LM_LEGACY) { - bind (Stacked); - movptr(tmpReg, Address (boxReg, 0)); // re-fetch - lock(); - cmpxchgptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Uses RAX which is box - // Intentional fall-thru into DONE_LABEL - } - - bind(DONE_LABEL); - - // ZFlag == 1 count in fast path - // ZFlag == 0 count in slow path - jccb(Assembler::notZero, NO_COUNT); - - bind(COUNT); - - if (LockingMode == LM_LEGACY) { - // Count monitors in fast path - decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); - } - - xorl(tmpReg, tmpReg); // Set ZF == 1 - - bind(NO_COUNT); -} - +// box: on-stack box address -- KILLED +// rax: tmp -- KILLED +// t : tmp -- KILLED void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register rax_reg, Register t, Register thread) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert(rax_reg == rax, "Used for CAS"); assert_different_registers(obj, box, rax_reg, t, thread); @@ -616,8 +383,39 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist // C2 uses the value of ZF to determine the continuation. } +// obj: object to lock +// rax: tmp -- KILLED +// t : tmp - cannot be obj nor rax -- KILLED +// +// Some commentary on balanced locking: +// +// fast_lock and fast_unlock are emitted only for provably balanced lock sites. +// Methods that don't have provably balanced locking are forced to run in the +// interpreter - such methods won't be compiled to use fast_lock and fast_unlock. +// The interpreter provides two properties: +// I1: At return-time the interpreter automatically and quietly unlocks any +// objects acquired in the current activation (frame). Recall that the +// interpreter maintains an on-stack list of locks currently held by +// a frame. +// I2: If a method attempts to unlock an object that is not held by the +// frame the interpreter throws IMSX. +// +// Lets say A(), which has provably balanced locking, acquires O and then calls B(). +// B() doesn't have provably balanced locking so it runs in the interpreter. +// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O +// is still locked by A(). +// +// The only other source of unbalanced locking would be JNI. The "Java Native Interface +// Specification" states that an object locked by JNI's MonitorEnter should not be +// unlocked by "normal" java-level locking and vice-versa. The specification doesn't +// specify what will occur if a program engages in such mixed-mode locking, however. +// Arguably given that the spec legislates the JNI case as undefined our implementation +// could reasonably *avoid* checking owner in fast_unlock(). +// In the interest of performance we elide m->Owner==Self check in unlock. +// A perfectly viable alternative is to elide the owner check except when +// Xcheck:jni is enabled. + void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, Register t, Register thread) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert(reg_rax == rax, "Used for CAS"); assert_different_registers(obj, reg_rax, t); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index e1652213688..950fcb75290 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -34,12 +34,7 @@ public: Assembler::AvxVectorLen vector_length_encoding(int vlen_in_bytes); // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. - // See full description in macroAssembler_x86.cpp. - void fast_lock(Register obj, Register box, Register tmp, - Register scr, Register cx1, Register cx2, Register thread, - Metadata* method_data); - void fast_unlock(Register obj, Register box, Register tmp); - + // See full description in c2_MacroAssembler_x86.cpp. void fast_lock_lightweight(Register obj, Register box, Register rax_reg, Register t, Register thread); void fast_unlock_lightweight(Register obj, Register reg_rax, Register t, Register thread); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 92233ee0d07..a6b4efbe4f2 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1024,100 +1024,25 @@ void InterpreterMacroAssembler::get_method_counters(Register method, void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - } else { - Label count_locking, done, slow_case; + Label done, slow_case; - const Register swap_reg = rax; // Must use rax for cmpxchg instruction - const Register tmp_reg = rbx; - const Register obj_reg = c_rarg3; // Will contain the oop - const Register rklass_decode_tmp = rscratch1; + const Register swap_reg = rax; // Must use rax for cmpxchg instruction + const Register tmp_reg = rbx; + const Register obj_reg = c_rarg3; // Will contain the oop - const int obj_offset = in_bytes(BasicObjectLock::obj_offset()); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + - BasicLock::displaced_header_offset_in_bytes(); + // Load object pointer into obj_reg + movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load object pointer into obj_reg - movptr(obj_reg, Address(lock_reg, obj_offset)); + lightweight_lock(lock_reg, obj_reg, swap_reg, tmp_reg, slow_case); + jmp(done); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(lock_reg, obj_reg, swap_reg, tmp_reg, slow_case); - } else if (LockingMode == LM_LEGACY) { - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp_reg, obj_reg, rklass_decode_tmp); - testb(Address(tmp_reg, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - jcc(Assembler::notZero, slow_case); - } + bind(slow_case); - // Load immediate 1 into swap_reg %rax - movl(swap_reg, 1); - - // Load (object->mark() | 1) into swap_reg %rax - orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - // Save (object->mark() | 1) into BasicLock's displaced header - movptr(Address(lock_reg, mark_offset), swap_reg); - - assert(lock_offset == 0, - "displaced header must be first word in BasicObjectLock"); - - lock(); - cmpxchgptr(lock_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - jcc(Assembler::zero, count_locking); - - const int zero_bits = 7; - - // Fast check for recursive lock. - // - // Can apply the optimization only if this is a stack lock - // allocated in this thread. For efficiency, we can focus on - // recently allocated stack locks (instead of reading the stack - // base and checking whether 'mark' points inside the current - // thread stack): - // 1) (mark & zero_bits) == 0, and - // 2) rsp <= mark < mark + os::pagesize() - // - // Warning: rsp + os::pagesize can overflow the stack base. We must - // neither apply the optimization for an inflated lock allocated - // just above the thread stack (this is why condition 1 matters) - // nor apply the optimization if the stack lock is inside the stack - // of another thread. The latter is avoided even in case of overflow - // because we have guard pages at the end of all stacks. Hence, if - // we go over the stack base and hit the stack of another thread, - // this should not be in a writeable area that could contain a - // stack lock allocated by that thread. As a consequence, a stack - // lock less than page size away from rsp is guaranteed to be - // owned by the current thread. - // - // These 3 tests can be done by evaluating the following - // expression: ((mark - rsp) & (zero_bits - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant bits clear. - // NOTE: the mark is in swap_reg %rax as the result of cmpxchg - subptr(swap_reg, rsp); - andptr(swap_reg, zero_bits - (int)os::vm_page_size()); - - // Save the test result, for recursive case, the result is zero - movptr(Address(lock_reg, mark_offset), swap_reg); - jcc(Assembler::notZero, slow_case); - - bind(count_locking); - inc_held_monitor_count(); - } - jmp(done); - - bind(slow_case); - - // Call the runtime routine for slow case - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - bind(done); - } + // Call the runtime routine for slow case + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); + bind(done); } @@ -1136,63 +1061,31 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) { void InterpreterMacroAssembler::unlock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - } else { - Label count_locking, done, slow_case; + Label done, slow_case; - const Register swap_reg = rax; // Must use rax for cmpxchg instruction - const Register header_reg = c_rarg2; // Will contain the old oopMark - const Register obj_reg = c_rarg3; // Will contain the oop + const Register swap_reg = rax; // Must use rax for cmpxchg instruction + const Register header_reg = c_rarg2; // Will contain the old oopMark + const Register obj_reg = c_rarg3; // Will contain the oop - save_bcp(); // Save in case of exception + save_bcp(); // Save in case of exception - if (LockingMode != LM_LIGHTWEIGHT) { - // Convert from BasicObjectLock structure to object and BasicLock - // structure Store the BasicLock address into %rax - lea(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset())); - } + // Load oop into obj_reg(%c_rarg3) + movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load oop into obj_reg(%c_rarg3) - movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); + // Free entry + movptr(Address(lock_reg, BasicObjectLock::obj_offset()), NULL_WORD); - // Free entry - movptr(Address(lock_reg, BasicObjectLock::obj_offset()), NULL_WORD); + lightweight_unlock(obj_reg, swap_reg, header_reg, slow_case); + jmp(done); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj_reg, swap_reg, header_reg, slow_case); - } else if (LockingMode == LM_LEGACY) { - // Load the old header from BasicLock structure - movptr(header_reg, Address(swap_reg, - BasicLock::displaced_header_offset_in_bytes())); + bind(slow_case); + // Call the runtime routine for slow case. + movptr(Address(lock_reg, BasicObjectLock::obj_offset()), obj_reg); // restore obj + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - // Test for recursion - testptr(header_reg, header_reg); + bind(done); - // zero for recursive case - jcc(Assembler::zero, count_locking); - - // Atomic swap back the old header - lock(); - cmpxchgptr(header_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - // zero for simple unlock of a stack-lock case - jcc(Assembler::notZero, slow_case); - - bind(count_locking); - dec_held_monitor_count(); - } - jmp(done); - - bind(slow_case); - // Call the runtime routine for slow case. - movptr(Address(lock_reg, BasicObjectLock::obj_offset()), obj_reg); // restore obj - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - - bind(done); - - restore_bcp(); - } + restore_bcp(); } void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86.cpp index b8a4b829159..17fdfa61d36 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86.cpp @@ -59,17 +59,10 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas __ movptr(result, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - if (LockingMode == LM_LIGHTWEIGHT) { - if (!UseObjectMonitorTable) { - // check if monitor - __ testptr(result, markWord::monitor_value); - __ jcc(Assembler::notZero, slowCase); - } - } else { - // check if locked - __ testptr(result, markWord::unlocked_value); - __ jcc(Assembler::zero, slowCase); + if (!UseObjectMonitorTable) { + // check if monitor + __ testptr(result, markWord::monitor_value); + __ jcc(Assembler::notZero, slowCase); } // get hash diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 7c7ca61f1ae..d60f535fefc 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -2133,7 +2133,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // We use the same pc/oopMap repeatedly when we call out Label native_return; - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // For convenience we use the pc we want to resume to in case of preemption on Object.wait. __ set_last_Java_frame(rsp, noreg, native_return, rscratch1); } else { @@ -2174,16 +2174,11 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, const Register swap_reg = rax; // Must use rax for cmpxchg instruction const Register obj_reg = rbx; // Will contain the oop const Register lock_reg = r13; // Address of compiler lock object (BasicLock) - const Register old_hdr = r13; // value of old header at unlock time Label slow_path_lock; Label lock_done; if (method->is_synchronized()) { - Label count_mon; - - const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); - // Get the handle (the 2nd argument) __ mov(oop_handle_reg, c_rarg1); @@ -2194,47 +2189,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Load the oop from the handle __ movptr(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_MONITOR) { - __ jmp(slow_path_lock); - } else if (LockingMode == LM_LEGACY) { - // Load immediate 1 into swap_reg %rax - __ movl(swap_reg, 1); - - // Load (object->mark() | 1) into swap_reg %rax - __ orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - // Save (object->mark() | 1) into BasicLock's displaced header - __ movptr(Address(lock_reg, mark_word_offset), swap_reg); - - // src -> dest iff dest == rax else rax <- dest - __ lock(); - __ cmpxchgptr(lock_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ jcc(Assembler::equal, count_mon); - - // Hmm should this move to the slow path code area??? - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 3) == 0, and - // 2) rsp <= mark < mark + os::pagesize() - // These 3 tests can be done by evaluating the following - // expression: ((mark - rsp) & (3 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 2 bits clear. - // NOTE: the oopMark is in swap_reg %rax as the result of cmpxchg - - __ subptr(swap_reg, rsp); - __ andptr(swap_reg, 3 - (int)os::vm_page_size()); - - // Save the test result, for recursive case, the result is zero - __ movptr(Address(lock_reg, mark_word_offset), swap_reg); - __ jcc(Assembler::notEqual, slow_path_lock); - - __ bind(count_mon); - __ inc_held_monitor_count(); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_lock(lock_reg, obj_reg, swap_reg, rscratch1, slow_path_lock); - } + __ lightweight_lock(lock_reg, obj_reg, swap_reg, rscratch1, slow_path_lock); // Slow path will re-enter here __ bind(lock_done); @@ -2322,7 +2277,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // change thread state __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // Check preemption for Object.wait() __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); __ cmpptr(rscratch1, NULL_WORD); @@ -2354,38 +2309,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Get locked oop from the handle we passed to jni __ movptr(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_LEGACY) { - Label not_recur; - // Simple recursive lock? - __ cmpptr(Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size), NULL_WORD); - __ jcc(Assembler::notEqual, not_recur); - __ dec_held_monitor_count(); - __ jmpb(fast_done); - __ bind(not_recur); - } - // Must save rax if it is live now because cmpxchg must use it if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { save_native_result(masm, ret_type, stack_slots); } - if (LockingMode == LM_MONITOR) { - __ jmp(slow_path_unlock); - } else if (LockingMode == LM_LEGACY) { - // get address of the stack lock - __ lea(rax, Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size)); - // get old displaced header - __ movptr(old_hdr, Address(rax, 0)); - - // Atomic swap old header if oop still contains the stack lock - __ lock(); - __ cmpxchgptr(old_hdr, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ jcc(Assembler::notEqual, slow_path_unlock); - __ dec_held_monitor_count(); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_unlock(obj_reg, swap_reg, lock_reg, slow_path_unlock); - } + __ lightweight_unlock(obj_reg, swap_reg, lock_reg, slow_path_unlock); // slow path re-enters here __ bind(unlock_done); diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index bd061d45fbd..47ef0aef2bb 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -1017,21 +1017,16 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // change thread state __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_Java); - if (LockingMode != LM_LEGACY) { - // Check preemption for Object.wait() - Label not_preempted; - __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); - __ cmpptr(rscratch1, NULL_WORD); - __ jccb(Assembler::equal, not_preempted); - __ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); - __ jmp(rscratch1); - __ bind(native_return); - __ restore_after_resume(true /* is_native */); - __ bind(not_preempted); - } else { - // any pc will do so just use this one for LM_LEGACY to keep code together. - __ bind(native_return); - } + // Check preemption for Object.wait() + Label not_preempted; + __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); + __ cmpptr(rscratch1, NULL_WORD); + __ jccb(Assembler::equal, not_preempted); + __ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); + __ jmp(rscratch1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + __ bind(not_preempted); // reset_last_Java_frame __ reset_last_Java_frame(true); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 5b5292fbde2..932dc9e1ca7 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -14073,33 +14073,7 @@ instruct jmpConUCF2_short(cmpOpUCF2 cop, rFlagsRegUCF cmp, label labl) %{ // ============================================================================ // inlined locking and unlocking -instruct cmpFastLock(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI tmp, rRegP scr) %{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastLock object box)); - effect(TEMP tmp, TEMP scr, USE_KILL box); - ins_cost(300); - format %{ "fastlock $object,$box\t! kills $box,$tmp,$scr" %} - ins_encode %{ - __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, - $scr$$Register, noreg, noreg, r15_thread, nullptr); - %} - ins_pipe(pipe_slow); -%} - -instruct cmpFastUnlock(rFlagsReg cr, rRegP object, rax_RegP box, rRegP tmp) %{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastUnlock object box)); - effect(TEMP tmp, USE_KILL box); - ins_cost(300); - format %{ "fastunlock $object,$box\t! kills $box,$tmp" %} - ins_encode %{ - __ fast_unlock($object$$Register, $box$$Register, $tmp$$Register); - %} - ins_pipe(pipe_slow); -%} - instruct cmpFastLockLightweight(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI rax_reg, rRegP tmp) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); effect(TEMP rax_reg, TEMP tmp, USE_KILL box); ins_cost(300); @@ -14111,7 +14085,6 @@ instruct cmpFastLockLightweight(rFlagsReg cr, rRegP object, rbx_RegP box, rax_Re %} instruct cmpFastUnlockLightweight(rFlagsReg cr, rRegP object, rax_RegP rax_reg, rRegP tmp) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastUnlock object rax_reg)); effect(TEMP tmp, USE_KILL rax_reg); ins_cost(300); From 3c0eed8e476e4da540e4ea44ee966f278e04a067 Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Tue, 12 Aug 2025 08:45:36 +0000 Subject: [PATCH 009/112] 8364406: Remove LockingMode related code from aarch64 Reviewed-by: aph, dholmes --- src/hotspot/cpu/aarch64/aarch64.ad | 34 --- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 14 +- .../cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 81 +------ .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 208 ------------------ .../cpu/aarch64/c2_MacroAssembler_aarch64.hpp | 3 - .../cpu/aarch64/interp_masm_aarch64.cpp | 171 +++----------- .../cpu/aarch64/macroAssembler_aarch64.cpp | 2 - .../cpu/aarch64/sharedRuntime_aarch64.cpp | 75 +------ .../templateInterpreterGenerator_aarch64.cpp | 27 +-- 9 files changed, 52 insertions(+), 563 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 517da8066de..9697ac31350 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -16281,41 +16281,8 @@ instruct branchLoopEnd(cmpOp cmp, rFlagsReg cr, label lbl) // ============================================================================ // inlined locking and unlocking -instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2, iRegPNoSp tmp3) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastLock object box)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - - ins_cost(5 * INSN_COST); - format %{ "fastlock $object,$box\t! kills $tmp,$tmp2,$tmp3" %} - - ins_encode %{ - __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register); - %} - - ins_pipe(pipe_serial); -%} - -instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastUnlock object box)); - effect(TEMP tmp, TEMP tmp2); - - ins_cost(5 * INSN_COST); - format %{ "fastunlock $object,$box\t! kills $tmp, $tmp2" %} - - ins_encode %{ - __ fast_unlock($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register); - %} - - ins_pipe(pipe_serial); -%} - instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2, iRegPNoSp tmp3) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); effect(TEMP tmp, TEMP tmp2, TEMP tmp3); @@ -16331,7 +16298,6 @@ instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2, iRegPNoSp tmp3) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastUnlock object box)); effect(TEMP tmp, TEMP tmp2, TEMP tmp3); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 31f1e97002a..bdba957d1df 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -410,11 +410,7 @@ int LIR_Assembler::emit_unwind_handler() { if (method()->is_synchronized()) { monitor_address(0, FrameMap::r0_opr); stub = new MonitorExitStub(FrameMap::r0_opr, true, 0); - if (LockingMode == LM_MONITOR) { - __ b(*stub->entry()); - } else { - __ unlock_object(r5, r4, r0, r6, *stub->entry()); - } + __ unlock_object(r5, r4, r0, r6, *stub->entry()); __ bind(*stub->continuation()); } @@ -2484,13 +2480,7 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); Register temp = op->scratch_opr()->as_register(); - if (LockingMode == LM_MONITOR) { - if (op->info() != nullptr) { - add_debug_info_for_null_check_here(op->info()); - __ null_check(obj, -1); - } - __ b(*op->stub()->entry()); - } else if (op->code() == lir_lock) { + if (op->code() == lir_lock) { assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, temp, *op->stub()->entry()); diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index b0f9d6d6c41..8a79274b2ff 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -60,8 +60,6 @@ void C1_MacroAssembler::float_cmp(bool is_float, int unordered_result, } int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, rscratch2); int null_check_offset = -1; @@ -72,95 +70,20 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr null_check_offset = offset(); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(disp_hdr, obj, hdr, temp, rscratch2, slow_case); - } else if (LockingMode == LM_LEGACY) { + lightweight_lock(disp_hdr, obj, hdr, temp, rscratch2, slow_case); - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(hdr, obj); - ldrb(hdr, Address(hdr, Klass::misc_flags_offset())); - tst(hdr, KlassFlags::_misc_is_value_based_class); - br(Assembler::NE, slow_case); - } - - Label done; - // Load object header - ldr(hdr, Address(obj, hdr_offset)); - // and mark it as unlocked - orr(hdr, hdr, markWord::unlocked_value); - // save unlocked object header into the displaced header location on the stack - str(hdr, Address(disp_hdr, 0)); - // test if object header is still the same (i.e. unlocked), and if so, store the - // displaced header address in the object header - if it is not the same, get the - // object header instead - lea(rscratch2, Address(obj, hdr_offset)); - cmpxchgptr(hdr, disp_hdr, rscratch2, rscratch1, done, /*fallthough*/nullptr); - // if the object header was the same, we're done - // if the object header was not the same, it is now in the hdr register - // => test if it is a stack pointer into the same stack (recursive locking), i.e.: - // - // 1) (hdr & aligned_mask) == 0 - // 2) sp <= hdr - // 3) hdr <= sp + page_size - // - // these 3 tests can be done by evaluating the following expression: - // - // (hdr - sp) & (aligned_mask - page_size) - // - // assuming both the stack pointer and page_size have their least - // significant 2 bits cleared and page_size is a power of 2 - mov(rscratch1, sp); - sub(hdr, hdr, rscratch1); - ands(hdr, hdr, aligned_mask - (int)os::vm_page_size()); - // for recursive locking, the result is zero => save it in the displaced header - // location (null in the displaced hdr location indicates recursive locking) - str(hdr, Address(disp_hdr, 0)); - // otherwise we don't care about the result and handle locking via runtime call - cbnz(hdr, slow_case); - // done - bind(done); - inc_held_monitor_count(rscratch1); - } return null_check_offset; } void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, rscratch2); - Label done; - - if (LockingMode != LM_LIGHTWEIGHT) { - // load displaced header - ldr(hdr, Address(disp_hdr, 0)); - // if the loaded hdr is null we had recursive locking - // if we had recursive locking, we are done - cbz(hdr, done); - } // load object ldr(obj, Address(disp_hdr, BasicObjectLock::obj_offset())); verify_oop(obj); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj, hdr, temp, rscratch2, slow_case); - } else if (LockingMode == LM_LEGACY) { - // test if object header is pointing to the displaced header, and if so, restore - // the displaced header in the object - if the object header is not pointing to - // the displaced header, get the object header instead - // if the object header was not pointing to the displaced header, - // we do unlocking via runtime call - if (hdr_offset) { - lea(rscratch1, Address(obj, hdr_offset)); - cmpxchgptr(disp_hdr, hdr, rscratch1, rscratch2, done, &slow_case); - } else { - cmpxchgptr(disp_hdr, hdr, obj, rscratch2, done, &slow_case); - } - // done - bind(done); - dec_held_monitor_count(rscratch1); - } + lightweight_unlock(obj, hdr, temp, rscratch2, slow_case); } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index e87cb478c8f..b1562c54f4e 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -147,215 +147,8 @@ address C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register return pc(); } -void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register tmpReg, - Register tmp2Reg, Register tmp3Reg) { - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmpReg; - Register tmp = tmp2Reg; - Label cont; - Label object_has_monitor; - Label count, no_count; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr, rscratch2); - - // Load markWord from object into displaced_header. - ldr(disp_hdr, Address(oop, oopDesc::mark_offset_in_bytes())); - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, oop); - ldrb(tmp, Address(tmp, Klass::misc_flags_offset())); - tst(tmp, KlassFlags::_misc_is_value_based_class); - br(Assembler::NE, cont); - } - - // Check for existing monitor - tbnz(disp_hdr, exact_log2(markWord::monitor_value), object_has_monitor); - - if (LockingMode == LM_MONITOR) { - tst(oop, oop); // Set NE to indicate 'failure' -> take slow-path. We know that oop != 0. - b(cont); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Set tmp to be (markWord of object | UNLOCK_VALUE). - orr(tmp, disp_hdr, markWord::unlocked_value); - - // Initialize the box. (Must happen before we update the object mark!) - str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // Compare object markWord with an unlocked value (tmp) and if - // equal exchange the stack address of our box with object markWord. - // On failure disp_hdr contains the possibly locked markWord. - cmpxchg(oop, tmp, box, Assembler::xword, /*acquire*/ true, - /*release*/ true, /*weak*/ false, disp_hdr); - br(Assembler::EQ, cont); - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // If the compare-and-exchange succeeded, then we found an unlocked - // object, will have now locked it will continue at label cont - - // Check if the owner is self by comparing the value in the - // markWord of object (disp_hdr) with the stack pointer. - mov(rscratch1, sp); - sub(disp_hdr, disp_hdr, rscratch1); - mov(tmp, (address) (~(os::vm_page_size()-1) | markWord::lock_mask_in_place)); - // If condition is true we are cont and hence we can store 0 as the - // displaced header in the box, which indicates that it is a recursive lock. - ands(tmp/*==0?*/, disp_hdr, tmp); // Sets flags for result - str(tmp/*==0, perhaps*/, Address(box, BasicLock::displaced_header_offset_in_bytes())); - b(cont); - } - - // Handle existing monitor. - bind(object_has_monitor); - - // Try to CAS owner (no owner => current thread's _monitor_owner_id). - ldr(rscratch2, Address(rthread, JavaThread::monitor_owner_id_offset())); - add(tmp, disp_hdr, (in_bytes(ObjectMonitor::owner_offset())-markWord::monitor_value)); - cmpxchg(tmp, zr, rscratch2, Assembler::xword, /*acquire*/ true, - /*release*/ true, /*weak*/ false, tmp3Reg); // Sets flags for result - - // Store a non-null value into the box to avoid looking like a re-entrant - // lock. The fast-path monitor unlock code checks for - // markWord::monitor_value so use markWord::unused_mark which has the - // relevant bit set, and also matches ObjectSynchronizer::enter. - mov(tmp, (address)markWord::unused_mark().value()); - str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - br(Assembler::EQ, cont); // CAS success means locking succeeded - - cmp(tmp3Reg, rscratch2); - br(Assembler::NE, cont); // Check for recursive locking - - // Recursive lock case - increment(Address(disp_hdr, in_bytes(ObjectMonitor::recursions_offset()) - markWord::monitor_value), 1); - // flag == EQ still from the cmp above, checking if this is a reentrant lock - - bind(cont); - // flag == EQ indicates success - // flag == NE indicates failure - br(Assembler::NE, no_count); - - bind(count); - if (LockingMode == LM_LEGACY) { - inc_held_monitor_count(rscratch1); - } - - bind(no_count); -} - -void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Register tmpReg, - Register tmp2Reg) { - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmpReg; - Register owner_addr = tmpReg; - Register tmp = tmp2Reg; - Label cont; - Label object_has_monitor; - Label count, no_count; - Label unlocked; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr); - - if (LockingMode == LM_LEGACY) { - // Find the lock address and load the displaced header from the stack. - ldr(disp_hdr, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // If the displaced header is 0, we have a recursive unlock. - cmp(disp_hdr, zr); - br(Assembler::EQ, cont); - } - - // Handle existing monitor. - ldr(tmp, Address(oop, oopDesc::mark_offset_in_bytes())); - tbnz(tmp, exact_log2(markWord::monitor_value), object_has_monitor); - - if (LockingMode == LM_MONITOR) { - tst(oop, oop); // Set NE to indicate 'failure' -> take slow-path. We know that oop != 0. - b(cont); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Check if it is still a light weight lock, this is is true if we - // see the stack address of the basicLock in the markWord of the - // object. - - cmpxchg(oop, box, disp_hdr, Assembler::xword, /*acquire*/ false, - /*release*/ true, /*weak*/ false, tmp); - b(cont); - } - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // Handle existing monitor. - bind(object_has_monitor); - STATIC_ASSERT(markWord::monitor_value <= INT_MAX); - add(tmp, tmp, -(int)markWord::monitor_value); // monitor - - ldr(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - - Label notRecursive; - cbz(disp_hdr, notRecursive); - - // Recursive lock - sub(disp_hdr, disp_hdr, 1u); - str(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - cmp(disp_hdr, disp_hdr); // Sets flags for result - b(cont); - - bind(notRecursive); - - // Compute owner address. - lea(owner_addr, Address(tmp, ObjectMonitor::owner_offset())); - - // Set owner to null. - // Release to satisfy the JMM - stlr(zr, owner_addr); - // We need a full fence after clearing owner to avoid stranding. - // StoreLoad achieves this. - membar(StoreLoad); - - // Check if the entry_list is empty. - ldr(rscratch1, Address(tmp, ObjectMonitor::entry_list_offset())); - cmp(rscratch1, zr); - br(Assembler::EQ, cont); // If so we are done. - - // Check if there is a successor. - ldr(rscratch1, Address(tmp, ObjectMonitor::succ_offset())); - cmp(rscratch1, zr); - br(Assembler::NE, unlocked); // If so we are done. - - // Save the monitor pointer in the current thread, so we can try to - // reacquire the lock in SharedRuntime::monitor_exit_helper(). - str(tmp, Address(rthread, JavaThread::unlocked_inflated_monitor_offset())); - - cmp(zr, rthread); // Set Flag to NE => slow path - b(cont); - - bind(unlocked); - cmp(zr, zr); // Set Flag to EQ => fast path - - // Intentional fall-through - - bind(cont); - // flag == EQ indicates success - // flag == NE indicates failure - br(Assembler::NE, no_count); - - bind(count); - if (LockingMode == LM_LEGACY) { - dec_held_monitor_count(rscratch1); - } - - bind(no_count); -} - void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register t1, Register t2, Register t3) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, t1, t2, t3, rscratch2); // Handle inflated monitor. @@ -512,7 +305,6 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Register t1, Register t2, Register t3) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, t1, t2, t3); // Handle inflated monitor. diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 233f600cb14..0403a27910f 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -51,9 +51,6 @@ FloatRegister vmul3, FloatRegister vpow, FloatRegister vpowm, BasicType eltype); - // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. - void fast_lock(Register object, Register box, Register tmp, Register tmp2, Register tmp3); - void fast_unlock(Register object, Register box, Register tmp, Register tmp2); // Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file. void fast_lock_lightweight(Register object, Register box, Register t1, Register t2, Register t3); void fast_unlock_lightweight(Register object, Register box, Register t1, Register t2, Register t3); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index e14829b7c89..607912e6e49 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -691,104 +691,27 @@ void InterpreterMacroAssembler::leave_jfr_critical_section() { void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - } else { - Label count, done; - const Register swap_reg = r0; - const Register tmp = c_rarg2; - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp2 = c_rarg4; - const Register tmp3 = c_rarg5; + const Register tmp = c_rarg2; + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp2 = c_rarg4; + const Register tmp3 = c_rarg5; - const int obj_offset = in_bytes(BasicObjectLock::obj_offset()); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + - BasicLock::displaced_header_offset_in_bytes(); + // Load object pointer into obj_reg %c_rarg3 + ldr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - Label slow_case; + Label slow_case, done; + lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); + b(done); - // Load object pointer into obj_reg %c_rarg3 - ldr(obj_reg, Address(lock_reg, obj_offset)); + bind(slow_case); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); - b(done); - } else if (LockingMode == LM_LEGACY) { + // Call the runtime routine for slow case + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, obj_reg); - ldrb(tmp, Address(tmp, Klass::misc_flags_offset())); - tst(tmp, KlassFlags::_misc_is_value_based_class); - br(Assembler::NE, slow_case); - } - - // Load (object->mark() | 1) into swap_reg - ldr(rscratch1, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - orr(swap_reg, rscratch1, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - str(swap_reg, Address(lock_reg, mark_offset)); - - assert(lock_offset == 0, - "displached header must be first word in BasicObjectLock"); - - Label fail; - cmpxchg_obj_header(swap_reg, lock_reg, obj_reg, rscratch1, count, /*fallthrough*/nullptr); - - // Fast check for recursive lock. - // - // Can apply the optimization only if this is a stack lock - // allocated in this thread. For efficiency, we can focus on - // recently allocated stack locks (instead of reading the stack - // base and checking whether 'mark' points inside the current - // thread stack): - // 1) (mark & 7) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // - // Warning: sp + os::pagesize can overflow the stack base. We must - // neither apply the optimization for an inflated lock allocated - // just above the thread stack (this is why condition 1 matters) - // nor apply the optimization if the stack lock is inside the stack - // of another thread. The latter is avoided even in case of overflow - // because we have guard pages at the end of all stacks. Hence, if - // we go over the stack base and hit the stack of another thread, - // this should not be in a writeable area that could contain a - // stack lock allocated by that thread. As a consequence, a stack - // lock less than page size away from sp is guaranteed to be - // owned by the current thread. - // - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (7 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 3 bits clear. - // NOTE: the mark is in swap_reg %r0 as the result of cmpxchg - // NOTE2: aarch64 does not like to subtract sp from rn so take a - // copy - mov(rscratch1, sp); - sub(swap_reg, swap_reg, rscratch1); - ands(swap_reg, swap_reg, (uint64_t)(7 - (int)os::vm_page_size())); - - // Save the test result, for recursive case, the result is zero - str(swap_reg, Address(lock_reg, mark_offset)); - br(Assembler::NE, slow_case); - - bind(count); - inc_held_monitor_count(rscratch1); - b(done); - } - bind(slow_case); - - // Call the runtime routine for slow case - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - - bind(done); - } + bind(done); } @@ -807,57 +730,29 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - } else { - Label count, done; + const Register swap_reg = r0; + const Register header_reg = c_rarg2; // Will contain the old oopMark + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock - const Register swap_reg = r0; - const Register header_reg = c_rarg2; // Will contain the old oopMark - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock + save_bcp(); // Save in case of exception - save_bcp(); // Save in case of exception + // Load oop into obj_reg(%c_rarg3) + ldr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - if (LockingMode != LM_LIGHTWEIGHT) { - // Convert from BasicObjectLock structure to object and BasicLock - // structure Store the BasicLock address into %r0 - lea(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset())); - } + // Free entry + str(zr, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load oop into obj_reg(%c_rarg3) - ldr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); + Label slow_case, done; + lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); + b(done); - // Free entry - str(zr, Address(lock_reg, BasicObjectLock::obj_offset())); - - Label slow_case; - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); - b(done); - } else if (LockingMode == LM_LEGACY) { - // Load the old header from BasicLock structure - ldr(header_reg, Address(swap_reg, - BasicLock::displaced_header_offset_in_bytes())); - - // Test for recursion - cbz(header_reg, count); - - // Atomic swap back the old header - cmpxchg_obj_header(swap_reg, header_reg, obj_reg, rscratch1, count, &slow_case); - - bind(count); - dec_held_monitor_count(rscratch1); - b(done); - } - - bind(slow_case); - // Call the runtime routine for slow case. - str(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - bind(done); - restore_bcp(); - } + bind(slow_case); + // Call the runtime routine for slow case. + str(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); + bind(done); + restore_bcp(); } void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 6ae6861e38b..e51b3c530de 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -7097,7 +7097,6 @@ void MacroAssembler::double_move(VMRegPair src, VMRegPair dst, Register tmp) { // - t1, t2, t3: temporary registers, will be destroyed // - slow: branched to if locking fails, absolute offset may larger than 32KB (imm14 encoding). void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register t1, Register t2, Register t3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(basic_lock, obj, t1, t2, t3, rscratch1); Label push; @@ -7157,7 +7156,6 @@ void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Registe // - t1, t2, t3: temporary registers // - slow: branched to if unlocking fails, absolute offset may larger than 32KB (imm14 encoding). void MacroAssembler::lightweight_unlock(Register obj, Register t1, Register t2, Register t3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); // cmpxchg clobbers rscratch1. assert_different_registers(obj, t1, t2, t3, rscratch1); diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 00f97ed04e4..748c3e8fb11 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -1721,7 +1721,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // We use the same pc/oopMap repeatedly when we call out. Label native_return; - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // For convenience we use the pc we want to resume to in case of preemption on Object.wait. __ set_last_Java_frame(sp, noreg, native_return, rscratch1); } else { @@ -1776,44 +1776,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Load the oop from the handle __ ldr(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_MONITOR) { - __ b(slow_path_lock); - } else if (LockingMode == LM_LEGACY) { - // Load (object->mark() | 1) into swap_reg %r0 - __ ldr(rscratch1, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ orr(swap_reg, rscratch1, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - __ str(swap_reg, Address(lock_reg, mark_word_offset)); - - // src -> dest iff dest == r0 else r0 <- dest - __ cmpxchg_obj_header(r0, lock_reg, obj_reg, rscratch1, count, /*fallthrough*/nullptr); - - // Hmm should this move to the slow path code area??? - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 3) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (3 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 2 bits clear. - // NOTE: the oopMark is in swap_reg %r0 as the result of cmpxchg - - __ sub(swap_reg, sp, swap_reg); - __ neg(swap_reg, swap_reg); - __ ands(swap_reg, swap_reg, 3 - (int)os::vm_page_size()); - - // Save the test result, for recursive case, the result is zero - __ str(swap_reg, Address(lock_reg, mark_word_offset)); - __ br(Assembler::NE, slow_path_lock); - - __ bind(count); - __ inc_held_monitor_count(rscratch1); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); - } + __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); // Slow path will re-enter here __ bind(lock_done); @@ -1888,7 +1851,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // Check preemption for Object.wait() __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); __ cbz(rscratch1, native_return); @@ -1917,48 +1880,18 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Get locked oop from the handle we passed to jni __ ldr(obj_reg, Address(oop_handle_reg, 0)); - Label done, not_recursive; - - if (LockingMode == LM_LEGACY) { - // Simple recursive lock? - __ ldr(rscratch1, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - __ cbnz(rscratch1, not_recursive); - __ dec_held_monitor_count(rscratch1); - __ b(done); - } - - __ bind(not_recursive); - // Must save r0 if if it is live now because cmpxchg must use it if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { save_native_result(masm, ret_type, stack_slots); } - if (LockingMode == LM_MONITOR) { - __ b(slow_path_unlock); - } else if (LockingMode == LM_LEGACY) { - // get address of the stack lock - __ lea(r0, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - // get old displaced header - __ ldr(old_hdr, Address(r0, 0)); - - // Atomic swap old header if oop still contains the stack lock - Label count; - __ cmpxchg_obj_header(r0, old_hdr, obj_reg, rscratch1, count, &slow_path_unlock); - __ bind(count); - __ dec_held_monitor_count(rscratch1); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, ""); - __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); - } + __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); // slow path re-enters here __ bind(unlock_done); if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { restore_native_result(masm, ret_type, stack_slots); } - - __ bind(done); } Label dtrace_method_exit, dtrace_method_exit_done; diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 6593406902f..c1eabed8ade 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -1478,22 +1478,17 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); - if (LockingMode != LM_LEGACY) { - // Check preemption for Object.wait() - Label not_preempted; - __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); - __ cbz(rscratch1, not_preempted); - __ str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset())); - __ br(rscratch1); - __ bind(native_return); - __ restore_after_resume(true /* is_native */); - // reload result_handler - __ ldr(result_handler, Address(rfp, frame::interpreter_frame_result_handler_offset*wordSize)); - __ bind(not_preempted); - } else { - // any pc will do so just use this one for LM_LEGACY to keep code together. - __ bind(native_return); - } + // Check preemption for Object.wait() + Label not_preempted; + __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ cbz(rscratch1, not_preempted); + __ str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ br(rscratch1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + // reload result_handler + __ ldr(result_handler, Address(rfp, frame::interpreter_frame_result_handler_offset*wordSize)); + __ bind(not_preempted); // reset_last_Java_frame __ reset_last_Java_frame(true); From 16e461ef31628ab47cd8484cdfc970c0008b97ba Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 12 Aug 2025 08:52:37 +0000 Subject: [PATCH 010/112] 8365122: G1: Minor clean up of G1SurvivorRegions Reviewed-by: sangheki, kbarrett --- src/hotspot/share/gc/g1/g1Policy.cpp | 16 +++++----------- src/hotspot/share/gc/g1/g1SurvivorRegions.cpp | 15 ++++++--------- src/hotspot/share/gc/g1/g1SurvivorRegions.hpp | 11 ++++++----- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 3bbc64e0fe7..bb54d344ca0 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -508,12 +508,9 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, } double G1Policy::predict_survivor_regions_evac_time() const { - const GrowableArray* survivor_regions = _g1h->survivor()->regions(); double survivor_regions_evac_time = predict_young_region_other_time_ms(_g1h->survivor()->length()); - for (GrowableArrayIterator it = survivor_regions->begin(); - it != survivor_regions->end(); - ++it) { - survivor_regions_evac_time += predict_region_copy_time_ms(*it, _g1h->collector_state()->in_young_only_phase()); + for (G1HeapRegion* r : _g1h->survivor()->regions()) { + survivor_regions_evac_time += predict_region_copy_time_ms(r, _g1h->collector_state()->in_young_only_phase()); } return survivor_regions_evac_time; @@ -1461,16 +1458,13 @@ uint G1Policy::calc_max_old_cset_length() const { void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { start_adding_survivor_regions(); - for (GrowableArrayIterator it = survivors->regions()->begin(); - it != survivors->regions()->end(); - ++it) { - G1HeapRegion* curr = *it; - set_region_survivor(curr); + for (G1HeapRegion* r : survivors->regions()) { + set_region_survivor(r); // The region is a non-empty survivor so let's add it to // the incremental collection set for the next evacuation // pause. - _collection_set->add_survivor_regions(curr); + _collection_set->add_survivor_regions(r); } stop_adding_survivor_regions(); diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp index edd4f35d5ed..4aa752e5823 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp @@ -28,18 +28,18 @@ #include "utilities/growableArray.hpp" G1SurvivorRegions::G1SurvivorRegions() : - _regions(new (mtGC) GrowableArray(8, mtGC)), + _regions(8, mtGC), _used_bytes(0), _regions_on_node() {} uint G1SurvivorRegions::add(G1HeapRegion* hr) { assert(hr->is_survivor(), "should be flagged as survivor region"); - _regions->append(hr); + _regions.append(hr); return _regions_on_node.add(hr); } uint G1SurvivorRegions::length() const { - return (uint)_regions->length(); + return (uint)_regions.length(); } uint G1SurvivorRegions::regions_on_node(uint node_index) const { @@ -47,17 +47,14 @@ uint G1SurvivorRegions::regions_on_node(uint node_index) const { } void G1SurvivorRegions::convert_to_eden() { - for (GrowableArrayIterator it = _regions->begin(); - it != _regions->end(); - ++it) { - G1HeapRegion* hr = *it; - hr->set_eden_pre_gc(); + for (G1HeapRegion* r : _regions) { + r->set_eden_pre_gc(); } clear(); } void G1SurvivorRegions::clear() { - _regions->clear(); + _regions.clear(); _used_bytes = 0; _regions_on_node.clear(); } diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp index cc6bbc44fa8..0532ee12162 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp @@ -27,16 +27,17 @@ #include "gc/g1/g1RegionsOnNodes.hpp" #include "runtime/globals.hpp" +#include "utilities/growableArray.hpp" template class GrowableArray; class G1HeapRegion; +// Set of current survivor regions. class G1SurvivorRegions { -private: - GrowableArray* _regions; - volatile size_t _used_bytes; - G1RegionsOnNodes _regions_on_node; + GrowableArray _regions; + volatile size_t _used_bytes; + G1RegionsOnNodes _regions_on_node; public: G1SurvivorRegions(); @@ -50,7 +51,7 @@ public: uint length() const; uint regions_on_node(uint node_index) const; - const GrowableArray* regions() const { + const GrowableArray& regions() const { return _regions; } From 95b7a8b3e300bf40a2a5bdf8ca8310485db363e6 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Tue, 12 Aug 2025 11:29:43 +0000 Subject: [PATCH 011/112] 8365237: Remove unused SoftRefPolicy::_all_soft_refs_clear Reviewed-by: tschatzl, kbarrett --- .../share/gc/shared/gcVMOperations.cpp | 5 ----- .../share/gc/shared/gcVMOperations.hpp | 9 -------- src/hotspot/share/gc/shared/softRefPolicy.hpp | 21 +------------------ 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/hotspot/share/gc/shared/gcVMOperations.cpp b/src/hotspot/share/gc/shared/gcVMOperations.cpp index 19a81504722..1299f64995f 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.cpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp @@ -62,11 +62,6 @@ void VM_Verify::doit() { Universe::verify(); } -VM_GC_Operation::~VM_GC_Operation() { - CollectedHeap* ch = Universe::heap(); - ch->soft_ref_policy()->set_all_soft_refs_clear(false); -} - const char* VM_GC_Operation::cause() const { return GCCause::to_string(_gc_cause); } diff --git a/src/hotspot/share/gc/shared/gcVMOperations.hpp b/src/hotspot/share/gc/shared/gcVMOperations.hpp index 6752e0bc32d..0121a468b56 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.hpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.hpp @@ -126,16 +126,7 @@ class VM_GC_Operation: public VM_Heap_Sync_Operation { _gc_cause = _cause; _full_gc_count_before = full_gc_count_before; - // In ParallelScavengeHeap::mem_allocate() collections can be - // executed within a loop and _all_soft_refs_clear can be set - // true after they have been cleared by a collection and another - // collection started so that _all_soft_refs_clear can be true - // when this collection is started. Don't assert that - // _all_soft_refs_clear have to be false here even though - // mutators have run. Soft refs will be cleared again in this - // collection. } - ~VM_GC_Operation(); virtual const char* cause() const; diff --git a/src/hotspot/share/gc/shared/softRefPolicy.hpp b/src/hotspot/share/gc/shared/softRefPolicy.hpp index fe2706288f7..1e8ec356c40 100644 --- a/src/hotspot/share/gc/shared/softRefPolicy.hpp +++ b/src/hotspot/share/gc/shared/softRefPolicy.hpp @@ -25,37 +25,18 @@ #ifndef SHARE_GC_SHARED_SOFTREFPOLICY_HPP #define SHARE_GC_SHARED_SOFTREFPOLICY_HPP -#include "memory/allocation.hpp" - class SoftRefPolicy { private: // Set to true when policy wants soft refs cleared. // Reset to false by gc after it clears all soft refs. bool _should_clear_all_soft_refs; - // Set to true by the GC if the just-completed gc cleared all - // softrefs. This is set to true whenever a gc clears all softrefs, and - // set to false each time gc returns to the mutator. For example, in the - // ParallelScavengeHeap case the latter would be done toward the end of - // mem_allocate() where it returns op.result() - bool _all_soft_refs_clear; - public: SoftRefPolicy() : - _should_clear_all_soft_refs(false), - _all_soft_refs_clear(false) {} + _should_clear_all_soft_refs(false) {} bool should_clear_all_soft_refs() { return _should_clear_all_soft_refs; } void set_should_clear_all_soft_refs(bool v) { _should_clear_all_soft_refs = v; } - - bool all_soft_refs_clear() { return _all_soft_refs_clear; } - void set_all_soft_refs_clear(bool v) { _all_soft_refs_clear = v; } - - // Called by the GC after Soft Refs have been cleared to indicate - // that the request in _should_clear_all_soft_refs has been fulfilled. - void cleared_all_soft_refs() { - _all_soft_refs_clear = true; - } }; #endif // SHARE_GC_SHARED_SOFTREFPOLICY_HPP From 19a76a45e9c8616414830c865801660bc6761e92 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Tue, 12 Aug 2025 11:58:37 +0000 Subject: [PATCH 012/112] 8365316: Remove unnecessary default arg value in gcVMOperations Reviewed-by: tschatzl --- src/hotspot/share/gc/shared/gcVMOperations.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/shared/gcVMOperations.hpp b/src/hotspot/share/gc/shared/gcVMOperations.hpp index 0121a468b56..5048bc3c1ed 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.hpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.hpp @@ -117,8 +117,8 @@ class VM_GC_Operation: public VM_Heap_Sync_Operation { public: VM_GC_Operation(uint gc_count_before, GCCause::Cause _cause, - uint full_gc_count_before = 0, - bool full = false) : VM_Heap_Sync_Operation() { + uint full_gc_count_before, + bool full) : VM_Heap_Sync_Operation() { _full = full; _prologue_succeeded = false; _gc_count_before = gc_count_before; @@ -147,8 +147,8 @@ class VM_GC_Service_Operation : public VM_GC_Operation { public: VM_GC_Service_Operation(uint gc_count_before, GCCause::Cause _cause, - uint full_gc_count_before = 0, - bool full = false) : + uint full_gc_count_before, + bool full) : VM_GC_Operation(gc_count_before, _cause, full_gc_count_before, full) {} }; From 391ea151184c5621f263742605416c3ccd2c3d73 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 12 Aug 2025 13:16:54 +0000 Subject: [PATCH 013/112] 8365307: AIX make fails after JDK-8364611 Reviewed-by: clanger, asteiner --- .../childSignalDisposition/exePrintSignalDisposition.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c index 5a96c2556fc..8235cdc7410 100644 --- a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c +++ b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c @@ -72,7 +72,11 @@ int main(int argc, char** argv) { } else { printf("%p ", handler); } +#ifdef _AIX + printf("%X\n", act.sa_flags); +#else printf("%X %X\n", act.sa_flags, act.sa_mask); +#endif } return 0; From a382996bb496d50b4eb5a6be9f61e5c2f8aaae2d Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Tue, 12 Aug 2025 13:42:53 +0000 Subject: [PATCH 014/112] 8364993: JFR: Disable jdk.ModuleExport in default.jfc Reviewed-by: mgronlun --- src/jdk.jfr/share/conf/jfr/default.jfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 20051864895..463639d7f1e 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -762,7 +762,7 @@ - true + false endChunk From ad0fd13f2007c93d8a109626a627823f30e4c8d7 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 12 Aug 2025 16:20:00 +0000 Subject: [PATCH 015/112] 8364454: ProblemList runtime/cds/DeterministicDump.java on macos for JDK-8363986 Reviewed-by: ccheung --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 7026b6f79d6..37986c67dd8 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -97,6 +97,7 @@ gc/shenandoah/TestEvilSyncBug.java#generational 8345501 generic-all # :hotspot_runtime +runtime/cds/DeterministicDump.java 8363986 macosx-x64,macosx-aarch64 runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64 runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64 From 4c03e5938df0a9cb10c2379af81163795dd3a086 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Tue, 12 Aug 2025 16:30:09 +0000 Subject: [PATCH 016/112] 8364750: Remove unused declaration in jvm.h Reviewed-by: shade --- src/hotspot/share/include/jvm.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index f97374553ca..4d39537e5a1 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -599,16 +599,6 @@ JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, jboolean publicOnly); JNIEXPORT jobjectArray JNICALL JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly); - -/* Differs from JVM_GetClassModifiers in treatment of inner classes. - This returns the access flags for the class as specified in the - class file rather than searching the InnerClasses attribute (if - present) to find the source-level access flags. Only the values of - the low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be - valid. */ -JNIEXPORT jint JNICALL -JVM_GetClassAccessFlags(JNIEnv *env, jclass cls); - /* Nestmates - since JDK 11 */ JNIEXPORT jboolean JNICALL From d023982600f8bb19053f579710953aa29e0f30c5 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Tue, 12 Aug 2025 17:39:14 +0000 Subject: [PATCH 017/112] 8361209: (bf) Use CharSequence::getChars for StringCharBuffer bulk get methods Reviewed-by: rriggs, alanb --- .../classes/java/nio/StringCharBuffer.java | 10 ++- .../classes/java/nio/X-Buffer.java.template | 29 ++++++-- .../nio/StringCharBufferBulkTransfer.java | 72 +++++++++++++++++++ 3 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/nio/StringCharBufferBulkTransfer.java diff --git a/src/java.base/share/classes/java/nio/StringCharBuffer.java b/src/java.base/share/classes/java/nio/StringCharBuffer.java index a62e8023363..39cd6910f5d 100644 --- a/src/java.base/share/classes/java/nio/StringCharBuffer.java +++ b/src/java.base/share/classes/java/nio/StringCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, 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 @@ -97,7 +97,13 @@ final class StringCharBuffer // package-private return str.charAt(index + offset); } - // ## Override bulk get methods for better performance + @Override + CharBuffer getArray(int index, char[] dst, int dstOffset, int length) { + // Note: the variable "offset" is defined and set in the superclass + int srcOffset = offset + index; + str.getChars(srcOffset, srcOffset + length, dst, dstOffset); + return this; + } public final CharBuffer put(char c) { throw new ReadOnlyBufferException(); diff --git a/src/java.base/share/classes/java/nio/X-Buffer.java.template b/src/java.base/share/classes/java/nio/X-Buffer.java.template index c43004cc0ac..b2b96288d57 100644 --- a/src/java.base/share/classes/java/nio/X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template @@ -950,12 +950,15 @@ public abstract sealed class $Type$Buffer return get(index, dst, 0, dst.length); } - private $Type$Buffer getArray(int index, $type$[] dst, int offset, int length) { - if ( + // + // This method does not perform bounds checking which it expects + // will have been done by the caller + // + $Type$Buffer getArray(int index, $type$[] dst, int offset, int length) { #if[char] - isAddressable() && + assert isAddressable(); #end[char] - ((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) { + if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) { long bufAddr = address + ((long)index << $LG_BYTES_PER_VALUE$); long dstOffset = ARRAY_BASE_OFFSET + ((long)offset << $LG_BYTES_PER_VALUE$); @@ -1116,6 +1119,10 @@ public abstract sealed class $Type$Buffer return this; } + // + // This method does not perform bounds checking which it expects + // will have been done by the caller + // void putBuffer(int pos, $Type$Buffer src, int srcPos, int n) { #if[rw] Object srcBase = src.base(); @@ -1150,9 +1157,13 @@ public abstract sealed class $Type$Buffer #if[char] } else { // src.isAddressable() == false assert StringCharBuffer.class.isInstance(src); - int posMax = pos + n; - for (int i = pos, j = srcPos; i < posMax; i++, j++) - put(i, src.get(j)); + if (this.hb != null && src instanceof StringCharBuffer scb) { + scb.getArray(srcPos, this.hb, pos, n); + } else { + int posMax = pos + n; + for (int i = pos, j = srcPos; i < posMax; i++, j++) + put(i, src.get(j)); + } } #end[char] #else[rw] @@ -1342,6 +1353,10 @@ public abstract sealed class $Type$Buffer return put(index, src, 0, src.length); } + // + // This method does not perform bounds checking which it expects + // will have been done by the caller + // $Type$Buffer putArray(int index, $type$[] src, int offset, int length) { #if[rw] if ( diff --git a/test/micro/org/openjdk/bench/java/nio/StringCharBufferBulkTransfer.java b/test/micro/org/openjdk/bench/java/nio/StringCharBufferBulkTransfer.java new file mode 100644 index 00000000000..246116e4d43 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/nio/StringCharBufferBulkTransfer.java @@ -0,0 +1,72 @@ +/* + * 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 org.openjdk.bench.java.nio; + +import java.nio.CharBuffer; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +/** + * Benchmark for bulk get methods of a {@code CharBuffer} created from a + * {@code CharSequence}. + */ + +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(1) +public class StringCharBufferBulkTransfer { + private static final int LENGTH = 16384; + + char[] buf = new char[LENGTH]; + CharBuffer cb = CharBuffer.wrap(new String(buf)); + char[] dst = new char[LENGTH]; + CharBuffer cbw = CharBuffer.allocate(LENGTH); + + @Benchmark + public void absoluteBulkGet() { + cb.get(0, dst, 0, dst.length); + } + + @Benchmark + public void relativeBulkGet() { + cb.get(dst, 0, dst.length); + cb.position(0); + } + + @Benchmark + public void getChars() { + cb.getChars(0, LENGTH, dst, 0); + } + + @Benchmark + public void absoluteBulkPut() { + cbw.put(0, cb, 0, dst.length); + } +} From 87d734012e3130501bfd37b23cee7f5e0a3a476f Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Tue, 12 Aug 2025 17:44:34 +0000 Subject: [PATCH 018/112] 8364756: JFR: Improve slow tests Reviewed-by: mgronlun --- .../api/consumer/streaming/TestFilledChunks.java | 2 ++ .../consumer/streaming/TestStartMultiChunk.java | 4 ++-- .../event/runtime/StressJavaMonitorEvents.java | 2 +- test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java | 6 ++++-- .../jdk/jfr/jmx/streaming/TestRemoteDump.java | 16 ++++++++++------ test/jdk/jdk/jfr/jvm/TestWaste.java | 2 +- .../TestMultipleStartupRecordings.java | 8 -------- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java b/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java index 423e3969af8..f435fe9747f 100644 --- a/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java +++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java @@ -26,6 +26,7 @@ package jdk.jfr.api.consumer.streaming; import java.util.Random; import jdk.jfr.Event; +import jdk.jfr.StackTrace; import jdk.jfr.consumer.RecordingStream; /** @@ -38,6 +39,7 @@ import jdk.jfr.consumer.RecordingStream; */ public class TestFilledChunks { + @StackTrace(false) static class FillEvent extends Event { String message; int value; diff --git a/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java index fbc27b051b0..71113631521 100644 --- a/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java +++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java @@ -44,7 +44,7 @@ import jdk.jfr.consumer.RecordingStream; */ public class TestStartMultiChunk { - @Period("10 s") + @Period("2 s") @Name("Zebra") static class ZebraEvent extends Event { } @@ -65,7 +65,7 @@ public class TestStartMultiChunk { CountDownLatch dogLatch = new CountDownLatch(1); CountDownLatch catLatch = new CountDownLatch(1); CountDownLatch mouseLatch = new CountDownLatch(1); - CountDownLatch zebraLatch = new CountDownLatch(3); + CountDownLatch zebraLatch = new CountDownLatch(2); FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> { ZebraEvent ze = new ZebraEvent(); diff --git a/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java b/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java index ed3b5454163..fa9bb0c18a2 100644 --- a/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java +++ b/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java @@ -52,7 +52,7 @@ import jdk.test.lib.thread.XRun; */ public class StressJavaMonitorEvents { - static final int RUN_TIME_MS = 10000; + static final int RUN_TIME_MS = 5000; static final int GC_EVERY_MS = 500; static final int THREADS = 4; static final int NUM_LOCKS = 1024; diff --git a/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java b/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java index b79fbc2bcf0..5f490594370 100644 --- a/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java +++ b/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.management.MBeanServerConnection; import jdk.jfr.Event; +import jdk.jfr.StackTrace; import jdk.management.jfr.RemoteRecordingStream; /** @@ -45,6 +46,7 @@ import jdk.management.jfr.RemoteRecordingStream; */ public class TestMaxSize { + @StackTrace(false) static class Monkey extends Event { } @@ -92,7 +94,7 @@ public class TestMaxSize { m.commit(); } System.out.println("Emitted " + count + " events"); - Thread.sleep(1000); + Thread.sleep(100); } private static int fileCount(Path dir) throws IOException { diff --git a/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java b/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java index 9390857c09e..6731f3c44cf 100644 --- a/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java +++ b/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java @@ -97,7 +97,7 @@ public class TestRemoteDump { } } - private static List recordWithPolicy(String filename, Consumer policy) throws Exception { + private static List recordWithPolicy(String filename, boolean awaitEvents, Consumer policy) throws Exception { CountDownLatch latch1 = new CountDownLatch(1); CountDownLatch latch2 = new CountDownLatch(2); CountDownLatch latch3 = new CountDownLatch(3); @@ -111,14 +111,18 @@ public class TestRemoteDump { rs.startAsync(); DumpEvent e1 = new DumpEvent(); e1.commit(); - latch1.await(); + if (awaitEvents) { + latch1.await(); + } // Force chunk rotation try (Recording r = new Recording()) { r.start(); DumpEvent e2 = new DumpEvent(); e2.commit(); } - latch2.await(); + if (awaitEvents) { + latch2.await(); + } DumpEvent e3 = new DumpEvent(); e3.commit(); latch3.await(); @@ -129,7 +133,7 @@ public class TestRemoteDump { } private static void testSetMaxSize() throws Exception { - var events = recordWithPolicy("max-size.jfr", rs -> { + var events = recordWithPolicy("max-size.jfr", false, rs -> { // keeps all events for the dump rs.setMaxSize(100_000_000); }); @@ -140,7 +144,7 @@ public class TestRemoteDump { } private static void testSetMaxAge() throws Exception { - var events = recordWithPolicy("max-age.jfr", rs -> { + var events = recordWithPolicy("max-age.jfr", false, rs -> { // keeps all events for the dump rs.setMaxAge(Duration.ofDays(1)); }); @@ -151,7 +155,7 @@ public class TestRemoteDump { } private static void testSetNoPolicy() throws Exception { - var events = recordWithPolicy("no-policy.jfr", rs -> { + var events = recordWithPolicy("no-policy.jfr", true, rs -> { // use default policy, remove after consumption }); // Since latch3 have been triggered at least two events/chunks diff --git a/test/jdk/jdk/jfr/jvm/TestWaste.java b/test/jdk/jdk/jfr/jvm/TestWaste.java index c755ca4c3d0..4de14fc2461 100644 --- a/test/jdk/jdk/jfr/jvm/TestWaste.java +++ b/test/jdk/jdk/jfr/jvm/TestWaste.java @@ -61,7 +61,7 @@ public class TestWaste { try (Recording r = new Recording(c)) { // Old objects that are cleared out should not create waste r.enable("jdk.OldObjectSample") - .with("cutoff", "infinity") + .with("cutoff", "2 s") .withStackTrace(); // No stack trace waste from allocation sample r.enable("jdk.ObjectAllocationSample") diff --git a/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java b/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java index c70f9810568..bfa02fd8a74 100644 --- a/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java +++ b/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java @@ -74,13 +74,6 @@ public class TestMultipleStartupRecordings { test(pb, "Started recording 1", "Started recording 2", "Started recording 3"); } - private static void testDefault() throws Exception { - System.out.println("testDefault"); - launchUnary(null); - launchBinary(null, null); - launchTernary(null, null, null); - } - private static void testColonDelimited() throws Exception { launchBinary(":name=myrecording1,filename=myrecording1.jfr", ":filename=myrecording2.jfr,name=myrecording2"); } @@ -99,7 +92,6 @@ public class TestMultipleStartupRecordings { } public static void main(String[] args) throws Exception { - testDefault(); testColonDelimited(); testMixed(); testWithFlightRecorderOptions(); From 636c61a3868d9c01b672b3b45cda1e476acdc045 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Wed, 13 Aug 2025 01:24:39 +0000 Subject: [PATCH 019/112] 8365302: RISC-V: compiler/loopopts/superword/TestAlignVector.java fails when vlen=128 Reviewed-by: fyang, fjiang --- .../loopopts/superword/TestAlignVector.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java index 97a3c4ed037..322c36c39e1 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java @@ -1063,8 +1063,16 @@ public class TestAlignVector { IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", IRNode.STORE_VECTOR, "> 0"}, applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"avx2", "true", "rvv", "true"}) + applyIfCPUFeature = {"avx2", "true"}) // require avx to ensure vectors are larger than what unrolling produces + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"riscv64", "true"}, + applyIfCPUFeature = {"rvv", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) static Object[] test13aIL(int[] a, long[] b) { for (int i = 0; i < RANGE; i++) { a[i]++; @@ -1175,8 +1183,16 @@ public class TestAlignVector { IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", IRNode.STORE_VECTOR, "> 0"}, applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"avx2", "true", "rvv", "true"}) + applyIfCPUFeature = {"avx2", "true"}) // require avx to ensure vectors are larger than what unrolling produces + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"riscv64", "true"}, + applyIfCPUFeature = {"rvv", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) static Object[] test13bIL(int[] a, long[] b) { for (int i = 1; i < RANGE; i++) { a[i]++; From 25480f0011297ad209eca1b1b56bcf983ea4ee5d Mon Sep 17 00:00:00 2001 From: Ramkumar Sunderbabu Date: Wed, 13 Aug 2025 01:45:49 +0000 Subject: [PATCH 020/112] 8365184: sun/tools/jhsdb/HeapDumpTestWithActiveProcess.java Re-enable SerialGC flag on debuggee process Reviewed-by: lmesnik, cjplummer, sspitsyn --- test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java b/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java index 9e9a6c79c7b..6fbc96362a9 100644 --- a/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java +++ b/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java @@ -162,12 +162,7 @@ public class JShellHeapDumpTest { long startTime = System.currentTimeMillis(); try { JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jshell"); - if (doSleep) { - launcher.addVMArgs(Utils.getTestJavaOpts()); - } else { - // Don't allow use of SerialGC. See JDK-8313655. - launcher.addVMArgs(Utils.getFilteredTestJavaOpts("-XX:\\+UseSerialGC")); - } + launcher.addVMArgs(Utils.getTestJavaOpts()); ProcessBuilder pb = new ProcessBuilder(launcher.getCommand()); // Needed so we can properly parse the "Welcome to JShell" output. pb.command().add("-J-Duser.language=en"); From 72e22b4de59a18f83c75be9a51fd99726f77f6f6 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 13 Aug 2025 08:07:45 +0000 Subject: [PATCH 021/112] 8362885: A more formal way to mark javac's Flags that belong to a specific Symbol type only Reviewed-by: ihse, liach, vromero, mcimadamore, erikj --- make/ToolsLangtools.gmk | 2 +- .../tools/flagsgenerator/FlagsGenerator.java | 161 +++++++++++ .../propertiesparser/parser/MessageType.java | 4 +- make/modules/jdk.compiler/Gensrc.gmk | 33 ++- .../com/sun/tools/javac/code/Flags.java | 269 ++++++++++-------- .../com/sun/tools/javac/comp/Modules.java | 4 +- .../javac/diags/ArgTypeCompilerFactory.java | 4 +- .../tools/javac/flags/FlagsTest.java | 85 +++++- 8 files changed, 422 insertions(+), 140 deletions(-) create mode 100644 make/langtools/tools/flagsgenerator/FlagsGenerator.java diff --git a/make/ToolsLangtools.gmk b/make/ToolsLangtools.gmk index 4146652bf8b..1a764d6019b 100644 --- a/make/ToolsLangtools.gmk +++ b/make/ToolsLangtools.gmk @@ -36,7 +36,7 @@ $(eval $(call SetupJavaCompilation, BUILD_TOOLS_LANGTOOLS, \ COMPILER := bootjdk, \ TARGET_RELEASE := $(TARGET_RELEASE_BOOTJDK), \ SRC := $(TOPDIR)/make/langtools/tools, \ - INCLUDES := compileproperties propertiesparser, \ + INCLUDES := compileproperties flagsgenerator propertiesparser, \ COPY := .properties, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes, \ )) diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java new file mode 100644 index 00000000000..1c192d214de --- /dev/null +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package flagsgenerator; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.ElementFilter; +import javax.tools.ToolProvider; + +public class FlagsGenerator { + public static void main(String... args) throws IOException { + var compiler = ToolProvider.getSystemJavaCompiler(); + + try (var fm = compiler.getStandardFileManager(null, null, null)) { + JavacTask task = (JavacTask) compiler.getTask(null, null, d -> {}, null, null, fm.getJavaFileObjects(args[0])); + Trees trees = Trees.instance(task); + CompilationUnitTree cut = task.parse().iterator().next(); + + task.analyze(); + + TypeElement clazz = (TypeElement) trees.getElement(new TreePath(new TreePath(cut), cut.getTypeDecls().get(0))); + Map> flag2Names = new TreeMap<>(); + Map>> target2FlagBit2Fields = new EnumMap<>(FlagTarget.class); + Map customToString = new HashMap<>(); + Set noToString = new HashSet<>(); + + for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { + String flagName = field.getSimpleName().toString(); + for (AnnotationMirror am : field.getAnnotationMirrors()) { + switch (am.getAnnotationType().toString()) { + case "com.sun.tools.javac.code.Flags.Use" -> { + long flagValue = ((Number) field.getConstantValue()).longValue(); + int flagBit = 63 - Long.numberOfLeadingZeros(flagValue); + + flag2Names.computeIfAbsent(flagBit, _ -> new ArrayList<>()) + .add(flagName); + + List originalTargets = (List) valueOfValueAttribute(am); + originalTargets.stream() + .map(value -> FlagTarget.valueOf(value.toString())) + .forEach(target -> target2FlagBit2Fields.computeIfAbsent(target, _ -> new HashMap<>()) + .computeIfAbsent(flagBit, _ -> new ArrayList<>()) + .add(flagName)); + } + case "com.sun.tools.javac.code.Flags.CustomToStringValue" -> { + customToString.put(flagName, (String) valueOfValueAttribute(am)); + } + case "com.sun.tools.javac.code.Flags.NoToStringValue" -> { + noToString.add(flagName); + } + } + } + } + + //verify there are no flag overlaps: + for (Entry>> targetAndFlag : target2FlagBit2Fields.entrySet()) { + for (Entry> flagAndFields : targetAndFlag.getValue().entrySet()) { + if (flagAndFields.getValue().size() > 1) { + throw new AssertionError("duplicate flag for target: " + targetAndFlag.getKey() + + ", flag: " + flagAndFields.getKey() + + ", flags fields: " + flagAndFields.getValue()); + } + } + } + + try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(args[1])))) { + out.println(""" + package com.sun.tools.javac.code; + + public enum FlagsEnum { + """); + for (Entry> e : flag2Names.entrySet()) { + String constantName = e.getValue().stream().collect(Collectors.joining("_OR_")); + String toString = e.getValue() + .stream() + .filter(n -> !noToString.contains(n)) + .map(n -> customToString.getOrDefault(n, n.toLowerCase(Locale.US))) + .collect(Collectors.joining(" or ")); + out.println(" " + constantName + "(1L<<" + e.getKey() + ", \"" + toString + "\"),"); + } + out.println(""" + ; + + private final long value; + private final String toString; + private FlagsEnum(long value, String toString) { + this.value = value; + this.toString = toString; + } + public long value() { + return value; + } + public String toString() { + return toString; + } + } + """); + } + } + } + + private static Object valueOfValueAttribute(AnnotationMirror am) { + return am.getElementValues() + .values() + .iterator() + .next() + .getValue(); + } + + private enum FlagTarget { + BLOCK, + CLASS, + METHOD, + MODULE, + PACKAGE, + TYPE_VAR, + VARIABLE; + } +} diff --git a/make/langtools/tools/propertiesparser/parser/MessageType.java b/make/langtools/tools/propertiesparser/parser/MessageType.java index ea518dc536b..a4ea0ddc3c0 100644 --- a/make/langtools/tools/propertiesparser/parser/MessageType.java +++ b/make/langtools/tools/propertiesparser/parser/MessageType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,7 +76,7 @@ public interface MessageType { ANNOTATION("annotation", "Compound", "com.sun.tools.javac.code.Attribute"), BOOLEAN("boolean", "boolean", null), COLLECTION("collection", "Collection", "java.util"), - FLAG("flag", "Flag", "com.sun.tools.javac.code.Flags"), + FLAG("flag", "FlagsEnum", "com.sun.tools.javac.code"), FRAGMENT("fragment", "Fragment", null), DIAGNOSTIC("diagnostic", "JCDiagnostic", "com.sun.tools.javac.util"), MODIFIER("modifier", "Modifier", "javax.lang.model.element"), diff --git a/make/modules/jdk.compiler/Gensrc.gmk b/make/modules/jdk.compiler/Gensrc.gmk index c6c5879745c..501ea37ae8b 100644 --- a/make/modules/jdk.compiler/Gensrc.gmk +++ b/make/modules/jdk.compiler/Gensrc.gmk @@ -41,17 +41,17 @@ $(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ TARGETS += $(COMPILE_PROPERTIES) -################################################################################ -# -# Compile properties files into enum-like classes using the propertiesparser tool -# - # To avoid reevaluating the compilation setup for the tools each time this file # is included, the following trick is used to be able to declare a dependency on # the built tools. BUILD_TOOLS_LANGTOOLS := $(call SetupJavaCompilationCompileTarget, \ BUILD_TOOLS_LANGTOOLS, $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes) +################################################################################ +# +# Compile properties files into enum-like classes using the propertiesparser tool +# + TOOL_PARSEPROPERTIES_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \ propertiesparser.PropertiesParser @@ -76,3 +76,26 @@ $(eval $(call SetupExecute, PARSEPROPERTIES, \ TARGETS += $(PARSEPROPERTIES) ################################################################################ +# +# Generate FlagsEnum from Flags constants +# + +TOOL_FLAGSGENERATOR_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \ + flagsgenerator.FlagsGenerator + +FLAGS_SRC := \ + $(MODULE_SRC)/share/classes/com/sun/tools/javac/code/Flags.java + +FLAGS_OUT := \ + $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/com/sun/tools/javac/code/FlagsEnum.java + +$(eval $(call SetupExecute, FLAGSGENERATOR, \ + WARN := Generating FlagsEnum, \ + DEPS := $(FLAGS_SRC) $(BUILD_TOOLS_LANGTOOLS), \ + OUTPUT_FILE := $(FLAGS_OUT), \ + COMMAND := $(TOOL_FLAGSGENERATOR_CMD) $(FLAGS_SRC) $(FLAGS_OUT), \ +)) + +TARGETS += $(FLAGSGENERATOR) + +################################################################################ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 26f0d338a2e..5b59e47027e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -25,6 +25,8 @@ package com.sun.tools.javac.code; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.EnumSet; import java.util.Map; @@ -35,7 +37,6 @@ import java.util.stream.Collectors; import javax.lang.model.element.Modifier; import com.sun.tools.javac.util.Assert; -import com.sun.tools.javac.util.StringUtils; /** Access flags and other modifiers for Java classes and members. * @@ -51,7 +52,7 @@ public class Flags { public static String toString(long flags) { StringBuilder buf = new StringBuilder(); String sep = ""; - for (Flag flag : asFlagSet(flags)) { + for (FlagsEnum flag : asFlagSet(flags)) { buf.append(sep); buf.append(flag); sep = " "; @@ -59,12 +60,12 @@ public class Flags { return buf.toString(); } - public static EnumSet asFlagSet(long flags) { - EnumSet flagSet = EnumSet.noneOf(Flag.class); - for (Flag flag : Flag.values()) { - if ((flags & flag.value) != 0) { + public static EnumSet asFlagSet(long flags) { + EnumSet flagSet = EnumSet.noneOf(FlagsEnum.class); + for (FlagsEnum flag : FlagsEnum.values()) { + if ((flags & flag.value()) != 0) { flagSet.add(flag); - flags &= ~flag.value; + flags &= ~flag.value(); } } Assert.check(flags == 0); @@ -73,42 +74,67 @@ public class Flags { /* Standard Java flags. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PUBLIC = 1; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PRIVATE = 1<<1; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PROTECTED = 1<<2; + @Use({FlagTarget.BLOCK, FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int STATIC = 1<<3; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int FINAL = 1<<4; + @Use({FlagTarget.METHOD}) public static final int SYNCHRONIZED = 1<<5; + @Use({FlagTarget.VARIABLE}) public static final int VOLATILE = 1<<6; + @Use({FlagTarget.VARIABLE}) public static final int TRANSIENT = 1<<7; + @Use({FlagTarget.METHOD}) public static final int NATIVE = 1<<8; + @Use({FlagTarget.CLASS}) public static final int INTERFACE = 1<<9; + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int ABSTRACT = 1<<10; + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int STRICTFP = 1<<11; /* Flag that marks a symbol synthetic, added in classfile v49.0. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int SYNTHETIC = 1<<12; /** Flag that marks attribute interfaces, added in classfile v49.0. */ + @Use({FlagTarget.CLASS}) public static final int ANNOTATION = 1<<13; /** An enumeration type or an enumeration constant, added in * classfile v49.0. */ + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE}) public static final int ENUM = 1<<14; /** Added in SE8, represents constructs implicitly declared in source. */ + @Use({FlagTarget.MODULE, FlagTarget.VARIABLE}) public static final int MANDATED = 1<<15; + @NotFlag public static final int StandardFlags = 0x0fff; // Because the following access flags are overloaded with other // bit positions, we translate them when reading and writing class // files into unique bits positions: ACC_SYNTHETIC <-> SYNTHETIC, // for example. - public static final int ACC_SUPER = 0x0020; - public static final int ACC_BRIDGE = 0x0040; - public static final int ACC_VARARGS = 0x0080; - public static final int ACC_MODULE = 0x8000; + @Use({FlagTarget.CLASS}) + @NoToStringValue + public static final int ACC_SUPER = 1<<5; + @Use({FlagTarget.METHOD}) + @NoToStringValue + public static final int ACC_BRIDGE = 1<<6; + @Use({FlagTarget.METHOD}) + @NoToStringValue + public static final int ACC_VARARGS = 1<<7; + @Use({FlagTarget.CLASS}) + @NoToStringValue + public static final int ACC_MODULE = 1<<15; /* *************************************** * Internal compiler flags (no bits in the lower 16). @@ -116,25 +142,30 @@ public class Flags { /** Flag is set if symbol is deprecated. See also DEPRECATED_REMOVAL. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final int DEPRECATED = 1<<17; /** Flag is set for a variable symbol if the variable's definition * has an initializer part. */ + @Use({FlagTarget.VARIABLE}) public static final int HASINIT = 1<<18; /** Class is an implicitly declared top level class. */ + @Use({FlagTarget.CLASS}) public static final int IMPLICIT_CLASS = 1<<19; /** Flag is set for compiler-generated anonymous method symbols * that `own' an initializer block. */ + @Use({FlagTarget.METHOD}) public static final int BLOCK = 1<<20; /** Flag is set for ClassSymbols that are being compiled from source. */ - public static final int FROM_SOURCE = 1<<21; //ClassSymbols + @Use({FlagTarget.CLASS}) + public static final int FROM_SOURCE = 1<<21; /** Flag is set for nested classes that do not access instance members * or `this' of an outer class and therefore don't need to be passed @@ -143,25 +174,30 @@ public class Flags { * todo: use this value for optimizing away this$n parameters in * other cases. */ + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE}) public static final int NOOUTERTHIS = 1<<22; /** Flag is set for package symbols if a package has a member or * directory and therefore exists. */ + @Use({FlagTarget.CLASS, FlagTarget.PACKAGE}) public static final int EXISTS = 1<<23; /** Flag is set for compiler-generated compound classes * representing multiple variable bounds */ + @Use({FlagTarget.CLASS}) public static final int COMPOUND = 1<<24; /** Flag is set for class symbols if a class file was found for this class. */ + @Use({FlagTarget.CLASS}) public static final int CLASS_SEEN = 1<<25; /** Flag is set for class symbols if a source file was found for this * class. */ + @Use({FlagTarget.CLASS}) public static final int SOURCE_SEEN = 1<<26; /* State flags (are reset during compilation). @@ -172,242 +208,291 @@ public class Flags { * relations. Similarly for constructor call cycle detection in * Attr. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int LOCKED = 1<<27; /** Flag for class symbols is set and later re-set to indicate that a class * has been entered but has not yet been attributed. */ + @Use({FlagTarget.CLASS}) public static final int UNATTRIBUTED = 1<<28; /** Flag for synthesized default constructors of anonymous classes. */ - public static final int ANONCONSTR = 1<<29; //non-class members + @Use({FlagTarget.METHOD}) + public static final int ANONCONSTR = 1<<29; /** * Flag to indicate the superclasses of this ClassSymbol has been attributed. */ - public static final int SUPER_OWNER_ATTRIBUTED = 1<<29; //ClassSymbols + @Use({FlagTarget.CLASS}) + public static final int SUPER_OWNER_ATTRIBUTED = 1<<29; /** Flag for class symbols to indicate it has been checked and found * acyclic. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.TYPE_VAR}) public static final int ACYCLIC = 1<<30; /** Flag that marks bridge methods. */ + @Use({FlagTarget.METHOD}) public static final long BRIDGE = 1L<<31; /** Flag that marks formal parameters. */ + @Use({FlagTarget.VARIABLE}) public static final long PARAMETER = 1L<<33; /** Flag that marks varargs methods. */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final long VARARGS = 1L<<34; /** Flag for annotation type symbols to indicate it has been * checked and found acyclic. */ + @Use({FlagTarget.CLASS}) public static final long ACYCLIC_ANN = 1L<<35; /** Flag that marks a generated default constructor. */ + @Use({FlagTarget.METHOD}) public static final long GENERATEDCONSTR = 1L<<36; /** Flag that marks a hypothetical method that need not really be * generated in the binary, but is present in the symbol table to * simplify checking for erasure clashes - also used for 292 poly sig methods. */ + @Use({FlagTarget.METHOD}) public static final long HYPOTHETICAL = 1L<<37; /** * Flag that marks an internal proprietary class. */ + @Use({FlagTarget.CLASS}) public static final long PROPRIETARY = 1L<<38; /** * Flag that marks a multi-catch parameter. */ + @Use({FlagTarget.VARIABLE}) public static final long UNION = 1L<<39; /** * Flags an erroneous TypeSymbol as viable for recovery. * TypeSymbols only. */ + @Use({FlagTarget.CLASS, FlagTarget.TYPE_VAR}) public static final long RECOVERABLE = 1L<<40; /** * Flag that marks an 'effectively final' local variable. */ + @Use({FlagTarget.VARIABLE}) public static final long EFFECTIVELY_FINAL = 1L<<41; /** * Flag that marks non-override equivalent methods with the same signature, * or a conflicting match binding (BindingSymbol). */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final long CLASH = 1L<<42; /** * Flag that marks either a default method or an interface containing default methods. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final long DEFAULT = 1L<<43; // part of ExtendedStandardFlags, cannot be reused /** * Flag that marks class as auxiliary, ie a non-public class following * the public class in a source file, that could block implicit compilation. */ + @Use({FlagTarget.CLASS}) public static final long AUXILIARY = 1L<<44; /** * Flag that marks that a symbol is not available in the current profile */ + @Use({FlagTarget.CLASS}) public static final long NOT_IN_PROFILE = 1L<<45; /** * Flag that indicates that an override error has been detected by Check. */ + @Use({FlagTarget.METHOD}) public static final long BAD_OVERRIDE = 1L<<45; /** * Flag that indicates a signature polymorphic method (292). */ + @Use({FlagTarget.METHOD}) public static final long SIGNATURE_POLYMORPHIC = 1L<<46; /** * Flag that indicates that an inference variable is used in a 'throws' clause. */ + @Use({FlagTarget.TYPE_VAR}) public static final long THROWS = 1L<<47; /** * Flag to indicate sealed class/interface declaration. */ + @Use({FlagTarget.CLASS}) public static final long SEALED = 1L<<48; // part of ExtendedStandardFlags, cannot be reused /** * Flag that marks a synthetic method body for a lambda expression */ - public static final long LAMBDA_METHOD = 1L<<49; //MethodSymbols only + @Use({FlagTarget.METHOD}) + public static final long LAMBDA_METHOD = 1L<<49; /** * Flag that marks a synthetic local capture field in a local/anon class */ - public static final long LOCAL_CAPTURE_FIELD = 1L<<49; //VarSymbols only + @Use({FlagTarget.VARIABLE}) + public static final long LOCAL_CAPTURE_FIELD = 1L<<49; /** * Flag to control recursion in TransTypes */ + @Use({FlagTarget.CLASS}) public static final long TYPE_TRANSLATED = 1L<<50; /** * Flag to indicate class symbol is for module-info */ + @Use({FlagTarget.CLASS}) public static final long MODULE = 1L<<51; /** * Flag to indicate the given ModuleSymbol is an automatic module. */ - public static final long AUTOMATIC_MODULE = 1L<<52; //ModuleSymbols only + @Use({FlagTarget.MODULE}) + public static final long AUTOMATIC_MODULE = 1L<<52; /** * Flag to indicate the given PackageSymbol contains any non-.java and non-.class resources. */ - public static final long HAS_RESOURCE = 1L<<52; //PackageSymbols only + @Use({FlagTarget.PACKAGE}) + public static final long HAS_RESOURCE = 1L<<52; /** * Flag to indicate the given ParamSymbol has a user-friendly name filled. */ - public static final long NAME_FILLED = 1L<<52; //ParamSymbols only + @Use({FlagTarget.VARIABLE}) //ParamSymbols only + public static final long NAME_FILLED = 1L<<52; /** * Flag to indicate the given ModuleSymbol is a system module. */ - public static final long SYSTEM_MODULE = 1L<<53; //ModuleSymbols only + @Use({FlagTarget.MODULE}) + public static final long SYSTEM_MODULE = 1L<<53; /** * Flag to indicate the given ClassSymbol is a value based. */ - public static final long VALUE_BASED = 1L<<53; //ClassSymbols only + @Use({FlagTarget.CLASS}) + public static final long VALUE_BASED = 1L<<53; /** * Flag to indicate the given symbol has a @Deprecated annotation. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long DEPRECATED_ANNOTATION = 1L<<54; /** * Flag to indicate the given symbol has been deprecated and marked for removal. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long DEPRECATED_REMOVAL = 1L<<55; /** * Flag to indicate the API element in question is for a preview API. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long PREVIEW_API = 1L<<56; //any Symbol kind /** * Flag for synthesized default constructors of anonymous classes that have an enclosing expression. */ + @Use({FlagTarget.METHOD}) public static final long ANONCONSTR_BASED = 1L<<57; /** * Flag that marks finalize block as body-only, should not be copied into catch clauses. * Used to implement try-with-resources. */ - public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only + @Use({FlagTarget.BLOCK}) + public static final long BODY_ONLY_FINALIZE = 1L<<17; /** * Flag to indicate the API element in question is for a preview API. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long PREVIEW_REFLECTIVE = 1L<<58; //any Symbol kind /** * Flag to indicate the given variable is a match binding variable. */ + @Use({FlagTarget.VARIABLE}) public static final long MATCH_BINDING = 1L<<59; /** * A flag to indicate a match binding variable whose scope extends after the current statement. */ + @Use({FlagTarget.VARIABLE}) public static final long MATCH_BINDING_TO_OUTER = 1L<<60; /** * Flag to indicate that a class is a record. The flag is also used to mark fields that are * part of the state vector of a record and to mark the canonical constructor */ - public static final long RECORD = 1L<<61; // ClassSymbols, MethodSymbols and VarSymbols + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE, FlagTarget.METHOD}) + public static final long RECORD = 1L<<61; /** * Flag to mark a record constructor as a compact one */ - public static final long COMPACT_RECORD_CONSTRUCTOR = 1L<<51; // MethodSymbols only + @Use({FlagTarget.METHOD}) + public static final long COMPACT_RECORD_CONSTRUCTOR = 1L<<51; /** * Flag to mark a record field that was not initialized in the compact constructor */ - public static final long UNINITIALIZED_FIELD= 1L<<51; // VarSymbols only + @Use({FlagTarget.VARIABLE}) + public static final long UNINITIALIZED_FIELD= 1L<<51; /** Flag is set for compiler-generated record members, it could be applied to * accessors and fields */ - public static final int GENERATED_MEMBER = 1<<24; // MethodSymbols and VarSymbols + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) + public static final int GENERATED_MEMBER = 1<<24; /** * Flag to indicate restricted method declaration. */ - public static final long RESTRICTED = 1L<<62; // MethodSymbols + @Use({FlagTarget.METHOD}) + public static final long RESTRICTED = 1L<<62; /** * Flag to indicate parameters that require identity. */ - public static final long REQUIRES_IDENTITY = 1L<<62; // VarSymbols (parameters) + @Use({FlagTarget.VARIABLE}) //ParamSymbols only + public static final long REQUIRES_IDENTITY = 1L<<62; /** * Flag to indicate type annotations have been queued for field initializers. */ - public static final long FIELD_INIT_TYPE_ANNOTATIONS_QUEUED = 1L<<53; // VarSymbols + @Use({FlagTarget.VARIABLE}) + public static final long FIELD_INIT_TYPE_ANNOTATIONS_QUEUED = 1L<<53; /** * Flag to indicate that the class/interface was declared with the non-sealed modifier. */ + @Use({FlagTarget.CLASS}) + @CustomToStringValue("non-sealed") public static final long NON_SEALED = 1L<<63; // part of ExtendedStandardFlags, cannot be reused /** @@ -422,6 +507,7 @@ public class Flags { /** Modifier masks. */ + @NotFlag public static final int AccessFlags = PUBLIC | PROTECTED | PRIVATE, LocalClassFlags = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC, @@ -438,6 +524,7 @@ public class Flags { SYNCHRONIZED | FINAL | STRICTFP, RecordMethodFlags = AccessFlags | ABSTRACT | STATIC | SYNCHRONIZED | FINAL | STRICTFP; + @NotFlag public static final long //NOTE: flags in ExtendedStandardFlags cannot be overlayed across Symbol kinds: ExtendedStandardFlags = (long)StandardFlags | DEFAULT | SEALED | NON_SEALED, @@ -491,91 +578,45 @@ public class Flags { return symbol.getConstValue() != null; } - - public enum Flag { - PUBLIC(Flags.PUBLIC), - PRIVATE(Flags.PRIVATE), - PROTECTED(Flags.PROTECTED), - STATIC(Flags.STATIC), - FINAL(Flags.FINAL), - SYNCHRONIZED(Flags.SYNCHRONIZED), - VOLATILE(Flags.VOLATILE), - TRANSIENT(Flags.TRANSIENT), - NATIVE(Flags.NATIVE), - INTERFACE(Flags.INTERFACE), - ABSTRACT(Flags.ABSTRACT), - DEFAULT(Flags.DEFAULT), - STRICTFP(Flags.STRICTFP), - BRIDGE(Flags.BRIDGE), - SYNTHETIC(Flags.SYNTHETIC), - ANNOTATION(Flags.ANNOTATION), - DEPRECATED(Flags.DEPRECATED), - HASINIT(Flags.HASINIT), - IMPLICIT_CLASS(Flags.IMPLICIT_CLASS), - BLOCK(Flags.BLOCK), - FROM_SOURCE(Flags.FROM_SOURCE), - ENUM(Flags.ENUM), - MANDATED(Flags.MANDATED), - NOOUTERTHIS(Flags.NOOUTERTHIS), - EXISTS(Flags.EXISTS), - COMPOUND(Flags.COMPOUND), - CLASS_SEEN(Flags.CLASS_SEEN), - SOURCE_SEEN(Flags.SOURCE_SEEN), - LOCKED(Flags.LOCKED), - UNATTRIBUTED(Flags.UNATTRIBUTED), - ANONCONSTR(Flags.ANONCONSTR), - ACYCLIC(Flags.ACYCLIC), - PARAMETER(Flags.PARAMETER), - VARARGS(Flags.VARARGS), - ACYCLIC_ANN(Flags.ACYCLIC_ANN), - GENERATEDCONSTR(Flags.GENERATEDCONSTR), - HYPOTHETICAL(Flags.HYPOTHETICAL), - PROPRIETARY(Flags.PROPRIETARY), - UNION(Flags.UNION), - EFFECTIVELY_FINAL(Flags.EFFECTIVELY_FINAL), - CLASH(Flags.CLASH), - AUXILIARY(Flags.AUXILIARY), - NOT_IN_PROFILE(Flags.NOT_IN_PROFILE), - BAD_OVERRIDE(Flags.BAD_OVERRIDE), - SIGNATURE_POLYMORPHIC(Flags.SIGNATURE_POLYMORPHIC), - THROWS(Flags.THROWS), - LAMBDA_METHOD(Flags.LAMBDA_METHOD), - TYPE_TRANSLATED(Flags.TYPE_TRANSLATED), - MODULE(Flags.MODULE), - AUTOMATIC_MODULE(Flags.AUTOMATIC_MODULE), - SYSTEM_MODULE(Flags.SYSTEM_MODULE), - DEPRECATED_ANNOTATION(Flags.DEPRECATED_ANNOTATION), - DEPRECATED_REMOVAL(Flags.DEPRECATED_REMOVAL), - HAS_RESOURCE(Flags.HAS_RESOURCE), - SEALED(Flags.SEALED), - ANONCONSTR_BASED(Flags.ANONCONSTR_BASED), - NAME_FILLED(Flags.NAME_FILLED), - PREVIEW_API(Flags.PREVIEW_API), - PREVIEW_REFLECTIVE(Flags.PREVIEW_REFLECTIVE), - MATCH_BINDING(Flags.MATCH_BINDING), - MATCH_BINDING_TO_OUTER(Flags.MATCH_BINDING_TO_OUTER), - RECORD(Flags.RECORD), - RECOVERABLE(Flags.RECOVERABLE), - RESTRICTED(Flags.RESTRICTED), - NON_SEALED(Flags.NON_SEALED) { - @Override - public String toString() { - return "non-sealed"; - } - }; - - Flag(long flag) { - this.value = flag; - this.lowercaseName = StringUtils.toLowerCase(name()); - } - - @Override - public String toString() { - return lowercaseName; - } - - final long value; - final String lowercaseName; + public enum FlagTarget { + /** This flag can appear the JCBlock. + */ + BLOCK, + /** This flag can appear on ClassSymbols. + */ + CLASS, + /** This flag can appear on ModuleSymbols. + */ + MODULE, + /** This flag can appear on PackageSymbols. + */ + PACKAGE, + /** This flag can appear on TypeVarSymbols. + */ + TYPE_VAR, + /** This flag can appear on MethodSymbols. + */ + METHOD, + /** This flag can appear on VarSymbols, includes + * including ParamSymbol, and BindingSymbol. + */ + VARIABLE; } + @Retention(RetentionPolicy.RUNTIME) + public @interface Use { + public FlagTarget[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface NotFlag {} + + @Retention(RetentionPolicy.RUNTIME) + public @interface CustomToStringValue { + public String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface NoToStringValue { + } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 47066b24de9..4d0af014d83 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -62,7 +62,7 @@ import com.sun.tools.javac.code.Directive.RequiresDirective; import com.sun.tools.javac.code.Directive.RequiresFlag; import com.sun.tools.javac.code.Directive.UsesDirective; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Flags.Flag; +import com.sun.tools.javac.code.FlagsEnum; import com.sun.tools.javac.code.Kinds; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; @@ -825,7 +825,7 @@ public class Modules extends JCTree.Visitor { } if (tree.isStaticPhase) { if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) { - log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(Flag.STATIC))); + log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(FlagsEnum.STATIC))); } else { flags.add(RequiresFlag.STATIC_PHASE); } diff --git a/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java b/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java index e40109f99f3..823a0f01a7f 100644 --- a/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java +++ b/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java @@ -29,7 +29,7 @@ import javax.tools.*; import com.sun.tools.javac.api.*; import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.DiagnosticPart; import com.sun.tools.javac.api.Formattable.LocalizedString; -import com.sun.tools.javac.code.Flags.Flag; +import com.sun.tools.javac.code.FlagsEnum; import com.sun.tools.javac.code.Kinds.KindName; import com.sun.tools.javac.code.*; import com.sun.tools.javac.file.*; @@ -303,7 +303,7 @@ class ArgTypeCompilerFactory implements Example.Compiler.Factory { return "number"; if (o instanceof String) return "string"; - if (o instanceof Flag) + if (o instanceof FlagsEnum) return "modifier"; if (o instanceof KindName) return "symbol kind"; diff --git a/test/langtools/tools/javac/flags/FlagsTest.java b/test/langtools/tools/javac/flags/FlagsTest.java index 1dcb0606a65..a7a9051af33 100644 --- a/test/langtools/tools/javac/flags/FlagsTest.java +++ b/test/langtools/tools/javac/flags/FlagsTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Google LLC. All rights reserved. + * 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 @@ -23,28 +24,84 @@ /** * @test - * @bug 8211138 + * @bug 8211138 8362885 * @summary Missing Flag enum constants * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.code - * @run main FlagsTest + * @compile FlagsTest.java + * @run main/manual FlagsTest */ import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Flags.FlagTarget; +import com.sun.tools.javac.code.Flags.NotFlag; +import com.sun.tools.javac.code.Flags.Use; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class FlagsTest { - public static void main(String[] args) throws IllegalAccessException { - for (Field f : Flags.class.getFields()) { - if (!Modifier.isStatic(f.getModifiers())) { - continue; - } - long flag = ((Number) f.get(null)).longValue(); - try { - Flags.asFlagSet(flag); - } catch (AssertionError e) { - throw new AssertionError("missing Flags enum constant for: " + f.getName(), e); - } + + private static final int U2_SIZE = 16; + + public static void main(String[] args) throws Throwable { + findFreeFlags(); + } + + private static void findFreeFlags() throws Throwable { + Map>> target2Flag2Fields = computeTarget2Flag2Fields(); + + for (FlagTarget target : FlagTarget.values()) { + long freeFlags = ~collectFlags(target2Flag2Fields, target); + + printFreeFlags(target.name(), freeFlags); } } + + private static Map>> computeTarget2Flag2Fields() throws Throwable { + Map>> target2Flag2Fields = new HashMap<>(); + for (Field f : Flags.class.getFields()) { + if (f.isAnnotationPresent(NotFlag.class)) { + continue; + } + + Use use = f.getAnnotation(Use.class); + + if (use == null) { + throw new AssertionError("No @Use and no @NotFlag for: " + f.getName()); + } + + long flagValue = ((Number) f.get(null)).longValue(); + + for (FlagTarget target : use.value()) { + target2Flag2Fields.computeIfAbsent(target, _ -> new HashMap<>()) + .computeIfAbsent(flagValue, _ -> new ArrayList<>()) + .add(f); + } + } + return target2Flag2Fields; + } + + private static void printFreeFlags(String comment, long freeFlags) { + System.err.print("free flags for " + comment + ": "); + for (int bit = U2_SIZE; bit < Long.SIZE; bit++) { //lowest 16 bits are used in classfiles, never suggest adding anything there + if ((freeFlags & (1L << bit)) != 0) { + System.err.print("1L<<" + bit + " "); + } + } + System.err.println(); + } + + private static long collectFlags(Map>> target2Flag2Fields, FlagTarget... forTargets) { + long flags = 0; + + for (FlagTarget target : forTargets) { + for (long used : target2Flag2Fields.get(target).keySet()) { + flags |= used; + } + } + + return flags; + } } From e77cdd93ead5601fea4bb1bf1847835e1097b851 Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Wed, 13 Aug 2025 08:47:08 +0000 Subject: [PATCH 022/112] 8364570: Remove LockingMode related code from riscv64 Reviewed-by: fyang, fjiang --- .../cpu/riscv/c1_LIRAssembler_riscv.cpp | 14 +- .../cpu/riscv/c1_MacroAssembler_riscv.cpp | 84 +------ .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 230 ------------------ .../cpu/riscv/c2_MacroAssembler_riscv.hpp | 7 +- src/hotspot/cpu/riscv/interp_masm_riscv.cpp | 152 +++--------- .../cpu/riscv/macroAssembler_riscv.cpp | 2 - src/hotspot/cpu/riscv/riscv.ad | 40 +-- src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 75 +----- .../templateInterpreterGenerator_riscv.cpp | 27 +- 9 files changed, 55 insertions(+), 576 deletions(-) diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 81b829bde9a..2a65270af92 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -339,11 +339,7 @@ int LIR_Assembler::emit_unwind_handler() { if (method()->is_synchronized()) { monitor_address(0, FrameMap::r10_opr); stub = new MonitorExitStub(FrameMap::r10_opr, true, 0); - if (LockingMode == LM_MONITOR) { - __ j(*stub->entry()); - } else { - __ unlock_object(x15, x14, x10, x16, *stub->entry()); - } + __ unlock_object(x15, x14, x10, x16, *stub->entry()); __ bind(*stub->continuation()); } @@ -1497,13 +1493,7 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); Register temp = op->scratch_opr()->as_register(); - if (LockingMode == LM_MONITOR) { - if (op->info() != nullptr) { - add_debug_info_for_null_check_here(op->info()); - __ null_check(obj, -1); - } - __ j(*op->stub()->entry()); - } else if (op->code() == lir_lock) { + if (op->code() == lir_lock) { assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, temp, *op->stub()->entry()); diff --git a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp index 3ce764b1861..8198192f506 100644 --- a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp @@ -49,8 +49,6 @@ void C1_MacroAssembler::float_cmp(bool is_float, int unordered_result, } int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord - 1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, t0, t1); int null_check_offset = -1; @@ -61,97 +59,19 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr null_check_offset = offset(); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(disp_hdr, obj, hdr, temp, t1, slow_case); - } else if (LockingMode == LM_LEGACY) { - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(hdr, obj); - lbu(hdr, Address(hdr, Klass::misc_flags_offset())); - test_bit(temp, hdr, exact_log2(KlassFlags::_misc_is_value_based_class)); - bnez(temp, slow_case, /* is_far */ true); - } - - Label done; - // Load object header - ld(hdr, Address(obj, hdr_offset)); - // and mark it as unlocked - ori(hdr, hdr, markWord::unlocked_value); - // save unlocked object header into the displaced header location on the stack - sd(hdr, Address(disp_hdr, 0)); - // test if object header is still the same (i.e. unlocked), and if so, store the - // displaced header address in the object header - if it is not the same, get the - // object header instead - la(temp, Address(obj, hdr_offset)); - // if the object header was the same, we're done - cmpxchgptr(hdr, disp_hdr, temp, t1, done, /*fallthough*/nullptr); - // if the object header was not the same, it is now in the hdr register - // => test if it is a stack pointer into the same stack (recursive locking), i.e.: - // - // 1) (hdr & aligned_mask) == 0 - // 2) sp <= hdr - // 3) hdr <= sp + page_size - // - // these 3 tests can be done by evaluating the following expression: - // - // (hdr -sp) & (aligned_mask - page_size) - // - // assuming both the stack pointer and page_size have their least - // significant 2 bits cleared and page_size is a power of 2 - sub(hdr, hdr, sp); - mv(temp, aligned_mask - (int)os::vm_page_size()); - andr(hdr, hdr, temp); - // for recursive locking, the result is zero => save it in the displaced header - // location (null in the displaced hdr location indicates recursive locking) - sd(hdr, Address(disp_hdr, 0)); - // otherwise we don't care about the result and handle locking via runtime call - bnez(hdr, slow_case, /* is_far */ true); - - // done - bind(done); - inc_held_monitor_count(t0); - } + lightweight_lock(disp_hdr, obj, hdr, temp, t1, slow_case); return null_check_offset; } void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord - 1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, t0, t1); - Label done; - - if (LockingMode != LM_LIGHTWEIGHT) { - // load displaced header - ld(hdr, Address(disp_hdr, 0)); - // if the loaded hdr is null we had recursive locking - // if we had recursive locking, we are done - beqz(hdr, done); - } // load object ld(obj, Address(disp_hdr, BasicObjectLock::obj_offset())); verify_oop(obj); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj, hdr, temp, t1, slow_case); - } else if (LockingMode == LM_LEGACY) { - // test if object header is pointing to the displaced header, and if so, restore - // the displaced header in the object - if the object header is not pointing to - // the displaced header, get the object header instead - // if the object header was not pointing to the displaced header, - // we do unlocking via runtime call - if (hdr_offset) { - la(temp, Address(obj, hdr_offset)); - cmpxchgptr(disp_hdr, hdr, temp, t1, done, &slow_case); - } else { - cmpxchgptr(disp_hdr, hdr, obj, t1, done, &slow_case); - } - - // done - bind(done); - dec_held_monitor_count(t0); - } + lightweight_unlock(obj, hdr, temp, t1, slow_case); } // Defines obj, preserves var_size_in_bytes diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index bf71d2c68f1..aac2c98fc86 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -43,240 +43,11 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") -void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, - Register tmp1Reg, Register tmp2Reg, Register tmp3Reg, Register tmp4Reg) { - // Use cr register to indicate the fast_lock result: zero for success; non-zero for failure. - Register flag = t1; - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmp1Reg; - Register tmp = tmp2Reg; - Label object_has_monitor; - // Finish fast lock successfully. MUST branch to with flag == 0 - Label locked; - // Finish fast lock unsuccessfully. slow_path MUST branch to with flag != 0 - Label slow_path; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr, flag, tmp3Reg, t0); - - mv(flag, 1); - - // Load markWord from object into displaced_header. - ld(disp_hdr, Address(oop, oopDesc::mark_offset_in_bytes())); - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, oop); - lbu(tmp, Address(tmp, Klass::misc_flags_offset())); - test_bit(tmp, tmp, exact_log2(KlassFlags::_misc_is_value_based_class)); - bnez(tmp, slow_path); - } - - // Check for existing monitor - test_bit(tmp, disp_hdr, exact_log2(markWord::monitor_value)); - bnez(tmp, object_has_monitor); - - if (LockingMode == LM_MONITOR) { - j(slow_path); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Set tmp to be (markWord of object | UNLOCK_VALUE). - ori(tmp, disp_hdr, markWord::unlocked_value); - - // Initialize the box. (Must happen before we update the object mark!) - sd(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // Compare object markWord with an unlocked value (tmp) and if - // equal exchange the stack address of our box with object markWord. - // On failure disp_hdr contains the possibly locked markWord. - cmpxchg(/*memory address*/oop, /*expected value*/tmp, /*new value*/box, Assembler::int64, - Assembler::aq, Assembler::rl, /*result*/disp_hdr); - beq(disp_hdr, tmp, locked); - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // If the compare-and-exchange succeeded, then we found an unlocked - // object, will have now locked it will continue at label locked - // We did not see an unlocked object so try the fast recursive case. - - // Check if the owner is self by comparing the value in the - // markWord of object (disp_hdr) with the stack pointer. - sub(disp_hdr, disp_hdr, sp); - mv(tmp, (intptr_t) (~(os::vm_page_size()-1) | (uintptr_t)markWord::lock_mask_in_place)); - // If (mark & lock_mask) == 0 and mark - sp < page_size, we are stack-locking and goto label - // locked, hence we can store 0 as the displaced header in the box, which indicates that it - // is a recursive lock. - andr(tmp/*==0?*/, disp_hdr, tmp); - sd(tmp/*==0, perhaps*/, Address(box, BasicLock::displaced_header_offset_in_bytes())); - beqz(tmp, locked); - j(slow_path); - } - - // Handle existing monitor. - bind(object_has_monitor); - - // Try to CAS owner (no owner => current thread's _monitor_owner_id). - add(tmp, disp_hdr, (in_bytes(ObjectMonitor::owner_offset()) - markWord::monitor_value)); - Register tid = tmp4Reg; - ld(tid, Address(xthread, JavaThread::monitor_owner_id_offset())); - cmpxchg(/*memory address*/tmp, /*expected value*/zr, /*new value*/tid, Assembler::int64, - Assembler::aq, Assembler::rl, /*result*/tmp3Reg); // cas succeeds if tmp3Reg == zr(expected) - - // Store a non-null value into the box to avoid looking like a re-entrant - // lock. The fast-path monitor unlock code checks for - // markWord::monitor_value so use markWord::unused_mark which has the - // relevant bit set, and also matches ObjectSynchronizer::slow_enter. - mv(tmp, (address)markWord::unused_mark().value()); - sd(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - beqz(tmp3Reg, locked); // CAS success means locking succeeded - - bne(tmp3Reg, tid, slow_path); // Check for recursive locking - - // Recursive lock case - increment(Address(disp_hdr, in_bytes(ObjectMonitor::recursions_offset()) - markWord::monitor_value), 1, tmp2Reg, tmp3Reg); - - bind(locked); - mv(flag, zr); - if (LockingMode == LM_LEGACY) { - inc_held_monitor_count(t0); - } - -#ifdef ASSERT - // Check that locked label is reached with flag == 0. - Label flag_correct; - beqz(flag, flag_correct); - stop("Fast Lock Flag != 0"); -#endif - - bind(slow_path); -#ifdef ASSERT - // Check that slow_path label is reached with flag != 0. - bnez(flag, flag_correct); - stop("Fast Lock Flag == 0"); - bind(flag_correct); -#endif - // C2 uses the value of flag (0 vs !0) to determine the continuation. -} - -void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, - Register tmp1Reg, Register tmp2Reg) { - // Use cr register to indicate the fast_unlock result: zero for success; non-zero for failure. - Register flag = t1; - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmp1Reg; - Register owner_addr = tmp1Reg; - Register tmp = tmp2Reg; - Label object_has_monitor; - // Finish fast lock successfully. MUST branch to with flag == 0 - Label unlocked; - // Finish fast lock unsuccessfully. slow_path MUST branch to with flag != 0 - Label slow_path; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr, flag, t0); - - mv(flag, 1); - - if (LockingMode == LM_LEGACY) { - // Find the lock address and load the displaced header from the stack. - ld(disp_hdr, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // If the displaced header is 0, we have a recursive unlock. - beqz(disp_hdr, unlocked); - } - - // Handle existing monitor. - ld(tmp, Address(oop, oopDesc::mark_offset_in_bytes())); - test_bit(t0, tmp, exact_log2(markWord::monitor_value)); - bnez(t0, object_has_monitor); - - if (LockingMode == LM_MONITOR) { - j(slow_path); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Check if it is still a light weight lock, this is true if we - // see the stack address of the basicLock in the markWord of the - // object. - - cmpxchg(/*memory address*/oop, /*expected value*/box, /*new value*/disp_hdr, Assembler::int64, - Assembler::relaxed, Assembler::rl, /*result*/tmp); - beq(box, tmp, unlocked); // box == tmp if cas succeeds - j(slow_path); - } - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // Handle existing monitor. - bind(object_has_monitor); - subi(tmp, tmp, (int)markWord::monitor_value); // monitor - ld(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - - Label notRecursive; - beqz(disp_hdr, notRecursive); // Will be 0 if not recursive. - - // Recursive lock - subi(disp_hdr, disp_hdr, 1); - sd(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - j(unlocked); - - bind(notRecursive); - // Compute owner address. - la(owner_addr, Address(tmp, ObjectMonitor::owner_offset())); - - // Set owner to null. - // Release to satisfy the JMM - membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - sd(zr, Address(owner_addr)); - // We need a full fence after clearing owner to avoid stranding. - // StoreLoad achieves this. - membar(StoreLoad); - - // Check if the entry_list is empty. - ld(t0, Address(tmp, ObjectMonitor::entry_list_offset())); - beqz(t0, unlocked); // If so we are done. - - // Check if there is a successor. - ld(t0, Address(tmp, ObjectMonitor::succ_offset())); - bnez(t0, unlocked); // If so we are done. - - // Save the monitor pointer in the current thread, so we can try to - // reacquire the lock in SharedRuntime::monitor_exit_helper(). - sd(tmp, Address(xthread, JavaThread::unlocked_inflated_monitor_offset())); - - mv(flag, 1); - j(slow_path); - - bind(unlocked); - mv(flag, zr); - if (LockingMode == LM_LEGACY) { - dec_held_monitor_count(t0); - } - -#ifdef ASSERT - // Check that unlocked label is reached with flag == 0. - Label flag_correct; - beqz(flag, flag_correct); - stop("Fast Lock Flag != 0"); -#endif - - bind(slow_path); -#ifdef ASSERT - // Check that slow_path label is reached with flag != 0. - bnez(flag, flag_correct); - stop("Fast Lock Flag == 0"); - bind(flag_correct); -#endif - // C2 uses the value of flag (0 vs !0) to determine the continuation. -} - void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register tmp1, Register tmp2, Register tmp3, Register tmp4) { // Flag register, zero for success; non-zero for failure. Register flag = t1; - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, tmp1, tmp2, tmp3, tmp4, flag, t0); mv(flag, 1); @@ -439,7 +210,6 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, // Flag register, zero for success; non-zero for failure. Register flag = t1; - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, tmp1, tmp2, tmp3, flag, t0); mv(flag, 1); diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index 73fceea3805..309ef8d9d5e 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -49,11 +49,6 @@ const int STUB_THRESHOLD, Label *STUB, Label *DONE); public: - // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. - void fast_lock(Register object, Register box, - Register tmp1, Register tmp2, Register tmp3, Register tmp4); - void fast_unlock(Register object, Register box, Register tmp1, Register tmp2); - // Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file. void fast_lock_lightweight(Register object, Register box, Register tmp1, Register tmp2, Register tmp3, Register tmp4); diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 92f7169f471..7c4b8444407 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -733,84 +733,26 @@ void InterpreterMacroAssembler::leave_jfr_critical_section() { void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - } else { - Label count, done; - const Register swap_reg = x10; - const Register tmp = c_rarg2; - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp2 = c_rarg4; - const Register tmp3 = c_rarg5; + const Register tmp = c_rarg2; + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp2 = c_rarg4; + const Register tmp3 = c_rarg5; - const int obj_offset = in_bytes(BasicObjectLock::obj_offset()); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + - BasicLock::displaced_header_offset_in_bytes(); + // Load object pointer into obj_reg (c_rarg3) + ld(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - Label slow_case; + Label done, slow_case; + lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); + j(done); - // Load object pointer into obj_reg c_rarg3 - ld(obj_reg, Address(lock_reg, obj_offset)); + bind(slow_case); + // Call the runtime routine for slow case + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); - j(done); - } else if (LockingMode == LM_LEGACY) { - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, obj_reg); - lbu(tmp, Address(tmp, Klass::misc_flags_offset())); - test_bit(tmp, tmp, exact_log2(KlassFlags::_misc_is_value_based_class)); - bnez(tmp, slow_case); - } - - // Load (object->mark() | 1) into swap_reg - ld(t0, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - ori(swap_reg, t0, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - sd(swap_reg, Address(lock_reg, mark_offset)); - - assert(lock_offset == 0, - "displached header must be first word in BasicObjectLock"); - - cmpxchg_obj_header(swap_reg, lock_reg, obj_reg, tmp, count, /*fallthrough*/nullptr); - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 7) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (7 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 3 bits clear. - // NOTE: the oopMark is in swap_reg x10 as the result of cmpxchg - sub(swap_reg, swap_reg, sp); - mv(t0, (int64_t)(7 - (int)os::vm_page_size())); - andr(swap_reg, swap_reg, t0); - - // Save the test result, for recursive case, the result is zero - sd(swap_reg, Address(lock_reg, mark_offset)); - bnez(swap_reg, slow_case); - - bind(count); - inc_held_monitor_count(t0); - j(done); - } - - bind(slow_case); - - // Call the runtime routine for slow case - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - - bind(done); - } + bind(done); } @@ -829,58 +771,30 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - } else { - Label count, done; + const Register swap_reg = x10; + const Register header_reg = c_rarg2; // Will contain the old oopMark + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock - const Register swap_reg = x10; - const Register header_reg = c_rarg2; // Will contain the old oopMark - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock + save_bcp(); // Save in case of exception - save_bcp(); // Save in case of exception + // Load oop into obj_reg (c_rarg3) + ld(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - if (LockingMode != LM_LIGHTWEIGHT) { - // Convert from BasicObjectLock structure to object and BasicLock - // structure Store the BasicLock address into x10 - la(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset())); - } + // Free entry + sd(zr, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load oop into obj_reg(c_rarg3) - ld(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); + Label done, slow_case; + lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); + j(done); - // Free entry - sd(zr, Address(lock_reg, BasicObjectLock::obj_offset())); + bind(slow_case); + // Call the runtime routine for slow case. + sd(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - Label slow_case; - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); - j(done); - } else if (LockingMode == LM_LEGACY) { - // Load the old header from BasicLock structure - ld(header_reg, Address(swap_reg, - BasicLock::displaced_header_offset_in_bytes())); - - // Test for recursion - beqz(header_reg, count); - - // Atomic swap back the old header - cmpxchg_obj_header(swap_reg, header_reg, obj_reg, tmp_reg, count, &slow_case); - - bind(count); - dec_held_monitor_count(t0); - j(done); - } - - bind(slow_case); - // Call the runtime routine for slow case. - sd(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - - bind(done); - restore_bcp(); - } + bind(done); + restore_bcp(); } diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index a4c44b0b1d1..453538e4a5a 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -6421,7 +6421,6 @@ void MacroAssembler::test_bit(Register Rd, Register Rs, uint32_t bit_pos) { // - tmp1, tmp2, tmp3: temporary registers, will be destroyed // - slow: branched to if locking fails void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register tmp1, Register tmp2, Register tmp3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(basic_lock, obj, tmp1, tmp2, tmp3, t0); Label push; @@ -6481,7 +6480,6 @@ void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Registe // - tmp1, tmp2, tmp3: temporary registers // - slow: branched to if unlocking fails void MacroAssembler::lightweight_unlock(Register obj, Register tmp1, Register tmp2, Register tmp3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(obj, tmp1, tmp2, tmp3, t0); #ifdef ASSERT diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 8e7b772414a..7d204007898 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. // Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -11021,45 +11021,9 @@ instruct tlsLoadP(javaThread_RegP dst) // inlined locking and unlocking // using t1 as the 'flag' register to bridge the BoolNode producers and consumers -instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, - iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegPNoSp tmp4) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastLock object box)); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4); - - ins_cost(10 * DEFAULT_COST); - format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2,$tmp3,$tmp4 #@cmpFastLock" %} - - ins_encode %{ - __ fast_lock($object$$Register, $box$$Register, - $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register); - %} - - ins_pipe(pipe_serial); -%} - -// using t1 as the 'flag' register to bridge the BoolNode producers and consumers -instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastUnlock object box)); - effect(TEMP tmp1, TEMP tmp2); - - ins_cost(10 * DEFAULT_COST); - format %{ "fastunlock $object,$box\t! kills $tmp1, $tmp2, #@cmpFastUnlock" %} - - ins_encode %{ - __ fast_unlock($object$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register); - %} - - ins_pipe(pipe_serial); -%} - instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegPNoSp tmp4) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4); @@ -11074,10 +11038,10 @@ instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, ins_pipe(pipe_serial); %} +// using t1 as the 'flag' register to bridge the BoolNode producers and consumers instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastUnlock object box)); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index c4e5a871a88..2cc1fb8eeff 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -1637,7 +1637,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // We use the same pc/oopMap repeatedly when we call out. Label native_return; - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // For convenience we use the pc we want to resume to in case of preemption on Object.wait. __ set_last_Java_frame(sp, noreg, native_return, t0); } else { @@ -1679,8 +1679,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Label lock_done; if (method->is_synchronized()) { - Label count; - const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); // Get the handle (the 2nd argument) @@ -1693,42 +1691,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Load the oop from the handle __ ld(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_MONITOR) { - __ j(slow_path_lock); - } else if (LockingMode == LM_LEGACY) { - // Load (object->mark() | 1) into swap_reg % x10 - __ ld(t0, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ ori(swap_reg, t0, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - __ sd(swap_reg, Address(lock_reg, mark_word_offset)); - - // src -> dest if dest == x10 else x10 <- dest - __ cmpxchg_obj_header(x10, lock_reg, obj_reg, lock_tmp, count, /*fallthrough*/nullptr); - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 3) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (3 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 2 bits clear. - // NOTE: the oopMark is in swap_reg % 10 as the result of cmpxchg - - __ sub(swap_reg, swap_reg, sp); - __ mv(t0, 3 - (int)os::vm_page_size()); - __ andr(swap_reg, swap_reg, t0); - - // Save the test result, for recursive case, the result is zero - __ sd(swap_reg, Address(lock_reg, mark_word_offset)); - __ bnez(swap_reg, slow_path_lock); - - __ bind(count); - __ inc_held_monitor_count(t0); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); - } + __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); // Slow path will re-enter here __ bind(lock_done); @@ -1789,7 +1752,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); __ sw(t0, Address(t1)); - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // Check preemption for Object.wait() __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); __ beqz(t1, native_return); @@ -1818,48 +1781,18 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Get locked oop from the handle we passed to jni __ ld(obj_reg, Address(oop_handle_reg, 0)); - Label done, not_recursive; - - if (LockingMode == LM_LEGACY) { - // Simple recursive lock? - __ ld(t0, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - __ bnez(t0, not_recursive); - __ dec_held_monitor_count(t0); - __ j(done); - } - - __ bind(not_recursive); - // Must save x10 if if it is live now because cmpxchg must use it if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { save_native_result(masm, ret_type, stack_slots); } - if (LockingMode == LM_MONITOR) { - __ j(slow_path_unlock); - } else if (LockingMode == LM_LEGACY) { - // get address of the stack lock - __ la(x10, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - // get old displaced header - __ ld(old_hdr, Address(x10, 0)); - - // Atomic swap old header if oop still contains the stack lock - Label count; - __ cmpxchg_obj_header(x10, old_hdr, obj_reg, lock_tmp, count, &slow_path_unlock); - __ bind(count); - __ dec_held_monitor_count(t0); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, ""); - __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); - } + __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); // slow path re-enters here __ bind(unlock_done); if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { restore_native_result(masm, ret_type, stack_slots); } - - __ bind(done); } Label dtrace_method_exit, dtrace_method_exit_done; diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 21164107053..61f4aa3e722 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1253,22 +1253,17 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ mv(t0, _thread_in_Java); __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); - if (LockingMode != LM_LEGACY) { - // Check preemption for Object.wait() - Label not_preempted; - __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); - __ beqz(t1, not_preempted); - __ sd(zr, Address(xthread, JavaThread::preempt_alternate_return_offset())); - __ jr(t1); - __ bind(native_return); - __ restore_after_resume(true /* is_native */); - // reload result_handler - __ ld(result_handler, Address(fp, frame::interpreter_frame_result_handler_offset * wordSize)); - __ bind(not_preempted); - } else { - // any pc will do so just use this one for LM_LEGACY to keep code together. - __ bind(native_return); - } + // Check preemption for Object.wait() + Label not_preempted; + __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ beqz(t1, not_preempted); + __ sd(zr, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ jr(t1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + // reload result_handler + __ ld(result_handler, Address(fp, frame::interpreter_frame_result_handler_offset * wordSize)); + __ bind(not_preempted); // reset_last_Java_frame __ reset_last_Java_frame(true); From f3b34d32d6ea409f8c8f0382e8f01e746366f842 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Wed, 13 Aug 2025 10:52:54 +0000 Subject: [PATCH 023/112] 8359235: C1 compilation fails with "assert(is_single_stack() && !is_virtual()) failed: type check" Reviewed-by: thartmann, dlong --- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 2 +- .../cpu/aarch64/c1_LIRGenerator_aarch64.cpp | 4 +- .../cpu/riscv/c1_LIRAssembler_riscv.cpp | 2 +- .../cpu/riscv/c1_LIRGenerator_riscv.cpp | 2 +- src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp | 6 +- .../intrinsics/TestStack2RegSlotMismatch.java | 76 +++++++++++++++++++ 6 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/TestStack2RegSlotMismatch.java diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index bdba957d1df..d7e2ff2e88d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -2813,7 +2813,7 @@ void LIR_Assembler::leal(LIR_Opr addr, LIR_Opr dest, LIR_PatchCode patch_code, C return; } - __ lea(dest->as_register_lo(), as_Address(addr->as_address_ptr())); + __ lea(dest->as_pointer_register(), as_Address(addr->as_address_ptr())); } diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp index f0d09edd0a9..ad26d494b2d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp @@ -981,7 +981,7 @@ void LIRGenerator::do_update_CRC32(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); @@ -1058,7 +1058,7 @@ void LIRGenerator::do_update_CRC32C(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 2a65270af92..f60be85a141 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -1821,7 +1821,7 @@ void LIR_Assembler::leal(LIR_Opr addr, LIR_Opr dest, LIR_PatchCode patch_code, C } LIR_Address* adr = addr->as_address_ptr(); - Register dst = dest->as_register_lo(); + Register dst = dest->as_pointer_register(); assert_different_registers(dst, t0); if (adr->base()->is_valid() && dst == adr->base()->as_pointer_register() && (!adr->index()->is_cpu_register())) { diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp index e450c04c47d..88565d9136f 100644 --- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp @@ -837,7 +837,7 @@ void LIRGenerator::do_update_CRC32(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 1fdfcc4f9cd..5459e8df229 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -961,7 +961,7 @@ void LIRGenerator::do_update_CRC32(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); @@ -1100,10 +1100,10 @@ void LIRGenerator::do_vectorizedMismatch(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr ptr_addr_a = new_pointer_register(); + LIR_Opr ptr_addr_a = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(addr_a), ptr_addr_a); - LIR_Opr ptr_addr_b = new_pointer_register(); + LIR_Opr ptr_addr_b = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(addr_b), ptr_addr_b); __ move(ptr_addr_a, cc->at(0)); diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestStack2RegSlotMismatch.java b/test/hotspot/jtreg/compiler/intrinsics/TestStack2RegSlotMismatch.java new file mode 100644 index 00000000000..d2942f061db --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestStack2RegSlotMismatch.java @@ -0,0 +1,76 @@ +/* + * 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 8359235 + * @summary Test C1 stack2reg after fixing incorrect use of T_LONG in intrinsic + * @requires vm.debug == true & vm.compiler1.enabled + * @run main/othervm -XX:TieredStopAtLevel=1 + * -XX:C1MaxInlineSize=200 + * -XX:CompileThreshold=10 + * -XX:CompileCommand=compileonly,java.lang.invoke.LambdaFormEditor::putInCache + * compiler.intrinsics.TestStack2RegSlotMismatch + */ +package compiler.intrinsics; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.List; + +public class TestStack2RegSlotMismatch { + public static int target(int x, int y) { + return x + y; + } + + public static void main(String[] args) throws Throwable { + MethodHandle mh = MethodHandles.lookup().findStatic( + TestStack2RegSlotMismatch.class, + "target", + MethodType.methodType(int.class, int.class, int.class) + ); + List argsList = new ArrayList<>(); + int j = 0; + + for (int i = 0; i < 50; i++) { + mh = MethodHandles.dropArguments(mh, 0, int.class); + argsList.add(0); + argsList.add(1); + argsList.add(2); + Object result = mh.invokeWithArguments(argsList); + j += (int) result; + argsList.remove(argsList.size() - 1); + argsList.remove(argsList.size() - 1); + if (i % 5 == 0) { + Thread.sleep(1000); + } + } + + if (j == 150) { + System.out.println("passed"); + } else { + throw new Exception("TestStack2RegSlotMismatch Error"); + } + } +} \ No newline at end of file From 001aaa1e49f2692061cad44d68c9e81a27ea3b98 Mon Sep 17 00:00:00 2001 From: Boris Ulasevich Date: Wed, 13 Aug 2025 12:45:48 +0000 Subject: [PATCH 024/112] 8365166: ARM32: missing os::fetch_bcp_from_context implementation Reviewed-by: shade --- src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp index 28b47877264..f4196d89fe3 100644 --- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp @@ -209,8 +209,16 @@ frame os::fetch_compiled_frame_from_context(const void* ucVoid) { } intptr_t* os::fetch_bcp_from_context(const void* ucVoid) { - Unimplemented(); - return nullptr; + assert(ucVoid != nullptr, "invariant"); + const ucontext_t* uc = (const ucontext_t*)ucVoid; + assert(os::Posix::ucontext_is_interpreter(uc), "invariant"); +#if (FP_REG_NUM == 11) + assert(Rbcp == R7, "expected FP=R11, Rbcp=R7"); + return (intptr_t*)uc->uc_mcontext.arm_r7; +#else + assert(Rbcp == R11, "expected FP=R7, Rbcp=R11"); + return (intptr_t*)uc->uc_mcontext.arm_fp; // r11 +#endif } frame os::get_sender_for_C_frame(frame* fr) { From 899e13f40a70c98d1d393ba6c3973abcb36e1f00 Mon Sep 17 00:00:00 2001 From: Nikita Gubarkov Date: Wed, 13 Aug 2025 17:36:07 +0000 Subject: [PATCH 025/112] 8364434: Inconsistent BufferedContext state after GC Reviewed-by: jdv, azvegint, avu --- .../sun/java2d/pipe/BufferedContext.java | 60 ++++++++------ .../java/awt/ColorClass/WeakColorTest.java | 83 +++++++++++++++++++ 2 files changed, 118 insertions(+), 25 deletions(-) create mode 100644 test/jdk/java/awt/ColorClass/WeakColorTest.java diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/BufferedContext.java b/src/java.desktop/share/classes/sun/java2d/pipe/BufferedContext.java index e57b240bce5..83e63fc75d8 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/BufferedContext.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/BufferedContext.java @@ -100,11 +100,11 @@ public abstract class BufferedContext { */ protected static BufferedContext currentContext; - private Reference validSrcDataRef = new WeakReference<>(null); - private Reference validDstDataRef = new WeakReference<>(null); - private Reference validClipRef = new WeakReference<>(null); - private Reference validCompRef = new WeakReference<>(null); - private Reference validPaintRef = new WeakReference<>(null); + private Reference validSrcDataRef = null; + private Reference validDstDataRef = null; + private Reference validClipRef = null; + private Reference validCompRef = null; + private Reference validPaintRef = null; // renamed from isValidatedPaintAColor as part of a work around for 6764257 private boolean isValidatedPaintJustAColor; private int validatedRGB; @@ -213,20 +213,18 @@ public abstract class BufferedContext { updatePaint = true; isValidatedPaintJustAColor = true; } - } else if (validPaintRef.get() != paint) { + } else if (stateChanged(validPaintRef, paint)) { updatePaint = true; // this should be set when we are switching from paint to color // in which case this condition will be true isValidatedPaintJustAColor = false; } - final AccelSurface validatedSrcData = validSrcDataRef.get(); - final AccelSurface validatedDstData = validDstDataRef.get(); - if ((currentContext != this) || - (srcData != validatedSrcData) || - (dstData != validatedDstData)) + final boolean srcChanged = stateChanged(validSrcDataRef, srcData); + final boolean dstChanged = stateChanged(validDstDataRef, dstData); + if ((currentContext != this) || srcChanged || dstChanged) { - if (dstData != validatedDstData) { + if (dstChanged) { // the clip is dependent on the destination surface, so we // need to update it if we have a new destination surface updateClip = true; @@ -243,13 +241,13 @@ public abstract class BufferedContext { setSurfaces(srcData, dstData); currentContext = this; - validSrcDataRef = new WeakReference<>(srcData); - validDstDataRef = new WeakReference<>(dstData); + validSrcDataRef = wrapState(srcData); + validDstDataRef = wrapState(dstData); } // validate clip - final Region validatedClip = validClipRef.get(); - if ((clip != validatedClip) || updateClip) { + final Region validatedClip = validClipRef == null ? null : validClipRef.get(); + if (stateChanged(validClipRef, clip) || updateClip) { if (clip != null) { if (updateClip || validatedClip == null || @@ -264,13 +262,13 @@ public abstract class BufferedContext { } else { resetClip(); } - validClipRef = new WeakReference<>(clip); + validClipRef = wrapState(clip); } // validate composite (note that a change in the context flags // may require us to update the composite state, even if the // composite has not changed) - if ((comp != validCompRef.get()) || (flags != validatedFlags)) { + if (stateChanged(validCompRef, comp) || (flags != validatedFlags)) { if (comp != null) { setComposite(comp, flags); } else { @@ -279,7 +277,7 @@ public abstract class BufferedContext { // the paint state is dependent on the composite state, so make // sure we update the color below updatePaint = true; - validCompRef = new WeakReference<>(comp); + validCompRef = wrapState(comp); validatedFlags = flags; } @@ -313,7 +311,7 @@ public abstract class BufferedContext { } else { BufferedPaints.resetPaint(rq); } - validPaintRef = new WeakReference<>(paint); + validPaintRef = wrapState(paint); } // mark dstData dirty @@ -321,6 +319,18 @@ public abstract class BufferedContext { dstData.markDirty(); } + private static boolean stateChanged(Reference ref, T obj) { + // null ref means "true" null object + if (ref == null) return obj != null; + T old = ref.get(); + // null ref value means the object was GC'ed, return true in that case + return old == null || old != obj; + } + + private static Reference wrapState(T obj) { + return obj == null ? null : new WeakReference<>(obj); + } + private void setSurfaces(AccelSurface srcData, AccelSurface dstData) { @@ -434,11 +444,11 @@ public abstract class BufferedContext { resetComposite(); resetClip(); BufferedPaints.resetPaint(rq); - validSrcDataRef.clear(); - validDstDataRef.clear(); - validCompRef.clear(); - validClipRef.clear(); - validPaintRef.clear(); + validSrcDataRef = null; + validDstDataRef = null; + validCompRef = null; + validClipRef = null; + validPaintRef = null; isValidatedPaintJustAColor = false; xformInUse = false; } diff --git a/test/jdk/java/awt/ColorClass/WeakColorTest.java b/test/jdk/java/awt/ColorClass/WeakColorTest.java new file mode 100644 index 00000000000..d4adbc3f111 --- /dev/null +++ b/test/jdk/java/awt/ColorClass/WeakColorTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.awt.image.VolatileImage; +import java.lang.ref.WeakReference; + +import jtreg.SkippedException; + +/* + * @test + * @key headful + * @bug 8364434 + * @summary Check that garbage-collecting Color before accelerated painting is complete does not cause artifacts. + * @requires (os.family != "linux") + * @library /test/lib + * @run main/othervm -Xms16m -Xmx16m WeakColorTest + */ + +public class WeakColorTest { + public static void main(String[] args) throws Exception { + BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); // This image is full-black. + VolatileImage image = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration().createCompatibleVolatileImage(100, 100); + Graphics2D g = image.createGraphics(); + + // Create a new Color - we want it to be collected later. + g.setColor(new Color(255, 0, 0)); + WeakReference color = new WeakReference<>(g.getColor()); + + g.fillRect(0, 0, 100, 100); + + // Change color to prevent Graphics from keeping our Color alive. + g.setColor(Color.BLACK); + + // Force Color to be GC'ed. + final int MAX_ITERATIONS = 1000, ARRAY_SIZE = 1000000; + WeakReference array = null; + for (int i = 0;; i++) { + System.gc(); + if (color.get() == null) { + System.out.println("Color collected at: " + i); + break; + } else if (i >= MAX_ITERATIONS) { + throw new SkippedException("Color was not collected after " + MAX_ITERATIONS + " iterations"); + } + Object[] a = new Object[ARRAY_SIZE]; + a[0] = array; + array = new WeakReference<>(a); + } + + // Do a blit. If it succeeds, the resulting image will be full-black. + g.drawImage(bi, 0, 0, null); + g.dispose(); + + // We expect black. If it's red, then the blit must have failed. + int actualColor = image.getSnapshot().getRGB(50, 50); + if ((actualColor & 0xFFFFFF) != 0) throw new Error("Wrong color: 0x" + Integer.toHexString(actualColor)); + } +} From 38a261415dc29aae01c9b878d94cb302c60a3983 Mon Sep 17 00:00:00 2001 From: Srinivas Vamsi Parasa Date: Wed, 13 Aug 2025 17:53:05 +0000 Subject: [PATCH 026/112] 8365265: x86 short forward jump exceeds 8-bit offset in methodHandles_x86.cpp when using Intel APX Reviewed-by: shade, jbhateja, aph --- src/hotspot/cpu/x86/methodHandles_x86.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp index 0363cb04341..b921a157a52 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.cpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp @@ -137,7 +137,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist case vmIntrinsicID::_invokeBasic: // Require compiled LambdaForm class to be fully initialized. __ cmpb(Address(method_holder, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); - __ jccb(Assembler::equal, L_ok); + __ jcc(Assembler::equal, L_ok); break; case vmIntrinsicID::_linkToStatic: @@ -154,7 +154,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist // init_state check failed, but it may be an abstract interface method __ load_unsigned_short(temp, Address(method, Method::access_flags_offset())); __ testl(temp, JVM_ACC_ABSTRACT); - __ jccb(Assembler::notZero, L_ok); + __ jcc(Assembler::notZero, L_ok); break; default: From ecbdd3405a1d46f555deb82098e1865b44601a1f Mon Sep 17 00:00:00 2001 From: Alex Menkov Date: Wed, 13 Aug 2025 18:24:56 +0000 Subject: [PATCH 027/112] 8361103: java_lang_Thread::async_get_stack_trace does not properly protect JavaThread Reviewed-by: sspitsyn, dholmes --- src/hotspot/share/classfile/javaClasses.cpp | 53 ++++++++++----------- src/hotspot/share/classfile/javaClasses.hpp | 2 +- src/hotspot/share/prims/jvm.cpp | 2 +- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 28bebee7d9a..5c41ea789b5 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1899,33 +1899,29 @@ oop java_lang_Thread::park_blocker(oop java_thread) { return java_thread->obj_field_access(_park_blocker_offset); } -oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { - ThreadsListHandle tlh(JavaThread::current()); - JavaThread* thread; - bool is_virtual = java_lang_VirtualThread::is_instance(java_thread); - if (is_virtual) { - oop carrier_thread = java_lang_VirtualThread::carrier_thread(java_thread); - if (carrier_thread == nullptr) { - return nullptr; - } - thread = java_lang_Thread::thread(carrier_thread); - } else { - thread = java_lang_Thread::thread(java_thread); - } - if (thread == nullptr) { +// Obtain stack trace for platform or mounted virtual thread. +// If jthread is a virtual thread and it has been unmounted (or remounted to different carrier) the method returns null. +// The caller (java.lang.VirtualThread) handles returned nulls via retry. +oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { + ThreadsListHandle tlh(THREAD); + JavaThread* java_thread = nullptr; + oop thread_oop; + + bool has_java_thread = tlh.cv_internal_thread_to_JavaThread(jthread, &java_thread, &thread_oop); + if (!has_java_thread) { return nullptr; } class GetStackTraceHandshakeClosure : public HandshakeClosure { public: - const Handle _java_thread; + const Handle _thread_h; int _depth; bool _retry_handshake; GrowableArray* _methods; GrowableArray* _bcis; - GetStackTraceHandshakeClosure(Handle java_thread) : - HandshakeClosure("GetStackTraceHandshakeClosure"), _java_thread(java_thread), _depth(0), _retry_handshake(false), + GetStackTraceHandshakeClosure(Handle thread_h) : + HandshakeClosure("GetStackTraceHandshakeClosure"), _thread_h(thread_h), _depth(0), _retry_handshake(false), _methods(nullptr), _bcis(nullptr) { } ~GetStackTraceHandshakeClosure() { @@ -1947,21 +1943,22 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { return; } - JavaThread* thread = JavaThread::cast(th); + JavaThread* java_thread = JavaThread::cast(th); - if (!thread->has_last_Java_frame()) { + if (!java_thread->has_last_Java_frame()) { return; } bool carrier = false; - if (java_lang_VirtualThread::is_instance(_java_thread())) { - // if (thread->vthread() != _java_thread()) // We might be inside a System.executeOnCarrierThread - const ContinuationEntry* ce = thread->vthread_continuation(); - if (ce == nullptr || ce->cont_oop(thread) != java_lang_VirtualThread::continuation(_java_thread())) { - return; // not mounted + if (java_lang_VirtualThread::is_instance(_thread_h())) { + // Ensure _thread_h is still mounted to java_thread. + const ContinuationEntry* ce = java_thread->vthread_continuation(); + if (ce == nullptr || ce->cont_oop(java_thread) != java_lang_VirtualThread::continuation(_thread_h())) { + // Target thread has been unmounted. + return; } } else { - carrier = (thread->vthread_continuation() != nullptr); + carrier = (java_thread->vthread_continuation() != nullptr); } const int max_depth = MaxJavaStackTraceDepth; @@ -1973,7 +1970,7 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { _bcis = new (mtInternal) GrowableArray(init_length, mtInternal); int total_count = 0; - for (vframeStream vfst(thread, false, false, carrier); // we don't process frames as we don't care about oops + for (vframeStream vfst(java_thread, false, false, carrier); // we don't process frames as we don't care about oops !vfst.at_end() && (max_depth == 0 || max_depth != total_count); vfst.next()) { @@ -1994,9 +1991,9 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { // Handshake with target ResourceMark rm(THREAD); HandleMark hm(THREAD); - GetStackTraceHandshakeClosure gsthc(Handle(THREAD, java_thread)); + GetStackTraceHandshakeClosure gsthc(Handle(THREAD, thread_oop)); do { - Handshake::execute(&gsthc, &tlh, thread); + Handshake::execute(&gsthc, &tlh, java_thread); } while (gsthc.read_reset_retry()); // Stop if no stack trace is found. diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index faa325d55dd..70fa519f0f0 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -463,7 +463,7 @@ class java_lang_Thread : AllStatic { static const char* thread_status_name(oop java_thread_oop); // Fill in current stack trace, can cause GC - static oop async_get_stack_trace(oop java_thread, TRAPS); + static oop async_get_stack_trace(jobject jthread, TRAPS); JFR_ONLY(static u2 jfr_epoch(oop java_thread);) JFR_ONLY(static void set_jfr_epoch(oop java_thread, u2 epoch);) diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 005776b00c2..098bd729c0b 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2945,7 +2945,7 @@ JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj)) JVM_END JVM_ENTRY(jobject, JVM_GetStackTrace(JNIEnv *env, jobject jthread)) - oop trace = java_lang_Thread::async_get_stack_trace(JNIHandles::resolve(jthread), THREAD); + oop trace = java_lang_Thread::async_get_stack_trace(jthread, THREAD); return JNIHandles::make_local(THREAD, trace); JVM_END From 4680dc983169d48fcf83eb50dc60e32e79d5d976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Wed, 13 Aug 2025 18:41:57 +0000 Subject: [PATCH 028/112] 8365264: Rename ResourceHashtable to HashTable Reviewed-by: iklam, ayang --- src/hotspot/share/asm/codeBuffer.hpp | 4 +- src/hotspot/share/cds/aotArtifactFinder.cpp | 4 +- src/hotspot/share/cds/aotClassLinker.hpp | 4 +- .../share/cds/aotConstantPoolResolver.hpp | 4 +- .../share/cds/aotReferenceObjSupport.cpp | 4 +- src/hotspot/share/cds/archiveBuilder.hpp | 8 +- src/hotspot/share/cds/archiveHeapLoader.cpp | 6 +- src/hotspot/share/cds/archiveHeapWriter.cpp | 2 +- src/hotspot/share/cds/archiveHeapWriter.hpp | 4 +- src/hotspot/share/cds/cdsHeapVerifier.hpp | 6 +- src/hotspot/share/cds/classListParser.hpp | 4 +- src/hotspot/share/cds/classListWriter.cpp | 2 +- src/hotspot/share/cds/dumpTimeClassInfo.hpp | 2 +- src/hotspot/share/cds/heapShared.cpp | 2 +- src/hotspot/share/cds/heapShared.hpp | 12 +-- .../share/cds/lambdaProxyClassDictionary.hpp | 4 +- src/hotspot/share/cds/metaspaceShared.cpp | 4 +- src/hotspot/share/cds/regeneratedClasses.cpp | 4 +- .../share/classfile/bytecodeAssembler.hpp | 5 +- .../share/classfile/classFileParser.cpp | 8 +- .../share/classfile/classLoaderStats.hpp | 6 +- .../share/classfile/loaderConstraints.cpp | 4 +- src/hotspot/share/classfile/moduleEntry.cpp | 4 +- src/hotspot/share/classfile/moduleEntry.hpp | 4 +- src/hotspot/share/classfile/packageEntry.cpp | 4 +- src/hotspot/share/classfile/packageEntry.hpp | 4 +- src/hotspot/share/classfile/placeholders.cpp | 4 +- .../share/classfile/resolutionErrors.cpp | 4 +- src/hotspot/share/classfile/stringTable.cpp | 4 +- .../share/classfile/systemDictionary.cpp | 4 +- .../classfile/systemDictionaryShared.cpp | 5 +- src/hotspot/share/classfile/verifier.hpp | 4 +- src/hotspot/share/code/codeCache.cpp | 2 +- src/hotspot/share/code/nmethod.cpp | 2 +- src/hotspot/share/compiler/disassembler.cpp | 5 +- src/hotspot/share/gc/z/zVerify.cpp | 4 +- .../methodtracer/jfrClassFilterClosure.cpp | 2 +- .../methodtracer/jfrClassFilterClosure.hpp | 4 +- .../support/methodtracer/jfrMethodTracer.cpp | 2 +- src/hotspot/share/jfr/utilities/jfrSet.hpp | 4 +- .../share/jvmci/jvmciCompilerToVMInit.cpp | 6 +- src/hotspot/share/logging/logAsyncWriter.hpp | 6 +- src/hotspot/share/memory/metaspaceClosure.hpp | 4 +- .../share/nmt/nativeCallStackPrinter.hpp | 4 +- src/hotspot/share/oops/constantPool.hpp | 4 +- src/hotspot/share/oops/instanceKlass.cpp | 2 +- src/hotspot/share/oops/trainingData.hpp | 6 +- src/hotspot/share/opto/compile.cpp | 8 +- src/hotspot/share/opto/loopnode.hpp | 2 +- src/hotspot/share/opto/mempointer.cpp | 2 +- src/hotspot/share/opto/node.hpp | 2 +- .../share/opto/superwordVTransformBuilder.hpp | 2 +- src/hotspot/share/prims/foreignGlobals.cpp | 4 +- src/hotspot/share/prims/jvmtiTagMapTable.hpp | 8 +- src/hotspot/share/runtime/arguments.hpp | 2 +- src/hotspot/share/runtime/sharedRuntime.cpp | 4 +- src/hotspot/share/runtime/synchronizer.hpp | 2 +- src/hotspot/share/runtime/threadSMR.cpp | 8 +- src/hotspot/share/runtime/vmOperations.cpp | 6 +- src/hotspot/share/services/heapDumper.cpp | 4 +- .../share/utilities/globalDefinitions.hpp | 2 +- .../{resourceHash.hpp => hashTable.hpp} | 48 +++++------ .../share/utilities/nativeCallStack.hpp | 2 +- src/hotspot/share/utilities/objectBitSet.hpp | 4 +- .../share/utilities/objectBitSet.inline.hpp | 4 +- ...esourceHash.hpp => resizableHashTable.hpp} | 28 +++---- .../gtest/runtime/test_os_reserve_between.cpp | 4 +- ...st_resourceHash.cpp => test_hashtable.cpp} | 82 +++++++++---------- 68 files changed, 214 insertions(+), 215 deletions(-) rename src/hotspot/share/utilities/{resourceHash.hpp => hashTable.hpp} (89%) rename src/hotspot/share/utilities/{resizeableResourceHash.hpp => resizableHashTable.hpp} (86%) rename test/hotspot/gtest/utilities/{test_resourceHash.cpp => test_hashtable.cpp} (82%) diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index ec9f347e334..57b4aaacdce 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -33,7 +33,7 @@ #include "utilities/debug.hpp" #include "utilities/growableArray.hpp" #include "utilities/linkedlist.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" #include "utilities/macros.hpp" template @@ -541,7 +541,7 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { }; typedef LinkedListImpl Offsets; - typedef ResizeableResourceHashtable SharedTrampolineRequests; + typedef ResizeableHashTable SharedTrampolineRequests; private: enum { diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index adcc4bfb50a..a42d8882099 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -37,7 +37,7 @@ #include "oops/instanceKlass.hpp" #include "oops/objArrayKlass.hpp" #include "oops/trainingData.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // All the classes that should be included in the AOT cache (in at least the "allocated" state) static GrowableArrayCHeap* _all_cached_classes = nullptr; @@ -47,7 +47,7 @@ static GrowableArrayCHeap* _all_cached_classes = nullptr; static GrowableArrayCHeap* _pending_aot_inited_classes = nullptr; static const int TABLE_SIZE = 15889; // prime number -using ClassesTable = ResourceHashtable; +using ClassesTable = HashTable; static ClassesTable* _seen_classes; // all classes that have been seen by AOTArtifactFinder static ClassesTable* _aot_inited_classes; // all classes that need to be AOT-initialized. diff --git a/src/hotspot/share/cds/aotClassLinker.hpp b/src/hotspot/share/cds/aotClassLinker.hpp index f15684a1ad2..7bbc9ce8138 100644 --- a/src/hotspot/share/cds/aotClassLinker.hpp +++ b/src/hotspot/share/cds/aotClassLinker.hpp @@ -31,8 +31,8 @@ #include "oops/oopsHierarchy.hpp" #include "utilities/exceptions.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" class AOTLinkedClassTable; class InstanceKlass; @@ -69,7 +69,7 @@ enum class AOTLinkedClassCategory : int; // class AOTClassLinker : AllStatic { static const int TABLE_SIZE = 15889; // prime number - using ClassesTable = ResourceHashtable; + using ClassesTable = HashTable; // Classes loaded inside vmClasses::resolve_all() static ClassesTable* _vm_classes; diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp index bab9e263a22..e49d9d1ad0b 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp @@ -31,8 +31,8 @@ #include "oops/oopsHierarchy.hpp" #include "runtime/handles.hpp" #include "utilities/exceptions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" class ConstantPool; class constantPoolHandle; @@ -53,7 +53,7 @@ template class GrowableArray; // if all of its supertypes are loaded from the CDS archive. class AOTConstantPoolResolver : AllStatic { static const int TABLE_SIZE = 15889; // prime number - using ClassesTable = ResourceHashtable ; + using ClassesTable = HashTable ; static ClassesTable* _processed_classes; #ifdef ASSERT diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index a62e9c7735a..b43c766b8db 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -35,7 +35,7 @@ #include "oops/oopHandle.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/javaCalls.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // Handling of java.lang.ref.Reference objects in the AOT cache // ============================================================ @@ -92,7 +92,7 @@ #if INCLUDE_CDS_JAVA_HEAP -class KeepAliveObjectsTable : public ResourceHashtable _src_obj_table; - ResizeableResourceHashtable _buffered_to_src_table; + ResizeableHashTable _src_obj_table; + ResizeableHashTable _buffered_to_src_table; GrowableArray* _klasses; GrowableArray* _symbols; unsigned int _entropy_seed; diff --git a/src/hotspot/share/cds/archiveHeapLoader.cpp b/src/hotspot/share/cds/archiveHeapLoader.cpp index 181fa6b2bb4..673c4baa6f6 100644 --- a/src/hotspot/share/cds/archiveHeapLoader.cpp +++ b/src/hotspot/share/cds/archiveHeapLoader.cpp @@ -348,10 +348,10 @@ bool ArchiveHeapLoader::load_heap_region(FileMapInfo* mapinfo) { } class VerifyLoadedHeapEmbeddedPointers: public BasicOopIterateClosure { - ResourceHashtable* _table; + HashTable* _table; public: - VerifyLoadedHeapEmbeddedPointers(ResourceHashtable* table) : _table(table) {} + VerifyLoadedHeapEmbeddedPointers(HashTable* table) : _table(table) {} virtual void do_oop(narrowOop* p) { // This should be called before the loaded region is modified, so all the embedded pointers @@ -411,7 +411,7 @@ void ArchiveHeapLoader::verify_loaded_heap() { log_info(aot, heap)("Verify all oops and pointers in loaded heap"); ResourceMark rm; - ResourceHashtable table; + HashTable table; VerifyLoadedHeapEmbeddedPointers verifier(&table); HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; HeapWord* top = (HeapWord*)_loaded_heap_top; diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index db93027e348..b651da8418b 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -70,7 +70,7 @@ ArchiveHeapWriter::BufferOffsetToSourceObjectTable* ArchiveHeapWriter::_buffer_offset_to_source_obj_table = nullptr; -typedef ResourceHashtable< +typedef HashTable< size_t, // offset of a filler from ArchiveHeapWriter::buffer_bottom() size_t, // size of this filler (in bytes) 127, // prime number diff --git a/src/hotspot/share/cds/archiveHeapWriter.hpp b/src/hotspot/share/cds/archiveHeapWriter.hpp index 1e1319cccc3..f8f55a745ee 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.hpp +++ b/src/hotspot/share/cds/archiveHeapWriter.hpp @@ -32,8 +32,8 @@ #include "utilities/bitMap.hpp" #include "utilities/exceptions.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" class MemRegion; @@ -152,7 +152,7 @@ private: }; static GrowableArrayCHeap* _source_objs_order; - typedef ResizeableResourceHashtable BufferOffsetToSourceObjectTable; static BufferOffsetToSourceObjectTable* _buffer_offset_to_source_obj_table; diff --git a/src/hotspot/share/cds/cdsHeapVerifier.hpp b/src/hotspot/share/cds/cdsHeapVerifier.hpp index c53f913e42b..811751e8ca2 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.hpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.hpp @@ -28,7 +28,7 @@ #include "cds/heapShared.hpp" #include "memory/iterator.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class InstanceKlass; class Symbol; @@ -47,7 +47,7 @@ class CDSHeapVerifier : public KlassClosure { Symbol* _name; }; - ResourceHashtable ID2KlassTable; enum { diff --git a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp index c1ac1b47b11..727cc03c216 100644 --- a/src/hotspot/share/cds/classListWriter.cpp +++ b/src/hotspot/share/cds/classListWriter.cpp @@ -66,7 +66,7 @@ void ClassListWriter::write(const InstanceKlass* k, const ClassFileStream* cfs) write_to_stream(k, w.stream(), cfs); } -class ClassListWriter::IDTable : public ResourceHashtable< +class ClassListWriter::IDTable : public HashTable< const InstanceKlass*, int, 15889, // prime number AnyObj::C_HEAP> {}; diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp index 0bb60dfbb29..0feed8682da 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp @@ -240,7 +240,7 @@ inline unsigned DumpTimeSharedClassTable_hash(T* const& k) { } } -using DumpTimeSharedClassTableBaseType = ResourceHashtable< +using DumpTimeSharedClassTableBaseType = HashTable< InstanceKlass*, DumpTimeClassInfo, 15889, // prime number diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 51572bcace7..ea9368c9277 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -367,7 +367,7 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra } } -class MetaspaceObjToOopHandleTable: public ResourceHashtable { diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index f4e86aa5895..c0e89809274 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -37,7 +37,7 @@ #include "oops/oopHandle.hpp" #include "oops/oopsHierarchy.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #if INCLUDE_CDS_JAVA_HEAP class DumpedInternedStrings; @@ -202,14 +202,14 @@ public: private: static const int INITIAL_TABLE_SIZE = 15889; // prime number static const int MAX_TABLE_SIZE = 1000000; - typedef ResizeableResourceHashtable ArchivedObjectCache; static ArchivedObjectCache* _archived_object_cache; class DumpTimeKlassSubGraphInfoTable - : public ResourceHashtable SeenObjectsTable; @@ -468,14 +468,14 @@ private: #if INCLUDE_CDS_JAVA_HEAP class DumpedInternedStrings : - public ResizeableResourceHashtable { public: DumpedInternedStrings(unsigned size, unsigned max_size) : - ResizeableResourceHashtable(size, max_size) {} diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp index 814dda80827..ff7acb15292 100644 --- a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp +++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp @@ -30,7 +30,7 @@ #include "classfile/javaClasses.hpp" #include "memory/metaspaceClosure.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // This file contains *legacy* optimization for lambdas before JEP 483. May be removed in the future. // @@ -249,7 +249,7 @@ public: }; class DumpTimeLambdaProxyClassDictionary - : public ResourceHashtable @@ -178,7 +178,7 @@ class DumpClassListCLDClosure : public CLDClosure { static const int MAX_TABLE_SIZE = 61333; fileStream *_stream; - ResizeableResourceHashtable _dumped_classes; void dump(InstanceKlass* ik) { diff --git a/src/hotspot/share/cds/regeneratedClasses.cpp b/src/hotspot/share/cds/regeneratedClasses.cpp index b36f360b82a..ae14866cea5 100644 --- a/src/hotspot/share/cds/regeneratedClasses.cpp +++ b/src/hotspot/share/cds/regeneratedClasses.cpp @@ -32,9 +32,9 @@ #include "runtime/mutexLocker.hpp" #include "runtime/thread.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" -using RegeneratedObjTable = ResourceHashtable; +using RegeneratedObjTable = HashTable; static RegeneratedObjTable* _regenerated_objs = nullptr; // InstanceKlass* and Method* orig_obj -> regen_obj static RegeneratedObjTable* _original_objs = nullptr; // InstanceKlass* and Method* regen_obj -> orig_obj static GrowableArrayCHeap* _regenerated_mirrors = nullptr; diff --git a/src/hotspot/share/classfile/bytecodeAssembler.hpp b/src/hotspot/share/classfile/bytecodeAssembler.hpp index fb51924a9c6..c0383a9678c 100644 --- a/src/hotspot/share/classfile/bytecodeAssembler.hpp +++ b/src/hotspot/share/classfile/bytecodeAssembler.hpp @@ -30,8 +30,7 @@ #include "oops/symbol.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" - +#include "utilities/hashTable.hpp" /** * Bytecode Assembler @@ -125,7 +124,7 @@ class BytecodeCPEntry { class BytecodeConstantPool : public ResourceObj { private: - typedef ResourceHashtable IndexHash; diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 9aca20ee29f..c678fa5e058 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -81,9 +81,9 @@ #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" #include "utilities/utf8.hpp" #if INCLUDE_CDS #include "classfile/systemDictionaryShared.hpp" @@ -782,7 +782,7 @@ class NameSigHash: public ResourceObj { } }; -using NameSigHashtable = ResourceHashtable; @@ -849,7 +849,7 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, // Check if there's any duplicates in interfaces ResourceMark rm(THREAD); // Set containing interface names - ResourceHashtable* interface_names = new ResourceHashtable(); + HashTable* interface_names = new HashTable(); for (index = 0; index < itfs_len; index++) { const InstanceKlass* const k = _local_interfaces->at(index); Symbol* interface_name = k->name(); @@ -2022,7 +2022,7 @@ void ClassFileParser::copy_localvariable_table(const ConstMethod* cm, ResourceMark rm(THREAD); - typedef ResourceHashtable LVT_HashTable; diff --git a/src/hotspot/share/classfile/classLoaderStats.hpp b/src/hotspot/share/classfile/classLoaderStats.hpp index 06d375b3e9b..23da6bff63c 100644 --- a/src/hotspot/share/classfile/classLoaderStats.hpp +++ b/src/hotspot/share/classfile/classLoaderStats.hpp @@ -25,15 +25,13 @@ #ifndef SHARE_CLASSFILE_CLASSLOADERSTATS_HPP #define SHARE_CLASSFILE_CLASSLOADERSTATS_HPP - #include "classfile/classLoaderData.hpp" #include "oops/klass.hpp" #include "oops/oop.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/vmOperation.hpp" #include "services/diagnosticCommand.hpp" -#include "utilities/resourceHash.hpp" - +#include "utilities/hashTable.hpp" class ClassLoaderStatsDCmd : public DCmd { public: @@ -105,7 +103,7 @@ protected: return hash; } - typedef ResourceHashtable StatsTable; diff --git a/src/hotspot/share/classfile/loaderConstraints.cpp b/src/hotspot/share/classfile/loaderConstraints.cpp index 21161f44326..13c1ff7319d 100644 --- a/src/hotspot/share/classfile/loaderConstraints.cpp +++ b/src/hotspot/share/classfile/loaderConstraints.cpp @@ -31,7 +31,7 @@ #include "oops/klass.inline.hpp" #include "oops/symbolHandle.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // Overview // @@ -147,7 +147,7 @@ class ConstraintSet { // copied into hashtable as }; -using InternalLoaderConstraintTable = ResourceHashtable; +using InternalLoaderConstraintTable = HashTable; static InternalLoaderConstraintTable* _loader_constraint_table; void LoaderConstraint::extend_loader_constraint(Symbol* class_name, diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 7f9bf09aa51..ce887081cdb 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -45,9 +45,9 @@ #include "runtime/safepoint.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/quickSort.hpp" -#include "utilities/resourceHash.hpp" ModuleEntry* ModuleEntryTable::_javabase_module = nullptr; @@ -403,7 +403,7 @@ void ModuleEntry::set_loader_data(ClassLoaderData* cld) { } #if INCLUDE_CDS_JAVA_HEAP -typedef ResourceHashtable< +typedef HashTable< const ModuleEntry*, ModuleEntry*, 557, // prime number diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index 1ae504577e3..1074c88f010 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -31,9 +31,9 @@ #include "oops/symbolHandle.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" #if INCLUDE_JFR #include "jfr/support/jfrTraceIdExtension.hpp" #endif @@ -233,7 +233,7 @@ class ModuleClosure: public StackObj { class ModuleEntryTable : public CHeapObj { private: static ModuleEntry* _javabase_module; - ResourceHashtable _table; public: diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp index 9a21578630f..ea2e6cd1def 100644 --- a/src/hotspot/share/classfile/packageEntry.cpp +++ b/src/hotspot/share/classfile/packageEntry.cpp @@ -38,9 +38,9 @@ #include "runtime/java.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/quickSort.hpp" -#include "utilities/resourceHash.hpp" PackageEntry::PackageEntry(Symbol* name, ModuleEntry* module) : _name(name), @@ -212,7 +212,7 @@ PackageEntryTable::~PackageEntryTable() { } #if INCLUDE_CDS_JAVA_HEAP -typedef ResourceHashtable< +typedef HashTable< const PackageEntry*, PackageEntry*, 557, // prime number diff --git a/src/hotspot/share/classfile/packageEntry.hpp b/src/hotspot/share/classfile/packageEntry.hpp index aa572dc15b2..84620785ebb 100644 --- a/src/hotspot/share/classfile/packageEntry.hpp +++ b/src/hotspot/share/classfile/packageEntry.hpp @@ -30,9 +30,9 @@ #include "oops/symbolHandle.hpp" #include "runtime/atomic.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" #if INCLUDE_JFR #include "jfr/support/jfrTraceIdExtension.hpp" #endif @@ -233,7 +233,7 @@ public: // The PackageEntryTable is a Hashtable containing a list of all packages defined // by a particular class loader. Each package is represented as a PackageEntry node. class PackageEntryTable : public CHeapObj { - ResourceHashtable _table; public: PackageEntryTable(); diff --git a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp index b2f1ed40106..f4c381eafdf 100644 --- a/src/hotspot/share/classfile/placeholders.cpp +++ b/src/hotspot/share/classfile/placeholders.cpp @@ -31,7 +31,7 @@ #include "oops/symbolHandle.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class PlaceholderKey { SymbolHandle _name; @@ -49,7 +49,7 @@ class PlaceholderKey { }; const int _placeholder_table_size = 503; // Does this really have to be prime? -using InternalPlaceholderTable = ResourceHashtable; static InternalPlaceholderTable* _placeholders; diff --git a/src/hotspot/share/classfile/resolutionErrors.cpp b/src/hotspot/share/classfile/resolutionErrors.cpp index 03af71bc26f..25aa370d257 100644 --- a/src/hotspot/share/classfile/resolutionErrors.cpp +++ b/src/hotspot/share/classfile/resolutionErrors.cpp @@ -30,7 +30,7 @@ #include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class ResolutionErrorKey { ConstantPool* _cpool; @@ -53,7 +53,7 @@ class ResolutionErrorKey { } }; -using InternalResolutionErrorTable = ResourceHashtable; diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 957ecd8ebe8..cf5d98650ce 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -59,7 +59,7 @@ #include "utilities/concurrentHashTable.inline.hpp" #include "utilities/concurrentHashTableTasks.inline.hpp" #include "utilities/macros.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" #include "utilities/utf8.hpp" #if INCLUDE_G1GC #include "gc/g1/g1CollectedHeap.hpp" @@ -810,7 +810,7 @@ class StringTable::VerifyCompStrings : StackObj { return java_lang_String::equals(a, b); } - ResizeableResourceHashtable _table; public: size_t _errors; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index f482a5bb303..783ae9d35ba 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -111,10 +111,10 @@ class InvokeMethodKey : public StackObj { }; -using InvokeMethodIntrinsicTable = ResourceHashtable; static InvokeMethodIntrinsicTable* _invoke_method_intrinsic_table; -using InvokeMethodTypeTable = ResourceHashtable; +using InvokeMethodTypeTable = HashTable; static InvokeMethodTypeTable* _invoke_method_type_table; OopHandle SystemDictionary::_java_system_loader; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 7dd3a7d1bb2..1845c28d819 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -22,6 +22,7 @@ * */ + #include "cds/aotClassFilter.hpp" #include "cds/aotClassLocation.hpp" #include "cds/aotLogging.hpp" @@ -74,7 +75,7 @@ #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #include "utilities/stringUtils.hpp" SystemDictionaryShared::ArchiveInfo SystemDictionaryShared::_static_archive; @@ -445,7 +446,7 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( return k; } -class UnregisteredClassesTable : public ResourceHashtable< +class UnregisteredClassesTable : public HashTable< Symbol*, InstanceKlass*, 15889, // prime number AnyObj::C_HEAP> {}; diff --git a/src/hotspot/share/classfile/verifier.hpp b/src/hotspot/share/classfile/verifier.hpp index 7857d472705..87ea8bd2970 100644 --- a/src/hotspot/share/classfile/verifier.hpp +++ b/src/hotspot/share/classfile/verifier.hpp @@ -31,7 +31,7 @@ #include "runtime/handles.hpp" #include "utilities/exceptions.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // The verifier class class Verifier : AllStatic { @@ -270,7 +270,7 @@ class sig_as_verification_types : public ResourceObj { // This hashtable is indexed by the Utf8 constant pool indexes pointed to // by constant pool (Interface)Method_refs' NameAndType signature entries. -typedef ResourceHashtable +typedef HashTable method_signatures_table_type; // A new instance of this class is created for each class being verified diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index f7c86ce58fa..8f2932f6abc 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -1185,7 +1185,7 @@ static void check_live_nmethods_dependencies(DepChange& changes) { // Turn off dependency tracing while actually testing dependencies. FlagSetting fs(Dependencies::_verify_in_progress, true); - typedef ResourceHashtable DepTable; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index d8cbf6b9e66..b8b3a70bf58 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -79,7 +79,7 @@ #include "utilities/dtrace.hpp" #include "utilities/events.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #include "utilities/xmlstream.hpp" #if INCLUDE_JVMCI #include "jvmci/jvmciRuntime.hpp" diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index f8313db66aa..c79c15e0f32 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -22,6 +22,7 @@ * */ + #include "asm/assembler.inline.hpp" #include "asm/macroAssembler.hpp" #include "ci/ciUtilities.hpp" @@ -40,7 +41,7 @@ #include "runtime/os.hpp" #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubRoutines.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" void* Disassembler::_library = nullptr; bool Disassembler::_tried_to_load_library = false; @@ -189,7 +190,7 @@ class decode_env { } }; - typedef ResourceHashtable< + typedef HashTable< address, SourceFileInfo, 15889, // prime number AnyObj::C_HEAP> SourceFileInfoTable; diff --git a/src/hotspot/share/gc/z/zVerify.cpp b/src/hotspot/share/gc/z/zVerify.cpp index 68290c2c009..53cbcd0a421 100644 --- a/src/hotspot/share/gc/z/zVerify.cpp +++ b/src/hotspot/share/gc/z/zVerify.cpp @@ -51,8 +51,8 @@ #include "runtime/thread.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/preserveException.hpp" -#include "utilities/resourceHash.hpp" #include "utilities/vmError.hpp" #ifdef ASSERT @@ -516,7 +516,7 @@ void ZVerify::after_weak_processing() { // Remembered set verification // -typedef ResourceHashtable ZStoreBarrierBufferTable; +typedef HashTable ZStoreBarrierBufferTable; static ZStoreBarrierBufferTable* z_verify_store_barrier_buffer_table = nullptr; diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp index 37f2d438bc6..dc2908e8a81 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp @@ -33,7 +33,7 @@ #include "oops/instanceKlass.inline.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" constexpr static unsigned int TABLE_SIZE = 1009; constexpr static unsigned int MAX_TABLE_SIZE = 0x3fffffff; diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp index a23b27eb12a..6cf1b321f1e 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp @@ -40,7 +40,7 @@ template class GrowableArray; template class ResizeableResourceHashtable; + bool (*EQUALS)(K const&, K const&)> class ResizeableHashTable; // Knuth multiplicative hashing. inline uint32_t knuth_hash(const traceid& id) { @@ -48,7 +48,7 @@ inline uint32_t knuth_hash(const traceid& id) { return v * UINT32_C(2654435761); } -typedef ResizeableResourceHashtable* JfrMethodTracer::_instrumented_classes = nullptr; diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index b4dfc4f6240..40a5d73f370 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp @@ -27,7 +27,7 @@ #include "jfr/utilities/jfrAllocation.hpp" #include "jfr/utilities/jfrTypes.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" template class ConfigTraceID : public AllStatic { @@ -60,7 +60,7 @@ template class JfrSet : public CONFIG::STORAGE { public: typedef typename CONFIG::TYPE TYPE; - typedef ResizeableResourceHashtable HashMap; + typedef ResizeableHashTable HashMap; constexpr static bool is_cheap() { return CONFIG::alloc_type() == AnyObj::C_HEAP; diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 753d673f54c..801a9bf8eb4 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -51,7 +51,7 @@ #include "runtime/flags/jvmFlag.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #if INCLUDE_SHENANDOAHGC #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" @@ -437,8 +437,8 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) { jobjectArray readConfiguration0(JNIEnv *env, JVMCI_TRAPS) { JavaThread* THREAD = JavaThread::current(); // For exception macros. - ResourceHashtable longs; - ResourceHashtable longs; + HashTable strings; diff --git a/src/hotspot/share/logging/logAsyncWriter.hpp b/src/hotspot/share/logging/logAsyncWriter.hpp index 5ffd9dc7b33..a93b1ca58f6 100644 --- a/src/hotspot/share/logging/logAsyncWriter.hpp +++ b/src/hotspot/share/logging/logAsyncWriter.hpp @@ -29,10 +29,10 @@ #include "logging/logMessageBuffer.hpp" #include "memory/allocation.hpp" #include "runtime/mutex.hpp" -#include "runtime/os.inline.hpp" #include "runtime/nonJavaThread.hpp" +#include "runtime/os.inline.hpp" #include "runtime/semaphore.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class LogFileStreamOutput; @@ -66,7 +66,7 @@ class AsyncLogWriter : public NonJavaThread { // account for dropped messages template - using AsyncLogMap = ResourceHashtable; diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index 8b419034093..c35363eb71f 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -33,7 +33,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" #include // The metadata hierarchy is separate from the oop hierarchy @@ -374,7 +374,7 @@ public: UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {} private: - ResizeableResourceHashtable _has_been_visited; }; diff --git a/src/hotspot/share/nmt/nativeCallStackPrinter.hpp b/src/hotspot/share/nmt/nativeCallStackPrinter.hpp index 78fd541fc98..12bd1176485 100644 --- a/src/hotspot/share/nmt/nativeCallStackPrinter.hpp +++ b/src/hotspot/share/nmt/nativeCallStackPrinter.hpp @@ -29,7 +29,7 @@ #include "memory/arena.hpp" #include "nmt/memTag.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class outputStream; class NativeCallStack; @@ -41,7 +41,7 @@ class NativeCallStackPrinter { // Cache-related data are mutable to be able to use NativeCallStackPrinter as // inline member in classes with const printing methods. mutable Arena _text_storage; - mutable ResourceHashtable _cache; + mutable HashTable _cache; outputStream* const _out; public: NativeCallStackPrinter(outputStream* out); diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index be4a7a474d4..8999acf4d3a 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -37,8 +37,8 @@ #include "utilities/align.hpp" #include "utilities/bytes.hpp" #include "utilities/constantTag.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" // A ConstantPool is an array containing class constants as described in the // class file. @@ -872,7 +872,7 @@ private: private: class SymbolHash: public CHeapObj { - ResourceHashtable _table; + HashTable _table; public: void add_if_absent(const Symbol* sym, u2 value) { diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 9266e8ca091..dcec45ff4e2 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1116,7 +1116,7 @@ void InstanceKlass::initialize_super_interfaces(TRAPS) { } } -using InitializationErrorTable = ResourceHashtable; +using InitializationErrorTable = HashTable; static InitializationErrorTable* _initialization_error_table; void InstanceKlass::add_initialization_error(JavaThread* current, Handle exception) { diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp index b4fd5fd61ab..baf19773a7b 100644 --- a/src/hotspot/share/oops/trainingData.hpp +++ b/src/hotspot/share/oops/trainingData.hpp @@ -37,7 +37,7 @@ #include "oops/objArrayKlass.hpp" #include "runtime/handles.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" class ciEnv; class ciBaseObject; @@ -173,7 +173,7 @@ public: // A set of TD objects that we collect during the training run. class TrainingDataSet { friend TrainingData; - ResizeableResourceHashtable @@ -229,7 +229,7 @@ public: // A widget to ensure that we visit TD object only once (TD objects can have pointer to // other TD object that are sometimes circular). class Visitor { - ResizeableResourceHashtable _visited; + ResizeableHashTable _visited; public: Visitor(unsigned size) : _visited(size, 0x3fffffff) { } bool is_visited(TrainingData* td) { diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 5a4a6c2667d..cef7aa61219 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -87,8 +87,8 @@ #include "runtime/timer.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" // -------------------- Compile::mach_constant_base_node ----------------------- // Constant table base node singleton. @@ -2786,7 +2786,7 @@ uint Compile::eval_macro_logic_op(uint func, uint in1 , uint in2, uint in3) { return res; } -static uint eval_operand(Node* n, ResourceHashtable& eval_map) { +static uint eval_operand(Node* n, HashTable& eval_map) { assert(n != nullptr, ""); assert(eval_map.contains(n), "absent"); return *(eval_map.get(n)); @@ -2794,7 +2794,7 @@ static uint eval_operand(Node* n, ResourceHashtable& eval_map) { static void eval_operands(Node* n, uint& func1, uint& func2, uint& func3, - ResourceHashtable& eval_map) { + HashTable& eval_map) { assert(is_vector_bitwise_op(n), ""); if (is_vector_unary_bitwise_op(n)) { @@ -2818,7 +2818,7 @@ uint Compile::compute_truth_table(Unique_Node_List& partition, Unique_Node_List& assert(inputs.size() <= 3, "sanity"); ResourceMark rm; uint res = 0; - ResourceHashtable eval_map; + HashTable eval_map; // Populate precomputed functions for inputs. // Each input corresponds to one column of 3 input truth-table. diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 27e397790d4..730f0750159 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1982,7 +1982,7 @@ class DataNodeGraph : public StackObj { DataNodeGraph(const Unique_Node_List& data_nodes, PhaseIdealLoop* phase) : _phase(phase), _data_nodes(data_nodes), - // Use 107 as best guess which is the first resize value in ResizeableResourceHashtable::large_table_sizes. + // Use 107 as best guess which is the first resize value in ResizeableHashTable::large_table_sizes. _orig_to_new(107, MaxNodeLimit) { #ifdef ASSERT diff --git a/src/hotspot/share/opto/mempointer.cpp b/src/hotspot/share/opto/mempointer.cpp index e400e40e4a2..0dadc14a686 100644 --- a/src/hotspot/share/opto/mempointer.cpp +++ b/src/hotspot/share/opto/mempointer.cpp @@ -25,7 +25,7 @@ #include "classfile/vmSymbols.hpp" #include "opto/addnode.hpp" #include "opto/mempointer.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" MemPointerParserCallback MemPointerParserCallback::_empty; diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index dc0ac474c4b..c2b3c4fb0ad 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -222,7 +222,7 @@ typedef Node** DUIterator_Fast; typedef Node** DUIterator_Last; #endif -typedef ResizeableResourceHashtable OrigToNewHashtable; +typedef ResizeableHashTable OrigToNewHashtable; // Node Sentinel #define NodeSentinel (Node*)-1 diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.hpp b/src/hotspot/share/opto/superwordVTransformBuilder.hpp index ecec380fbfe..f2198113d31 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.hpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.hpp @@ -35,7 +35,7 @@ private: const PackSet& _packset; VTransform& _vtransform; - ResourceHashtable _idx_to_vtnode; + HashTable _idx_to_vtnode; public: SuperWordVTransformBuilder(const PackSet& packset, diff --git a/src/hotspot/share/prims/foreignGlobals.cpp b/src/hotspot/share/prims/foreignGlobals.cpp index d3d0c2cefae..f8aa73bed05 100644 --- a/src/hotspot/share/prims/foreignGlobals.cpp +++ b/src/hotspot/share/prims/foreignGlobals.cpp @@ -26,7 +26,7 @@ #include "memory/resourceArea.hpp" #include "prims/foreignGlobals.inline.hpp" #include "runtime/jniHandles.inline.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" StubLocations::StubLocations() { for (uint32_t i = 0; i < LOCATION_LIMIT; i++) { @@ -201,7 +201,7 @@ class ArgumentShuffle::ComputeMoveOrder: public StackObj { return a.type() == b.type() && a.index_or_offset() == b.index_or_offset(); } - using KillerTable = ResourceHashtable< + using KillerTable = HashTable< VMStorage, MoveOperation*, 32, // doesn't need to be big. don't have that many argument registers (in known ABIs) AnyObj::RESOURCE_AREA, diff --git a/src/hotspot/share/prims/jvmtiTagMapTable.hpp b/src/hotspot/share/prims/jvmtiTagMapTable.hpp index f86b6386a9a..f717552da2b 100644 --- a/src/hotspot/share/prims/jvmtiTagMapTable.hpp +++ b/src/hotspot/share/prims/jvmtiTagMapTable.hpp @@ -28,7 +28,7 @@ #include "gc/shared/collectedHeap.hpp" #include "memory/allocation.hpp" #include "oops/weakHandle.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" class JvmtiEnv; class JvmtiTagMapKeyClosure; @@ -65,14 +65,14 @@ class JvmtiTagMapKey : public CHeapObj { }; typedef -ResizeableResourceHashtable ResizableResourceHT; + JvmtiTagMapKey::equals> ResizableHT; class JvmtiTagMapTable : public CHeapObj { private: - ResizableResourceHT _table; + ResizableHT _table; public: JvmtiTagMapTable(); diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 5eb9601e4c5..3d83959d76a 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -27,8 +27,8 @@ #include "logging/logLevel.hpp" #include "logging/logTag.hpp" -#include "memory/allStatic.hpp" #include "memory/allocation.hpp" +#include "memory/allStatic.hpp" #include "runtime/globals.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 149ebef4294..beed5df5254 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -80,7 +80,7 @@ #include "utilities/dtrace.hpp" #include "utilities/events.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/xmlstream.hpp" #ifdef COMPILER1 @@ -2450,7 +2450,7 @@ class ArchivedAdapterTable : public OffsetCompactHashtable< #endif // INCLUDE_CDS // A hashtable mapping from AdapterFingerPrints to AdapterHandlerEntries -using AdapterHandlerTable = ResourceHashtable; diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index 089a432a8b5..e35c3651f5b 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -30,7 +30,7 @@ #include "runtime/basicLock.hpp" #include "runtime/handles.hpp" #include "runtime/javaThread.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" template class GrowableArray; class LogStream; diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index c8af260f66e..6247b351573 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -39,9 +39,9 @@ #include "utilities/copy.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" -#include "utilities/resourceHash.hpp" #include "utilities/vmError.hpp" // The '_cnt', '_max' and '_times" fields are enabled via @@ -191,15 +191,15 @@ class ThreadScanHashtable : public CHeapObj { return (unsigned int)(((uint32_t)(uintptr_t)s1) * 2654435761u); } - // ResourceHashtable SIZE is specified at compile time so we + // HashTable SIZE is specified at compile time so we // use 1031 which is the first prime after 1024. - typedef ResourceHashtable PtrTable; PtrTable * _ptrs; public: - // ResourceHashtable is passed to various functions and populated in + // HashTable is passed to various functions and populated in // different places so we allocate it using C_HEAP to make it immune // from any ResourceMarks that happen to be in the code paths. ThreadScanHashtable() : _ptrs(new (mtThread) PtrTable()) {} diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp index da833150d74..2cfc84376af 100644 --- a/src/hotspot/share/runtime/vmOperations.cpp +++ b/src/hotspot/share/runtime/vmOperations.cpp @@ -291,9 +291,9 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { AnyObj::C_HEAP, mtThread, AllocFailStrategy::RETURN_NULL> {}; - // ResourceHashtable SIZE is specified at compile time so we + // HashTable SIZE is specified at compile time so we // use 1031 which is the first prime after 1024. - typedef ResourceHashtable PtrTable; PtrTable* _ptrs; size_t _key_count; @@ -326,7 +326,7 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { } public: - // ResourceHashtable is passed to various functions and populated in + // HashTable is passed to various functions and populated in // different places so we allocate it using C_HEAP to make it immune // from any ResourceMarks that happen to be in the code paths. ObjectMonitorsDump() : _ptrs(new (mtThread) PtrTable), _key_count(0), _om_count(0) {} diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index 2e299083ac3..051096ae24a 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -831,7 +831,7 @@ public: class DumperClassCacheTable { private: - // ResourceHashtable SIZE is specified at compile time so we + // HashTable SIZE is specified at compile time so we // use 1031 which is the first prime after 1024. static constexpr size_t TABLE_SIZE = 1031; @@ -841,7 +841,7 @@ private: // sized table from overloading. static constexpr int CACHE_TOP = 256; - typedef ResourceHashtable PtrTable; PtrTable* _ptrs; diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 181786cd238..2a179f5814f 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1332,7 +1332,7 @@ typedef const char* ccstr; typedef const char* ccstrlist; // represents string arguments which accumulate //---------------------------------------------------------------------------------------------------- -// Default hash/equals functions used by ResourceHashtable +// Default hash/equals functions used by HashTable template unsigned primitive_hash(const K& k) { unsigned hash = (unsigned)((uintptr_t)k); diff --git a/src/hotspot/share/utilities/resourceHash.hpp b/src/hotspot/share/utilities/hashTable.hpp similarity index 89% rename from src/hotspot/share/utilities/resourceHash.hpp rename to src/hotspot/share/utilities/hashTable.hpp index a99239b21a0..cd85328657a 100644 --- a/src/hotspot/share/utilities/resourceHash.hpp +++ b/src/hotspot/share/utilities/hashTable.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_UTILITIES_RESOURCEHASH_HPP -#define SHARE_UTILITIES_RESOURCEHASH_HPP +#ifndef SHARE_UTILITIES_HASHTABLE_HPP +#define SHARE_UTILITIES_HASHTABLE_HPP #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" @@ -33,20 +33,20 @@ #include template -class ResourceHashtableNode : public AnyObj { +class HashTableNode : public AnyObj { public: unsigned _hash; K _key; V _value; - ResourceHashtableNode* _next; + HashTableNode* _next; - ResourceHashtableNode(unsigned hash, K const& key, V const& value, - ResourceHashtableNode* next = nullptr) : + HashTableNode(unsigned hash, K const& key, V const& value, + HashTableNode* next = nullptr) : _hash(hash), _key(key), _value(value), _next(next) {} // Create a node with a default-constructed value. - ResourceHashtableNode(unsigned hash, K const& key, - ResourceHashtableNode* next = nullptr) : + HashTableNode(unsigned hash, K const& key, + HashTableNode* next = nullptr) : _hash(hash), _key(key), _value(), _next(next) {} }; @@ -58,12 +58,12 @@ template< unsigned (*HASH) (K const&), bool (*EQUALS)(K const&, K const&) > -class ResourceHashtableBase : public STORAGE { +class HashTableBase : public STORAGE { static_assert(ALLOC_TYPE == AnyObj::C_HEAP || std::is_trivially_destructible::value, "Destructor for K is only called with C_HEAP"); static_assert(ALLOC_TYPE == AnyObj::C_HEAP || std::is_trivially_destructible::value, "Destructor for V is only called with C_HEAP"); - using Node = ResourceHashtableNode; + using Node = HashTableNode; private: int _number_of_entries; @@ -94,17 +94,17 @@ class ResourceHashtableBase : public STORAGE { Node const** lookup_node(unsigned hash, K const& key) const { return const_cast( - const_cast(this)->lookup_node(hash, key)); + const_cast(this)->lookup_node(hash, key)); } protected: Node** table() const { return STORAGE::table(); } - ResourceHashtableBase() : STORAGE(), _number_of_entries(0) {} - ResourceHashtableBase(unsigned size) : STORAGE(size), _number_of_entries(0) {} - NONCOPYABLE(ResourceHashtableBase); + HashTableBase() : STORAGE(), _number_of_entries(0) {} + HashTableBase(unsigned size) : STORAGE(size), _number_of_entries(0) {} + NONCOPYABLE(HashTableBase); - ~ResourceHashtableBase() { + ~HashTableBase() { if (ALLOC_TYPE == AnyObj::C_HEAP) { Node* const* bucket = table(); const unsigned sz = table_size(); @@ -343,13 +343,13 @@ class ResourceHashtableBase : public STORAGE { }; template -class FixedResourceHashtableStorage : public AnyObj { - using Node = ResourceHashtableNode; +class FixedHashTableStorage : public AnyObj { + using Node = HashTableNode; Node* _table[TABLE_SIZE]; protected: - FixedResourceHashtableStorage() { memset(_table, 0, sizeof(_table)); } - ~FixedResourceHashtableStorage() = default; + FixedHashTableStorage() { memset(_table, 0, sizeof(_table)); } + ~FixedHashTableStorage() = default; constexpr unsigned table_size() const { return TABLE_SIZE; @@ -368,13 +368,13 @@ template< unsigned (*HASH) (K const&) = primitive_hash, bool (*EQUALS)(K const&, K const&) = primitive_equals > -class ResourceHashtable : public ResourceHashtableBase< - FixedResourceHashtableStorage, +class HashTable : public HashTableBase< + FixedHashTableStorage, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS> { - NONCOPYABLE(ResourceHashtable); + NONCOPYABLE(HashTable); public: - ResourceHashtable() : ResourceHashtableBase, + HashTable() : HashTableBase, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS>() {} }; -#endif // SHARE_UTILITIES_RESOURCEHASH_HPP +#endif // SHARE_UTILITIES_HASHTABLE_HPP diff --git a/src/hotspot/share/utilities/nativeCallStack.hpp b/src/hotspot/share/utilities/nativeCallStack.hpp index 5038fe31aef..32635a72d51 100644 --- a/src/hotspot/share/utilities/nativeCallStack.hpp +++ b/src/hotspot/share/utilities/nativeCallStack.hpp @@ -27,8 +27,8 @@ #include "memory/allocation.hpp" #include "nmt/nmtCommon.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" /* * This class represents a native call path (does not include Java frame) diff --git a/src/hotspot/share/utilities/objectBitSet.hpp b/src/hotspot/share/utilities/objectBitSet.hpp index 124188cd321..73f1c89dd40 100644 --- a/src/hotspot/share/utilities/objectBitSet.hpp +++ b/src/hotspot/share/utilities/objectBitSet.hpp @@ -29,7 +29,7 @@ #include "oops/oop.hpp" #include "oops/oopsHierarchy.hpp" #include "utilities/bitMap.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" class MemRegion; @@ -52,7 +52,7 @@ class ObjectBitSet : public CHeapObj { return hash ^ (hash >> 3); } - typedef ResizeableResourceHashtable BitMapFragmentTable; CHeapBitMap* get_fragment_bits(uintptr_t addr); diff --git a/src/hotspot/share/utilities/objectBitSet.inline.hpp b/src/hotspot/share/utilities/objectBitSet.inline.hpp index 482a97bc2d1..e1b4d728b10 100644 --- a/src/hotspot/share/utilities/objectBitSet.inline.hpp +++ b/src/hotspot/share/utilities/objectBitSet.inline.hpp @@ -52,8 +52,8 @@ ObjectBitSet::~ObjectBitSet() { delete current; current = next; } - // destructors for ResourceHashtable base deletes nodes, and - // ResizeableResourceHashtableStorage deletes the table. + // destructors for HashTable base deletes nodes, and + // ResizeableHashTableStorage deletes the table. } template diff --git a/src/hotspot/share/utilities/resizeableResourceHash.hpp b/src/hotspot/share/utilities/resizableHashTable.hpp similarity index 86% rename from src/hotspot/share/utilities/resizeableResourceHash.hpp rename to src/hotspot/share/utilities/resizableHashTable.hpp index b0c992bf1ef..b445ed8a55e 100644 --- a/src/hotspot/share/utilities/resizeableResourceHash.hpp +++ b/src/hotspot/share/utilities/resizableHashTable.hpp @@ -22,28 +22,28 @@ * */ -#ifndef SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP -#define SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP +#ifndef SHARE_UTILITIES_RESIZEABLEHASHTABLE_HPP +#define SHARE_UTILITIES_RESIZEABLEHASHTABLE_HPP -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" template< typename K, typename V, AnyObj::allocation_type ALLOC_TYPE, MemTag MEM_TAG> -class ResizeableResourceHashtableStorage : public AnyObj { - using Node = ResourceHashtableNode; +class ResizeableHashTableStorage : public AnyObj { + using Node = HashTableNode; protected: unsigned _table_size; Node** _table; - ResizeableResourceHashtableStorage(unsigned table_size) { + ResizeableHashTableStorage(unsigned table_size) { _table_size = table_size; _table = alloc_table(table_size); } - ~ResizeableResourceHashtableStorage() { + ~ResizeableHashTableStorage() { if (ALLOC_TYPE == C_HEAP) { FREE_C_HEAP_ARRAY(Node*, _table); } @@ -76,15 +76,15 @@ template< unsigned (*HASH) (K const&) = primitive_hash, bool (*EQUALS)(K const&, K const&) = primitive_equals > -class ResizeableResourceHashtable : public ResourceHashtableBase< - ResizeableResourceHashtableStorage, +class ResizeableHashTable : public HashTableBase< + ResizeableHashTableStorage, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS> { unsigned _max_size; - using BASE = ResourceHashtableBase, + using BASE = HashTableBase, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS>; - using Node = ResourceHashtableNode; - NONCOPYABLE(ResizeableResourceHashtable); + using Node = HashTableNode; + NONCOPYABLE(ResizeableHashTable); // Calculate next "good" hashtable size based on requested count int calculate_resize(bool use_large_table_sizes) const { @@ -111,7 +111,7 @@ class ResizeableResourceHashtable : public ResourceHashtableBase< } public: - ResizeableResourceHashtable(unsigned size, unsigned max_size) + ResizeableHashTable(unsigned size, unsigned max_size) : BASE(size), _max_size(max_size) { assert(size <= 0x3fffffff && max_size <= 0x3fffffff, "avoid overflow in resize"); } @@ -180,4 +180,4 @@ public: #endif // ASSERT }; -#endif // SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP +#endif // SHARE_UTILITIES_RESIZABLEHASHTABLE_HPP diff --git a/test/hotspot/gtest/runtime/test_os_reserve_between.cpp b/test/hotspot/gtest/runtime/test_os_reserve_between.cpp index ee91ff49ded..8e68be22749 100644 --- a/test/hotspot/gtest/runtime/test_os_reserve_between.cpp +++ b/test/hotspot/gtest/runtime/test_os_reserve_between.cpp @@ -27,8 +27,8 @@ #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" // #define LOG_PLEASE #include "testutils.hpp" @@ -206,7 +206,7 @@ static void test_attempt_reserve_memory_between_random_distribution(unsigned num // Allocate n times within that hole (with subsequent deletions) and remember unique addresses returned. constexpr unsigned num_tries_per_attach_point = 100; ResourceMark rm; - ResourceHashtable ht; + HashTable ht; const unsigned num_tries = expect_failure ? 3 : (num_possible_attach_points * num_tries_per_attach_point); unsigned num_uniq = 0; // Number of uniq addresses returned diff --git a/test/hotspot/gtest/utilities/test_resourceHash.cpp b/test/hotspot/gtest/utilities/test_hashtable.cpp similarity index 82% rename from test/hotspot/gtest/utilities/test_resourceHash.cpp rename to test/hotspot/gtest/utilities/test_hashtable.cpp index 7f89f6c6b91..4fa4bcfa8fc 100644 --- a/test/hotspot/gtest/utilities/test_resourceHash.cpp +++ b/test/hotspot/gtest/utilities/test_hashtable.cpp @@ -29,9 +29,9 @@ #include "unittest.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" -class CommonResourceHashtableTest : public ::testing::Test { +class CommonHashTableTest : public ::testing::Test { protected: typedef void* K; typedef uintx V; @@ -79,7 +79,7 @@ class CommonResourceHashtableTest : public ::testing::Test { }; -class SmallResourceHashtableTest : public CommonResourceHashtableTest { +class SmallHashTableTest : public CommonHashTableTest { protected: template< @@ -93,7 +93,7 @@ class SmallResourceHashtableTest : public CommonResourceHashtableTest { static void test(V step) { EqualityTestIter et; - ResourceHashtable rh; + HashTable rh; ASSERT_FALSE(rh.contains(as_K(step))); @@ -157,61 +157,61 @@ class SmallResourceHashtableTest : public CommonResourceHashtableTest { }; }; -TEST_VM_F(SmallResourceHashtableTest, default) { +TEST_VM_F(SmallHashTableTest, default) { ResourceMark rm; Runner<>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, default_shifted) { +TEST_VM_F(SmallHashTableTest, default_shifted) { ResourceMark rm; Runner<>::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash) { +TEST_VM_F(SmallHashTableTest, bad_hash) { ResourceMark rm; Runner::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash_shifted) { +TEST_VM_F(SmallHashTableTest, bad_hash_shifted) { ResourceMark rm; Runner::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash) { +TEST_VM_F(SmallHashTableTest, identity_hash) { ResourceMark rm; Runner::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash_shifted) { +TEST_VM_F(SmallHashTableTest, identity_hash_shifted) { ResourceMark rm; Runner::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, primitive_hash_no_rm) { +TEST_VM_F(SmallHashTableTest, primitive_hash_no_rm) { Runner, primitive_equals, 512, AnyObj::C_HEAP>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, primitive_hash_no_rm_shifted) { +TEST_VM_F(SmallHashTableTest, primitive_hash_no_rm_shifted) { Runner, primitive_equals, 512, AnyObj::C_HEAP>::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash_no_rm) { +TEST_VM_F(SmallHashTableTest, bad_hash_no_rm) { Runner, 512, AnyObj::C_HEAP>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash_no_rm_shifted) { +TEST_VM_F(SmallHashTableTest, bad_hash_no_rm_shifted) { Runner, 512, AnyObj::C_HEAP>::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash_no_rm) { +TEST_VM_F(SmallHashTableTest, identity_hash_no_rm) { Runner, 1, AnyObj::C_HEAP>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash_no_rm_shifted) { +TEST_VM_F(SmallHashTableTest, identity_hash_no_rm_shifted) { Runner, 1, AnyObj::C_HEAP>::test(0x10); } -class GenericResourceHashtableTest : public CommonResourceHashtableTest { +class GenericHashTableTest : public CommonHashTableTest { protected: template< @@ -225,7 +225,7 @@ class GenericResourceHashtableTest : public CommonResourceHashtableTest { static void test(unsigned num_elements = SIZE) { EqualityTestIter et; - ResourceHashtable rh; + HashTable rh; for (uintptr_t i = 0; i < num_elements; ++i) { ASSERT_TRUE(rh.put(as_K(i), i)); @@ -263,39 +263,39 @@ class GenericResourceHashtableTest : public CommonResourceHashtableTest { }; }; -TEST_VM_F(GenericResourceHashtableTest, default) { +TEST_VM_F(GenericHashTableTest, default) { ResourceMark rm; Runner<>::test(); } -TEST_VM_F(GenericResourceHashtableTest, bad_hash) { +TEST_VM_F(GenericHashTableTest, bad_hash) { ResourceMark rm; Runner::test(); } -TEST_VM_F(GenericResourceHashtableTest, identity_hash) { +TEST_VM_F(GenericHashTableTest, identity_hash) { ResourceMark rm; Runner::test(); } -TEST_VM_F(GenericResourceHashtableTest, primitive_hash_no_rm) { +TEST_VM_F(GenericHashTableTest, primitive_hash_no_rm) { Runner, primitive_equals, 512, AnyObj::C_HEAP>::test(); } -TEST_VM_F(GenericResourceHashtableTest, bad_hash_no_rm) { +TEST_VM_F(GenericHashTableTest, bad_hash_no_rm) { Runner, 512, AnyObj::C_HEAP>::test(); } -TEST_VM_F(GenericResourceHashtableTest, identity_hash_no_rm) { +TEST_VM_F(GenericHashTableTest, identity_hash_no_rm) { Runner, 1, AnyObj::C_HEAP>::test(512); } -// Simple ResourceHashtable whose key is a SymbolHandle and value is an int +// Simple HashTable whose key is a SymbolHandle and value is an int // This test is to show that the SymbolHandle will correctly handle the refcounting // in the table. -class SimpleResourceHashtableDeleteTest : public ::testing::Test { +class SimpleHashTableDeleteTest : public ::testing::Test { public: - ResourceHashtable _simple_test_table; + HashTable _simple_test_table; class SimpleDeleter : public StackObj { public: @@ -305,7 +305,7 @@ class SimpleResourceHashtableDeleteTest : public ::testing::Test { }; }; -TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_remove) { +TEST_VM_F(SimpleHashTableDeleteTest, simple_remove) { TempNewSymbol t = SymbolTable::new_symbol("abcdefg_simple"); Symbol* s = t; int s_orig_count = s->refcount(); @@ -317,7 +317,7 @@ TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_remove) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be same as start"; } -TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_delete) { +TEST_VM_F(SimpleHashTableDeleteTest, simple_delete) { TempNewSymbol t = SymbolTable::new_symbol("abcdefg_simple"); Symbol* s = t; int s_orig_count = s->refcount(); @@ -330,10 +330,10 @@ TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_delete) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be same as start"; } -// More complicated ResourceHashtable with SymbolHandle in the key. Since the *same* Symbol is part +// More complicated HashTable with SymbolHandle in the key. Since the *same* Symbol is part // of the value, it's not necessary to manipulate the refcount of the key, but you must in the value. // Luckily SymbolHandle does this. -class ResourceHashtableDeleteTest : public ::testing::Test { +class HashTableDeleteTest : public ::testing::Test { public: class TestValue : public CHeapObj { SymbolHandle _s; @@ -348,8 +348,8 @@ class ResourceHashtableDeleteTest : public ::testing::Test { // Symbol* s() const { return _s; } // needed for conversion from TempNewSymbol to SymbolHandle member }; - // ResourceHashtable whose value is a *copy* of TestValue. - ResourceHashtable _test_table; + // HashTable whose value is a *copy* of TestValue. + HashTable _test_table; class Deleter : public StackObj { public: @@ -362,8 +362,8 @@ class ResourceHashtableDeleteTest : public ::testing::Test { } }; - // ResourceHashtable whose value is a pointer to TestValue. - ResourceHashtable _ptr_test_table; + // HashTable whose value is a pointer to TestValue. + HashTable _ptr_test_table; class PtrDeleter : public StackObj { public: @@ -378,7 +378,7 @@ class ResourceHashtableDeleteTest : public ::testing::Test { }; -TEST_VM_F(ResourceHashtableDeleteTest, value_remove) { +TEST_VM_F(HashTableDeleteTest, value_remove) { TempNewSymbol s = SymbolTable::new_symbol("abcdefg"); int s_orig_count = s->refcount(); { @@ -396,7 +396,7 @@ TEST_VM_F(ResourceHashtableDeleteTest, value_remove) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be as we started"; } -TEST_VM_F(ResourceHashtableDeleteTest, value_delete) { +TEST_VM_F(HashTableDeleteTest, value_delete) { TempNewSymbol d = SymbolTable::new_symbol("defghijklmnop"); int d_orig_count = d->refcount(); { @@ -412,7 +412,7 @@ TEST_VM_F(ResourceHashtableDeleteTest, value_delete) { ASSERT_EQ(d->refcount(), d_orig_count) << "refcount should be as we started"; } -TEST_VM_F(ResourceHashtableDeleteTest, check_delete_ptr) { +TEST_VM_F(HashTableDeleteTest, check_delete_ptr) { TempNewSymbol s = SymbolTable::new_symbol("abcdefg_ptr"); int s_orig_count = s->refcount(); { @@ -432,7 +432,7 @@ TEST_VM_F(ResourceHashtableDeleteTest, check_delete_ptr) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be as we started"; } -class ResourceHashtablePrintTest : public ::testing::Test { +class HashTablePrintTest : public ::testing::Test { public: class TestValue { int _i; @@ -441,7 +441,7 @@ class ResourceHashtablePrintTest : public ::testing::Test { public: TestValue(int i) : _i(i), _j(i+1), _k(i+2) {} }; - ResourceHashtable _test_table; + HashTable _test_table; class TableDeleter { public: @@ -452,7 +452,7 @@ class ResourceHashtablePrintTest : public ::testing::Test { }; }; -TEST_VM_F(ResourceHashtablePrintTest, print_test) { +TEST_VM_F(HashTablePrintTest, print_test) { for (int i = 0; i < 300; i++) { TestValue* tv = new TestValue(i); _test_table.put(i, tv); // all the entries can be the same. From 9660320041d0ba0f22ebe074a64472557b85a24c Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 13 Aug 2025 20:43:46 +0000 Subject: [PATCH 029/112] 8364781: Re-examine DigitList digits resizing during parsing Reviewed-by: liach, naoto --- src/java.base/share/classes/java/text/DigitList.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/text/DigitList.java b/src/java.base/share/classes/java/text/DigitList.java index 3eeabb7e77e..26238345047 100644 --- a/src/java.base/share/classes/java/text/DigitList.java +++ b/src/java.base/share/classes/java/text/DigitList.java @@ -42,6 +42,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import jdk.internal.math.FloatingDecimal; +import jdk.internal.util.ArraysSupport; /** * Digit List. Private to DecimalFormat. @@ -153,7 +154,7 @@ final class DigitList implements Cloneable { */ public void append(char digit) { if (count == digits.length) { - char[] data = new char[count + 100]; + char[] data = new char[ArraysSupport.newLength(count, 1, count)]; System.arraycopy(digits, 0, data, 0, count); digits = data; } From 9c266ae83c047025d778da41e413701ac3b50b03 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 13 Aug 2025 20:49:16 +0000 Subject: [PATCH 030/112] 8365229: ARM32: c2i_no_clinit_check_entry assert failed after JDK-8364269 Reviewed-by: kvn, adinn, bulasevich, phh --- src/hotspot/cpu/zero/sharedRuntime_zero.cpp | 6 +++--- src/hotspot/share/code/codeBlob.cpp | 6 ++++-- src/hotspot/share/runtime/sharedRuntime.cpp | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp index 2c5fb717c40..df162fbfa74 100644 --- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp +++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp @@ -56,11 +56,11 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, const BasicType *sig_bt, const VMRegPair *regs, AdapterHandlerEntry* handler) { + // VM expects i2c entry to be always filled. The rest can be unset. handler->set_entry_points(CAST_FROM_FN_PTR(address,zero_null_code_stub), - CAST_FROM_FN_PTR(address,zero_null_code_stub), - CAST_FROM_FN_PTR(address,zero_null_code_stub), + nullptr, + nullptr, nullptr); - return; } nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 5b87e8c5670..9ec5478a071 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -450,8 +450,10 @@ AdapterBlob::AdapterBlob(int size, CodeBuffer* cb, int entry_offset[AdapterBlob: BufferBlob("I2C/C2I adapters", CodeBlobKind::Adapter, cb, size, sizeof(AdapterBlob)) { assert(entry_offset[0] == 0, "sanity check"); for (int i = 1; i < AdapterBlob::ENTRY_COUNT; i++) { - assert(entry_offset[i] > 0 && entry_offset[i] < cb->insts()->size(), - "invalid entry offset 0x%x", entry_offset[i]); + // The entry is within the adapter blob or unset. + assert((entry_offset[i] > 0 && entry_offset[i] < cb->insts()->size()) || + (entry_offset[i] == -1), + "invalid entry offset[%d] = 0x%x", i, entry_offset[i]); } _c2i_offset = entry_offset[1]; _c2i_unverified_offset = entry_offset[2]; diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index beed5df5254..5bfddb8a04a 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -2778,7 +2778,12 @@ AdapterBlob* AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handle adapter_blob->get_offsets(offsets); address i2c_entry = adapter_blob->content_begin(); assert(offsets[0] == 0, "sanity check"); - handler->set_entry_points(i2c_entry, i2c_entry + offsets[1], i2c_entry + offsets[2], i2c_entry + offsets[3]); + handler->set_entry_points( + i2c_entry, + (offsets[1] != -1) ? (i2c_entry + offsets[1]) : nullptr, + (offsets[2] != -1) ? (i2c_entry + offsets[2]) : nullptr, + (offsets[3] != -1) ? (i2c_entry + offsets[3]) : nullptr + ); } return adapter_blob; } @@ -2842,9 +2847,12 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, assert(AdapterBlob::ENTRY_COUNT == 4, "sanity"); address i2c_entry = handler->get_i2c_entry(); entry_offset[0] = 0; // i2c_entry offset - entry_offset[1] = handler->get_c2i_entry() - i2c_entry; - entry_offset[2] = handler->get_c2i_unverified_entry() - i2c_entry; - entry_offset[3] = handler->get_c2i_no_clinit_check_entry() - i2c_entry; + entry_offset[1] = (handler->get_c2i_entry() != nullptr) ? + (handler->get_c2i_entry() - i2c_entry) : -1; + entry_offset[2] = (handler->get_c2i_unverified_entry() != nullptr) ? + (handler->get_c2i_unverified_entry() - i2c_entry) : -1; + entry_offset[3] = (handler->get_c2i_no_clinit_check_entry() != nullptr) ? + (handler->get_c2i_no_clinit_check_entry() - i2c_entry) : -1; adapter_blob = AdapterBlob::create(&buffer, entry_offset); if (adapter_blob == nullptr) { From 9dcc502cc83773561707f2afe9aee1f9e2386b9e Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 14 Aug 2025 04:55:02 +0000 Subject: [PATCH 031/112] 8365375: Method SU3.setAcceleratorSelectionForeground assigns to acceleratorForeground Reviewed-by: aivanov, prr, kizune --- .../share/classes/com/sun/java/swing/SwingUtilities3.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java index a17f1f480ed..5e1e149eb9e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -248,7 +248,7 @@ public class SwingUtilities3 { } public static void setAcceleratorSelectionForeground(Color acceleratorSelectionFg) { - acceleratorForeground = acceleratorSelectionFg; + acceleratorSelectionForeground = acceleratorSelectionFg; } public static void setAcceleratorForeground(Color acceleratorFg) { From c22e01d77648036db4ed640521e82c49f8791ca1 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 14 Aug 2025 07:02:08 +0000 Subject: [PATCH 032/112] 8341342: Elements.getAllModuleElements() does not work properly before JavacTask.analyze() Reviewed-by: vromero, liach --- .../sun/tools/javac/model/JavacElements.java | 1 + test/jdk/tools/sincechecker/SinceChecker.java | 1 - .../elements/TestElementsProgrammatic.java | 74 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/langtools/tools/javac/processing/model/util/elements/TestElementsProgrammatic.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java index 7cf419f3ca8..73219ca38ae 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java @@ -128,6 +128,7 @@ public class JavacElements implements Elements { @Override @DefinedBy(Api.LANGUAGE_MODEL) public Set getAllModuleElements() { + ensureEntered("getAllModuleElements"); if (allowModules) return Collections.unmodifiableSet(modules.allModules()); else diff --git a/test/jdk/tools/sincechecker/SinceChecker.java b/test/jdk/tools/sincechecker/SinceChecker.java index a7ec90d53f8..f9a8e3d49dd 100644 --- a/test/jdk/tools/sincechecker/SinceChecker.java +++ b/test/jdk/tools/sincechecker/SinceChecker.java @@ -297,7 +297,6 @@ public class SinceChecker { Collections.singletonList(SimpleJavaFileObject.forSource(URI.create("myfo:/Test.java"), ""))); ct.analyze(); Elements elements = ct.getElements(); - elements.getModuleElement("java.base"); try (EffectiveSourceSinceHelper javadocHelper = EffectiveSourceSinceHelper.create(ct, List.of(root), this)) { processModuleCheck(elements.getModuleElement(moduleName), ct, moduleDirectory, javadocHelper); } catch (Exception e) { diff --git a/test/langtools/tools/javac/processing/model/util/elements/TestElementsProgrammatic.java b/test/langtools/tools/javac/processing/model/util/elements/TestElementsProgrammatic.java new file mode 100644 index 00000000000..dda3faac232 --- /dev/null +++ b/test/langtools/tools/javac/processing/model/util/elements/TestElementsProgrammatic.java @@ -0,0 +1,74 @@ +/* + * 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 8341342 + * @summary Test Elements methods programmatically + * @modules jdk.compiler + * @run junit TestElementsProgrammatic + */ + +import com.sun.source.util.JavacTask; +import java.net.URI; +import java.util.List; +import java.util.function.Consumer; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Programmatically test workings of various methods of Elements. + */ +public class TestElementsProgrammatic { + + private final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler(); + + @ParameterizedTest + @MethodSource + public void automaticallyEnter(Consumer verify) { + //make sure the get{All,}{Module,Package,Type}Element{s,} methods will automatically enter: + JavaFileObject input = + SimpleJavaFileObject.forSource(URI.create("mem://Test.java"), ""); + List inputs = List.of(input); + JavacTask task = (JavacTask) systemCompiler.getTask(null, null, null, null, null, inputs); + verify.accept(task); + } + + private static List> automaticallyEnter() { + return List.of( + task -> assertFalse(task.getElements().getAllModuleElements().isEmpty()), + task -> assertNotNull(task.getElements().getModuleElement("java.base")), + task -> assertNotNull(task.getElements().getPackageElement("java.lang")), + task -> assertFalse(task.getElements().getAllPackageElements("java.lang").isEmpty()), + task -> assertNotNull(task.getElements().getTypeElement("java.lang.Object")), + task -> assertFalse(task.getElements().getAllTypeElements("java.lang.Object").isEmpty()) + ); + } +} From a6be2286421e069a292c749eecd6bdc38a8deaf2 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 14 Aug 2025 07:04:40 +0000 Subject: [PATCH 033/112] 8365314: javac fails with an exception for erroneous source Reviewed-by: vromero --- .../com/sun/tools/javac/code/Lint.java | 17 ++++++------ .../javac/recovery/AnnotationRecovery.java | 26 ++++++++++++++++++- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 5a4d09b67ba..6d83f95fce4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -528,14 +528,15 @@ public class Lint { // Given a @SuppressWarnings annotation, extract the recognized suppressions private EnumSet suppressionsFrom(Attribute.Compound suppressWarnings) { EnumSet result = LintCategory.newEmptySet(); - Attribute.Array values = (Attribute.Array)suppressWarnings.member(names.value); - for (Attribute value : values.values) { - Optional.of(value) - .filter(val -> val instanceof Attribute.Constant) - .map(val -> (String) ((Attribute.Constant) val).value) - .flatMap(LintCategory::get) - .filter(lc -> lc.annotationSuppression) - .ifPresent(result::add); + if (suppressWarnings.member(names.value) instanceof Attribute.Array values) { + for (Attribute value : values.values) { + Optional.of(value) + .filter(val -> val instanceof Attribute.Constant) + .map(val -> (String) ((Attribute.Constant) val).value) + .flatMap(LintCategory::get) + .filter(lc -> lc.annotationSuppression) + .ifPresent(result::add); + } } return result; } diff --git a/test/langtools/tools/javac/recovery/AnnotationRecovery.java b/test/langtools/tools/javac/recovery/AnnotationRecovery.java index e5e52b82159..ea94f0f582d 100644 --- a/test/langtools/tools/javac/recovery/AnnotationRecovery.java +++ b/test/langtools/tools/javac/recovery/AnnotationRecovery.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8270139 8361445 + * @bug 8270139 8361445 8365314 * @summary Verify error recovery w.r.t. annotations * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -224,4 +224,28 @@ public class AnnotationRecovery extends TestRunner { } } + @Test //JDK-8365314 + public void testSuppressWarningsMissingAttribute() throws Exception { + String code = """ + @SuppressWarnings + public class Test { + } + """; + Path curPath = Path.of("."); + List actual = new JavacTask(tb) + .options("-XDrawDiagnostics", "-XDdev") + .sources(code) + .outdir(curPath) + .run(Expect.FAIL) + .getOutputLines(OutputKind.DIRECT); + + List expected = List.of( + "Test.java:1:1: compiler.err.annotation.missing.default.value: java.lang.SuppressWarnings, value", + "1 error" + ); + + if (!Objects.equals(actual, expected)) { + error("Expected: " + expected + ", but got: " + actual); + } + } } From 3e3298509f136583b18e5ab8bf75a8b012016f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Thu, 14 Aug 2025 07:37:10 +0000 Subject: [PATCH 034/112] 8365317: ZGC: Setting ZYoungGCThreads lower than ZOldGCThreads may result in a crash Reviewed-by: tschatzl, eosterlund --- src/hotspot/share/gc/z/zDirector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index e46b1c9e167..380a1e48ab4 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -725,8 +725,8 @@ static ZWorkerCounts select_worker_threads(const ZDirectorStats& stats, uint you // Adjust down the old workers so the next minor during major will be less sad old_workers = old_workers_clamped; // Since collecting the old generation depends on the initial young collection - // finishing, we don't want it to have fewer workers than the old generation. - young_workers = MAX2(old_workers, young_workers); + // finishing, we ideally don't want it to have fewer workers than the old generation. + young_workers = clamp(MAX2(old_workers, young_workers), 1u, ZYoungGCThreads); } else if (type == ZWorkerSelectionType::minor_during_old) { // Adjust young and old workers for minor during old to fit within ConcGCThreads young_workers = young_workers_clamped; From e320162815d529bc65cd058b34ec39d60d032ce7 Mon Sep 17 00:00:00 2001 From: Yudi Zheng Date: Thu, 14 Aug 2025 07:39:49 +0000 Subject: [PATCH 035/112] 8365218: [JVMCI] AArch64 CPU features are not computed correctly after 8364128 Reviewed-by: dnsimon --- .../hotspot/HotSpotJVMCIBackendFactory.java | 65 ++----------------- .../AArch64HotSpotJVMCIBackendFactory.java | 2 +- .../AMD64HotSpotJVMCIBackendFactory.java | 16 +++-- .../RISCV64HotSpotJVMCIBackendFactory.java | 2 +- 4 files changed, 20 insertions(+), 65 deletions(-) diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java index 319b9b9bc44..e0fc314de5b 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java @@ -27,10 +27,10 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.function.LongFunction; import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.runtime.JVMCIBackend; -import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE; public interface HotSpotJVMCIBackendFactory { @@ -48,7 +48,8 @@ public interface HotSpotJVMCIBackendFactory { * @param enumType the class of {@code CPUFeatureType} * @param constants VM constants. Each entry whose key starts with {@code "VM_Version::CPU_"} * specifies a CPU feature and its value is a mask for a bit in {@code features} - * @param features bits specifying CPU features + * @param bitMaskSupplier supplier to get the bit mask for the corresponding VM constant + * @param featuresSupplier supplier to get the bits specifying CPU features * @param renaming maps from VM feature names to enum constant names where the two differ * @throws IllegalArgumentException if any VM CPU feature constant cannot be converted to an * enum value @@ -57,18 +58,19 @@ public interface HotSpotJVMCIBackendFactory { static > EnumSet convertFeatures( Class enumType, Map constants, - long features, + LongFunction bitMaskSupplier, + LongFunction featuresSupplier, Map renaming) { EnumSet outFeatures = EnumSet.noneOf(enumType); List missing = new ArrayList<>(); for (Entry e : constants.entrySet()) { - long bitMask = e.getValue(); + long bitMask = bitMaskSupplier.apply(e.getValue()); String key = e.getKey(); if (key.startsWith("VM_Version::CPU_")) { String name = key.substring("VM_Version::CPU_".length()); try { CPUFeatureType feature = Enum.valueOf(enumType, renaming.getOrDefault(name, name)); - if ((features & bitMask) != 0) { + if ((featuresSupplier.apply(e.getValue()) & bitMask) != 0) { outFeatures.add(feature); } } catch (IllegalArgumentException iae) { @@ -82,57 +84,4 @@ public interface HotSpotJVMCIBackendFactory { return outFeatures; } - /** - * Converts CPU features bit map into enum constants. - * - * @param CPU feature enum type - * @param enumType the class of {@code CPUFeatureType} - * @param constants VM constants. Each entry whose key starts with {@code "VM_Version::CPU_"} - * specifies a CPU feature and its value is a mask for a bit in {@code features} - * @param featuresBitMapAddress pointer to {@code VM_Features::_features_bitmap} field of {@code VM_Version::_features} - * @param featuresBitMapSize size of feature bit map in bytes - * @param renaming maps from VM feature names to enum constant names where the two differ - * @throws IllegalArgumentException if any VM CPU feature constant cannot be converted to an - * enum value - * @return the set of converted values - */ - static > EnumSet convertFeatures( - Class enumType, - Map constants, - long featuresBitMapAddress, - long featuresBitMapSize, - Map renaming) { - EnumSet outFeatures = EnumSet.noneOf(enumType); - List missing = new ArrayList<>(); - - for (Entry e : constants.entrySet()) { - String key = e.getKey(); - long bitIndex = e.getValue(); - if (key.startsWith("VM_Version::CPU_")) { - String name = key.substring("VM_Version::CPU_".length()); - try { - final long featuresElementShiftCount = 6; // log (# of bits per long) - final long featuresElementMask = (1L << featuresElementShiftCount) - 1; - - CPUFeatureType feature = Enum.valueOf(enumType, renaming.getOrDefault(name, name)); - - long featureIndex = bitIndex >>> featuresElementShiftCount; - long featureBitMask = 1L << (bitIndex & featuresElementMask); - assert featureIndex < featuresBitMapSize; - - long featuresElement = UNSAFE.getLong(featuresBitMapAddress + featureIndex * Long.BYTES); - - if ((featuresElement & featureBitMask) != 0) { - outFeatures.add(feature); - } - } catch (IllegalArgumentException iae) { - missing.add(name); - } - } - } - if (!missing.isEmpty()) { - throw new JVMCIError("Missing CPU feature constants: %s", missing); - } - return outFeatures; - } } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java index 19c161d6bde..1ee016b73a8 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java @@ -49,7 +49,7 @@ public class AArch64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFac private static EnumSet computeFeatures(AArch64HotSpotVMConfig config) { // Configure the feature set using the HotSpot flag settings. Map constants = config.getStore().getConstants(); - return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, config.vmVersionFeatures, emptyMap()); + return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, idx -> 1L << idx, _ -> config.vmVersionFeatures, emptyMap()); } private static TargetDescription createTarget(AArch64HotSpotVMConfig config) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java index 040f39e3b8a..cae6d18e71e 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java @@ -26,6 +26,8 @@ import static jdk.vm.ci.common.InitTimer.timer; import java.util.EnumSet; import java.util.Map; + +import jdk.internal.misc.Unsafe; import jdk.internal.util.OperatingSystem; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.amd64.AMD64.CPUFeature; @@ -50,11 +52,15 @@ public class AMD64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFacto Map constants = config.getStore().getConstants(); Map renaming = Map.of("3DNOW_PREFETCH", "AMD_3DNOW_PREFETCH"); long featuresBitMapAddress = config.vmVersionFeatures + config.vmFeaturesFeaturesOffset; - EnumSet features = HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, - constants, - featuresBitMapAddress, - config.vmFeaturesFeaturesSize, - renaming); + EnumSet features = HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, idx -> { + final long featuresElementShiftCount = 6; // log (# of bits per long) + final long featuresElementMask = (1L << featuresElementShiftCount) - 1; + return 1L << (idx & featuresElementMask); + }, idx -> { + final long featuresElementShiftCount = 6; // log (# of bits per long) + long featureIndex = idx >>> featuresElementShiftCount; + return Unsafe.getUnsafe().getLong(featuresBitMapAddress + featureIndex * Long.BYTES); + }, renaming); assert features.contains(AMD64.CPUFeature.SSE) : "minimum config for x64"; assert features.contains(AMD64.CPUFeature.SSE2) : "minimum config for x64"; return features; diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java index c0e31124500..f50fcf1dbbc 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java @@ -49,7 +49,7 @@ public class RISCV64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFac private static EnumSet computeFeatures(RISCV64HotSpotVMConfig config) { // Configure the feature set using the HotSpot flag settings. Map constants = config.getStore().getConstants(); - return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, config.vmVersionFeatures, emptyMap()); + return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, mask -> mask, _ -> config.vmVersionFeatures, emptyMap()); } private static TargetDescription createTarget(RISCV64HotSpotVMConfig config) { From 7698c373a684235812c9dc11edd751059f9e8e81 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Thu, 14 Aug 2025 10:43:21 +0000 Subject: [PATCH 036/112] 8364556: JFR: Disable SymbolTableStatistics and StringTableStatistics in default.jfc Reviewed-by: mgronlun --- src/jdk.jfr/share/conf/jfr/default.jfc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 463639d7f1e..eb3b8626722 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -33,12 +33,12 @@ - true + false 10 s - true + false 10 s From 98f54d90ea56f63c2fc5137af98b57dbc90fe150 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 14 Aug 2025 11:11:47 +0000 Subject: [PATCH 037/112] 8365487: [asan] some oops (mode) related tests fail Reviewed-by: kbarrett, syan --- .../jtreg/runtime/CompressedOops/UseCompressedOops.java | 4 +++- .../TestGCHeapConfigurationEventWith32BitOops.java | 2 ++ .../TestGCHeapConfigurationEventWithZeroBasedOops.java | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java b/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java index 3d118bf73b1..e5e2a23c565 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox + * @comment Asan changes memory layout and we get a different coops mode + * @requires !vm.asan * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=480 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. UseCompressedOops */ diff --git a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java index 9ef90afb063..c98f055aa72 100644 --- a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java +++ b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java @@ -35,6 +35,8 @@ import jdk.test.whitebox.WhiteBox; * @requires vm.gc == "Parallel" | vm.gc == null * @requires os.family == "linux" | os.family == "windows" * @requires sun.arch.data.model == "64" + * @comment Asan changes memory layout and we get a different coops mode + * @requires !vm.asan * @library /test/lib /test/jdk * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java index 62d858eef98..f69d192ca5f 100644 --- a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java +++ b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java @@ -33,6 +33,8 @@ import jdk.test.lib.jfr.EventVerifier; * @requires vm.gc == "Parallel" | vm.gc == null * @requires os.family == "linux" | os.family == "windows" * @requires sun.arch.data.model == "64" + * @comment Asan changes memory layout and we get a different coops mode + * @requires !vm.asan * @library /test/lib /test/jdk * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseCompressedOops -Xmx4g jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWithZeroBasedOops */ From 41520998aa8808452ee384b213b2a77c7bad668d Mon Sep 17 00:00:00 2001 From: Roman Marchenko Date: Thu, 14 Aug 2025 12:31:20 +0000 Subject: [PATCH 038/112] 8365098: make/RunTests.gmk generates a wrong path to test artifacts on Alpine Reviewed-by: erikj, ihse --- make/RunTests.gmk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 60ae1bd4763..574fd869092 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1243,7 +1243,7 @@ UseSpecialTestHandler = \ # Now process each test to run and setup a proper make rule $(foreach test, $(TESTS_TO_RUN), \ $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ - $(TR) -cs '[a-z][A-Z][0-9]\n' '[_*1000]')) \ + $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(eval ALL_TEST_IDS += $(TEST_ID)) \ $(if $(call UseCustomTestHandler, $(test)), \ $(eval $(call SetupRunCustomTest, $(TEST_ID), \ @@ -1323,9 +1323,9 @@ run-test-report: post-run-test TEST TOTAL PASS FAIL ERROR SKIP " " $(foreach test, $(TESTS_TO_RUN), \ $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ - $(TR) -cs '[a-z][A-Z][0-9]\n' '[_*1000]')) \ + $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(ECHO) >> $(TEST_LAST_IDS) $(TEST_ID) $(NEWLINE) \ - $(eval NAME_PATTERN := $(shell $(ECHO) $(test) | $(TR) -c '\n' '[_*1000]')) \ + $(eval NAME_PATTERN := $(shell $(ECHO) $(test) | $(TR) -c '\n' '_')) \ $(if $(filter __________________________________________________%, $(NAME_PATTERN)), \ $(eval TEST_NAME := ) \ $(PRINTF) >> $(TEST_SUMMARY) "%2s %-49s\n" " " "$(test)" $(NEWLINE) \ From dd113c8df06cc7e1465fb3dfef2e9b2a5a99f1fb Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Thu, 14 Aug 2025 14:50:56 +0000 Subject: [PATCH 039/112] 8364628: Serial: Refactor SerialHeap::mem_allocate_work Reviewed-by: phh, kbarrett --- src/hotspot/share/gc/serial/serialHeap.cpp | 51 +++++++------------ src/hotspot/share/gc/serial/serialHeap.hpp | 4 -- .../share/gc/serial/tenuredGeneration.hpp | 2 + .../gc/serial/tenuredGeneration.inline.hpp | 8 +++ 4 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 48d6a1b74ea..6b9328b8697 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -281,15 +281,6 @@ size_t SerialHeap::max_capacity() const { return _young_gen->max_capacity() + _old_gen->max_capacity(); } -// Return true if any of the following is true: -// . the allocation won't fit into the current young gen heap -// . heap memory is tight -bool SerialHeap::should_try_older_generation_allocation(size_t word_size) const { - size_t young_capacity = _young_gen->capacity_before_gc(); - return (word_size > heap_word_size(young_capacity)) - || _is_heap_almost_full; -} - HeapWord* SerialHeap::expand_heap_and_allocate(size_t size, bool is_tlab) { HeapWord* result = nullptr; if (_old_gen->should_allocate(size, is_tlab)) { @@ -308,32 +299,26 @@ HeapWord* SerialHeap::expand_heap_and_allocate(size_t size, bool is_tlab) { HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { HeapWord* result = nullptr; - // Loop until the allocation is satisfied, or unsatisfied after GC. - for (uint try_count = 1; /* return or throw */; try_count += 1) { - // First allocation attempt is lock-free. - DefNewGeneration *young = _young_gen; - if (young->should_allocate(size, is_tlab)) { - result = young->par_allocate(size); + for (uint try_count = 1; /* break */; try_count++) { + if (_young_gen->should_allocate(size, is_tlab)) { + result = _young_gen->par_allocate(size); if (result != nullptr) { - assert(is_in_reserved(result), "result not in heap"); - return result; + break; + } + } + // Try old-gen allocation for non-TLAB. + if (!is_tlab) { + // If it's too large for young-gen or heap is too full. + if (size > heap_word_size(_young_gen->capacity_before_gc()) || _is_heap_almost_full) { + result = _old_gen->par_allocate(size); + if (result != nullptr) { + break; + } } } uint gc_count_before; // Read inside the Heap_lock locked region. { MutexLocker ml(Heap_lock); - log_trace(gc, alloc)("SerialHeap::mem_allocate_work: attempting locked slow path allocation"); - // Note that only large objects get a shot at being - // allocated in later generations. - bool first_only = !should_try_older_generation_allocation(size); - - result = attempt_allocation(size, is_tlab, first_only); - if (result != nullptr) { - assert(is_in_reserved(result), "result not in heap"); - return result; - } - - // Read the gc count while the heap lock is held. gc_count_before = total_collections(); } @@ -341,10 +326,7 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { VMThread::execute(&op); if (op.gc_succeeded()) { result = op.result(); - - assert(result == nullptr || is_in_reserved(result), - "result not in heap"); - return result; + break; } // Give a warning if we seem to be looping forever. @@ -354,6 +336,9 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { " size=%zu %s", try_count, size, is_tlab ? "(TLAB)" : ""); } } + + assert(result == nullptr || is_in_reserved(result), "postcondition"); + return result; } HeapWord* SerialHeap::attempt_allocation(size_t size, diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index e86eac58515..b89edb33307 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -229,10 +229,6 @@ public: void save_marks(); private: - // Return true if an allocation should be attempted in the older generation - // if it fails in the younger generation. Return false, otherwise. - bool should_try_older_generation_allocation(size_t word_size) const; - // Try to allocate space by expanding the heap. HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab); diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.hpp index 892fc5bb86c..1225005aff4 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.hpp @@ -126,6 +126,8 @@ public: // Allocate and returns a block of the requested size, or returns "null". // Assumes the caller has done any necessary locking. inline HeapWord* allocate(size_t word_size); + // Multi-threaded version. + inline HeapWord* par_allocate(size_t word_size); // Expand the old-gen then invoke allocate above. HeapWord* expand_and_allocate(size_t size); diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp index d88b2566299..1c82566bdf4 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp @@ -57,4 +57,12 @@ HeapWord* TenuredGeneration::allocate(size_t word_size) { return res; } +HeapWord* TenuredGeneration::par_allocate(size_t word_size) { + HeapWord* res = _the_space->par_allocate(word_size); + if (res != nullptr) { + _bts->update_for_block(res, res + word_size); + } + return res; +} + #endif // SHARE_GC_SERIAL_TENUREDGENERATION_INLINE_HPP From b0f98df75aee1e94a8c4b3eb8d0b1f4e715011ae Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 14 Aug 2025 15:20:47 +0000 Subject: [PATCH 040/112] 8365416: java.desktop no longer needs preview feature access Reviewed-by: alanb, jpai --- src/java.base/share/classes/module-info.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 43d148a3428..92ed53d2176 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -154,7 +154,6 @@ module java.base { // module declaration be annotated with jdk.internal.javac.ParticipatesInPreview exports jdk.internal.javac to java.compiler, - java.desktop, // for ScopedValue jdk.compiler, jdk.incubator.vector, // participates in preview features jdk.jartool, // participates in preview features From 26ccb3cef17a7a2a4b09af1e1e29b96d54a418aa Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Thu, 14 Aug 2025 16:59:05 +0000 Subject: [PATCH 041/112] 8362530: VM crash with -XX:+PrintTieredEvents when collecting AOT profiling Reviewed-by: chagedorn, kvn --- .../share/compiler/compilationPolicy.cpp | 117 ++++++++++-------- .../share/compiler/compilationPolicy.hpp | 5 +- .../runtime/cds/appcds/aotFlags/AOTFlags.java | 10 ++ 3 files changed, 75 insertions(+), 57 deletions(-) diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index fd1357398f5..9c49d941bbc 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -404,7 +404,7 @@ double CompilationPolicy::threshold_scale(CompLevel level, int feedback_k) { return 1; } -void CompilationPolicy::print_counters(const char* prefix, Method* m) { +void CompilationPolicy::print_counters_on(outputStream* st, const char* prefix, Method* m) { int invocation_count = m->invocation_count(); int backedge_count = m->backedge_count(); MethodData* mdh = m->method_data(); @@ -416,133 +416,140 @@ void CompilationPolicy::print_counters(const char* prefix, Method* m) { mdo_invocations_start = mdh->invocation_count_start(); mdo_backedges_start = mdh->backedge_count_start(); } - tty->print(" %stotal=%d,%d %smdo=%d(%d),%d(%d)", prefix, - invocation_count, backedge_count, prefix, - mdo_invocations, mdo_invocations_start, - mdo_backedges, mdo_backedges_start); - tty->print(" %smax levels=%d,%d", prefix, - m->highest_comp_level(), m->highest_osr_comp_level()); + st->print(" %stotal=%d,%d %smdo=%d(%d),%d(%d)", prefix, + invocation_count, backedge_count, prefix, + mdo_invocations, mdo_invocations_start, + mdo_backedges, mdo_backedges_start); + st->print(" %smax levels=%d,%d", prefix, m->highest_comp_level(), m->highest_osr_comp_level()); } -void CompilationPolicy::print_training_data(const char* prefix, Method* method) { +void CompilationPolicy::print_training_data_on(outputStream* st, const char* prefix, Method* method) { methodHandle m(Thread::current(), method); - tty->print(" %smtd: ", prefix); + st->print(" %smtd: ", prefix); MethodTrainingData* mtd = MethodTrainingData::find(m); if (mtd == nullptr) { - tty->print("null"); + st->print("null"); } else { MethodData* md = mtd->final_profile(); - tty->print("mdo="); + st->print("mdo="); if (md == nullptr) { - tty->print("null"); + st->print("null"); } else { int mdo_invocations = md->invocation_count(); int mdo_backedges = md->backedge_count(); int mdo_invocations_start = md->invocation_count_start(); int mdo_backedges_start = md->backedge_count_start(); - tty->print("%d(%d), %d(%d)", mdo_invocations, mdo_invocations_start, mdo_backedges, mdo_backedges_start); + st->print("%d(%d), %d(%d)", mdo_invocations, mdo_invocations_start, mdo_backedges, mdo_backedges_start); } CompileTrainingData* ctd = mtd->last_toplevel_compile(CompLevel_full_optimization); - tty->print(", deps="); + st->print(", deps="); if (ctd == nullptr) { - tty->print("null"); + st->print("null"); } else { - tty->print("%d", ctd->init_deps_left()); + st->print("%d", ctd->init_deps_left()); } } } // Print an event. -void CompilationPolicy::print_event(EventType type, Method* m, Method* im, int bci, CompLevel level) { +void CompilationPolicy::print_event_on(outputStream *st, EventType type, Method* m, Method* im, int bci, CompLevel level) { bool inlinee_event = m != im; - ttyLocker tty_lock; - tty->print("%lf: [", os::elapsedTime()); + st->print("%lf: [", os::elapsedTime()); switch(type) { case CALL: - tty->print("call"); + st->print("call"); break; case LOOP: - tty->print("loop"); + st->print("loop"); break; case COMPILE: - tty->print("compile"); + st->print("compile"); break; case FORCE_COMPILE: - tty->print("force-compile"); + st->print("force-compile"); break; case REMOVE_FROM_QUEUE: - tty->print("remove-from-queue"); + st->print("remove-from-queue"); break; case UPDATE_IN_QUEUE: - tty->print("update-in-queue"); + st->print("update-in-queue"); break; case REPROFILE: - tty->print("reprofile"); + st->print("reprofile"); break; case MAKE_NOT_ENTRANT: - tty->print("make-not-entrant"); + st->print("make-not-entrant"); break; default: - tty->print("unknown"); + st->print("unknown"); } - tty->print(" level=%d ", level); + st->print(" level=%d ", level); ResourceMark rm; char *method_name = m->name_and_sig_as_C_string(); - tty->print("[%s", method_name); + st->print("[%s", method_name); if (inlinee_event) { char *inlinee_name = im->name_and_sig_as_C_string(); - tty->print(" [%s]] ", inlinee_name); + st->print(" [%s]] ", inlinee_name); } - else tty->print("] "); - tty->print("@%d queues=%d,%d", bci, CompileBroker::queue_size(CompLevel_full_profile), - CompileBroker::queue_size(CompLevel_full_optimization)); + else st->print("] "); + st->print("@%d queues=%d,%d", bci, CompileBroker::queue_size(CompLevel_full_profile), + CompileBroker::queue_size(CompLevel_full_optimization)); - tty->print(" rate="); - if (m->prev_time() == 0) tty->print("n/a"); - else tty->print("%f", m->rate()); + st->print(" rate="); + if (m->prev_time() == 0) st->print("n/a"); + else st->print("%f", m->rate()); - tty->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback), - threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback)); + st->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback), + threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback)); if (type != COMPILE) { - print_counters("", m); + print_counters_on(st, "", m); if (inlinee_event) { - print_counters("inlinee ", im); + print_counters_on(st, "inlinee ", im); } - tty->print(" compilable="); + st->print(" compilable="); bool need_comma = false; if (!m->is_not_compilable(CompLevel_full_profile)) { - tty->print("c1"); + st->print("c1"); need_comma = true; } if (!m->is_not_osr_compilable(CompLevel_full_profile)) { - if (need_comma) tty->print(","); - tty->print("c1-osr"); + if (need_comma) st->print(","); + st->print("c1-osr"); need_comma = true; } if (!m->is_not_compilable(CompLevel_full_optimization)) { - if (need_comma) tty->print(","); - tty->print("c2"); + if (need_comma) st->print(","); + st->print("c2"); need_comma = true; } if (!m->is_not_osr_compilable(CompLevel_full_optimization)) { - if (need_comma) tty->print(","); - tty->print("c2-osr"); + if (need_comma) st->print(","); + st->print("c2-osr"); } - tty->print(" status="); + st->print(" status="); if (m->queued_for_compilation()) { - tty->print("in-queue"); - } else tty->print("idle"); - print_training_data("", m); + st->print("in-queue"); + } else st->print("idle"); + + print_training_data_on(st, "", m); if (inlinee_event) { - print_training_data("inlinee ", im); + print_training_data_on(st, "inlinee ", im); } } - tty->print_cr("]"); + st->print_cr("]"); + +} + +void CompilationPolicy::print_event(EventType type, Method* m, Method* im, int bci, CompLevel level) { + stringStream s; + print_event_on(&s, type, m, im, bci, level); + ResourceMark rm; + tty->print("%s", s.as_string()); } void CompilationPolicy::initialize() { diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index 75374a2f830..f4a7c4c249b 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -287,8 +287,8 @@ class CompilationPolicy : AllStatic { // loop_event checks if a method should be OSR compiled at a different // level. static CompLevel loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD); - static void print_counters(const char* prefix, Method* m); - static void print_training_data(const char* prefix, Method* method); + 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); // 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()). @@ -318,6 +318,7 @@ class CompilationPolicy : AllStatic { static void set_c2_count(int x) { _c2_count = x; } enum EventType { CALL, LOOP, COMPILE, FORCE_COMPILE, FORCE_RECOMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT }; + static void print_event_on(outputStream *st, EventType type, Method* m, Method* im, int bci, CompLevel level); static void print_event(EventType type, Method* m, Method* im, int bci, CompLevel level); // Check if the method can be compiled, change level if necessary static void compile(const methodHandle& mh, int bci, CompLevel level, TRAPS); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java index 0d03313bec2..6db81351e6b 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java @@ -257,6 +257,16 @@ public class AOTFlags { out.shouldContain("AOTCache creation is complete: hello.aot"); out.shouldMatch("Picked up JAVA_TOOL_OPTIONS:.* -Dmy.prop=My' 'string' '-Xshare:off' 'here"); out.shouldHaveExitValue(0); + + // Training run with -XX:+PrintTieredEvents (see JDK-8362530). + printTestCase("Training run with -XX:+PrintTieredEvents"); + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTMode=record", + "-XX:+PrintTieredEvents", + "-XX:AOTConfiguration=" + aotConfigFile, + "-cp", appJar, helloClass); + out = CDSTestUtils.executeAndLog(pb, "train-with-tiered-events"); + out.shouldHaveExitValue(0); } static void negativeTests() throws Exception { From ba231052319676ece5105253b58efa4e906feab4 Mon Sep 17 00:00:00 2001 From: David Beaumont Date: Thu, 14 Aug 2025 17:02:05 +0000 Subject: [PATCH 042/112] 8365048: idea.sh script does not correctly detect/handle git worktrees Reviewed-by: shade, vyazici, erikj, mcimadamore, ihse --- bin/idea.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/idea.sh b/bin/idea.sh index eb37964f396..a184884b61a 100644 --- a/bin/idea.sh +++ b/bin/idea.sh @@ -125,7 +125,8 @@ if [ -d "$TOPLEVEL_DIR/.hg" ] ; then VCS_TYPE="hg4idea" fi -if [ -d "$TOPLEVEL_DIR/.git" ] ; then +# Git worktrees use a '.git' file rather than directory, so test both. +if [ -d "$TOPLEVEL_DIR/.git" -o -f "$TOPLEVEL_DIR/.git" ] ; then VCS_TYPE="Git" fi From dccca0fb7a892d31179b70fa861b8b3cdde54e84 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Thu, 14 Aug 2025 19:58:54 +0000 Subject: [PATCH 043/112] 8365572: Shenandoah: Remove unused thread local _paced_time field Reviewed-by: shade --- .../gc/shenandoah/shenandoahThreadLocalData.cpp | 1 - .../gc/shenandoah/shenandoahThreadLocalData.hpp | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp index dd500462d0f..ace5ab5e69a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp @@ -37,7 +37,6 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() : _card_table(nullptr), _gclab(nullptr), _gclab_size(0), - _paced_time(0), _plab(nullptr), _plab_desired_size(0), _plab_actual_size(0), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index 098e20a72ec..37d935d1f78 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -58,8 +58,6 @@ private: PLAB* _gclab; size_t _gclab_size; - double _paced_time; - // Thread-local allocation buffer only used in generational mode. // Used both by mutator threads and by GC worker threads // for evacuations within the old generation and @@ -237,18 +235,6 @@ public: return data(thread)->_plab_actual_size; } - static void add_paced_time(Thread* thread, double v) { - data(thread)->_paced_time += v; - } - - static double paced_time(Thread* thread) { - return data(thread)->_paced_time; - } - - static void reset_paced_time(Thread* thread) { - data(thread)->_paced_time = 0; - } - // Evacuation OOM handling static bool is_oom_during_evac(Thread* thread) { return data(thread)->_oom_during_evac; From c5cbcac828e1c7aa845cf16e68f6306ae49e050c Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 14 Aug 2025 20:27:08 +0000 Subject: [PATCH 044/112] 8361730: The CodeBuilder.trying(BlockCodeBuilder,CatchBuilder) method generates corrupted bytecode in certain cases Reviewed-by: asotona --- .../share/classes/java/lang/classfile/CodeBuilder.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java index e640700c2e9..fe82d5d0bf0 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java @@ -175,6 +175,11 @@ public sealed interface CodeBuilder * A builder for blocks of code. Its {@link #startLabel()} and {@link * #endLabel()} do not enclose the entire method body, but from the start to * the end of the block. + *

+ * The location where a block of code merges back to its parent block, as + * represented by the {@link #breakLabel()}, is expected to be reachable, + * either from this block or the parent block. The built code may be + * malformed if there is no executable code at that location. * * @since 24 */ From 8c363b3e3e5c1273a5e9b3393ed09a31b0647a21 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 14 Aug 2025 21:41:14 +0000 Subject: [PATCH 045/112] 8364319: Move java.lang.constant.AsTypeMethodHandleDesc to jdk.internal Reviewed-by: redestad --- .../java/lang/constant/ConstantDescs.java | 4 ---- .../java/lang/constant/MethodHandleDesc.java | 3 ++- .../constant/AsTypeMethodHandleDesc.java | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 10 deletions(-) rename src/java.base/share/classes/{java/lang => jdk/internal}/constant/AsTypeMethodHandleDesc.java (75%) diff --git a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java index c976342033f..0ef45ec34bb 100644 --- a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java +++ b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java @@ -44,7 +44,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static java.lang.constant.DirectMethodHandleDesc.*; import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; /** @@ -338,9 +337,6 @@ public final class ConstantDescs { */ public static final MethodTypeDesc MTD_void = MethodTypeDesc.of(CD_void); - static final DirectMethodHandleDesc MHD_METHODHANDLE_ASTYPE - = MethodHandleDesc.ofMethod(Kind.VIRTUAL, CD_MethodHandle, "asType", - MethodTypeDesc.of(CD_MethodHandle, CD_MethodType)); /** * Returns a {@link MethodHandleDesc} corresponding to a bootstrap method for * an {@code invokedynamic} callsite, which is a static method whose leading diff --git a/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java b/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java index 6a3541bf5e9..18786481fa6 100644 --- a/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java +++ b/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -28,6 +28,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import jdk.internal.constant.AsTypeMethodHandleDesc; import jdk.internal.constant.DirectMethodHandleDescImpl; import static java.lang.constant.ConstantDescs.CD_void; diff --git a/src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java b/src/java.base/share/classes/jdk/internal/constant/AsTypeMethodHandleDesc.java similarity index 75% rename from src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java rename to src/java.base/share/classes/jdk/internal/constant/AsTypeMethodHandleDesc.java index 32818a9bc00..8b250c0fff8 100644 --- a/src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java +++ b/src/java.base/share/classes/jdk/internal/constant/AsTypeMethodHandleDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -22,8 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; +import java.lang.constant.ConstantDescs; +import java.lang.constant.DirectMethodHandleDesc; +import java.lang.constant.DynamicConstantDesc; +import java.lang.constant.MethodHandleDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -37,15 +42,19 @@ import static java.util.Objects.requireNonNull; * {@link MethodHandle} constant that performs a {@link MethodHandle#asType(MethodType)} * adaptation on another {@link MethodHandle}. */ -final class AsTypeMethodHandleDesc extends DynamicConstantDesc +public final class AsTypeMethodHandleDesc extends DynamicConstantDesc implements MethodHandleDesc { + private static final DirectMethodHandleDesc MHD_METHODHANDLE_ASTYPE + = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.VIRTUAL, CD_MethodHandle, "asType", + MethodTypeDesc.of(CD_MethodHandle, ConstantDescs.CD_MethodType)); + private final MethodHandleDesc underlying; private final MethodTypeDesc type; - AsTypeMethodHandleDesc(MethodHandleDesc underlying, MethodTypeDesc type) { + public AsTypeMethodHandleDesc(MethodHandleDesc underlying, MethodTypeDesc type) { super(BSM_INVOKE, ConstantDescs.DEFAULT_NAME, CD_MethodHandle, - ConstantDescs.MHD_METHODHANDLE_ASTYPE, underlying, type); + MHD_METHODHANDLE_ASTYPE, underlying, type); this.underlying = requireNonNull(underlying); this.type = requireNonNull(type); } From a65f20022080e627da4782b9b643912a9dd69335 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Thu, 14 Aug 2025 23:59:34 +0000 Subject: [PATCH 046/112] 8365512: Replace -Xcomp with -Xmixed for AOT assembly phase Reviewed-by: shade --- src/hotspot/share/cds/cdsConfig.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 85c40df2606..51899490a12 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -643,8 +643,17 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla } if (is_dumping_static_archive()) { - if (is_dumping_preimage_static_archive() || is_dumping_final_static_archive()) { - // Don't tweak execution mode + if (is_dumping_preimage_static_archive()) { + // Don't tweak execution mode during AOT training run + } else if (is_dumping_final_static_archive()) { + if (Arguments::mode() == Arguments::_comp) { + // AOT assembly phase submits the non-blocking compilation requests + // for methods collected during training run, then waits for all compilations + // to complete. With -Xcomp, we block for each compilation request, which is + // counter-productive. Switching back to mixed mode improves testing time + // with AOT and -Xcomp. + Arguments::set_mode_flags(Arguments::_mixed); + } } else if (!mode_flag_cmd_line) { // By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive. // From 44b19c01acdfff07a4f017466be3f03fae6013c6 Mon Sep 17 00:00:00 2001 From: David Beaumont Date: Fri, 15 Aug 2025 02:53:42 +0000 Subject: [PATCH 047/112] 8365532: java/lang/module/ModuleReader/ModuleReaderTest.testImage fails Reviewed-by: alanb --- .../share/classes/jdk/internal/module/SystemModuleFinders.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java index 09ad3dc456d..39f433d4041 100644 --- a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java +++ b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java @@ -430,6 +430,7 @@ public final class SystemModuleFinders { @Override public Optional find(String name) throws IOException { + Objects.requireNonNull(name); String resourcePath = "/" + module + "/" + name; if (containsResource(resourcePath)) { URI u = JNUA.create("jrt", resourcePath); From 6fb6f3d39b321e2a1c1fa2cef2c19222a6dcf7b9 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Fri, 15 Aug 2025 04:25:37 +0000 Subject: [PATCH 048/112] 8361638: java.lang.classfile.CodeBuilder.CatchBuilder should not throw IllegalArgumentException for representable exception handlers Reviewed-by: asotona --- .../java/lang/classfile/CodeBuilder.java | 28 +++++++++++---- .../classfile/instruction/ExceptionCatch.java | 6 ++-- .../classfile/impl/CatchBuilderImpl.java | 35 ++++++++++--------- .../jdk/classfile/BuilderTryCatchTest.java | 34 ++++++++++++++---- 4 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java index fe82d5d0bf0..c1070bbcc77 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java @@ -330,6 +330,11 @@ public sealed interface CodeBuilder /** * A builder to add catch blocks. + *

+ * The order of catch blocks is significant. When an exception is thrown + * by the try block, the first catch block whose exception type is {@linkplain + * Class#isAssignableFrom(Class) the same class as or a superclass of} the + * class of exception thrown is branched to (JVMS {@jvms 2.10}). * * @see #trying * @see ExceptionCatch @@ -348,13 +353,16 @@ public sealed interface CodeBuilder * If the type of exception is {@code null} then the catch block catches * all exceptions. * + * @apiNote + * If the type of exception to catch is already handled by previous + * catch blocks, this block will never be executed. + * * @param exceptionType the type of exception to catch, may be {@code null} * @param catchHandler handler that receives a {@link BlockCodeBuilder} to * generate the body of the catch block * @return this builder - * @throws IllegalArgumentException if an existing catch block catches - * an exception of the given type or {@code exceptionType} - * represents a primitive type + * @throws IllegalArgumentException if {@code exceptionType} represents + * a primitive type * @see #catchingMulti * @see #catchingAll */ @@ -372,12 +380,16 @@ public sealed interface CodeBuilder * If list of exception types is empty then the catch block catches all * exceptions. * + * @apiNote + * If every type of exception to catch is already handled by previous + * catch blocks, this block will never be executed. + * * @param exceptionTypes the types of exception to catch * @param catchHandler handler that receives a {@link BlockCodeBuilder} * to generate the body of the catch block * @return this builder - * @throws IllegalArgumentException if an existing catch block catches - * one or more exceptions of the given types + * @throws IllegalArgumentException if any exception type represents a + * primitive type * @see #catching * @see #catchingAll */ @@ -392,10 +404,12 @@ public sealed interface CodeBuilder * The caught exception will be on top of the operand stack when the * catch block is entered. * + * @apiNote + * Since this block intercepts all exceptions, all subsequent catch + * blocks will never be executed. + * * @param catchAllHandler handler that receives a {@link BlockCodeBuilder} * to generate the body of the catch block - * @throws IllegalArgumentException if an existing catch block catches - * all exceptions * @see #catching * @see #catchingMulti */ diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java index e924ca718e7..b0e5af2941e 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java @@ -39,8 +39,10 @@ import jdk.internal.classfile.impl.AbstractPseudoInstruction; * A pseudo-instruction modeling an entry in the {@code exception_table} array * of a {@link CodeAttribute Code} attribute. Catch (JVMS {@jvms 3.12}) and * finally (JVMS {@jvms 3.14}) blocks in Java source code compile to exception - * table entries. Delivered as a {@link CodeElement} when traversing the - * contents of a {@link CodeModel}. + * table entries. The order of exception table entries is significant: when an + * exception is thrown in a method, execution branches to the first matching + * exception handler if such a handler exists (JVMS {@jvms 2.10}). Delivered as + * a {@link CodeElement} when traversing the contents of a {@link CodeModel}. *

* An exception table entry is composite: * {@snippet lang=text : diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java index d520753326e..89d43135551 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,9 @@ package jdk.internal.classfile.impl; import java.lang.classfile.CodeBuilder; import java.lang.classfile.Label; import java.lang.classfile.Opcode; +import java.lang.classfile.constantpool.ClassEntry; import java.lang.constant.ClassDesc; -import java.lang.constant.ConstantDesc; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -39,14 +40,12 @@ public final class CatchBuilderImpl implements CodeBuilder.CatchBuilder { final CodeBuilder b; final BlockCodeBuilderImpl tryBlock; final Label tryCatchEnd; - final Set catchTypes; BlockCodeBuilderImpl catchBlock; public CatchBuilderImpl(CodeBuilder b, BlockCodeBuilderImpl tryBlock, Label tryCatchEnd) { this.b = b; this.tryBlock = tryBlock; this.tryCatchEnd = tryCatchEnd; - this.catchTypes = new HashSet<>(); } @Override @@ -59,18 +58,24 @@ public final class CatchBuilderImpl implements CodeBuilder.CatchBuilder { Objects.requireNonNull(exceptionTypes); Objects.requireNonNull(catchHandler); + // nullable list of CP entries - null means catching all (0) + List entries = new ArrayList<>(Math.max(1, exceptionTypes.size())); + if (exceptionTypes.isEmpty()) { + entries.add(null); + } else { + for (var exceptionType : exceptionTypes) { + var entry = b.constantPool().classEntry(exceptionType); // throws IAE + entries.add(entry); + } + } + // End validation + if (catchBlock == null) { if (tryBlock.reachable()) { b.branch(Opcode.GOTO, tryCatchEnd); } } - for (var exceptionType : exceptionTypes) { - if (!catchTypes.add(exceptionType)) { - throw new IllegalArgumentException("Existing catch block catches exception of type: " + exceptionType); - } - } - // Finish prior catch block if (catchBlock != null) { catchBlock.end(); @@ -82,13 +87,9 @@ public final class CatchBuilderImpl implements CodeBuilder.CatchBuilder { catchBlock = new BlockCodeBuilderImpl(b, tryCatchEnd); Label tryStart = tryBlock.startLabel(); Label tryEnd = tryBlock.endLabel(); - if (exceptionTypes.isEmpty()) { - catchBlock.exceptionCatchAll(tryStart, tryEnd, catchBlock.startLabel()); - } - else { - for (var exceptionType : exceptionTypes) { - catchBlock.exceptionCatch(tryStart, tryEnd, catchBlock.startLabel(), exceptionType); - } + for (var entry : entries) { + // This accepts null for catching all + catchBlock.exceptionCatch(tryStart, tryEnd, catchBlock.startLabel(), entry); } catchBlock.start(); catchHandler.accept(catchBlock); diff --git a/test/jdk/jdk/classfile/BuilderTryCatchTest.java b/test/jdk/jdk/classfile/BuilderTryCatchTest.java index 3de12559163..0a966e2a655 100644 --- a/test/jdk/jdk/classfile/BuilderTryCatchTest.java +++ b/test/jdk/jdk/classfile/BuilderTryCatchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* * @test + * @bug 8361638 * @summary Testing ClassFile builder blocks. * @run junit BuilderTryCatchTest */ @@ -37,6 +38,7 @@ import java.lang.classfile.instruction.ExceptionCatch; import static java.lang.classfile.ClassFile.ACC_PUBLIC; import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.*; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -47,20 +49,40 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import static java.lang.constant.ConstantDescs.CD_Double; -import static java.lang.constant.ConstantDescs.CD_Integer; -import static java.lang.constant.ConstantDescs.CD_Object; -import static java.lang.constant.ConstantDescs.CD_String; - class BuilderTryCatchTest { static final ClassDesc CD_IOOBE = IndexOutOfBoundsException.class.describeConstable().get(); static final ClassDesc CD_NPE = NullPointerException.class.describeConstable().get(); static final MethodTypeDesc MTD_String = MethodType.methodType(String.class).describeConstable().get(); + @Test + void testExceptionalContracts() throws Throwable { + generateTryCatchMethod(catchBuilder -> { + Consumer handler = tb -> tb.pop().aconst_null().areturn(); + assertThrows(NullPointerException.class, () -> catchBuilder.catching(CD_NPE, null)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingMulti(null, handler)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingMulti(List.of(), null)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingMulti(Collections.singletonList(null), null)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingAll(null)); + catchBuilder.catchingMulti(List.of(CD_IOOBE, CD_NPE), tb -> { + tb.invokevirtual(CD_Object, "toString", MTD_String); + tb.astore(1); + }); + catchBuilder.catchingAll(tb -> tb.pop().loadConstant("all").areturn()); + assertThrows(IllegalArgumentException.class, () -> catchBuilder.catching(CD_int, handler)); + assertDoesNotThrow(() -> catchBuilder.catching(CD_NPE, handler)); + assertDoesNotThrow(() -> catchBuilder.catching(null, handler)); + assertDoesNotThrow(() -> catchBuilder.catchingMulti(List.of(), handler)); + assertDoesNotThrow(() -> catchBuilder.catchingMulti(List.of(CD_Exception, CD_IOOBE), handler)); + assertThrows(IllegalArgumentException.class, () -> catchBuilder.catchingMulti(List.of(CD_long, CD_Throwable), handler)); + assertDoesNotThrow(() -> catchBuilder.catchingAll(handler)); + }); + } + @Test void testTryCatchCatchAll() throws Throwable { byte[] bytes = generateTryCatchMethod(catchBuilder -> { From e3aeebec1798b9adbb02e11f285951d4275c52e8 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Fri, 15 Aug 2025 07:35:52 +0000 Subject: [PATCH 049/112] 8365468: EagerJVMCI should only apply to the CompilerBroker JVMCI runtime Reviewed-by: never --- .../share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java index 492f20f1b59..c35f216e624 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java @@ -186,7 +186,10 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime { // Can only do eager initialization of the JVMCI compiler // once the singleton instance is available. if (result.config.getFlag("EagerJVMCI", Boolean.class)) { - result.getCompiler(); + // EagerJVMCI only applies to JVMCI when used by the CompileBroker. + if (result.getCompilerToVM().isCompilerThread()) { + result.getCompiler(); + } } } // Ensures JVMCIRuntime::_HotSpotJVMCIRuntime_instance is From fa2eb616482250dff6a3b667798aec37114005a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 15 Aug 2025 08:55:11 +0000 Subject: [PATCH 050/112] 8365491: VSCode IDE: add basic configuration for the Oracle Java extension Reviewed-by: ihse, jlahoda --- make/ide/vscode/hotspot/template-workspace.jsonc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/make/ide/vscode/hotspot/template-workspace.jsonc b/make/ide/vscode/hotspot/template-workspace.jsonc index 30533c7ce84..c582c48047d 100644 --- a/make/ide/vscode/hotspot/template-workspace.jsonc +++ b/make/ide/vscode/hotspot/template-workspace.jsonc @@ -12,12 +12,17 @@ ], "extensions": { "recommendations": [ + "oracle.oracle-java", // {{INDEXER_EXTENSIONS}} ] }, "settings": { // {{INDEXER_SETTINGS}} + // Java extension + "jdk.project.jdkhome": "{{OUTPUTDIR}}/jdk", + "jdk.java.onSave.organizeImports": false, // prevents unnecessary changes + // Additional conventions "files.associations": { "*.gmk": "makefile" From 5856dc34c82de9f840be1dc28a9917224971491f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Fri, 15 Aug 2025 09:32:51 +0000 Subject: [PATCH 051/112] 8365199: Use a set instead of a list as the intermediary Klass* storage to reduce typeset processing Reviewed-by: egahlin --- .../checkpoint/objectSampleCheckpoint.cpp | 78 +++---- .../leakprofiler/sampling/objectSample.hpp | 13 +- .../recorder/checkpoint/jfrMetadataEvent.cpp | 5 +- .../recorder/checkpoint/jfrMetadataEvent.hpp | 4 +- .../recorder/checkpoint/types/jfrTypeSet.cpp | 13 +- .../checkpoint/types/jfrTypeSetUtils.cpp | 53 +++-- .../checkpoint/types/jfrTypeSetUtils.hpp | 74 +++---- .../recorder/service/jfrRecorderService.cpp | 7 +- .../share/jfr/support/jfrKlassUnloading.cpp | 15 +- src/hotspot/share/jfr/utilities/jfrSet.hpp | 197 +++++++++++++----- test/jdk/jdk/jfr/event/runtime/TestFlush.java | 2 - 11 files changed, 281 insertions(+), 180 deletions(-) diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 3d87e86e9bd..300786daf05 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -48,14 +48,6 @@ #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" -const unsigned int initial_size = 431; - -static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) { - return new JfrCHeapTraceIdSet(size); -} - -static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr; - class ThreadIdExclusiveAccess : public StackObj { private: static Semaphore _mutex_semaphore; @@ -66,19 +58,13 @@ class ThreadIdExclusiveAccess : public StackObj { Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1); -static bool has_thread_exited(traceid tid) { - assert(tid != 0, "invariant"); - if (unloaded_thread_id_set == nullptr) { - return false; - } - ThreadIdExclusiveAccess lock; - return unloaded_thread_id_set->contains(tid); -} +static const unsigned initial_set_size = 512; +static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr; static void add_to_unloaded_thread_set(traceid tid) { ThreadIdExclusiveAccess lock; if (unloaded_thread_id_set == nullptr) { - unloaded_thread_id_set = c_heap_allocate_set(); + unloaded_thread_id_set = new (mtTracing) JfrCHeapTraceIdSet(initial_set_size); } unloaded_thread_id_set->add(tid); } @@ -193,12 +179,6 @@ inline void BlobCache::on_unlink(BlobEntry* entry) const { assert(entry != nullptr, "invariant"); } -static JfrResourceAreaTraceIdSet* id_set = nullptr; - -static void prepare_for_resolution() { - id_set = new JfrResourceAreaTraceIdSet(initial_size); -} - static bool stack_trace_precondition(const ObjectSample* sample) { assert(sample != nullptr, "invariant"); return sample->has_stack_trace_id() && !sample->is_dead(); @@ -213,6 +193,8 @@ static void add_to_leakp_set(const ObjectSample* sample) { JfrTraceId::load_leakp(object->klass()); } +static JfrResourceAreaTraceIdSet* resolution_set = nullptr; + class StackTraceBlobInstaller { private: BlobCache _cache; @@ -220,8 +202,9 @@ class StackTraceBlobInstaller { const JfrStackTrace* resolve(const ObjectSample* sample) const; public: StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) { - prepare_for_resolution(); + resolution_set = new JfrResourceAreaTraceIdSet(initial_set_size); } + void sample_do(ObjectSample* sample) { if (stack_trace_precondition(sample)) { add_to_leakp_set(sample); @@ -314,8 +297,8 @@ static bool is_klass_unloaded(traceid klass_id) { static bool is_processed(traceid method_id) { assert(method_id != 0, "invariant"); - assert(id_set != nullptr, "invariant"); - return !id_set->add(method_id); + assert(resolution_set != nullptr, "invariant"); + return !resolution_set->add(method_id); } void ObjectSampleCheckpoint::add_to_leakp_set(const InstanceKlass* ik, traceid method_id) { @@ -356,7 +339,7 @@ static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { assert(sample->has_thread(), "invariant"); - if (sample->is_virtual_thread() || has_thread_exited(sample->thread_id())) { + if (sample->is_virtual_thread() || sample->thread_exited()) { write_blob(sample->thread(), writer); } } @@ -372,13 +355,13 @@ static inline bool should_write(const JfrStackTrace* stacktrace) { class LeakProfilerStackTraceWriter { private: JfrCheckpointWriter& _writer; - int _count; + unsigned _count; public: LeakProfilerStackTraceWriter(JfrCheckpointWriter& writer) : _writer(writer), _count(0) { assert(_stacktrace_id_set != nullptr, "invariant"); } - int count() const { return _count; } + unsigned count() const { return _count; } void operator()(const JfrStackTrace* stacktrace) { if (should_write(stacktrace)) { @@ -394,12 +377,10 @@ void ObjectSampleCheckpoint::write_stacktraces(Thread* thread) { JfrCheckpointWriter writer(thread); writer.write_type(TYPE_STACKTRACE); - const int64_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet - + writer.write_count(_stacktrace_id_set->size()); LeakProfilerStackTraceWriter lpstw(writer); JfrStackTraceRepository::iterate_leakprofiler(lpstw); assert(lpstw.count() == _stacktrace_id_set->size(), "invariant"); - writer.write_count(lpstw.count(), count_offset); } static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { @@ -422,6 +403,16 @@ static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer) write_type_set_blob(sample, writer); } +static void check_if_thread_exited(const ObjectSample* sample) { + assert(sample != nullptr, "invariant"); + if (sample->thread_exited() || unloaded_thread_id_set == nullptr) { + return; + } + if (unloaded_thread_id_set->contains(sample->thread_id())) { + sample->set_thread_exited(); + } +} + class BlobWriter { private: const ObjectSampler* _sampler; @@ -431,23 +422,36 @@ class BlobWriter { BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) : _sampler(sampler), _writer(writer), _last_sweep(last_sweep) {} void sample_do(ObjectSample* sample) { + check_if_thread_exited(sample); if (sample->is_alive_and_older_than(_last_sweep)) { write_blobs(sample, _writer); } } }; +static void delete_unloaded_thread_id_set() { + if (unloaded_thread_id_set != nullptr) { + delete unloaded_thread_id_set; + unloaded_thread_id_set = nullptr; + } +} + static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) { // sample set is predicated on time of last sweep const jlong last_sweep = emit_all ? max_jlong : ObjectSampler::last_sweep(); JfrCheckpointWriter writer(thread, false); BlobWriter cbw(sampler, writer, last_sweep); + ThreadIdExclusiveAccess lock; iterate_samples(cbw, true); + delete_unloaded_thread_id_set(); } -static inline unsigned int set_size() { - const unsigned int queue_size = static_cast(JfrOptionSet::old_object_queue_size()); - return queue_size > initial_size ? queue_size : initial_size; +static inline unsigned stacktrace_id_set_size() { + unsigned queue_size = static_cast(JfrOptionSet::old_object_queue_size()); + if (!is_power_of_2(queue_size)) { + queue_size = next_power_of_2(queue_size); + } + return queue_size > initial_set_size ? queue_size : initial_set_size; } void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { @@ -456,7 +460,9 @@ void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge assert(thread != nullptr, "invariant"); { ResourceMark rm(thread); - _stacktrace_id_set = new JfrResourceAreaTraceIdSet(set_size()); + const unsigned stacktrace_set_size = stacktrace_id_set_size(); + assert(is_power_of_2(stacktrace_set_size), "invariant"); + _stacktrace_id_set = new JfrResourceAreaTraceIdSet(stacktrace_set_size); write_sample_blobs(sampler, emit_all, thread); if (_stacktrace_id_set->is_nonempty()) { write_stacktraces(thread); diff --git a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp index 214de827d03..66ed9145c81 100644 --- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp +++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp @@ -59,6 +59,7 @@ class ObjectSample : public JfrCHeapObj { size_t _heap_used_at_last_gc; int _index; bool _virtual_thread; + mutable bool _thread_exited; void release_references() { _stacktrace.~JfrBlobHandle(); @@ -82,7 +83,8 @@ class ObjectSample : public JfrCHeapObj { _allocated(0), _heap_used_at_last_gc(0), _index(0), - _virtual_thread(false) {} + _virtual_thread(false), + _thread_exited(false) {} ObjectSample* next() const { return _next; @@ -225,6 +227,15 @@ class ObjectSample : public JfrCHeapObj { _virtual_thread = true; } + bool thread_exited() const { + return _thread_exited; + } + + void set_thread_exited() const { + assert(!_thread_exited, "invariant"); + _thread_exited = true; + } + const JfrBlobHandle& type_set() const { return _type_set; } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp index bf27fa59031..20e9c1e6798 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp @@ -64,11 +64,11 @@ static void write_metadata_blob(JfrChunkWriter& chunkwriter, JavaThread* thread) chunkwriter.write_unbuffered(data_address, length); } -void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { +size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { assert(chunkwriter.is_valid(), "invariant"); check_internal_types(); if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) { - return; + return 0; } JavaThread* const jt = JavaThread::current(); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); @@ -87,6 +87,7 @@ void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset); chunkwriter.set_last_metadata_offset(metadata_offset); last_metadata_id = metadata_id; + return 1; } void JfrMetadataEvent::update(jbyteArray metadata) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp index abadbfb0b13..1b5bd45c946 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ class JfrChunkWriter; // class JfrMetadataEvent : AllStatic { public: - static void write(JfrChunkWriter& writer); + static size_t write(JfrChunkWriter& writer); static void update(jbyteArray metadata); }; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 69f002138ec..375ab4d04e9 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -1048,19 +1048,15 @@ class MethodIteratorHost { private: MethodCallback _method_cb; KlassCallback _klass_cb; - KlassUsedPredicate _klass_used_predicate; - MethodUsedPredicate _method_used_predicate; MethodFlagPredicate _method_flag_predicate; public: MethodIteratorHost(JfrCheckpointWriter* writer) : _method_cb(writer, unloading(), false), _klass_cb(writer, unloading(), false), - _klass_used_predicate(current_epoch()), - _method_used_predicate(current_epoch()), _method_flag_predicate(current_epoch()) {} bool operator()(KlassPtr klass) { - if (_method_used_predicate(klass)) { + if (klass->is_instance_klass()) { const InstanceKlass* ik = InstanceKlass::cast(klass); while (ik != nullptr) { const int len = ik->methods()->length(); @@ -1075,7 +1071,7 @@ class MethodIteratorHost { ik = ik->previous_versions(); } } - return _klass_used_predicate(klass) ? _klass_cb(klass) : true; + return _klass_cb(klass); } int count() const { return _method_cb.count(); } @@ -1280,10 +1276,11 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer _class_unload = class_unload; _flushpoint = flushpoint; if (_artifacts == nullptr) { - _artifacts = new JfrArtifactSet(class_unload); + _artifacts = new JfrArtifactSet(class_unload, previous_epoch()); } else { - _artifacts->initialize(class_unload); + _artifacts->initialize(class_unload, previous_epoch()); } + assert(current_epoch() || _leakp_writer != nullptr, "invariant"); assert(_artifacts != nullptr, "invariant"); assert(!_artifacts->has_klass_entries(), "invariant"); } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp index d213ecd7d75..c60556927ad 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp @@ -29,18 +29,23 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" -JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_table(nullptr), - _klass_list(nullptr), - _total_count(0), - _class_unload(class_unload) { - initialize(class_unload); - assert(_klass_list != nullptr, "invariant"); +JfrArtifactSet::JfrArtifactSet(bool class_unload, bool previous_epoch) : _symbol_table(nullptr), + _klass_set(nullptr), + _klass_loader_set(nullptr), + _klass_loader_leakp_set(nullptr), + _total_count(0), + _class_unload(class_unload) { + initialize(class_unload, previous_epoch); + assert(!previous_epoch || _klass_loader_leakp_set != nullptr, "invariant"); + assert(_klass_loader_set != nullptr, "invariant"); + assert(_klass_set != nullptr, "invariant"); } -static const size_t initial_klass_list_size = 4096; -const int initial_klass_loader_set_size = 64; +static unsigned initial_klass_set_size = 4096; +static unsigned initial_klass_loader_set_size = 64; +static unsigned initial_klass_loader_leakp_set_size = 64; -void JfrArtifactSet::initialize(bool class_unload) { +void JfrArtifactSet::initialize(bool class_unload, bool previous_epoch) { _class_unload = class_unload; if (_symbol_table == nullptr) { _symbol_table = JfrSymbolTable::create(); @@ -50,9 +55,11 @@ void JfrArtifactSet::initialize(bool class_unload) { _symbol_table->set_class_unload(class_unload); _total_count = 0; // Resource allocations. Keep in this allocation order. - _klass_loader_leakp_set = new GrowableArray(initial_klass_loader_set_size); - _klass_loader_set = new GrowableArray(initial_klass_loader_set_size); - _klass_list = new GrowableArray(initial_klass_list_size); + if (previous_epoch) { + _klass_loader_leakp_set = new JfrKlassSet(initial_klass_loader_leakp_set_size); + } + _klass_loader_set = new JfrKlassSet(initial_klass_loader_set_size); + _klass_set = new JfrKlassSet(initial_klass_set_size); } void JfrArtifactSet::clear() { @@ -93,17 +100,12 @@ traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp) } bool JfrArtifactSet::has_klass_entries() const { - return _klass_list->is_nonempty(); + return _klass_set->is_nonempty(); } - -int JfrArtifactSet::entries() const { - return _klass_list->length(); -} - -static inline bool not_in_set(GrowableArray* set, const Klass* k) { +static inline bool not_in_set(JfrArtifactSet::JfrKlassSet* set, const Klass* k) { assert(set != nullptr, "invariant"); assert(k != nullptr, "invariant"); - return !JfrMutablePredicate::test(set, k); + return set->add(k); } bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) { @@ -116,16 +118,21 @@ bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) { void JfrArtifactSet::register_klass(const Klass* k) { assert(k != nullptr, "invariant"); assert(IS_SERIALIZED(k), "invariant"); - assert(_klass_list != nullptr, "invariant"); - _klass_list->append(k); + assert(_klass_set != nullptr, "invariant"); + _klass_set->add(k); } size_t JfrArtifactSet::total_count() const { + assert(_klass_set != nullptr, "invariant"); + initial_klass_set_size = MAX2(initial_klass_set_size, _klass_set->table_size()); + assert(_klass_loader_set != nullptr, "invariant"); + initial_klass_loader_set_size = MAX2(initial_klass_loader_set_size, _klass_loader_set->table_size()); return _total_count; } void JfrArtifactSet::increment_checkpoint_id() { assert(_symbol_table != nullptr, "invariant"); _symbol_table->increment_checkpoint_id(); + assert(_klass_loader_leakp_set != nullptr, "invariant"); + initial_klass_loader_leakp_set_size = MAX2(initial_klass_loader_leakp_set_size, _klass_loader_leakp_set->table_size()); } - diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp index 657aee9dc53..74200aef1f1 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp @@ -28,12 +28,10 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" #include "jfr/support/jfrSymbolTable.hpp" #include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrSet.hpp" #include "oops/klass.hpp" #include "oops/method.hpp" -template -class GrowableArray; - // Composite callback/functor building block template class CompositeFunctor { @@ -135,27 +133,6 @@ class SymbolPredicate { } }; -class KlassUsedPredicate { - bool _current_epoch; - public: - KlassUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} - bool operator()(const Klass* klass) { - return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass); - } -}; - -class MethodUsedPredicate { - bool _current_epoch; -public: - MethodUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} - bool operator()(const Klass* klass) { - if (!klass->is_instance_klass()) { - return false; - } - return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass); - } -}; - template class MethodFlagPredicate { bool _current_epoch; @@ -203,20 +180,46 @@ class LeakPredicate { * in the respective VM subsystems. */ class JfrArtifactSet : public JfrCHeapObj { + public: + class JfrArtifactSetConfig : public AllStatic { + public: + typedef const Klass* KEY_TYPE; + + constexpr static AnyObj::allocation_type alloc_type() { + return AnyObj::RESOURCE_AREA; + } + + constexpr static MemTag memory_tag() { + return mtInternal; + } + + // Knuth multiplicative hashing. + static uint32_t hash(const KEY_TYPE& k) { + const uint32_t v = static_cast(JfrTraceId::load_raw(k)); + return v * UINT32_C(2654435761); + } + + static bool cmp(const KEY_TYPE& lhs, const KEY_TYPE& rhs) { + return lhs == rhs; + } + }; + + typedef JfrSet JfrKlassSet; + private: JfrSymbolTable* _symbol_table; - GrowableArray* _klass_list; - GrowableArray* _klass_loader_set; - GrowableArray* _klass_loader_leakp_set; + JfrKlassSet* _klass_set; + JfrKlassSet* _klass_loader_set; + JfrKlassSet* _klass_loader_leakp_set; size_t _total_count; bool _class_unload; public: - JfrArtifactSet(bool class_unload); + JfrArtifactSet(bool class_unload, bool previous_epoch); ~JfrArtifactSet(); // caller needs ResourceMark - void initialize(bool class_unload); + void initialize(bool class_unload, bool previous_epoch); void clear(); traceid mark(uintptr_t hash, const Symbol* sym, bool leakp); @@ -231,7 +234,6 @@ class JfrArtifactSet : public JfrCHeapObj { const JfrSymbolTable::StringEntry* map_string(uintptr_t hash) const; bool has_klass_entries() const; - int entries() const; size_t total_count() const; void register_klass(const Klass* k); bool should_do_cld_klass(const Klass* k, bool leakp); @@ -254,19 +256,17 @@ class JfrArtifactSet : public JfrCHeapObj { template void iterate_klasses(Functor& functor) const { - if (iterate(functor, _klass_list)) { + if (iterate(functor, _klass_set)) { iterate(functor, _klass_loader_set); } } private: template - bool iterate(Functor& functor, GrowableArray* list) const { - assert(list != nullptr, "invariant"); - for (int i = 0; i < list->length(); ++i) { - if (!functor(list->at(i))) { - return false; - } + bool iterate(Functor& functor, JfrKlassSet* set) const { + assert(set != nullptr, "invariant"); + if (set->is_nonempty()) { + set->iterate(functor); } return true; } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index f0170bac460..7784148aee5 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -367,13 +367,14 @@ static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter class MetadataEvent : public StackObj { private: JfrChunkWriter& _cw; + size_t _elements; public: - MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {} + MetadataEvent(JfrChunkWriter& cw) : _cw(cw), _elements(0) {} bool process() { - JfrMetadataEvent::write(_cw); + _elements = JfrMetadataEvent::write(_cw); return true; } - size_t elements() const { return 1; } + size_t elements() const { return _elements; } }; typedef WriteContent WriteMetadata; diff --git a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp index ce5de54ed16..d136eeab53a 100644 --- a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp +++ b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp @@ -30,10 +30,10 @@ #include "runtime/mutexLocker.hpp" #include "utilities/macros.hpp" -static const int initial_size = 1009; +static const int initial_size = 1024; static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) { - return new JfrCHeapTraceIdSet(size); + return new (mtTracing) JfrCHeapTraceIdSet(size); } // Track the set of unloaded klasses during a chunk / epoch. @@ -68,18 +68,9 @@ static JfrCHeapTraceIdSet* get_unload_set_previous_epoch() { return get_unload_set(JfrTraceIdEpoch::previous()); } -static bool is_nonempty_set(u1 epoch) { - if (epoch == 0) { - return _unload_set_epoch_0 != nullptr && _unload_set_epoch_0->is_nonempty(); - } - return _unload_set_epoch_1 != nullptr && _unload_set_epoch_1->is_nonempty(); -} - void JfrKlassUnloading::clear() { assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - if (is_nonempty_set(JfrTraceIdEpoch::previous())) { - get_unload_set_previous_epoch()->clear(); - } + get_unload_set_previous_epoch()->clear(); } static void add_to_unloaded_klass_set(traceid klass_id) { diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index 40a5d73f370..b6458d3f9a4 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp @@ -25,15 +25,13 @@ #ifndef SHARE_JFR_UTILITIES_JFRSET_HPP #define SHARE_JFR_UTILITIES_JFRSET_HPP -#include "jfr/utilities/jfrAllocation.hpp" +#include "memory/allocation.hpp" #include "jfr/utilities/jfrTypes.hpp" -#include "utilities/resizableHashTable.hpp" -template -class ConfigTraceID : public AllStatic { +template +class JfrSetConfig : public AllStatic { public: - typedef AllocPolicy STORAGE; - typedef traceid TYPE; + typedef K KEY_TYPE; constexpr static AnyObj::allocation_type alloc_type() { return AllocType; @@ -44,80 +42,171 @@ class ConfigTraceID : public AllStatic { } // Knuth multiplicative hashing. - static uint32_t hash(const TYPE& id) { - const uint32_t v = static_cast(id); - return v * UINT32_C(2654435761); + static uint32_t hash(const KEY_TYPE& key) { + const uint32_t k = static_cast(key); + return k * UINT32_C(2654435761); } - static bool cmp(const TYPE& lhs, const TYPE& rhs) { + static bool cmp(const KEY_TYPE& lhs, const KEY_TYPE& rhs) { return lhs == rhs; } }; -constexpr static unsigned int MAX_TABLE_SIZE = 0x3fffffff; - template -class JfrSet : public CONFIG::STORAGE { - public: - typedef typename CONFIG::TYPE TYPE; - typedef ResizeableHashTable HashMap; +class JfrSetStorage : public AnyObj { + typedef typename CONFIG::KEY_TYPE K; + protected: + K* _table; + unsigned _table_size; + unsigned _elements; - constexpr static bool is_cheap() { - return CONFIG::alloc_type() == AnyObj::C_HEAP; + static K* alloc_table(unsigned table_size) { + K* table; + if (CONFIG::alloc_type() == C_HEAP) { + table = NEW_C_HEAP_ARRAY(K, table_size, CONFIG::memory_tag()); + } else { + table = NEW_RESOURCE_ARRAY(K, table_size); + } + memset(table, 0, table_size * sizeof(K)); + return table; } - JfrSet(unsigned int initial_size, unsigned int max_size = MAX_TABLE_SIZE) : - _map(is_cheap() ? new (CONFIG::memory_tag()) HashMap(initial_size, max_size) : new HashMap(initial_size, max_size)) {} + JfrSetStorage(unsigned table_size) : + _table(alloc_table(table_size)), + _table_size(table_size), + _elements(0) {} - ~JfrSet() { - if (is_cheap()) { - delete _map; + ~JfrSetStorage() { + if (CONFIG::alloc_type() == C_HEAP) { + FREE_C_HEAP_ARRAY(K, _table); } } - bool add(const TYPE& k) { - bool inserted; - _map->put_if_absent(k, &inserted); - return inserted; + public: + template + void iterate(Functor& functor) { + assert(is_nonempty(), "invariant"); + for (unsigned i = 0; i < _table_size; ++i) { + K k = _table[i]; + if (k != 0) { + functor(k); + } + } } - bool remove(const TYPE& k) { - return _map->remove(k); + unsigned table_size() const { + return _table_size; } - bool contains(const TYPE& k) const { - return _map->contains(k); - } - - bool is_empty() const { - return _map->number_of_entries() == 0; + unsigned size() const { + return _elements; } bool is_nonempty() const { - return !is_empty(); - } - - int size() const { - return _map->number_of_entries(); + return _elements > 0; } void clear() { - if (is_nonempty()) { - _map->unlink(this); - } - assert(is_empty(), "invariant"); + memset(_table, 0, _table_size * sizeof(K)); } - - // Callback for node deletion, used by clear(). - bool do_entry(const TYPE& k, const TYPE& v) { - return true; - } - - private: - HashMap* _map; }; -typedef JfrSet > JfrCHeapTraceIdSet; -typedef JfrSet > JfrResourceAreaTraceIdSet; +template +class JfrSet : public JfrSetStorage { + typedef typename CONFIG::KEY_TYPE K; + static_assert(sizeof(K) > 1, "invalid size of CONFIG::KEY_TYPE"); + private: + static const constexpr unsigned max_initial_size = 1 << 30; + unsigned _table_mask; + unsigned _resize_threshold; // 0.5 load factor + + uint32_t slot_idx(const uint32_t hash) const { + return hash & _table_mask; + } + + void resize() { + assert(this->_elements == _resize_threshold, "invariant"); + K* const old_table = this->_table; + assert(old_table != nullptr, "invariant"); + const unsigned old_table_size = this->table_size(); + guarantee(old_table_size <= max_initial_size, "overflow"); + this->_table_size = old_table_size << 1; + this->_table = JfrSetStorage::alloc_table(this->_table_size); + _table_mask = this->_table_size - 1; + _resize_threshold = old_table_size; + for (unsigned i = 0; i < old_table_size; ++i) { + const K k = old_table[i]; + if (k != 0) { + uint32_t idx = slot_idx(CONFIG::hash(k)); + do { + K v = this->_table[idx]; + if (v == 0) { + this->_table[idx] = k; + break; + } + idx = slot_idx(idx + 1); + } while (true); + } + } + if (CONFIG::alloc_type() == AnyObj::C_HEAP) { + FREE_C_HEAP_ARRAY(K, old_table); + } + assert(_table_mask + 1 == this->_table_size, "invariant"); + assert(_resize_threshold << 1 == this->_table_size, "invariant"); + } + + K* find_slot(K const& k) const { + uint32_t idx = slot_idx(CONFIG::hash(k)); + assert(idx < this->table_size(), "invariant"); + K* result = nullptr; + while (true) { + K v = this->_table[idx]; + if (v == 0) { + result = &this->_table[idx]; + break; + } + if (CONFIG::cmp(v, k)) { + result = reinterpret_cast(p2i(&this->_table[idx]) | 1); + break; + } + idx = slot_idx(idx + 1); + } + assert(result != nullptr, "invariant"); + return result; + } + + public: + JfrSet(unsigned size) : + JfrSetStorage(size), + _table_mask(size - 1), + _resize_threshold(size >> 1) { + assert(size >= 2, "invariant"); + assert(size % 2 == 0, "invariant"); + assert(size <= max_initial_size, "avoid overflow in resize"); + } + + bool contains(K const& k) const { + K* const slot = find_slot(k); + return p2i(slot) & 1; + } + + bool add(K const& k) { + K* const slot = find_slot(k); + if (p2i(slot) & 1) { + // Already exists. + return false; + } + assert(*slot == 0, "invariant"); + *slot = k; + if (++this->_elements == _resize_threshold) { + resize(); + } + assert(this->_elements < _resize_threshold, "invariant"); + return true; + } +}; + +typedef JfrSet > JfrCHeapTraceIdSet; +typedef JfrSet > JfrResourceAreaTraceIdSet; #endif // SHARE_JFR_UTILITIES_JFRSET_HPP diff --git a/test/jdk/jdk/jfr/event/runtime/TestFlush.java b/test/jdk/jdk/jfr/event/runtime/TestFlush.java index 5c450eab05f..fa570406f2a 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestFlush.java +++ b/test/jdk/jdk/jfr/event/runtime/TestFlush.java @@ -143,8 +143,6 @@ public class TestFlush { printFlushEvent(re); Asserts.assertTrue(re.getEventType().getName().contains("Flush"), "invalid Event type"); Asserts.assertGT((long) re.getValue("flushId"), 0L, "Invalid flush ID"); - Asserts.assertGT((long) re.getValue("elements"), 0L, "No elements"); - Asserts.assertGT((long) re.getValue("size"), 0L, "Empty size"); } private static void acknowledgeFlushEvent() { From b6d5f49b8dc2cb7c8e93d7885c2432a28d04e57e Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Fri, 15 Aug 2025 09:41:17 +0000 Subject: [PATCH 052/112] 8365296: Build failure with Clang due to -Wformat warning after JDK-8364611 Reviewed-by: ayang, mbaesken --- .../childSignalDisposition/exePrintSignalDisposition.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c index 8235cdc7410..8f18a32a0f9 100644 --- a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c +++ b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -72,11 +72,7 @@ int main(int argc, char** argv) { } else { printf("%p ", handler); } -#ifdef _AIX printf("%X\n", act.sa_flags); -#else - printf("%X %X\n", act.sa_flags, act.sa_mask); -#endif } return 0; From 059b49b9551ad52f211613a3da2ac0a79deb5ed4 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Fri, 15 Aug 2025 10:37:26 +0000 Subject: [PATCH 053/112] 8365244: Some test control variables are undocumented in doc/testing.md Reviewed-by: erikj --- doc/testing.html | 19 +++++++++++++------ doc/testing.md | 18 +++++++++++++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/doc/testing.html b/doc/testing.html index b9d1f4ed22f..f75e0da309e 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -72,11 +72,9 @@ id="toc-notes-for-specific-tests">Notes for Specific Tests

  • Non-US locale
  • PKCS11 Tests
  • -
  • ### Testing Ahead-of-time -Optimizations -
      +id="toc-testing-ahead-of-time-optimizations">Testing Ahead-of-time +Optimizations
    • Testing with alternative security providers
    • @@ -435,6 +433,9 @@ the diff between the specified revision and the repository tip.

      The report is stored in build/$BUILD/test-results/jcov-output/diff_coverage_report file.

      +

      AOT_JDK

      +

      See Testing +Ahead-of-time optimizations.

      JTReg keywords

      JOBS

      The test concurrency (-concurrency).

      @@ -556,6 +557,12 @@ each fork. Same as specifying -wi <num>.

      same values as -rff, i.e., text, csv, scsv, json, or latex.

      +

      TEST_JDK

      +

      The path to the JDK that will be used to run the benchmarks.

      +

      Defaults to build/<CONF-NAME>/jdk.

      +

      BENCHMARKS_JAR

      +

      The path to the JAR containing the benchmarks.

      +

      Defaults to test/micro/benchmarks.jar.

      VM_OPTIONS

      Additional VM arguments to provide to forked off VMs. Same as -jvmArgs <args>

      @@ -601,8 +608,8 @@ element of the appropriate @Artifact class. (See JTREG="JAVA_OPTIONS=-Djdk.test.lib.artifacts.nsslib-linux_aarch64=/path/to/NSS-libs"

      For more notes about the PKCS11 tests, please refer to test/jdk/sun/security/pkcs11/README.

      -

      ### Testing Ahead-of-time -Optimizations

      +

      Testing Ahead-of-time +Optimizations

      One way to improve test coverage of ahead-of-time (AOT) optimizations in the JDK is to run existing jtreg test cases in a special "AOT_JDK" mode. Example:

      diff --git a/doc/testing.md b/doc/testing.md index bb56c05c295..525b85a8438 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -367,6 +367,10 @@ between the specified revision and the repository tip. The report is stored in `build/$BUILD/test-results/jcov-output/diff_coverage_report` file. +#### AOT_JDK + +See [Testing Ahead-of-time optimizations](#testing-ahead-of-time-optimizations). + ### JTReg keywords #### JOBS @@ -545,6 +549,18 @@ Amount of time to spend in each warmup iteration. Same as specifying `-w Specify to have the test run save a log of the values. Accepts the same values as `-rff`, i.e., `text`, `csv`, `scsv`, `json`, or `latex`. +#### TEST_JDK + +The path to the JDK that will be used to run the benchmarks. + +Defaults to `build//jdk`. + +#### BENCHMARKS_JAR + +The path to the JAR containing the benchmarks. + +Defaults to `test/micro/benchmarks.jar`. + #### VM_OPTIONS Additional VM arguments to provide to forked off VMs. Same as `-jvmArgs ` @@ -612,7 +628,7 @@ For more notes about the PKCS11 tests, please refer to test/jdk/sun/security/pkcs11/README. ### Testing Ahead-of-time Optimizations -------------------------------------------------------------------------------- + One way to improve test coverage of ahead-of-time (AOT) optimizations in the JDK is to run existing jtreg test cases in a special "AOT_JDK" mode. Example: From dbae90c950200cb417aebeab65e5fce7a7e5f94f Mon Sep 17 00:00:00 2001 From: Francesco Andreuzzi Date: Fri, 15 Aug 2025 10:45:00 +0000 Subject: [PATCH 054/112] 8364723: Sort share/interpreter includes Reviewed-by: shade, ayang --- src/hotspot/share/interpreter/abstractInterpreter.cpp | 5 ++--- src/hotspot/share/interpreter/bytecodeStream.cpp | 2 +- src/hotspot/share/interpreter/bytecodeTracer.cpp | 4 ++-- src/hotspot/share/interpreter/bytecodeUtils.cpp | 2 +- src/hotspot/share/interpreter/interpreter.cpp | 5 ++--- src/hotspot/share/interpreter/interpreterRuntime.cpp | 2 +- src/hotspot/share/interpreter/templateInterpreter.cpp | 2 +- .../share/interpreter/templateInterpreterGenerator.cpp | 2 +- src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp | 2 +- .../share/interpreter/zero/bytecodeInterpreter.inline.hpp | 3 +-- .../share/interpreter/zero/zeroInterpreterGenerator.hpp | 1 - test/hotspot/jtreg/sources/TestIncludesAreSorted.java | 1 + 12 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 0d6ccf3a710..05590add8ff 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -22,23 +22,22 @@ * */ -#include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" #include "cds/metaspaceShared.hpp" #include "compiler/disassembler.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/bytecodeStream.hpp" +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateTable.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/arrayOop.hpp" #include "oops/constantPool.inline.hpp" #include "oops/cpCache.inline.hpp" -#include "oops/methodData.hpp" #include "oops/method.inline.hpp" +#include "oops/methodData.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" diff --git a/src/hotspot/share/interpreter/bytecodeStream.cpp b/src/hotspot/share/interpreter/bytecodeStream.cpp index 7454c749828..5a9e32a46b0 100644 --- a/src/hotspot/share/interpreter/bytecodeStream.cpp +++ b/src/hotspot/share/interpreter/bytecodeStream.cpp @@ -22,8 +22,8 @@ * */ -#include "interpreter/bytecodeStream.hpp" #include "interpreter/bytecodes.hpp" +#include "interpreter/bytecodeStream.hpp" #include "runtime/handles.inline.hpp" Bytecodes::Code RawBytecodeStream::raw_next_special(Bytecodes::Code code) { diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 1f912b2b00f..34170108dc4 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -25,14 +25,14 @@ #include "classfile/classPrinter.hpp" #include "classfile/javaClasses.inline.hpp" #include "interpreter/bytecodeHistogram.hpp" +#include "interpreter/bytecodes.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/bytecodeTracer.hpp" -#include "interpreter/bytecodes.hpp" #include "interpreter/interpreter.hpp" #include "memory/resourceArea.hpp" #include "oops/constantPool.inline.hpp" -#include "oops/methodData.hpp" #include "oops/method.hpp" +#include "oops/methodData.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" diff --git a/src/hotspot/share/interpreter/bytecodeUtils.cpp b/src/hotspot/share/interpreter/bytecodeUtils.cpp index eb4557d6ba0..a5503cc4b88 100644 --- a/src/hotspot/share/interpreter/bytecodeUtils.cpp +++ b/src/hotspot/share/interpreter/bytecodeUtils.cpp @@ -28,8 +28,8 @@ #include "gc/shared/gcLocker.hpp" #include "interpreter/bytecodeUtils.hpp" #include "memory/resourceArea.hpp" -#include "runtime/signature.hpp" #include "runtime/safepointVerifiers.hpp" +#include "runtime/signature.hpp" #include "utilities/events.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index 6272deea349..2cc163186e8 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -22,18 +22,17 @@ * */ -#include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" #include "compiler/disassembler.hpp" +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateTable.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/arrayOop.hpp" -#include "oops/methodData.hpp" #include "oops/method.hpp" +#include "oops/methodData.hpp" #include "oops/oop.inline.hpp" #include "prims/forte.hpp" #include "prims/jvmtiExport.hpp" diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 8e7e5772ba6..50115f842e2 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -46,8 +46,8 @@ #include "oops/cpCache.inline.hpp" #include "oops/instanceKlass.inline.hpp" #include "oops/klass.inline.hpp" -#include "oops/methodData.hpp" #include "oops/method.inline.hpp" +#include "oops/methodData.hpp" #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" diff --git a/src/hotspot/share/interpreter/templateInterpreter.cpp b/src/hotspot/share/interpreter/templateInterpreter.cpp index 0dd82addbfe..44804221dc7 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.cpp +++ b/src/hotspot/share/interpreter/templateInterpreter.cpp @@ -22,9 +22,9 @@ * */ +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateInterpreter.hpp" #include "interpreter/templateInterpreterGenerator.hpp" #include "interpreter/templateTable.hpp" diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp index 9a316f3ba46..96f53c517a3 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -23,9 +23,9 @@ */ #include "compiler/disassembler.hpp" +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateInterpreter.hpp" #include "interpreter/templateInterpreterGenerator.hpp" #include "interpreter/templateTable.hpp" diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index 07ec57a6ffe..bfed16f2769 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -29,9 +29,9 @@ #include "gc/shared/threadLocalAllocBuffer.inline.hpp" #include "gc/shared/tlab_globals.hpp" #include "interpreter/bytecodeHistogram.hpp" -#include "interpreter/zero/bytecodeInterpreter.inline.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" +#include "interpreter/zero/bytecodeInterpreter.inline.hpp" #include "jvm_io.h" #include "logging/log.hpp" #include "memory/resourceArea.hpp" diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp index 2a396aec9ad..effee05c2f1 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp @@ -27,6 +27,7 @@ #include "interpreter/zero/bytecodeInterpreter.hpp" +#include "bytecodeInterpreter_zero.inline.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/macros.hpp" @@ -42,6 +43,4 @@ #define VERIFY_OOP(o) #endif -# include "bytecodeInterpreter_zero.inline.hpp" - #endif // SHARE_INTERPRETER_BYTECODEINTERPRETER_INLINE_HPP diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp index 8c745e51949..f8db4a07e9a 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp @@ -29,7 +29,6 @@ // of the Zero interpreter generator. # include "entry_zero.hpp" -// # include "interpreter/interp_masm.hpp" class ZeroInterpreterGenerator: public AbstractInterpreterGenerator { diff --git a/test/hotspot/jtreg/sources/TestIncludesAreSorted.java b/test/hotspot/jtreg/sources/TestIncludesAreSorted.java index 072784cda9d..7460a160ee6 100644 --- a/test/hotspot/jtreg/sources/TestIncludesAreSorted.java +++ b/test/hotspot/jtreg/sources/TestIncludesAreSorted.java @@ -50,6 +50,7 @@ public class TestIncludesAreSorted { "share/classfile", "share/code", "share/compiler", + "share/interpreter", "share/jvmci", "share/libadt", "share/metaprogramming", From 08db4b99622e488558dd7987c34f1c515fa30426 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 15 Aug 2025 17:56:47 +0000 Subject: [PATCH 055/112] 8365571: GenShen: PLAB promotions may remain disabled for evacuation threads Reviewed-by: kdnilsen, ysr, shade --- .../gc/shenandoah/shenandoahConcurrentGC.cpp | 23 ++++--------------- .../share/gc/shenandoah/shenandoahHeap.cpp | 5 ++++ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 81154aff9f0..d4c7ad5df50 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -823,7 +823,6 @@ bool ShenandoahConcurrentGC::has_in_place_promotions(ShenandoahHeap* heap) { return heap->mode()->is_generational() && heap->old_generation()->has_in_place_promotions(); } -template class ShenandoahConcurrentEvacThreadClosure : public ThreadClosure { private: OopClosure* const _oops; @@ -833,13 +832,9 @@ public: void do_thread(Thread* thread) override { JavaThread* const jt = JavaThread::cast(thread); StackWatermarkSet::finish_processing(jt, _oops, StackWatermarkKind::gc); - if (GENERATIONAL) { - ShenandoahThreadLocalData::enable_plab_promotions(thread); - } } }; -template class ShenandoahConcurrentEvacUpdateThreadTask : public WorkerTask { private: ShenandoahJavaThreadsIterator _java_threads; @@ -851,30 +846,20 @@ public: } void work(uint worker_id) override { - if (GENERATIONAL) { - Thread* worker_thread = Thread::current(); - ShenandoahThreadLocalData::enable_plab_promotions(worker_thread); - } - // ShenandoahEvacOOMScope has to be setup by ShenandoahContextEvacuateUpdateRootsClosure. // Otherwise, may deadlock with watermark lock ShenandoahContextEvacuateUpdateRootsClosure oops_cl; - ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl); + ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl); _java_threads.threads_do(&thr_cl, worker_id); } }; void ShenandoahConcurrentGC::op_thread_roots() { - ShenandoahHeap* const heap = ShenandoahHeap::heap(); + const ShenandoahHeap* const heap = ShenandoahHeap::heap(); assert(heap->is_evacuation_in_progress(), "Checked by caller"); ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_thread_roots); - if (heap->mode()->is_generational()) { - ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); - heap->workers()->run_task(&task); - } else { - ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); - heap->workers()->run_task(&task); - } + ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); + heap->workers()->run_task(&task); } void ShenandoahConcurrentGC::op_weak_refs() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 2baf7e25cba..b90468501f6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1216,6 +1216,11 @@ public: // 1. We need to make the plab memory parsable by remembered-set scanning. // 2. We need to establish a trustworthy UpdateWaterMark value within each old-gen heap region ShenandoahGenerationalHeap::heap()->retire_plab(plab, thread); + + // Re-enable promotions for the next evacuation phase. + ShenandoahThreadLocalData::enable_plab_promotions(thread); + + // Reset the fill size for next evacuation phase. if (_resize && ShenandoahThreadLocalData::plab_size(thread) > 0) { ShenandoahThreadLocalData::set_plab_size(thread, 0); } From 39a365296882b0df49398cd7ac36e801a9aa1c35 Mon Sep 17 00:00:00 2001 From: Dean Long Date: Fri, 15 Aug 2025 18:52:45 +0000 Subject: [PATCH 056/112] 8278874: tighten VerifyStack constraints Co-authored-by: Tom Rodriguez Reviewed-by: mhaessig, never --- src/hotspot/share/classfile/javaClasses.cpp | 5 +- src/hotspot/share/runtime/deoptimization.cpp | 180 ++++++++++--------- src/hotspot/share/runtime/vframeArray.cpp | 117 +++++++----- src/hotspot/share/runtime/vframeArray.hpp | 9 +- 4 files changed, 178 insertions(+), 133 deletions(-) diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 5c41ea789b5..20534b93290 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1886,8 +1886,9 @@ JavaThreadStatus java_lang_Thread::get_thread_status(oop java_thread) { // Make sure the caller is operating on behalf of the VM or is // running VM code (state == _thread_in_vm). assert(Threads_lock->owned_by_self() || Thread::current()->is_VM_thread() || - JavaThread::current()->thread_state() == _thread_in_vm, - "Java Thread is not running in vm"); + JavaThread::current()->thread_state() == _thread_in_vm || + JavaThread::current() == java_lang_Thread::thread(java_thread), + "unsafe call to java_lang_Thread::get_thread_status()?"); GET_FIELDHOLDER_FIELD(java_thread, get_thread_status, JavaThreadStatus::NEW /* not initialized */); } diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 5f9a80cf100..243903ed233 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -561,7 +561,8 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread #endif // !PRODUCT GrowableArray* expressions = trap_scope->expressions(); - guarantee(expressions != nullptr && expressions->length() > 0, "must have exception to throw"); + guarantee(expressions != nullptr && expressions->length() == 1, "should have only exception on stack"); + guarantee(exec_mode != Unpack_exception, "rethrow_exception set with Unpack_exception"); ScopeValue* topOfStack = expressions->top(); exceptionObject = StackValue::create_stack_value(&deoptee, &map, topOfStack)->get_obj(); guarantee(exceptionObject() != nullptr, "exception oop can not be null"); @@ -737,6 +738,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread if (exceptionObject() != nullptr) { current->set_exception_oop(exceptionObject()); exec_mode = Unpack_exception; + assert(array->element(0)->rethrow_exception(), "must be"); } #endif @@ -844,6 +846,7 @@ void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_arr } #ifndef PRODUCT +#ifdef ASSERT // Return true if the execution after the provided bytecode continues at the // next bytecode in the code. This is not the case for gotos, returns, and // throws. @@ -868,6 +871,7 @@ static bool falls_through(Bytecodes::Code bc) { } } #endif +#endif // Return BasicType of value being returned JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_mode)) @@ -932,116 +936,114 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m RegisterMap::ProcessFrames::include, RegisterMap::WalkContinuation::skip); rm.set_include_argument_oops(false); - bool is_top_frame = true; int callee_size_of_parameters = 0; - int callee_max_locals = 0; - for (int i = 0; i < cur_array->frames(); i++) { - vframeArrayElement* el = cur_array->element(i); + for (int frame_idx = 0; frame_idx < cur_array->frames(); frame_idx++) { + bool is_top_frame = (frame_idx == 0); + vframeArrayElement* el = cur_array->element(frame_idx); frame* iframe = el->iframe(); guarantee(iframe->is_interpreted_frame(), "Wrong frame type"); + methodHandle mh(thread, iframe->interpreter_frame_method()); + bool reexecute = el->should_reexecute(); + + int cur_invoke_parameter_size = 0; + int top_frame_expression_stack_adjustment = 0; + int max_bci = mh->code_size(); + BytecodeStream str(mh, iframe->interpreter_frame_bci()); + assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); + Bytecodes::Code cur_code = str.next(); + + if (!reexecute && !Bytecodes::is_invoke(cur_code)) { + // We can only compute OopMaps for the before state, so we need to roll forward + // to the next bytecode. + assert(is_top_frame, "must be"); + assert(falls_through(cur_code), "must be"); + assert(cur_code != Bytecodes::_illegal, "illegal bytecode"); + assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); + + // Need to subtract off the size of the result type of + // the bytecode because this is not described in the + // debug info but returned to the interpreter in the TOS + // caching register + BasicType bytecode_result_type = Bytecodes::result_type(cur_code); + if (bytecode_result_type != T_ILLEGAL) { + top_frame_expression_stack_adjustment = type2size[bytecode_result_type]; + } + assert(top_frame_expression_stack_adjustment >= 0, "stack adjustment must be positive"); + + cur_code = str.next(); + // Reflect the fact that we have rolled forward and now need + // top_frame_expression_stack_adjustment + reexecute = true; + } + + assert(cur_code != Bytecodes::_illegal, "illegal bytecode"); + assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); // Get the oop map for this bci InterpreterOopMap mask; - int cur_invoke_parameter_size = 0; - bool try_next_mask = false; - int next_mask_expression_stack_size = -1; - int top_frame_expression_stack_adjustment = 0; - methodHandle mh(thread, iframe->interpreter_frame_method()); - OopMapCache::compute_one_oop_map(mh, iframe->interpreter_frame_bci(), &mask); - BytecodeStream str(mh, iframe->interpreter_frame_bci()); - int max_bci = mh->code_size(); - // Get to the next bytecode if possible - assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); + OopMapCache::compute_one_oop_map(mh, str.bci(), &mask); // Check to see if we can grab the number of outgoing arguments // at an uncommon trap for an invoke (where the compiler // generates debug info before the invoke has executed) - Bytecodes::Code cur_code = str.next(); - Bytecodes::Code next_code = Bytecodes::_shouldnotreachhere; if (Bytecodes::is_invoke(cur_code)) { - Bytecode_invoke invoke(mh, iframe->interpreter_frame_bci()); + Bytecode_invoke invoke(mh, str.bci()); cur_invoke_parameter_size = invoke.size_of_parameters(); - if (i != 0 && invoke.has_member_arg()) { + if (!is_top_frame && invoke.has_member_arg()) { callee_size_of_parameters++; } } - if (str.bci() < max_bci) { - next_code = str.next(); - if (next_code >= 0) { - // The interpreter oop map generator reports results before - // the current bytecode has executed except in the case of - // calls. It seems to be hard to tell whether the compiler - // has emitted debug information matching the "state before" - // a given bytecode or the state after, so we try both - if (!Bytecodes::is_invoke(cur_code) && falls_through(cur_code)) { - // Get expression stack size for the next bytecode - InterpreterOopMap next_mask; - OopMapCache::compute_one_oop_map(mh, str.bci(), &next_mask); - next_mask_expression_stack_size = next_mask.expression_stack_size(); - if (Bytecodes::is_invoke(next_code)) { - Bytecode_invoke invoke(mh, str.bci()); - next_mask_expression_stack_size += invoke.size_of_parameters(); - } - // Need to subtract off the size of the result type of - // the bytecode because this is not described in the - // debug info but returned to the interpreter in the TOS - // caching register - BasicType bytecode_result_type = Bytecodes::result_type(cur_code); - if (bytecode_result_type != T_ILLEGAL) { - top_frame_expression_stack_adjustment = type2size[bytecode_result_type]; - } - assert(top_frame_expression_stack_adjustment >= 0, "stack adjustment must be positive"); - try_next_mask = true; - } - } - } // Verify stack depth and oops in frame - // This assertion may be dependent on the platform we're running on and may need modification (tested on x86 and sparc) - if (!( - /* SPARC */ - (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + callee_size_of_parameters) || - /* x86 */ - (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + callee_max_locals) || - (try_next_mask && - (iframe->interpreter_frame_expression_stack_size() == (next_mask_expression_stack_size - - top_frame_expression_stack_adjustment))) || - (is_top_frame && (exec_mode == Unpack_exception) && iframe->interpreter_frame_expression_stack_size() == 0) || - (is_top_frame && (exec_mode == Unpack_uncommon_trap || exec_mode == Unpack_reexecute || el->should_reexecute()) && - (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + cur_invoke_parameter_size)) - )) { - { - // Print out some information that will help us debug the problem - tty->print_cr("Wrong number of expression stack elements during deoptimization"); - tty->print_cr(" Error occurred while verifying frame %d (0..%d, 0 is topmost)", i, cur_array->frames() - 1); - tty->print_cr(" Current code %s", Bytecodes::name(cur_code)); - if (try_next_mask) { - tty->print_cr(" Next code %s", Bytecodes::name(next_code)); - } - tty->print_cr(" Fabricated interpreter frame had %d expression stack elements", - iframe->interpreter_frame_expression_stack_size()); - tty->print_cr(" Interpreter oop map had %d expression stack elements", mask.expression_stack_size()); - tty->print_cr(" try_next_mask = %d", try_next_mask); - tty->print_cr(" next_mask_expression_stack_size = %d", next_mask_expression_stack_size); - tty->print_cr(" callee_size_of_parameters = %d", callee_size_of_parameters); - tty->print_cr(" callee_max_locals = %d", callee_max_locals); - tty->print_cr(" top_frame_expression_stack_adjustment = %d", top_frame_expression_stack_adjustment); - tty->print_cr(" exec_mode = %d", exec_mode); - tty->print_cr(" cur_invoke_parameter_size = %d", cur_invoke_parameter_size); - tty->print_cr(" Thread = " INTPTR_FORMAT ", thread ID = %d", p2i(thread), thread->osthread()->thread_id()); - tty->print_cr(" Interpreted frames:"); - for (int k = 0; k < cur_array->frames(); k++) { - vframeArrayElement* el = cur_array->element(k); - tty->print_cr(" %s (bci %d)", el->method()->name_and_sig_as_C_string(), el->bci()); - } - cur_array->print_on_2(tty); + auto match = [&]() { + int iframe_expr_ssize = iframe->interpreter_frame_expression_stack_size(); +#if INCLUDE_JVMCI + if (is_top_frame && el->rethrow_exception()) { + return iframe_expr_ssize == 1; } +#endif + // This should only be needed for C1 + if (is_top_frame && exec_mode == Unpack_exception && iframe_expr_ssize == 0) { + return true; + } + if (reexecute) { + int expr_ssize_before = iframe_expr_ssize + top_frame_expression_stack_adjustment; + int oopmap_expr_invoke_ssize = mask.expression_stack_size() + cur_invoke_parameter_size; + return expr_ssize_before == oopmap_expr_invoke_ssize; + } else { + int oopmap_expr_callee_ssize = mask.expression_stack_size() + callee_size_of_parameters; + return iframe_expr_ssize == oopmap_expr_callee_ssize; + } + }; + if (!match()) { + // Print out some information that will help us debug the problem + tty->print_cr("Wrong number of expression stack elements during deoptimization"); + tty->print_cr(" Error occurred while verifying frame %d (0..%d, 0 is topmost)", frame_idx, cur_array->frames() - 1); + tty->print_cr(" Current code %s", Bytecodes::name(cur_code)); + tty->print_cr(" Fabricated interpreter frame had %d expression stack elements", + iframe->interpreter_frame_expression_stack_size()); + tty->print_cr(" Interpreter oop map had %d expression stack elements", mask.expression_stack_size()); + tty->print_cr(" callee_size_of_parameters = %d", callee_size_of_parameters); + tty->print_cr(" top_frame_expression_stack_adjustment = %d", top_frame_expression_stack_adjustment); + tty->print_cr(" exec_mode = %d", exec_mode); + tty->print_cr(" original should_reexecute = %s", el->should_reexecute() ? "true" : "false"); + tty->print_cr(" reexecute = %s%s", reexecute ? "true" : "false", + (reexecute != el->should_reexecute()) ? " (changed)" : ""); +#if INCLUDE_JVMCI + tty->print_cr(" rethrow_exception = %s", el->rethrow_exception() ? "true" : "false"); +#endif + tty->print_cr(" cur_invoke_parameter_size = %d", cur_invoke_parameter_size); + tty->print_cr(" Thread = " INTPTR_FORMAT ", thread ID = %d", p2i(thread), thread->osthread()->thread_id()); + tty->print_cr(" Interpreted frames:"); + for (int k = 0; k < cur_array->frames(); k++) { + vframeArrayElement* el = cur_array->element(k); + tty->print_cr(" %s (bci %d)", el->method()->name_and_sig_as_C_string(), el->bci()); + } + cur_array->print_on_2(tty); guarantee(false, "wrong number of expression stack elements during deopt"); } VerifyOopClosure verify; iframe->oops_interpreted_do(&verify, &rm, false); callee_size_of_parameters = mh->size_of_parameters(); - callee_max_locals = mh->max_locals(); - is_top_frame = false; } } #endif // !PRODUCT diff --git a/src/hotspot/share/runtime/vframeArray.cpp b/src/hotspot/share/runtime/vframeArray.cpp index e17961fc424..224bce4513a 100644 --- a/src/hotspot/share/runtime/vframeArray.cpp +++ b/src/hotspot/share/runtime/vframeArray.cpp @@ -23,6 +23,7 @@ */ #include "classfile/vmSymbols.hpp" +#include "code/scopeDesc.hpp" #include "code/vmreg.inline.hpp" #include "interpreter/bytecode.hpp" #include "interpreter/bytecode.inline.hpp" @@ -61,7 +62,10 @@ void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { _method = vf->method(); _bci = vf->raw_bci(); - _reexecute = vf->should_reexecute(); + _reexecute = vf->should_reexecute(); // initial value, updated in unpack_on_stack +#if INCLUDE_JVMCI + _rethrow = vf->scope()->rethrow_exception(); +#endif #ifdef ASSERT _removed_monitors = false; #endif @@ -171,7 +175,34 @@ void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { } } -int unpack_counter = 0; +static int unpack_counter = 0; + +bool vframeArrayElement::should_reexecute(bool is_top_frame, int exec_mode) const { + if (is_top_frame) { + switch (exec_mode) { + case Deoptimization::Unpack_uncommon_trap: + case Deoptimization::Unpack_reexecute: + return true; + case Deoptimization::Unpack_exception: + assert(raw_bci() >= 0, "bad bci %d for Unpack_exception", raw_bci()); + default: + break; + } + } + if (raw_bci() == SynchronizationEntryBCI) { + return true; + } + bool reexec = should_reexecute(); + assert(is_top_frame || reexec == false, "unexepected should_reexecute()"); +#ifdef ASSERT + if (!reexec) { + address bcp = method()->bcp_from(bci()); + Bytecodes::Code code = Bytecodes::code_at(method(), bcp); + assert(!Interpreter::bytecode_should_reexecute(code), "should_reexecute mismatch"); + } +#endif + return reexec; +} void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, int callee_parameters, @@ -189,20 +220,37 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, // C++ interpreter doesn't need a pc since it will figure out what to do when it // begins execution address pc; - bool use_next_mdp = false; // true if we should use the mdp associated with the next bci - // rather than the one associated with bcp - if (raw_bci() == SynchronizationEntryBCI) { + bool reexecute = should_reexecute(is_top_frame, exec_mode); + if (is_top_frame && exec_mode == Deoptimization::Unpack_exception) { + assert(raw_bci() >= 0, "bad bci %d for Unpack_exception", raw_bci()); + bcp = method()->bcp_from(bci()); + // exception is pending + pc = Interpreter::rethrow_exception_entry(); + // [phh] We're going to end up in some handler or other, so it doesn't + // matter what mdp we point to. See exception_handler_for_exception() + // in interpreterRuntime.cpp. + } else if (raw_bci() == SynchronizationEntryBCI) { // We are deoptimizing while hanging in prologue code for synchronized method bcp = method()->bcp_from(0); // first byte code pc = Interpreter::deopt_entry(vtos, 0); // step = 0 since we don't skip current bytecode - } else if (should_reexecute()) { //reexecute this bytecode + assert(reexecute, "must be"); + } else if (reexecute) { //reexecute this bytecode assert(is_top_frame, "reexecute allowed only for the top frame"); bcp = method()->bcp_from(bci()); - pc = Interpreter::deopt_reexecute_entry(method(), bcp); + switch (exec_mode) { + case Deoptimization::Unpack_uncommon_trap: + case Deoptimization::Unpack_reexecute: + // Do not special-case _athrow or _return_register_finalizer + pc = Interpreter::deopt_entry(vtos, 0); + break; + default: + // Yes, special-case _athrow and _return_register_finalizer + pc = Interpreter::deopt_reexecute_entry(method(), bcp); + } } else { bcp = method()->bcp_from(bci()); + assert(!reexecute, "must be"); pc = Interpreter::deopt_continue_after_entry(method(), bcp, callee_parameters, is_top_frame); - use_next_mdp = true; } assert(Bytecodes::is_defined(*bcp), "must be a valid bytecode"); @@ -239,44 +287,32 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, } else { // Reexecute invoke in top frame pc = Interpreter::deopt_entry(vtos, 0); - use_next_mdp = false; +#ifdef ASSERT + Bytecodes::Code code = Bytecodes::code_at(method(), bcp); + assert(Bytecodes::is_invoke(code), "must be"); + assert(!reexecute, "must be"); +#endif + // It would be nice if the VerifyStack logic in unpack_frames() was refactored so + // we could check the stack before and after changing the reexecute mode, but + // it should pass either way because an invoke uses the same stack state for both modes, + // which is: args popped but result not yet pushed. + reexecute = true; popframe_preserved_args_size_in_bytes = in_bytes(thread->popframe_preserved_args_size()); // Note: the PopFrame-related extension of the expression stack size is done in // Deoptimization::fetch_unroll_info_helper popframe_preserved_args_size_in_words = in_words(thread->popframe_preserved_args_size_in_words()); } - } else if (!realloc_failure_exception && JvmtiExport::can_force_early_return() && state != nullptr && - state->is_earlyret_pending()) { - // Force early return from top frame after deoptimization - pc = Interpreter::remove_activation_early_entry(state->earlyret_tos()); - } else { - if (realloc_failure_exception && JvmtiExport::can_force_early_return() && state != nullptr && state->is_earlyret_pending()) { + } else if (JvmtiExport::can_force_early_return() && state != nullptr && state->is_earlyret_pending()) { + if (!realloc_failure_exception) { + // Force early return from top frame after deoptimization + pc = Interpreter::remove_activation_early_entry(state->earlyret_tos()); + } else { state->clr_earlyret_pending(); state->set_earlyret_oop(nullptr); state->clr_earlyret_value(); } - // Possibly override the previous pc computation of the top (youngest) frame - switch (exec_mode) { - case Deoptimization::Unpack_deopt: - // use what we've got - break; - case Deoptimization::Unpack_exception: - // exception is pending - pc = SharedRuntime::raw_exception_handler_for_return_address(thread, pc); - // [phh] We're going to end up in some handler or other, so it doesn't - // matter what mdp we point to. See exception_handler_for_exception() - // in interpreterRuntime.cpp. - break; - case Deoptimization::Unpack_uncommon_trap: - case Deoptimization::Unpack_reexecute: - // redo last byte code - pc = Interpreter::deopt_entry(vtos, 0); - use_next_mdp = false; - break; - default: - ShouldNotReachHere(); - } } + _reexecute = reexecute; } // Setup the interpreter frame @@ -317,17 +353,16 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, assert(src->obj() != nullptr || ObjectSynchronizer::current_thread_holds_lock(thread, Handle(thread, src->obj())), "should be held, after move_to"); } - if (ProfileInterpreter) { - iframe()->interpreter_frame_set_mdp(nullptr); // clear out the mdp. - } iframe()->interpreter_frame_set_bcp(bcp); if (ProfileInterpreter) { MethodData* mdo = method()->method_data(); - if (mdo != nullptr) { + if (mdo != nullptr && exec_mode != Deoptimization::Unpack_exception) { int bci = iframe()->interpreter_frame_bci(); - if (use_next_mdp) ++bci; + if (!reexecute) ++bci; address mdp = mdo->bci_to_dp(bci); iframe()->interpreter_frame_set_mdp(mdp); + } else { + iframe()->interpreter_frame_set_mdp(nullptr); // clear out the mdp. } } diff --git a/src/hotspot/share/runtime/vframeArray.hpp b/src/hotspot/share/runtime/vframeArray.hpp index b270046252d..d7390bedff4 100644 --- a/src/hotspot/share/runtime/vframeArray.hpp +++ b/src/hotspot/share/runtime/vframeArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,9 @@ class vframeArrayElement { frame _frame; // the interpreter frame we will unpack into int _bci; // raw bci for this vframe bool _reexecute; // whether we should reexecute this bytecode +#if INCLUDE_JVMCI + bool _rethrow; // from ScopeDesc::rethrow_exception() +#endif Method* _method; // the method for this vframe MonitorChunk* _monitors; // active monitors for this vframe StackValueCollection* _locals; @@ -71,7 +74,11 @@ class vframeArrayElement { int bci(void) const; int raw_bci(void) const { return _bci; } + bool should_reexecute(bool is_top_frame, int exec_mode) const; bool should_reexecute(void) const { return _reexecute; } +#if INCLUDE_JVMCI + bool rethrow_exception(void) const { return _rethrow; } +#endif Method* method(void) const { return _method; } From 6e760b9b746eba3d40ec246f3e194ce9f8c5ae29 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 15 Aug 2025 20:00:01 +0000 Subject: [PATCH 057/112] 8365622: Shenandoah: Fix Shenandoah simple bit map test Reviewed-by: ysr --- .../hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp index 45d6cf47fe0..0d81320befc 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp @@ -443,7 +443,7 @@ public: }; -TEST(BasicShenandoahSimpleBitMapTest, minimum_test) { +TEST_F(ShenandoahSimpleBitMapTest, minimum_test) { bool result = ShenandoahSimpleBitMapTest::run_test(); ASSERT_EQ(result, true); From b69a3849b21b4bb1e21ad276633de45da6200168 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 15 Aug 2025 20:02:43 +0000 Subject: [PATCH 058/112] 8365198: Remove unnecessary mention of finalize in ImageIO reader/writer docs Reviewed-by: bchristi, azvegint --- src/java.desktop/share/classes/javax/imageio/ImageReader.java | 3 +-- src/java.desktop/share/classes/javax/imageio/ImageWriter.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/javax/imageio/ImageReader.java b/src/java.desktop/share/classes/javax/imageio/ImageReader.java index 3488f60b9b7..281e7298354 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageReader.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageReader.java @@ -2501,8 +2501,7 @@ public abstract class ImageReader { /** * Allows any resources held by this object to be released. The - * result of calling any other method (other than - * {@code finalize}) subsequent to a call to this method + * result of calling any other method subsequent to a call to this method * is undefined. * *

      It is important for applications to call this method when they diff --git a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java index a688d9aea91..fd8cc3a753d 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java @@ -2000,8 +2000,7 @@ public abstract class ImageWriter implements ImageTranscoder { /** * Allows any resources held by this object to be released. The - * result of calling any other method (other than - * {@code finalize}) subsequent to a call to this method + * result of calling any other method subsequent to a call to this method * is undefined. * *

      It is important for applications to call this method when they From b023fea06216d5196592ff5239dc592aa8e34a02 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 15 Aug 2025 22:12:57 +0000 Subject: [PATCH 059/112] 8365558: Fix stub entry init and blob creation on Zero Reviewed-by: asmehra, kvn --- src/hotspot/cpu/zero/sharedRuntime_zero.cpp | 6 ++-- .../cpu/zero/stubDeclarations_zero.hpp | 4 +-- src/hotspot/share/runtime/sharedRuntime.cpp | 33 +++++++++++++++---- src/hotspot/share/runtime/stubRoutines.cpp | 19 ++++++----- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp index df162fbfa74..b2e406c205b 100644 --- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp +++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp @@ -56,10 +56,10 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, const BasicType *sig_bt, const VMRegPair *regs, AdapterHandlerEntry* handler) { - // VM expects i2c entry to be always filled. The rest can be unset. + // foil any attempt to call the i2c, c2i or unverified c2i entries handler->set_entry_points(CAST_FROM_FN_PTR(address,zero_null_code_stub), - nullptr, - nullptr, + CAST_FROM_FN_PTR(address,zero_null_code_stub), + CAST_FROM_FN_PTR(address,zero_null_code_stub), nullptr); } diff --git a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp index 3126cf71460..2357bbb5169 100644 --- a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp +++ b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp @@ -37,7 +37,7 @@ do_arch_blob, \ do_arch_entry, \ do_arch_entry_init) \ - do_arch_blob(initial, 32) \ + do_arch_blob(initial, 0) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ @@ -58,7 +58,7 @@ do_arch_blob, \ do_arch_entry, \ do_arch_entry_init) \ - do_arch_blob(final, 32) \ + do_arch_blob(final, 0) \ #endif // CPU_ZERO_STUBDECLARATIONS_HPP diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 5bfddb8a04a..13612496fbd 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -2602,19 +2602,24 @@ void AdapterHandlerLibrary::initialize() { BasicType obj_obj_args[] = { T_OBJECT, T_OBJECT }; _obj_obj_arg_handler = create_adapter(obj_obj_arg_blob, 2, obj_obj_args); - assert(no_arg_blob != nullptr && - obj_arg_blob != nullptr && - int_arg_blob != nullptr && - obj_int_arg_blob != nullptr && - obj_obj_arg_blob != nullptr, "Initial adapters must be properly created"); + // we should always get an entry back but we don't have any + // associated blob on Zero + assert(_no_arg_handler != nullptr && + _obj_arg_handler != nullptr && + _int_arg_handler != nullptr && + _obj_int_arg_handler != nullptr && + _obj_obj_arg_handler != nullptr, "Initial adapter handlers must be properly created"); } // Outside of the lock +#ifndef ZERO + // no blobs to register when we are on Zero post_adapter_creation(no_arg_blob, _no_arg_handler); post_adapter_creation(obj_arg_blob, _obj_arg_handler); post_adapter_creation(int_arg_blob, _int_arg_handler); post_adapter_creation(obj_int_arg_blob, _obj_int_arg_handler); post_adapter_creation(obj_obj_arg_blob, _obj_obj_arg_handler); +#endif // ZERO } AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint) { @@ -2709,12 +2714,15 @@ const char* AdapterHandlerEntry::_entry_names[] = { #ifdef ASSERT void AdapterHandlerLibrary::verify_adapter_sharing(int total_args_passed, BasicType* sig_bt, AdapterHandlerEntry* cached_entry) { + // we can only check for the same code if there is any +#ifndef ZERO AdapterBlob* comparison_blob = nullptr; AdapterHandlerEntry* comparison_entry = create_adapter(comparison_blob, total_args_passed, sig_bt, true); assert(comparison_blob == nullptr, "no blob should be created when creating an adapter for comparison"); assert(comparison_entry->compare_code(cached_entry), "code must match"); // Release the one just created AdapterHandlerEntry::deallocate(comparison_entry); +# endif // ZERO } #endif /* ASSERT*/ @@ -2792,8 +2800,13 @@ AdapterBlob* AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handle void AdapterHandlerLibrary::print_adapter_handler_info(outputStream* st, AdapterHandlerEntry* handler, AdapterBlob* adapter_blob) { ttyLocker ttyl; ResourceMark rm; - int insts_size = adapter_blob->code_size(); + int insts_size; + // on Zero the blob may be null handler->print_adapter_on(tty); + if (adapter_blob == nullptr) { + return; + } + insts_size = adapter_blob->code_size(); st->print_cr("i2c argument handler for: %s %s (%d bytes generated)", handler->fingerprint()->as_basic_args_string(), handler->fingerprint()->as_string(), insts_size); @@ -2834,6 +2847,11 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, sig_bt, regs, handler); +#ifdef ZERO + // On zero there is no code to save and no need to create a blob and + // or relocate the handler. + adapter_blob = nullptr; +#else #ifdef ASSERT if (VerifyAdapterSharing) { handler->save_code(buf->code_begin(), buffer.insts_size()); @@ -2869,12 +2887,15 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, assert(success || !AOTCodeCache::is_dumping_adapter(), "caching of adapter must be disabled"); } handler->relocate(adapter_blob->content_begin()); +#endif // ZERO + #ifndef PRODUCT // debugging support if (PrintAdapterHandlers || PrintStubCode) { print_adapter_handler_info(tty, handler, adapter_blob); } #endif + return true; } diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 86975f7d0a6..82608737be8 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -168,14 +168,6 @@ static BufferBlob* initialize_stubs(BlobId blob_id, const char* assert_msg) { assert(StubInfo::is_stubgen(blob_id), "not a stubgen blob %s", StubInfo::name(blob_id)); ResourceMark rm; - if (code_size == 0) { - LogTarget(Info, stubs) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - ls.print_cr("%s\t not generated", buffer_name); - } - return nullptr; - } TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime)); // Add extra space for large CodeEntryAlignment int size = code_size + CodeEntryAlignment * max_aligned_stubs; @@ -196,9 +188,18 @@ static BufferBlob* initialize_stubs(BlobId blob_id, } CodeBuffer buffer(stubs_code); StubGenerator_generate(&buffer, blob_id); + if (code_size == 0) { + assert(buffer.insts_size() == 0, "should not write into buffer when bob size declared as 0"); + LogTarget(Info, stubs) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("%s\t not generated", buffer_name); + } + return nullptr; + } // When new stubs added we need to make sure there is some space left // to catch situation when we should increase size again. - assert(code_size == 0 || buffer.insts_remaining() > 200, + assert(buffer.insts_remaining() > 200, "increase %s, code_size: %d, used: %d, free: %d", assert_msg, code_size, buffer.total_content_size(), buffer.insts_remaining()); From a70521c62e0841895d71cce2c872bd12f1183e33 Mon Sep 17 00:00:00 2001 From: Leonid Mesnik Date: Fri, 15 Aug 2025 22:45:01 +0000 Subject: [PATCH 060/112] 8364973: Add JVMTI stress testing mode Reviewed-by: erikj, ihse, sspitsyn --- doc/starting-next-release.html | 5 +- doc/testing.html | 6 + doc/testing.md | 7 + make/RunTests.gmk | 14 +- make/RunTestsPrebuiltSpec.gmk | 16 + .../jtreg/ProblemList-jvmti-stress-agent.txt | 101 ++ .../lib/ir_framework/TestFramework.java | 13 + test/jdk/ProblemList-jvmti-stress-agent.txt | 47 + test/jtreg-ext/requires/VMProps.java | 7 +- .../test/lib/jvmti/libJvmtiStressAgent.cpp | 987 ++++++++++++++++++ 10 files changed, 1196 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt create mode 100644 test/jdk/ProblemList-jvmti-stress-agent.txt create mode 100644 test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp diff --git a/doc/starting-next-release.html b/doc/starting-next-release.html index 421229f9fbc..6cffdf38b0f 100644 --- a/doc/starting-next-release.html +++ b/doc/starting-next-release.html @@ -11,11 +11,8 @@ div.columns{display: flex; gap: min(4vw, 1.5em);} div.column{flex: auto; overflow-x: auto;} div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} - /* The extra [class] is a hack that increases specificity enough to - override a similar rule in reveal.js */ - ul.task-list[class]{list-style: none;} + ul.task-list{list-style: none;} ul.task-list li input[type="checkbox"] { - font-size: inherit; width: 0.8em; margin: 0 0.8em 0.2em -1.6em; vertical-align: middle; diff --git a/doc/testing.html b/doc/testing.html index f75e0da309e..fa774aa312f 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -458,6 +458,12 @@ class, named Virtual, is currently part of the JDK build in the test/jtreg_test_thread_factory/ directory. This class gets compiled during the test image build. The implementation of the Virtual class creates a new virtual thread for executing each test class.

      +

      JVMTI_STRESS_AGENT

      +

      Executes JTReg tests with JVM TI stress agent. The stress agent is +the part of test library and located in +test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp. The +value of this argument is set as JVM TI agent options. This mode uses +ProblemList-jvmti-stress-agent.txt as an additional exclude list.

      TEST_MODE

      The test mode (agentvm or othervm).

      Defaults to agentvm.

      diff --git a/doc/testing.md b/doc/testing.md index 525b85a8438..4cfc36c85f9 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -401,6 +401,13 @@ the `test/jtreg_test_thread_factory/` directory. This class gets compiled during the test image build. The implementation of the Virtual class creates a new virtual thread for executing each test class. +#### JVMTI_STRESS_AGENT + +Executes JTReg tests with JVM TI stress agent. The stress agent is the part of +test library and located in `test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp`. +The value of this argument is set as JVM TI agent options. +This mode uses ProblemList-jvmti-stress-agent.txt as an additional exclude list. + #### TEST_MODE The test mode (`agentvm` or `othervm`). diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 574fd869092..46f9a2e4047 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -204,8 +204,9 @@ $(eval $(call SetTestOpt,AOT_JDK,JTREG)) $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \ - TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY MAX_MEM RUN_PROBLEM_LISTS \ - RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ + TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY JVMTI_STRESS_AGENT \ + MAX_MEM RUN_PROBLEM_LISTS RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT \ + AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \ $(CUSTOM_JTREG_STRING_KEYWORDS), \ @@ -876,6 +877,15 @@ define SetupRunJtregTestBody )) endif + ifneq ($$(JTREG_JVMTI_STRESS_AGENT), ) + AGENT := $$(LIBRARY_PREFIX)JvmtiStressAgent$$(SHARED_LIBRARY_SUFFIX)=$$(JTREG_JVMTI_STRESS_AGENT) + $1_JTREG_BASIC_OPTIONS += -javaoption:'-agentpath:$(TEST_IMAGE_DIR)/hotspot/jtreg/native/$$(AGENT)' + $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ + $$(addprefix $$($1_TEST_ROOT)/, ProblemList-jvmti-stress-agent.txt) \ + )) + endif + + ifneq ($$(JTREG_LAUNCHER_OPTIONS), ) $1_JTREG_LAUNCHER_OPTIONS += $$(JTREG_LAUNCHER_OPTIONS) endif diff --git a/make/RunTestsPrebuiltSpec.gmk b/make/RunTestsPrebuiltSpec.gmk index 03c877a4277..e9fd901c9e1 100644 --- a/make/RunTestsPrebuiltSpec.gmk +++ b/make/RunTestsPrebuiltSpec.gmk @@ -176,3 +176,19 @@ ULIMIT := ulimit ifeq ($(OPENJDK_BUILD_OS), windows) PATHTOOL := cygpath endif + +# These settings are needed to run testing with jvmti agent +ifeq ($(OPENJDK_BUILD_OS), linux) + LIBRARY_PREFIX := lib + SHARED_LIBRARY_SUFFIX := .so +endif + +ifeq ($(OPENJDK_BUILD_OS), windows) + LIBRARY_PREFIX := + SHARED_LIBRARY_SUFFIX := .dll +endif + +ifeq ($(OPENJDK_BUILD_OS), macosx) + LIBRARY_PREFIX := lib + SHARED_LIBRARY_SUFFIX := .dylib +endif diff --git a/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt new file mode 100644 index 00000000000..636d02cb8b0 --- /dev/null +++ b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt @@ -0,0 +1,101 @@ +# +# 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. +# + +############################################################################# +# +# List of quarantined tests failing with jvmti stress agent in any mode. +# +############################################################################# + + +compiler/macronodes/TestTopInMacroElimination.java 8362832 generic-all + +gc/stringdedup/TestStringDeduplicationAgeThreshold.java 8362562 generic-all +gc/stringdedup/TestStringDeduplicationInterned.java 8362562 generic-all +gc/stringdedup/TestStringDeduplicationPrintOptions.java 8362562 generic-all + + +serviceability/jvmti/events/SingleStep/singlestep02/singlestep02.java 8362350 generic-all +vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t007/TestDescription.java 8362350 generic-all + +# Incompatbile tests + +# IR +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v032-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v032-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v032-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v032-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v004-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v004-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v008-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v008-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v004-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v004-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v008-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v008-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v032-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v032-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v064-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v064-U 0000000 generic-all + +# Requires solo jvmti capabilities + +compiler/jvmci/events/JvmciShutdownEventTest.java 0000000 generic-all + +# jdwp +runtime/6294277/SourceDebugExtension.java 0000000 generic-all + +# heap stats +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInitialAllocationTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorMultiArrayTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorRecursiveTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatIntervalTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadDisabledTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id0 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id1 0000000 generic-all diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 6782bd68fed..8e509c66c9a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -36,6 +36,7 @@ import jdk.test.lib.Platform; import jdk.test.lib.Utils; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; import java.io.PrintWriter; import java.io.StringWriter; @@ -349,6 +350,7 @@ public class TestFramework { if (shouldInstallWhiteBox()) { installWhiteBox(); } + checkCompatibleFlags(); checkIRRuleCompilePhasesFormat(); disableIRVerificationIfNotFeasible(); @@ -775,12 +777,23 @@ public class TestFramework { return Arrays.stream(testClass.getDeclaredMethods()).anyMatch(m -> m.getAnnotationsByType(IR.class).length > 0); } + private void checkCompatibleFlags() { + for (String flag : Utils.getTestJavaOpts()) { + if (flag.contains("-agentpath")) { + throw new SkippedException("Can't run test with agent."); + } + } + } + private List anyNonWhitelistedJTregVMAndJavaOptsFlags() { List flags = Arrays.stream(Utils.getTestJavaOpts()) .map(s -> s.replaceFirst("-XX:[+|-]?|-(?=[^D|^e])", "")) .collect(Collectors.toList()); List nonWhiteListedFlags = new ArrayList(); for (String flag : flags) { + if (flag.contains("agentpath")) { + throw new SkippedException("Can't run test with -javaagent"); + } // Property flags (prefix -D), -ea and -esa are whitelisted. if (!flag.startsWith("-D") && !flag.startsWith("-e") && JTREG_WHITELIST_FLAGS.stream().noneMatch(flag::contains)) { // Found VM flag that is not whitelisted diff --git a/test/jdk/ProblemList-jvmti-stress-agent.txt b/test/jdk/ProblemList-jvmti-stress-agent.txt new file mode 100644 index 00000000000..4a6e80a9402 --- /dev/null +++ b/test/jdk/ProblemList-jvmti-stress-agent.txt @@ -0,0 +1,47 @@ +# +# 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. +# + +############################################################################# +# +# List of quarantined tests failing with jvmti stress agent in any mode. +# +############################################################################# + + +sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTClient.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTNoSessionCreation.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTParallel.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTSequence.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java 8362658 generic-all +sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java 8362658 generic-all + + +# List of tests incompatible with jvmti stress agent or requiring more investigation + +com/sun/jdi/EATests.java#id0 0000000 generic-all +com/sun/jdi/ThreadMemoryLeakTest.java 0000000 generic-all + +# weak referenced are not cleared +java/lang/WeakPairMap/Driver.java 0000000 generic-all +java/lang/ref/ReachabilityFenceTest.java 0000000 generic-all diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 5ac0bea3937..74ea525b415 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -422,7 +422,12 @@ public class VMProps implements Callable> { * @return true if CDS is supported by the VM to be tested. */ protected String vmCDS() { - return "" + WB.isCDSIncluded(); + boolean noJvmtiAdded = allFlags() + .filter(s -> s.startsWith("-agentpath")) + .findAny() + .isEmpty(); + + return "" + (noJvmtiAdded && WB.isCDSIncluded()); } /** diff --git a/test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp b/test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp new file mode 100644 index 00000000000..c760edc66d6 --- /dev/null +++ b/test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp @@ -0,0 +1,987 @@ +/* + * 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. + */ + +#include "jvmti.h" +#include "jvmti_common.hpp" + +/* +* + * The jtreg tests might be executed with this agent to ensure that corresponding + * JDK functionality is not broken. + * + * IMPORTANT + * The tests that are incompatible with agent should be placed + * into ProblemList-jvmti-stress-agent.txt with 000000 bug. + * + * Test supports 2 modes: + * - standard, where the agent doesn't require debugging capabilities + * - debug, where the agent additionally test debug-related functionality + * The debug mode is incompatible with debugger tests and debug jvmti tests. + * The standard mode should be compatible with all tests except problemlisted. + * + * The JVMTI agent starts jvmti agent tread that enable/disable different + * events and call different jvmti functions concurrently with test execution. + * + * The main requirement is to don't change test behaviour. + * + */ + +#define JVMTI_AGENT_NAME "JvmtiStressAgent" + +/* Global settings and some statistics counters */ +typedef struct { + + /* Verbose logging support */ + jboolean is_verbose; + + /* If debugging functionality could be used. Set from agent args.*/ + jboolean is_debugger_enabled; + + /* Monitor and flags to synchronize agent completion.*/ + jrawMonitorID finished_lock; + volatile jboolean request_agent_thread_stop; + volatile jboolean is_agent_finished; + + /* Some settings configured in gdata_init(). */ + + /* If agent enabled or not. */ + jboolean is_tracing_enabled; + + /* If events testing is enabled. */ + jboolean are_events_enabled; + + /* If interponly and frequent events testing is enabled. */ + jboolean are_frequent_events_enabled; + + /* Should we iterate heap */ + jboolean is_heap_iterate_enabled; + + /* Is Heap sampling enabled */ + jboolean is_heap_sampling_enabled; + + jint heap_sampling_interval; + jint events_interval; + jint frequent_events_interval; + + /* Excluded events */ + jint* events_excluded; + jsize events_excluded_size; + + /* Event statistics */ + + /* The counters are racy intentionally to avoid synchronization. */ + jlong cbBreakpoint; + jlong cbClassFileLoadHook; + jlong cbClassLoad; + jlong cbClassPrepare; + jlong cbCompiledMethodLoad; + jlong cbCompiledMethodUnload; + jlong cbDataDumpRequest; + jlong cbDynamicCodeGenerated; + jlong cbException; + jlong cbExceptionCatch; + jlong cbFieldAccess; + jlong cbFieldModification; + jlong cbFramePop; + jlong cbGarbageCollectionFinish; + jlong cbGarbageCollectionStart; + jlong cbMethodEntry; + jlong cbMethodExit; + jlong cbMonitorContendedEnter; + jlong cbMonitorContendedEntered; + jlong cbMonitorWait; + jlong cbMonitorWaited; + jlong cbNativeMethodBind; + jlong cbObjectFree; + jlong cbResourceExhausted; + jlong cbSampledObjectAlloc; + jlong cbSingleStep; + jlong cbThreadEnd; + jlong cbThreadStart; + jlong cbVirtualThreadEnd; + jlong cbVirtualThreadStart; + jlong cbVMDeath; + jlong cbVMInit; + jlong cbVMObjectAlloc; + + /* Inspector statistics are intentionally racy. */ + jlong inspectedMethods; + jlong inspectedVariables; + + /* File for debug output, agent shouldn't write into stdout. */ + FILE* log_file; +} GlobalData; + +GlobalData *gdata; + +static GlobalData* +gdata_init(jboolean is_debugger_enabled, jboolean is_verbose) { + static GlobalData data; + (void) memset(&data, 0, sizeof (GlobalData)); + + data.is_debugger_enabled = is_debugger_enabled; + data.is_verbose = is_verbose; + + data.request_agent_thread_stop = JNI_FALSE; + data.is_agent_finished = JNI_FALSE; + + /* Set jvmti stress properties */ + data.heap_sampling_interval = 1000; + data.frequent_events_interval = 10; + + data.is_tracing_enabled = JNI_TRUE; + data.are_events_enabled = JNI_TRUE; + data.are_frequent_events_enabled = JNI_TRUE; + // disabled so far + data.is_heap_iterate_enabled = JNI_FALSE; + data.is_heap_sampling_enabled = JNI_FALSE; + + + if (data.is_debugger_enabled) { + data.events_excluded_size = 0; + data.events_excluded = nullptr; + } else { + data.events_excluded_size = 4; + data.events_excluded = new jint[4] { + JVMTI_EVENT_BREAKPOINT, + JVMTI_EVENT_FIELD_ACCESS, + JVMTI_EVENT_FIELD_MODIFICATION, + JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, + }; + } + if (data.is_verbose) { + data.log_file = fopen("JvmtiStressAgent.out", "w"); + } + + return &data; +} + +void +gdata_close() { + free(gdata->events_excluded); + if (gdata->is_verbose) { + fclose(gdata->log_file); + } +} + +// Internal buffer length for all messages +#define MESSAGE_LIMIT 16384 + +void +debug(const char* format, ...) { + if (!gdata->is_verbose) { + return; + } + char dest[MESSAGE_LIMIT]; + va_list argptr; + va_start(argptr, format); + vsnprintf(dest, MESSAGE_LIMIT, format, argptr); + va_end(argptr); + // Enable if needed, tests might fail with unexpected output + //printf("%s\n", dest); + fprintf(gdata->log_file, "%s\n", dest); + fflush(gdata->log_file); +} + +/* Some helper functions to start/stop jvmti stress agent thread. */ +void +check_jni_exception(JNIEnv *jni, const char *message) { + jobject exception = jni->ExceptionOccurred(); + if (exception != nullptr) { + jni->ExceptionDescribe(); + fatal(jni, message); + } +} + +jclass +find_class(JNIEnv *jni, const char *name) { + char message[MESSAGE_LIMIT]; + jclass clazz = jni->FindClass(name); + snprintf(message, MESSAGE_LIMIT, "Failed to find class %s.", name); + check_jni_exception(jni, message); + return clazz; +} + +jmethodID +get_method_id(JNIEnv *jni, jclass clazz, const char *name, const char *sig) { + char message[MESSAGE_LIMIT]; + jmethodID method = jni->GetMethodID(clazz, name, sig); + snprintf(message, MESSAGE_LIMIT, "Failed to find method %s.", name); + check_jni_exception(jni, message); + return method; +} + +void +create_agent_thread(jvmtiEnv *jvmti, JNIEnv *jni, const char *name, jvmtiStartFunction func) { + + check_jni_exception(jni, "JNIException before creating Agent Thread."); + jclass clazz = find_class(jni, "java/lang/Thread"); + jmethodID thread_ctor = get_method_id(jni, clazz, "", + "(Ljava/lang/String;)V"); + + jstring name_utf = jni->NewStringUTF(name); + check_jni_exception(jni, "Error creating utf name of thread."); + + jthread thread = jni->NewObject(clazz, thread_ctor, name_utf); + check_jni_exception(jni, "Error during instantiation of Thread object."); + jvmtiError err = jvmti->RunAgentThread( + thread, func, nullptr, JVMTI_THREAD_NORM_PRIORITY); + check_jvmti_status(jni, err, "RunAgentThread"); +} + +/* + * The method blocks execution until agent thread finishes. + * Should be executed during VMDeath to don't run JVMTI functionality + * during dead phase. + */ +void +request_agent_thread_stop_and_wait(jvmtiEnv *jvmti, JNIEnv *jni) { + RawMonitorLocker rml(jvmti, jni, gdata->finished_lock); + gdata->request_agent_thread_stop = JNI_TRUE; + while (!gdata->is_agent_finished) { + rml.wait(1000); + } + debug("Native agent stopped"); +} + +/* + * The method is called by agent thread to ensure that thread correctly exits. + */ +static jboolean +should_stop(jvmtiEnv *jvmti, JNIEnv *jni) { + jboolean should_stop = JNI_FALSE; + RawMonitorLocker rml(jvmti, jni, gdata->finished_lock); + should_stop = gdata->request_agent_thread_stop; + if (should_stop == JNI_TRUE) { + gdata->is_agent_finished = JNI_TRUE; + rml.notify_all(); + } + return should_stop; +} + +/* + * Agent stress functions. The agent is stopped in VMDeath only and should be + * always ready to get JVMTI_ERROR_THREAD_NOT_ALIVE error. + */ + +/* Read stack, frames, method, variables, etc. */ +static void +walk_stack(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) { + jvmtiError err = JVMTI_ERROR_NONE; + debug("In walk_stack: %p", thread); + + jvmtiFrameInfo frames[5]; + jint count = 0; + err = jvmti->GetStackTrace(thread, 0, 5, frames, &count); + if (err == JVMTI_ERROR_THREAD_NOT_ALIVE || err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_error(err, "GetStackTrace"); + + debug("Stack depth: %d", count); + + for (int frame_index = 0; frame_index < count; frame_index++) { + char *method_name = nullptr; + jint method_modifiers = 0; + err = jvmti->GetMethodName(frames[frame_index].method, &method_name, nullptr, nullptr); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_status(jni, err, "GetMethodName"); + + err = jvmti->GetMethodModifiers(frames[frame_index].method, &method_modifiers); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_status(jni, err, "GetMethodModifiers"); + + debug("Inspecting method: %s, %d", method_name, method_modifiers); + deallocate(jvmti, jni, method_name); + + jvmtiLocalVariableEntry* table = nullptr; + jint entry_count = 0; + err = jvmti->GetLocalVariableTable(frames[frame_index].method, &entry_count, &table); + if (err == JVMTI_ERROR_NATIVE_METHOD || err == JVMTI_ERROR_ABSENT_INFORMATION + || err == JVMTI_ERROR_WRONG_PHASE) { + continue; + } + check_jvmti_status(jni, err, "GetLocalVariableTable"); + + gdata->inspectedMethods += 1; + gdata->inspectedVariables += entry_count; + + debug("Variables: "); + for (int cnt = 0; cnt < entry_count; cnt++) { + debug(" %s %d", table[cnt].name, table[cnt].slot); + deallocate(jvmti, jni, table[cnt].name); + deallocate(jvmti, jni, table[cnt].signature); + deallocate(jvmti, jni, table[cnt].generic_signature); + } + deallocate(jvmti, jni, table); + } + debug("---- End of stack inspection %d -----", count); +} + +/* Iterate with walk_stack through all thread. */ +static void JNICALL +walk_all_threads_stacks(jvmtiEnv *jvmti, JNIEnv *jni) { + jint threads_count = 0; + jthread *threads = nullptr; + jvmtiError err = JVMTI_ERROR_NONE; + debug("Inspect: Starting cycle..."); + err = jvmti->GetAllThreads(&threads_count, &threads); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_status(jni, err, "GetAllThreads"); + for (int t = 0; t < (int)threads_count; t++) { + jvmtiThreadInfo info; + debug("Inspecting thread num %d at addr [%p]",t, threads[t]); + err = jvmti->GetThreadInfo(threads[t], &info); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_status(jni, err, "GetThreadInfo"); + // Skip agent thread itself and JFR threads to avoid potential deadlocks + if (strstr(info.name, JVMTI_AGENT_NAME) == nullptr + && strstr(info.name, "JFR") == nullptr) { + // The non-intrusive actions are allowed to ensure that results of target + // thread are not affected. + jthread thread = threads[t]; + walk_stack(jvmti, jni, thread); + + // Suspend/resume are solo capabilities and are treated like debugging + if (gdata->is_debugger_enabled) { + debug("Inspect: Trying to suspend thread %s", info.name); + err = jvmti->SuspendThread(thread); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + if (err == JVMTI_ERROR_THREAD_NOT_ALIVE) { + debug("Inspect: thread %s is not alive. Skipping.", info.name); + continue; + } + check_jvmti_status(jni, err, "SuspendThread"); + debug("Inspect: Suspended thread %s", info.name); + + walk_stack(jvmti, jni, thread); + + debug("Inspect: Trying to resume thread %s", info.name); + err = jvmti->ResumeThread(thread); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_status(jni, err, "ResumeThread"); + debug("Inspect: Resumed thread %s", info.name); + } + + } + deallocate(jvmti, jni, info.name); + jni->DeleteLocalRef(info.thread_group); + jni->DeleteLocalRef(info.context_class_loader); + jni->DeleteLocalRef(threads[t]); + } + deallocate(jvmti, jni, threads); +} + +/* Heap inspection helpers. */ +static jint JNICALL +heap_iteration_callback(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data) { + int* count = (int*) user_data; + *count += 1; + return JVMTI_VISIT_OBJECTS; +} + +static jint +get_heap_info(jvmtiEnv *jvmti, JNIEnv *jni, jclass klass) { + jvmtiError err = JVMTI_ERROR_NONE; + int count = 0; + jvmtiHeapCallbacks callbacks; + (void) memset(&callbacks, 0, sizeof (callbacks)); + callbacks.heap_iteration_callback = &heap_iteration_callback; + err = jvmti->IterateThroughHeap(0, klass, &callbacks, &count); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return count; + } + check_jvmti_status(jni, err, "IterateThroughHeap"); + return count; +} + + +/* + * Events testing helper functions. + */ + + +int +is_event_frequent(int event) { + // Should include all interpreter-only events and all frequent events. + return event == JVMTI_EVENT_SINGLE_STEP + || event == JVMTI_EVENT_METHOD_ENTRY + || event == JVMTI_EVENT_METHOD_EXIT + || event == JVMTI_EVENT_FRAME_POP + || event == JVMTI_EVENT_FIELD_ACCESS + || event == JVMTI_EVENT_FIELD_MODIFICATION + || event == JVMTI_EVENT_EXCEPTION_CATCH + || event == JVMTI_EVENT_EXCEPTION + ; +} + +int +is_event_excluded(int event) { + for (int i = 0; i < gdata->events_excluded_size; i++) { + if (event == gdata->events_excluded[i]) { + return JNI_TRUE; + } + } + return JNI_FALSE; +} + +static void +enable_events(jvmtiEnv *jvmti, jboolean update_frequent_events) { + debug("Enabling events\n"); + for(int event = JVMTI_MIN_EVENT_TYPE_VAL; event < JVMTI_MAX_EVENT_TYPE_VAL; event++) { + if (is_event_excluded(event)) { + debug("Event %d excluded.", event); + continue; + } + if (is_event_frequent(event) != update_frequent_events ) { + debug("Event %d is not enabled as frequent/slow.", event); + continue; + } + jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, + static_cast(event), nullptr); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_error(err, "SetEventNotificationMode"); + } + debug("Enabling events done\n"); +} + +static void +enable_frequent_events(jvmtiEnv *jvmti) { + enable_events(jvmti, JNI_TRUE); +} + +static void +enable_common_events(jvmtiEnv *jvmti) { + enable_events(jvmti,JNI_FALSE); +} + + +static void +disable_all_events(jvmtiEnv *jvmti) { + jvmtiError err = JVMTI_ERROR_NONE; + for (int event = JVMTI_MIN_EVENT_TYPE_VAL; event < JVMTI_MAX_EVENT_TYPE_VAL; event++) { + // VM_DEATH is used to stop agent + if (event == JVMTI_EVENT_VM_DEATH) { + continue; + } + err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, static_cast(event), nullptr); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_error(err, "SetEventNotificationMode"); + } +} + +/* + * The JVMTI agent main loop. + */ + +static void JNICALL +stress_agent(jvmtiEnv *jvmti, JNIEnv *jni, void *p) { + jvmtiError err = JVMTI_ERROR_NONE; + debug("Debugger: Thread started."); + while (!should_stop(jvmti, jni)) { + + if (gdata->are_events_enabled) { + enable_common_events(jvmti); + } + + // Iterate through heap and get some statistics + if (gdata->is_heap_iterate_enabled) { + jclass kls = find_class(jni, "java/lang/String"); + jlong obj_count = get_heap_info(jvmti, jni, kls); + debug("Debugger: Heap info: %d", obj_count); + } + + + // requires can_generate_sampled_object_alloc_events + // which is solo capability + if (gdata->is_heap_sampling_enabled) { + err = jvmti->SetHeapSamplingInterval(gdata->heap_sampling_interval); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + check_jvmti_status(jni, err, "SetHeapSamplingInterval"); + } + + if (gdata->is_tracing_enabled) { + walk_all_threads_stacks(jvmti, jni); + } + + sleep_ms(gdata->events_interval); + + err = jvmti->SetHeapSamplingInterval(0); + if (err == JVMTI_ERROR_WRONG_PHASE) { + return; + } + if (gdata->is_heap_sampling_enabled) { + check_jvmti_status(jni, err, "SetHeapSamplingInterval"); + } + + if (gdata->are_frequent_events_enabled) { + enable_frequent_events(jvmti); + sleep_ms(gdata->frequent_events_interval); + } + disable_all_events(jvmti); + sleep_ms(gdata->events_interval); + } + debug("Debugger: Thread finished."); +} + + +/* + * Events section. + * Most of the events just increase counter and print debug info. + * The VMInit/VMDeath are also start and stop jvmti stress agent. + */ + +static void +register_event(jlong *event) { + (*event)++; +} + +static void JNICALL +cbVMInit(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) { + register_event(&gdata->cbVMInit); + debug("Event cbVMInit\n"); + create_agent_thread(jvmti, jni, JVMTI_AGENT_NAME, &stress_agent); +} + +static void JNICALL +cbVMDeath(jvmtiEnv *jvmti, JNIEnv *jni) { + register_event(&gdata->cbVMDeath); + debug("Event cbVMDeath\n"); + request_agent_thread_stop_and_wait(jvmti, jni); + destroy_raw_monitor(jvmti, jni, gdata->finished_lock); +} + +static void JNICALL +cbThreadStart(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) { + register_event(&gdata->cbThreadStart); + debug("Event cbThreadStart\n"); +} + +static void JNICALL +cbThreadEnd(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) { + register_event(&gdata->cbThreadEnd); + debug("Event cbThreadEnd\n"); +} + +static void JNICALL +cbVirtualThreadStart(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) { + register_event(&gdata->cbThreadStart); + debug("Event cbThreadStart\n"); +} + +static void JNICALL +cbVirtualThreadEnd(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) { + register_event(&gdata->cbThreadEnd); + debug("Event cbThreadEnd\n"); +} + +static void JNICALL +cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* jni, + jclass class_being_redefined, jobject loader, + const char* name, jobject protection_domain, + jint class_data_len, const unsigned char *class_data, + jint *new_class_data_len, unsigned char **new_class_data) { + /* TODO uncomment for more stress + unsigned char* new_class_data_copy = (unsigned char*) malloc(class_data_len); + memcpy(new_class_data_copy, class_data, class_data_len); + *new_class_data_len = class_data_len; + *new_class_data = new_class_data_copy; + */ + register_event(&gdata->cbClassFileLoadHook); + debug("Event cbClassFileLoadHook\n"); +} + +static void JNICALL +cbClassLoad(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jclass klass) { + register_event(&gdata->cbClassLoad); + debug("Event cbClassLoad\n"); +} + +static void JNICALL +cbClassPrepare(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread, jclass klass) { + register_event(&gdata->cbClassPrepare); + debug("Event cbClassPrepare\n"); +} + +static void JNICALL +cbDataDumpRequest(jvmtiEnv *jvmti) { + register_event(&gdata->cbDataDumpRequest); + debug("Event cbDataDumpRequest\n"); +} + +static void JNICALL +cbException(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + jlocation location, + jobject exception, + jmethodID catch_method, + jlocation catch_location) { + register_event(&gdata->cbException); + debug("Event cbException\n"); +} + +static void JNICALL +cbExceptionCatch(jvmtiEnv *jvmti, JNIEnv *jni, + jthread thread, jmethodID method, jlocation location, + jobject exception) { + register_event(&gdata->cbExceptionCatch); + debug("Event cbExceptionCatch\n"); +} + +static void JNICALL +cbMonitorWait(jvmtiEnv *jvmti, JNIEnv *jni, + jthread thread, jobject object, jlong timeout) { + register_event(&gdata->cbMonitorWait); + debug("Event cbMonitorWait\n"); +} + +static void JNICALL +cbMonitorWaited(jvmtiEnv *jvmti, JNIEnv *jni, + jthread thread, jobject object, jboolean timed_out) { + register_event(&gdata->cbMonitorWaited); + debug("Event cbMonitorWaited\n"); +} + +static void JNICALL +cbMonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv *jni, + jthread thread, jobject object) { + register_event(&gdata->cbMonitorContendedEnter); + debug("Event cbMonitorContendedEnter\n"); +} + +static void JNICALL +cbMonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv* jni, + jthread thread, jobject object) { + register_event(&gdata->cbMonitorContendedEntered); + debug("Event cbMonitorContendedEntered\n"); +} + +static void JNICALL +cbGarbageCollectionStart(jvmtiEnv *jvmti) { + register_event(&gdata->cbGarbageCollectionStart); + debug("Event cbGarbageCollectionStart\n"); +} + +static void JNICALL +cbGarbageCollectionFinish(jvmtiEnv *jvmti) { + register_event(&gdata->cbGarbageCollectionFinish); + debug("Event cbGarbageCollectionFinish\n"); +} + +static void JNICALL +cbObjectFree(jvmtiEnv *jvmti, jlong tag) { + register_event(&gdata->cbObjectFree); + debug("Event cbObjectFree\n"); +} + +static void JNICALL +cbBreakpoint(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + jlocation location) { + register_event(&gdata->cbBreakpoint); + debug("Event cbBreakpoint\n"); +} + +static void JNICALL +cbSingleStep(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + jlocation location) { + register_event(&gdata->cbSingleStep); + debug("Event cbSingleStep\n"); +} + +static void JNICALL +cbFieldAccess(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field) { + register_event(&gdata->cbFieldAccess); + debug("Event cbFieldAccess\n"); +} + +static void JNICALL +cbFieldModification(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field, + char signature_type, + jvalue new_value) { + register_event(&gdata->cbFieldModification); + debug("Event cbFieldModification\n"); +} + +static void JNICALL +cbFramePop(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception) { + register_event(&gdata->cbFramePop); + debug("Event cbFramePop\n"); +} + +static void JNICALL +cbMethodEntry(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method) { + register_event(&gdata->cbMethodEntry); + debug("Event cbMethodEntry\n"); +} + +static void JNICALL +cbMethodExit(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception, + jvalue return_value) { + register_event(&gdata->cbMethodExit); + debug("Event cbMethodExit\n"); +} + +static void JNICALL +cbNativeMethodBind(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jmethodID method, + void* address, + void** new_address_ptr) { + register_event(&gdata->cbNativeMethodBind); + debug("Event cbNativeMethodBind\n"); +} + +static void JNICALL +cbCompiledMethodLoad(jvmtiEnv *jvmti, + jmethodID method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info) { + register_event(&gdata->cbCompiledMethodLoad); + debug("Event cbCompiledMethodLoad\n"); +} + +static void JNICALL +cbCompiledMethodUnload(jvmtiEnv *jvmti, + jmethodID method, + const void* code_addr) { + register_event(&gdata->cbCompiledMethodUnload); + debug("Event cbCompiledMethodUnload\n"); +} + +static void JNICALL +cbDynamicCodeGenerated(jvmtiEnv *jvmti, + const char* name, + const void* address, + jint length) { + register_event(&gdata->cbDynamicCodeGenerated); + debug("Event cbDynamicCodeGenerated\n"); +} + +static void JNICALL +cbResourceExhausted(jvmtiEnv *jvmti, + JNIEnv *jni, + jint flags, + const void* reserved, + const char* description) { + register_event(&gdata->cbResourceExhausted); + debug("Event cbResourceExhausted\n"); +} + +static void JNICALL +cbVMObjectAlloc(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jobject object, + jclass object_klass, + jlong size) { + register_event(&gdata->cbVMObjectAlloc); + debug("Event cbVMObjectAlloc\n"); +} + +static void JNICALL +cbSampledObjectAlloc(jvmtiEnv *jvmti, + JNIEnv *jni, + jthread thread, + jobject object, + jclass object_klass, + jlong size) { + register_event(&gdata->cbSampledObjectAlloc); + debug("Event cbSampledObjectAlloc\n"); +} + + + +static void +set_callbacks(jvmtiEnv *jvmti, jboolean on) { + jvmtiError err = JVMTI_ERROR_NONE; + jvmtiEventCallbacks callbacks; + + (void) memset(&callbacks, 0, sizeof (callbacks)); + if (on == JNI_FALSE) { + err = jvmti->SetEventCallbacks(&callbacks, (int) sizeof (jvmtiEventCallbacks)); + check_jvmti_error(err, "SetEventCallbacks"); + return; + } + callbacks.Breakpoint = &cbBreakpoint; + callbacks.ClassFileLoadHook = &cbClassFileLoadHook; + callbacks.ClassLoad = &cbClassLoad; + callbacks.ClassPrepare = &cbClassPrepare; + callbacks.CompiledMethodLoad = &cbCompiledMethodLoad; + callbacks.CompiledMethodUnload = &cbCompiledMethodUnload; + callbacks.DataDumpRequest = &cbDataDumpRequest; + callbacks.DynamicCodeGenerated = &cbDynamicCodeGenerated; + callbacks.Exception = &cbException; + callbacks.ExceptionCatch = &cbExceptionCatch; + callbacks.FieldAccess = &cbFieldAccess; + callbacks.FieldModification = &cbFieldModification; + callbacks.FramePop = &cbFramePop; + callbacks.GarbageCollectionFinish = &cbGarbageCollectionFinish; + callbacks.GarbageCollectionStart = &cbGarbageCollectionStart; + callbacks.MethodEntry = &cbMethodEntry; + callbacks.MethodExit = &cbMethodExit; + callbacks.MonitorContendedEnter = &cbMonitorContendedEnter; + callbacks.MonitorContendedEntered = &cbMonitorContendedEntered; + callbacks.MonitorWait = &cbMonitorWait; + callbacks.MonitorWaited = &cbMonitorWaited; + callbacks.NativeMethodBind = &cbNativeMethodBind; + callbacks.ObjectFree = &cbObjectFree; + callbacks.ResourceExhausted = &cbResourceExhausted; + callbacks.SampledObjectAlloc = &cbSampledObjectAlloc; + callbacks.SingleStep = &cbSingleStep; + callbacks.ThreadEnd = &cbThreadEnd; + callbacks.ThreadStart = &cbThreadStart; + callbacks.VirtualThreadEnd = &cbVirtualThreadEnd; + callbacks.VirtualThreadStart = &cbVirtualThreadStart; + callbacks.VMDeath = &cbVMDeath; + callbacks.VMInit = &cbVMInit; + callbacks.VMObjectAlloc = &cbVMObjectAlloc; + err = jvmti->SetEventCallbacks(&callbacks, (int) sizeof (jvmtiEventCallbacks)); + check_jvmti_error(err, "SetEventCallbacks"); +} + +static +void get_capabilities(jvmtiEnv *jvmti) { + jvmtiError err = JVMTI_ERROR_NONE; + jvmtiCapabilities capabilities; + (void) memset(&capabilities, 0, sizeof (capabilities)); + err = jvmti->GetPotentialCapabilities(&capabilities); + + if (!gdata->is_debugger_enabled) { + //init_always_solo_capabilities + capabilities.can_suspend = false; + + // onload_solo + capabilities.can_generate_breakpoint_events = false; + capabilities.can_generate_field_access_events = false; + capabilities.can_generate_field_modification_events = false; + } + + capabilities.can_generate_early_vmstart = false; + + check_jvmti_error(err, "GetPotentialCapabilities"); + err = jvmti->AddCapabilities(&capabilities); + check_jvmti_error(err, "AddCapabilities"); +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { + jvmtiEnv *jvmti = nullptr; + jint res = vm->GetEnv((void **) &jvmti, JVMTI_VERSION_21); + if (res != JNI_OK) { + return JNI_ERR; + } + + jboolean is_debugger_enabled = JNI_TRUE; + jboolean is_verbose = JNI_FALSE; + + if (options != nullptr) { + char *opts = strdup(options); + char *token = strtok(opts, ","); + + while (token != nullptr) { + if (strncmp(token, "debugger=", 9) == 0) { + if (strcmp(token + 9, "true") == 0) { + is_debugger_enabled = JNI_TRUE; + } else { + is_debugger_enabled = JNI_FALSE; + } + } + if (strncmp(token, "verbose", 7) == 0) { + is_verbose = JNI_TRUE; + } + token = strtok(nullptr, ","); + } + free(opts); + } + gdata = gdata_init(is_debugger_enabled, is_verbose); + get_capabilities(jvmti); + gdata->finished_lock = create_raw_monitor(jvmti, "Finished lock"); + set_callbacks(jvmti, JNI_TRUE); + jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_VM_INIT, nullptr); + check_jvmti_error(err, "SetEventNotificationMode"); + return JNI_OK; +} + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm) { + if (!gdata->request_agent_thread_stop) { + printf("Agent_OnUnload happened before requested stop.\n"); + } + gdata_close(); +} From 57210af9bceb582be112564465ab66cebd43a4c0 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sat, 16 Aug 2025 04:41:25 +0000 Subject: [PATCH 061/112] 8365555: Cleanup redundancies in jpackage implementation Reviewed-by: almatvee --- .../jpackage/internal/DesktopIntegration.java | 2 +- .../internal/LinuxApplicationLayout.java | 25 +- .../internal/LinuxApplicationLayoutMixin.java | 2 +- .../jpackage/internal/LinuxDebBundler.java | 3 +- .../internal/LinuxPackageBuilder.java | 31 +- .../jpackage/internal/model/LinuxPackage.java | 3 - .../internal/model/LinuxPackageMixin.java | 7 +- .../jdk/jpackage/internal/AppImageSigner.java | 38 +- .../jdk/jpackage/internal/MacAppBundler.java | 4 +- .../internal/MacApplicationBuilder.java | 21 +- .../internal/MacApplicationLayout.java | 29 +- .../internal/MacApplicationLayoutMixin.java | 7 +- .../internal/MacBuildEnvFromParams.java} | 32 +- .../jdk/jpackage/internal/MacBundle.java | 17 +- .../jdk/jpackage/internal/MacDmgBundler.java | 2 +- .../internal/MacDmgPackageBuilder.java | 1 - .../jdk/jpackage/internal/MacFromParams.java | 16 +- .../jpackage/internal/MacPackageBuilder.java | 13 +- .../internal/MacPackagingPipeline.java | 148 ++- .../jdk/jpackage/internal/MacPkgBundler.java | 2 +- .../jpackage/internal/model/MacPackage.java | 27 +- .../jpackage/internal/ApplicationBuilder.java | 27 +- .../internal/ApplicationImageUtils.java | 2 +- .../jdk/jpackage/internal/BuildEnv.java | 82 +- .../jpackage/internal/BuildEnvBuilder.java | 29 +- .../jpackage/internal/BuildEnvFromParams.java | 24 +- .../jdk/jpackage/internal/FromParams.java | 7 +- .../internal/JLinkRuntimeBuilder.java | 2 +- .../jdk/jpackage/internal/PackageBuilder.java | 38 +- .../jpackage/internal/PackagingPipeline.java | 211 ++--- .../internal/model/AppImageLayout.java | 99 +- .../internal/model/ApplicationLayout.java | 40 +- .../jdk/jpackage/internal/model/Package.java | 73 +- .../internal/model/RuntimeBuilder.java | 2 +- .../internal/model/RuntimeLayout.java | 26 +- .../internal/resources/ResourceLocator.java | 4 +- .../jdk/jpackage/internal/util/PathGroup.java | 17 + .../jdk/jpackage/internal/util/PathUtils.java | 11 +- .../internal/WinPackagingPipeline.java | 2 - .../internal/model/WinExePackage.java | 1 + .../jpackage/helpers-test/TEST.properties | 2 +- .../jdk/jpackage/test/JavaAppDescTest.java | 15 +- .../jdk/jpackage/test/JPackageCommand.java | 13 +- test/jdk/tools/jpackage/junit/TEST.properties | 2 + .../internal/LinuxApplicationLayoutTest.java | 57 ++ .../jdk/tools/jpackage/junit/linux/junit.java | 32 + .../internal/MacApplicationLayoutTest.java | 57 ++ .../tools/jpackage/junit/macosx/junit.java | 32 + .../jdk/jpackage/internal/BuildEnvTest.java | 140 +++ .../internal/PackagingPipelineTest.java | 872 ++++++++++++++++++ .../internal/model/AppImageLayoutTest.java | 99 +- .../internal/model/ApplicationLayoutTest.java | 63 +- .../jpackage/internal/util/PathGroupTest.java | 8 + .../jdk/jpackage/test/JUnitAdapter.java | 14 +- .../tools/jpackage/junit/windows/junit.java | 3 +- 55 files changed, 2114 insertions(+), 422 deletions(-) rename src/jdk.jpackage/{share/classes/jdk/jpackage/internal/AppImageDesc.java => macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java} (59%) create mode 100644 test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxApplicationLayoutTest.java create mode 100644 test/jdk/tools/jpackage/junit/linux/junit.java create mode 100644 test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationLayoutTest.java create mode 100644 test/jdk/tools/jpackage/junit/macosx/junit.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/BuildEnvTest.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java rename test/jdk/tools/jpackage/{helpers-test => junit/tools}/jdk/jpackage/test/JUnitAdapter.java (90%) diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java index dad9917c48f..2d0c78c2474 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -355,7 +355,7 @@ final class DesktopIntegration extends ShellCustomAction { * - installPath(): path where it should be installed by package manager; */ private InstallableFile createDesktopFile(String fileName) { - var srcPath = pkg.asPackageApplicationLayout().orElseThrow().resolveAt(env.appImageDir()).desktopIntegrationDirectory().resolve(fileName); + var srcPath = env.asApplicationLayout().orElseThrow().desktopIntegrationDirectory().resolve(fileName); var installPath = pkg.asInstalledPackageApplicationLayout().orElseThrow().desktopIntegrationDirectory().resolve(fileName); return new InstallableFile(srcPath, installPath); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java index ea87363a9bb..677ee199a84 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java @@ -24,9 +24,10 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.util.CompositeProxy; @@ -40,7 +41,25 @@ interface LinuxApplicationLayout extends ApplicationLayout, LinuxApplicationLayo @Override default LinuxApplicationLayout resolveAt(Path root) { - return create(ApplicationLayout.super.resolveAt(root), - resolveNullablePath(root, libAppLauncher())); + return (LinuxApplicationLayout)ApplicationLayout.super.resolveAt(root); + } + + @Override + default LinuxApplicationLayout unresolve() { + return (LinuxApplicationLayout)ApplicationLayout.super.unresolve(); + } + + @Override + default LinuxApplicationLayout resetRootDirectory() { + if (isResolved()) { + return create(ApplicationLayout.super.resetRootDirectory(), libAppLauncher()); + } else { + return this; + } + } + + @Override + default LinuxApplicationLayout map(UnaryOperator mapper) { + return create(ApplicationLayout.super.map(mapper), mapNullablePath(mapper, libAppLauncher())); } } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java index a88dbc9e16e..21606153669 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java @@ -26,7 +26,7 @@ package jdk.jpackage.internal; import java.nio.file.Path; -// Must be publc to allow access from AppImageLayout.toPathGroup() +// Must be public to allow access from AppImageLayout.toPathGroup() public interface LinuxApplicationLayoutMixin { /** diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java index 1915fd00c0e..d5b369ba23a 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java @@ -351,8 +351,7 @@ public class LinuxDebBundler extends LinuxPackageBundler { data.put("APPLICATION_LICENSE_TEXT", licenseText); data.put("APPLICATION_ARCH", pkg.arch()); data.put("APPLICATION_INSTALLED_SIZE", Long.toString( - AppImageLayout.toPathGroup(pkg.packageLayout().resolveAt( - env.appImageDir())).sizeInBytes() >> 10)); + AppImageLayout.toPathGroup(env.appImageLayout()).sizeInBytes() >> 10)); data.put("APPLICATION_HOMEPAGE", pkg.aboutURL().map( value -> "Homepage: " + value).orElse("")); data.put("APPLICATION_VERSION_WITH_RELEASE", ((LinuxDebPackage) pkg).versionWithRelease()); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java index 4ed84909958..cc00d7816f5 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java @@ -33,7 +33,9 @@ import java.util.regex.Pattern; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.LinuxApplication; import jdk.jpackage.internal.model.LinuxPackage; +import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.model.LinuxPackageMixin; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.StandardPackageType; @@ -52,24 +54,35 @@ final class LinuxPackageBuilder { pkgBuilder.name(pkgBuilder.create().packageName().toLowerCase().replaceAll("[ _]", "-")); } - final var pkg = pkgBuilder.create(); + final var tmpPkg = pkgBuilder.create(); - final var stdPkgType = pkg.asStandardPackageType(); + final var stdPkgType = tmpPkg.asStandardPackageType(); if (stdPkgType.isPresent()) { - validatePackageName(pkg.packageName(), stdPkgType.orElseThrow()); + validatePackageName(tmpPkg.packageName(), stdPkgType.orElseThrow()); } - var reply = create(pkg, pkg.packageLayout()); - if (reply.isInstallDirInUsrTree()) { - reply = create(pkg, usrTreePackageLayout(pkg.relativeInstallDir(), pkg.packageName())); + final AppImageLayout relativeInstalledLayout; + if (create(tmpPkg).isInstallDirInUsrTree()) { + final var usrTreeLayout = usrTreePackageLayout(tmpPkg.relativeInstallDir(), tmpPkg.packageName()); + if (tmpPkg.isRuntimeInstaller()) { + relativeInstalledLayout = RuntimeLayout.create(usrTreeLayout.runtimeDirectory()); + } else { + relativeInstalledLayout = usrTreeLayout; + } + } else { + relativeInstalledLayout = tmpPkg.appImageLayout().resolveAt(tmpPkg.relativeInstallDir()).resetRootDirectory(); } - return reply; + final var app = ApplicationBuilder.overrideAppImageLayout(pkgBuilder.app(), relativeInstalledLayout); + + return create(pkgBuilder + .app(LinuxApplication.create(app)) + .installedPackageLayout(relativeInstalledLayout.resolveAt(Path.of("/")).resetRootDirectory()) + .create()); } - private LinuxPackage create(Package pkg, AppImageLayout pkgLayout) throws ConfigException { + private LinuxPackage create(Package pkg) throws ConfigException { return LinuxPackage.create(pkg, new LinuxPackageMixin.Stub( - pkgLayout, Optional.ofNullable(menuGroupName).orElseGet(DEFAULTS::menuGroupName), Optional.ofNullable(category), Optional.ofNullable(additionalDependencies), diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java index aef1c5eb700..28d47fc46a8 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java @@ -36,9 +36,6 @@ public interface LinuxPackage extends Package, LinuxPackageMixin { LinuxApplication app(); - @Override - AppImageLayout packageLayout(); - @Override default String packageFileName() { String packageFileNameTemlate = asStandardPackageType().map(stdType -> { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java index f890aa6ff39..5bcf57194f6 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java @@ -31,11 +31,6 @@ import java.util.Optional; */ public interface LinuxPackageMixin { - /** - * Overrides {@link Package#packageLayout()}. - */ - AppImageLayout packageLayout(); - /** * Gets the name of the start menu group where to create shortcuts for * application launchers of this package. @@ -88,7 +83,7 @@ public interface LinuxPackageMixin { /** * Default implementation of {@link LinuxPackageMixin} interface. */ - record Stub(AppImageLayout packageLayout, String menuGroupName, + record Stub(String menuGroupName, Optional category, Optional additionalDependencies, Optional release, String arch) implements LinuxPackageMixin { } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java index d7b2431c1d3..7563dfa0ed3 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java @@ -25,6 +25,8 @@ package jdk.jpackage.internal; import static java.util.stream.Collectors.joining; +import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; +import static jdk.jpackage.internal.model.MacPackage.RUNTIME_BUNDLE_LAYOUT; import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; import java.io.IOException; @@ -44,13 +46,14 @@ import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; +import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ExceptionBox; final class AppImageSigner { - static Consumer createSigner(MacApplication app, CodesignConfig signingCfg) { + static Consumer createSigner(MacApplication app, CodesignConfig signingCfg) { return toConsumer(appImage -> { try { new AppImageSigner(Codesigners.create(signingCfg)).sign(app, appImage); @@ -67,12 +70,12 @@ final class AppImageSigner { private static final class SignFilter implements Predicate { - SignFilter(Application app, Path appImage) { + SignFilter(Application app, MacBundle appImage) { Objects.requireNonNull(appImage); // Don't explicitly sign main launcher. It will be implicitly signed when the bundle is signed. otherExcludePaths = app.asApplicationLayout().map(appLayout -> { - return appLayout.resolveAt(appImage); + return appLayout.resolveAt(appImage.root()); }).map(ApplicationLayout::launchersDirectory).flatMap(launchersDir -> { return app.mainLauncher().map(Launcher::executableNameWithSuffix).map(launchersDir::resolve); }).map(Set::of).orElseGet(Set::of); @@ -98,11 +101,16 @@ final class AppImageSigner { private final Set otherExcludePaths; } - private void sign(MacApplication app, Path appImage) throws CodesignException, IOException { + private void sign(MacApplication app, MacBundle appImage) throws CodesignException, IOException { + if (!appImage.isValid()) { + throw new IllegalArgumentException(); + } + + app = normalizeAppImageLayout(app); final var fileFilter = new SignFilter(app, appImage); - try (var content = Files.walk(appImage)) { + try (var content = Files.walk(appImage.root())) { content.filter(fileFilter).forEach(toConsumer(path -> { final var origPerms = ensureCanWrite(path); try { @@ -118,10 +126,10 @@ final class AppImageSigner { // Sign runtime root directory if present app.asApplicationLayout().map(appLayout -> { - return appLayout.resolveAt(appImage); + return appLayout.resolveAt(appImage.root()); }).map(MacApplicationLayout.class::cast).map(MacApplicationLayout::runtimeRootDirectory).ifPresent(codesigners); - final var frameworkPath = appImage.resolve("Contents/Frameworks"); + final var frameworkPath = appImage.contentsDir().resolve("Frameworks"); if (Files.isDirectory(frameworkPath)) { try (var content = Files.list(frameworkPath)) { content.forEach(toConsumer(path -> { @@ -131,7 +139,7 @@ final class AppImageSigner { } // Sign the app image itself - codesigners.accept(appImage); + codesigners.accept(appImage.root()); } private static Set ensureCanWrite(Path path) { @@ -235,5 +243,19 @@ final class AppImageSigner { } } + private static MacApplication normalizeAppImageLayout(MacApplication app) { + switch (app.imageLayout()) { + case MacApplicationLayout macLayout -> { + return MacApplicationBuilder.overrideAppImageLayout(app, APPLICATION_LAYOUT); + } + case RuntimeLayout macLayout -> { + return MacApplicationBuilder.overrideAppImageLayout(app, RUNTIME_BUNDLE_LAYOUT); + } + default -> { + throw new IllegalArgumentException(); + } + } + } + private final Codesigners codesigners; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java index 28d91156059..cce35ece117 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java @@ -42,11 +42,11 @@ public class MacAppBundler extends AppImageBundler { final BuildEnv env; if (StandardBundlerParam.hasPredefinedAppImage(params)) { - env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); + env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params); final var pkg = MacPackagingPipeline.createSignAppImagePackage(app, env); MacPackagingPipeline.build(Optional.of(pkg)).create().execute(env, pkg, output); } else { - env = BuildEnv.withAppImageDir(BuildEnvFromParams.BUILD_ENV.fetchFrom(params), output); + env = BuildEnv.withAppImageDir(MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params), output); MacPackagingPipeline.build(Optional.empty()) .excludeDirFromCopying(output.getParent()) .excludeDirFromCopying(OUTPUT_DIR.fetchFrom(params)).create().execute(env, app); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java index ca05be519ff..fc1dd97d9ab 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java @@ -34,6 +34,7 @@ import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacApplicationMixin; +import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.AppImageSigningConfig; final class MacApplicationBuilder { @@ -95,12 +96,28 @@ final class MacApplicationBuilder { validateAppVersion(app); - final var mixin = new MacApplicationMixin.Stub(validatedIcon(), validatedBundleName(), - validatedBundleIdentifier(), validatedCategory(), appStore, createSigningConfig()); + final var mixin = new MacApplicationMixin.Stub( + validatedIcon(), + validatedBundleName(), + validatedBundleIdentifier(), + validatedCategory(), + appStore, + createSigningConfig()); return MacApplication.create(app, mixin); } + static MacApplication overrideAppImageLayout(MacApplication app, AppImageLayout appImageLayout) { + final var mixin = new MacApplicationMixin.Stub( + app.icon(), + app.bundleName(), + app.bundleIdentifier(), + app.category(), + app.appStore(), + app.signingConfig()); + return MacApplication.create(ApplicationBuilder.overrideAppImageLayout(app, appImageLayout), mixin); + } + static boolean isValidBundleIdentifier(String id) { for (int i = 0; i < id.length(); i++) { char a = id.charAt(i); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java index 971548ea4f5..7c90134e288 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java @@ -24,9 +24,10 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.util.CompositeProxy; @@ -35,12 +36,32 @@ interface MacApplicationLayout extends ApplicationLayout, MacApplicationLayoutMi static MacApplicationLayout create(ApplicationLayout layout, Path runtimeRootDir) { return CompositeProxy.build() .invokeTunnel(CompositeProxyTunnel.INSTANCE) - .create(MacApplicationLayout.class, layout, new MacApplicationLayoutMixin.Stub(runtimeRootDir)); + .create(MacApplicationLayout.class, layout, + new MacApplicationLayoutMixin.Stub(runtimeRootDir)); } @Override default MacApplicationLayout resolveAt(Path root) { - return create(ApplicationLayout.super.resolveAt(root), - resolveNullablePath(root, runtimeRootDirectory())); + return (MacApplicationLayout)ApplicationLayout.super.resolveAt(root); } + + @Override + default MacApplicationLayout unresolve() { + return (MacApplicationLayout)ApplicationLayout.super.unresolve(); + } + + @Override + default MacApplicationLayout resetRootDirectory() { + if (isResolved()) { + return create(ApplicationLayout.super.resetRootDirectory(), runtimeRootDirectory()); + } else { + return this; + } + } + + @Override + default MacApplicationLayout map(UnaryOperator mapper) { + return create(ApplicationLayout.super.map(mapper), mapNullablePath(mapper, runtimeRootDirectory())); + } + } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java index b6342b173dc..0acc853f58b 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java @@ -26,12 +26,15 @@ package jdk.jpackage.internal; import java.nio.file.Path; -// Must be publc to allow access from AppImageLayout.toPathGroup() +// Must be public to allow access from AppImageLayout.toPathGroup() public interface MacApplicationLayoutMixin { /** - * Path to the root Java runtime directory in the application image. + * Returns path to the root Java runtime directory in the application image. + *

      * The root Java runtime directory should have "Contents/Home" subdirectory. + * + * @return the path to the root Java runtime directory in the application image */ Path runtimeRootDirectory(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageDesc.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java similarity index 59% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageDesc.java rename to src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java index fafcb7777a6..31759c8c529 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageDesc.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java @@ -22,35 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.jpackage.internal; -import java.nio.file.Path; -import java.util.Objects; -import java.util.Optional; -import jdk.jpackage.internal.model.AppImageLayout; -import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.MacPackage; -record AppImageDesc(AppImageLayout appImageLayout, Path path) { +final class MacBuildEnvFromParams { - AppImageDesc { - Objects.requireNonNull(appImageLayout); - Objects.requireNonNull(path); - } - - AppImageLayout resolvedAppImagelayout() { - return appImageLayout.resolveAt(path); - } - - Optional asResolvedApplicationLayout() { - return asApplicationLayout().map(v -> v.resolveAt(path)); - } - - Optional asApplicationLayout() { - if (appImageLayout instanceof ApplicationLayout layout) { - return Optional.of(layout); - } else { - return Optional.empty(); - } - } + static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam(BuildEnv.class, params -> { + return BuildEnvFromParams.create(params, MacPackagingPipeline.APPLICATION_LAYOUT::resolveAt, MacPackage::guessRuntimeLayout); + }); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java index af07a1145dc..f485653cf80 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java @@ -28,6 +28,7 @@ package jdk.jpackage.internal; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; +import java.util.Optional; import jdk.jpackage.internal.model.AppImageLayout; /** @@ -73,7 +74,19 @@ record MacBundle(Path root) { return new MacBundle(dir).isValid(); } - static MacBundle fromAppImageLayout(AppImageLayout layout) { - return new MacBundle(layout.rootDirectory()); + static Optional fromAppImageLayout(AppImageLayout layout) { + final var root = layout.rootDirectory(); + final var bundleSubdir = root.relativize(layout.runtimeDirectory()); + final var contentsDirname = Path.of("Contents"); + var bundleRoot = root; + for (int i = 0; i != bundleSubdir.getNameCount(); i++) { + var nameComponent = bundleSubdir.getName(i); + if (contentsDirname.equals(nameComponent)) { + return Optional.of(new MacBundle(bundleRoot)); + } else { + bundleRoot = bundleRoot.resolve(nameComponent); + } + } + return Optional.empty(); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java index 56aacf6e44a..d2c72765d7a 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java @@ -71,7 +71,7 @@ public class MacDmgBundler extends MacBaseInstallerBundler { Path outputParentDir) throws PackagerException { final var pkg = MacFromParams.DMG_PACKAGE.fetchFrom(params); - var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); + var env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params); final var packager = MacDmgPackager.build().outputDir(outputParentDir).pkg(pkg).env(env); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java index d453bb59184..c2b9c25f327 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java @@ -53,7 +53,6 @@ final class MacDmgPackageBuilder { } MacDmgPackage create() throws ConfigException { - final var superPkgBuilder = pkgBuilder.pkgBuilder(); final var pkg = pkgBuilder.create(); return MacDmgPackage.create(pkg, new MacDmgPackageMixin.Stub( diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java index 754d09a7156..4be2f9859c8 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java @@ -39,13 +39,12 @@ import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE_FI import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE; import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage; -import static jdk.jpackage.internal.model.MacPackage.RUNTIME_PACKAGE_LAYOUT; +import static jdk.jpackage.internal.model.MacPackage.RUNTIME_BUNDLE_LAYOUT; import static jdk.jpackage.internal.model.StandardPackageType.MAC_DMG; import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.List; @@ -64,6 +63,7 @@ import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacDmgPackage; import jdk.jpackage.internal.model.MacFileAssociation; import jdk.jpackage.internal.model.MacLauncher; +import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; @@ -75,20 +75,16 @@ final class MacFromParams { private static MacApplication createMacApplication( Map params) throws ConfigException, IOException { - final var predefinedRuntimeLayout = PREDEFINED_RUNTIME_IMAGE.findIn(params).map(predefinedRuntimeImage -> { - if (Files.isDirectory(RUNTIME_PACKAGE_LAYOUT.resolveAt(predefinedRuntimeImage).runtimeDirectory())) { - return RUNTIME_PACKAGE_LAYOUT; - } else { - return RuntimeLayout.DEFAULT; - } - }); + final var predefinedRuntimeLayout = PREDEFINED_RUNTIME_IMAGE.findIn(params) + .map(MacPackage::guessRuntimeLayout) + .map(RuntimeLayout::unresolve); final var launcherFromParams = new LauncherFromParams(Optional.of(MacFromParams::createMacFa)); final var superAppBuilder = createApplicationBuilder(params, toFunction(launcherParams -> { var launcher = launcherFromParams.create(launcherParams); return MacLauncher.create(launcher); - }), APPLICATION_LAYOUT, predefinedRuntimeLayout); + }), APPLICATION_LAYOUT, RUNTIME_BUNDLE_LAYOUT, predefinedRuntimeLayout); if (hasPredefinedAppImage(params)) { // Set the main launcher start up info. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java index a16194a6260..cf5c6a934f7 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java @@ -24,8 +24,11 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.MacPackagingPipeline.LayoutUtils.packagerLayout; + import java.util.Objects; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.MacPackageMixin; @@ -45,7 +48,15 @@ final class MacPackageBuilder { } MacPackage create() throws ConfigException { - final var pkg = pkgBuilder.create(); + + final var app = (MacApplication)pkgBuilder.app(); + + var pkg = pkgBuilder.create(); + + pkgBuilder.app(MacApplicationBuilder.overrideAppImageLayout(app, packagerLayout(pkg))) + .installedPackageLayout(pkg.installedPackageLayout()); + + pkg = pkgBuilder.create(); return MacPackage.create(pkg, new MacPackageMixin.Stub(pkg.predefinedAppImage().map(v -> predefinedAppImageSigned))); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java index 556efdd0fd3..a6ed164d9f7 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java @@ -39,6 +39,7 @@ import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; import java.io.StringWriter; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; @@ -49,10 +50,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.UnaryOperator; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import jdk.jpackage.internal.PackagingPipeline.AppImageBuildEnv; +import jdk.jpackage.internal.PackagingPipeline.AppImageTaskAction; import jdk.jpackage.internal.PackagingPipeline.ApplicationImageTaskAction; import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID; import jdk.jpackage.internal.PackagingPipeline.CopyAppImageTaskID; @@ -98,21 +101,10 @@ final class MacPackagingPipeline { COPY_SIGN } - static AppImageLayout packagingLayout(Package pkg) { - return pkg.appImageLayout().resolveAt(pkg.relativeInstallDir().getFileName()); - } - static PackagingPipeline.Builder build(Optional pkg) { final var builder = PackagingPipeline.buildStandard() - .appContextMapper(appContext -> { - return new TaskContextProxy(appContext, true, false); - }) - .pkgContextMapper(appContext -> { - final var isRuntimeInstaller = pkg.map(Package::isRuntimeInstaller).orElse(false); - final var withPredefinedAppImage = pkg.flatMap(Package::predefinedAppImage).isPresent(); - return new TaskContextProxy(appContext, false, isRuntimeInstaller || withPredefinedAppImage); - }) - .appImageLayoutForPackaging(MacPackagingPipeline::packagingLayout) + .contextMapper(pkg.map(MacPackagingPipeline::mapPackageTaskContext) + .orElseGet(MacPackagingPipeline::mapAppTaskContext)) .task(PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT) .packageAction(MacPackagingPipeline::runPostAppImageUserScript).add() .task(CopyAppImageTaskID.COPY) @@ -156,13 +148,13 @@ final class MacPackagingPipeline { .addDependent(BuildApplicationTaskID.CONTENT).add(); builder.task(MacBuildApplicationTaskID.SIGN) - .appImageAction(MacPackagingPipeline::sign) + .appImageAction(LayoutUtils.withBundleLayout(MacPackagingPipeline::sign)) .addDependencies(builder.taskGraphSnapshot().getAllTailsOf(PrimaryTaskID.BUILD_APPLICATION_IMAGE)) .addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE) .add(); builder.task(MacCopyAppImageTaskID.COPY_SIGN) - .appImageAction(MacPackagingPipeline::sign) + .appImageAction(LayoutUtils.withBundleLayout(MacPackagingPipeline::sign)) .addDependencies(builder.taskGraphSnapshot().getAllTailsOf(PrimaryTaskID.COPY_APP_IMAGE)) .addDependent(PrimaryTaskID.COPY_APP_IMAGE) .add(); @@ -182,7 +174,6 @@ final class MacPackagingPipeline { disabledTasks.add(PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT); builder.task(MacCopyAppImageTaskID.REPLACE_APP_IMAGE_FILE) .applicationAction(createWriteAppImageFileAction()).add(); - builder.appImageLayoutForPackaging(Package::appImageLayout); } else if (p.isRuntimeInstaller()) { builder.task(MacCopyAppImageTaskID.COPY_RUNTIME_JLILIB) @@ -236,23 +227,83 @@ final class MacPackagingPipeline { }).get(); } - private static void copyAppImage(MacPackage pkg, AppImageDesc srcAppImage, - AppImageDesc dstAppImage) throws IOException { + static final class LayoutUtils { + /** + * Returns unresolved app image layout for the specified package for use with + * the signing function defined in {@link MacPackagingPipeline} class and + * {@link MacPkgPackager} and {@link MacDmgPackager} packagers. + *

      + * Paths of the result app image layout will start with the bundle name. E.g.: + * for a package with relative installation directory set to + * {@code "Applications/Acme/MyApp.app"} and the "launchers" directory of an + * application layout set to {@code "Contents/MacOS"}, the result application + * layout object will be such that the value of its "launchers" directory will + * be {@code "MyApp.app/Contents/MacOS"}. The root directory of the result app + * image layout will be an empty path ({@link Path.of("")}), i.e. the app image + * layout will be unresolved. + * + * @param pkg the package + * @return the unresolved app image layout for the specified package suitable + * for the use with macosx packaging pipeline and packagers + */ + static AppImageLayout packagerLayout(Package pkg) { + return pkg.appImageLayout().resolveAt(pkg.relativeInstallDir().getFileName()).resetRootDirectory(); + } + + static AppImageBuildEnv fromPackagerLayout(AppImageBuildEnv cfg) { + + var bundleDirectoryName = cfg.envLayout().runtimeDirectory().getName(0); + var bundleLayout = cfg.envLayout().map(bundleDirectoryName::relativize).resetRootDirectory(); + var bundleRoot = cfg.env().appImageDir().resolve(bundleDirectoryName); + var app = MacApplicationBuilder.overrideAppImageLayout(cfg.app(), bundleLayout); + var env = BuildEnv.withAppImageLayout(cfg.env(), bundleLayout.resolveAt(bundleRoot)); + + return new AppImageBuildEnv<>(env, app); + } + + static AppImageTaskAction withBundleLayout(AppImageTaskAction action) { + return new AppImageTaskAction<>() { + @Override + public void execute(AppImageBuildEnv env) throws IOException, PackagerException { + if (!env.envLayout().runtimeDirectory().getName(0).equals(Path.of("Contents"))) { + env = LayoutUtils.fromPackagerLayout(env); + } + action.execute(env); + } + }; + } + } + + private static void copyAppImage(MacPackage pkg, AppImageLayout srcAppImage, + AppImageLayout dstAppImage) throws IOException { boolean predefinedAppImageSigned = pkg.predefinedAppImageSigned().orElse(false); - var inputRootDirectory = srcAppImage.resolvedAppImagelayout().rootDirectory(); + final Optional srcMacBundle; + if (pkg.isRuntimeInstaller()) { + srcMacBundle = MacBundle.fromAppImageLayout(srcAppImage); + } else { + srcMacBundle = Optional.empty(); + } - if (pkg.isRuntimeInstaller() && MacBundle.isDirectoryMacBundle(inputRootDirectory)) { + srcMacBundle.ifPresentOrElse(inputBundle -> { // Building runtime package from the input runtime bundle. // Copy the input bundle verbatim. - FileUtils.copyRecursive( - inputRootDirectory, - dstAppImage.resolvedAppImagelayout().rootDirectory(), - LinkOption.NOFOLLOW_LINKS); - } else { - PackagingPipeline.copyAppImage(srcAppImage, dstAppImage, !predefinedAppImageSigned); - } + try { + FileUtils.copyRecursive( + inputBundle.root(), + MacBundle.fromAppImageLayout(dstAppImage).orElseThrow().root(), + LinkOption.NOFOLLOW_LINKS); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }, () -> { + try { + PackagingPipeline.copyAppImage(srcAppImage, dstAppImage, !predefinedAppImageSigned); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); } private static void copyJliLib( @@ -272,10 +323,23 @@ final class MacPackagingPipeline { } } - private static void runPostAppImageUserScript(PackageBuildEnv env) throws IOException { - PackagingPipeline.runPostAppImageUserScript(new PackageBuildEnv<>( - BuildEnv.withAppImageDir(env.env(), env.env().appImageDir().resolve(env.envLayout().rootDirectory())), - env.pkg(), env.pkg().appImageLayout(), env.outputDir())); + private static void runPostAppImageUserScript(PackageBuildEnv cfg) throws IOException { + var appCfg = LayoutUtils.fromPackagerLayout( + new AppImageBuildEnv<>(cfg.env(), (MacApplication)cfg.pkg().app())); + + var pkg = cfg.pkg(); pkg = new Package.Stub( + appCfg.app(), + pkg.type(), + pkg.packageName(), + pkg.description(), + pkg.version(), + pkg.aboutURL(), + pkg.licenseFile(), + pkg.predefinedAppImage(), + pkg.installedPackageLayout(), + pkg.relativeInstallDir()); + + PackagingPipeline.runPostAppImageUserScript(new PackageBuildEnv<>(appCfg.env(), pkg, cfg.outputDir())); } private static void writePackageFile(PackageBuildEnv env) throws IOException { @@ -331,7 +395,7 @@ final class MacPackagingPipeline { final var app = env.app(); - final var infoPlistFile = MacBundle.fromAppImageLayout(env.resolvedLayout()).infoPlistFile(); + final var infoPlistFile = MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow().infoPlistFile(); Log.verbose(I18N.format("message.preparing-info-plist", PathUtils.normalizedAbsolutePathString(infoPlistFile))); @@ -384,8 +448,7 @@ final class MacPackagingPipeline { } final Runnable signAction = () -> { - final var appImageDir = env.resolvedLayout().rootDirectory(); - AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(appImageDir); + AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow()); }; app.signingConfig().flatMap(AppImageSigningConfig::keychain).map(Keychain::new).ifPresentOrElse(keychain -> { @@ -467,7 +530,7 @@ final class MacPackagingPipeline { private static MacBundle runtimeBundle(AppImageBuildEnv env) { if (env.app().isRuntime()) { - return new MacBundle(env.resolvedLayout().rootDirectory()); + return MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow(); } else { return new MacBundle(((MacApplicationLayout)env.resolvedLayout()).runtimeRootDirectory()); } @@ -498,6 +561,21 @@ final class MacPackagingPipeline { } } + private static UnaryOperator mapAppTaskContext() { + return ctx -> { + return new TaskContextProxy(ctx, true, false); + }; + } + + private static UnaryOperator mapPackageTaskContext(Package pkg) { + return ctx -> { + final var isRuntimeInstaller = pkg.isRuntimeInstaller(); + final var withPredefinedAppImage = pkg.predefinedAppImage().isPresent(); + return new TaskContextProxy(ctx, false, isRuntimeInstaller || withPredefinedAppImage); + }; + } + + private record TaskContextProxy(TaskContext delegate, boolean forApp, boolean copyAppImage) implements TaskContext { @Override diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java index f785cc49f67..bc19e7c4a1a 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java @@ -73,7 +73,7 @@ public class MacPkgBundler extends MacBaseInstallerBundler { Path outputParentDir) throws PackagerException { final var pkg = MacFromParams.PKG_PACKAGE.fetchFrom(params); - var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); + var env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params); final var packager = MacPkgPackager.build().outputDir(outputParentDir).pkg(pkg).env(env); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java index 083fc225581..4d38cf4ed70 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java @@ -24,21 +24,14 @@ */ package jdk.jpackage.internal.model; +import java.nio.file.Files; import java.nio.file.Path; import jdk.jpackage.internal.util.CompositeProxy; public interface MacPackage extends Package, MacPackageMixin { - MacApplication app(); - @Override - default AppImageLayout appImageLayout() { - if (isRuntimeInstaller()) { - return RUNTIME_PACKAGE_LAYOUT; - } else { - return Package.super.appImageLayout(); - } - } + MacApplication app(); default Path installDir() { return Path.of("/").resolve(relativeInstallDir()); @@ -48,5 +41,19 @@ public interface MacPackage extends Package, MacPackageMixin { return CompositeProxy.create(MacPackage.class, pkg, mixin); } - public static final RuntimeLayout RUNTIME_PACKAGE_LAYOUT = RuntimeLayout.create(Path.of("Contents/Home")); + /** + * Guesses layout of a runtime image at the given path. + * + * @param path the path to a runtime image + * @return the runtime image layout resolved at the given path + */ + public static RuntimeLayout guessRuntimeLayout(Path path) { + if (Files.isDirectory(RUNTIME_BUNDLE_LAYOUT.resolveAt(path).runtimeDirectory())) { + return RUNTIME_BUNDLE_LAYOUT.resolveAt(path); + } else { + return RuntimeLayout.DEFAULT.resolveAt(path); + } + } + + public static final RuntimeLayout RUNTIME_BUNDLE_LAYOUT = RuntimeLayout.create(Path.of("Contents/Home")); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java index 09bf04372ce..141a1b5155f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java @@ -153,9 +153,30 @@ final class ApplicationBuilder { } static Launcher overrideLauncherStartupInfo(Launcher launcher, LauncherStartupInfo startupInfo) { - return new Launcher.Stub(launcher.name(), Optional.of(startupInfo), - launcher.fileAssociations(), launcher.isService(), launcher.description(), - launcher.icon(), launcher.defaultIconResourceName(), launcher.extraAppImageFileData()); + return new Launcher.Stub( + launcher.name(), + Optional.of(startupInfo), + launcher.fileAssociations(), + launcher.isService(), + launcher.description(), + launcher.icon(), + launcher.defaultIconResourceName(), + launcher.extraAppImageFileData()); + } + + static Application overrideAppImageLayout(Application app, AppImageLayout appImageLayout) { + return new Application.Stub( + app.name(), + app.description(), + app.version(), + app.vendor(), + app.copyright(), + app.srcDir(), + app.contentDirs(), + Objects.requireNonNull(appImageLayout), + app.runtimeBuilder(), + app.launchers(), + app.extraAppImageFileData()); } record MainLauncherStartupInfo(String qualifiedClassName) implements LauncherStartupInfo { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java index e2686c17128..7d3250ff7b9 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java @@ -84,7 +84,7 @@ final class ApplicationImageUtils { static ApplicationImageTaskAction createWriteRuntimeAction() { return env -> { - env.app().runtimeBuilder().orElseThrow().createRuntime(env.resolvedLayout()); + env.app().runtimeBuilder().orElseThrow().create(env.resolvedLayout()); }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java index 1d2a43bf5d6..527f747b229 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java @@ -27,57 +27,111 @@ package jdk.jpackage.internal; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.ApplicationLayout; +/** + * Build environment. + */ interface BuildEnv { + /** + * Returns root directory for intermediate build files. + * + * @return the root directory for intermediate build files + */ Path buildRoot(); + /** + * Returns true if the build should be verbose output. + * + * @return true if the build should be verbose output + */ boolean verbose(); + /** + * Returns the path of the resource directory or an empty {@link Optional} + * instance if none is configured with the build. + * + * @return the path of the resource directory or an empty {@link Optional} + * instance if non is configured with the build + */ Optional resourceDir(); /** - * Returns path to application image directory. + * Returns the path of the app image directory of this build. * - * The return value is supposed to be used as a parameter for - * ApplicationLayout#resolveAt function. + * @return the path of the app image directory of this build */ default Path appImageDir() { - return buildRoot().resolve("image"); + return appImageLayout().rootDirectory(); } + /** + * Returns resolved app image layout of the app image directory. The return + * layout is resolved at {@link #appImageDir()} path. + * + * @return the resolved app image layout of the app image directory + */ + AppImageLayout appImageLayout(); + + default Optional asApplicationLayout() { + if (appImageLayout() instanceof ApplicationLayout layout) { + return Optional.of(layout); + } else { + return Optional.empty(); + } + } + + /** + * Returns a path to a directory for intermediate configuration files. + * @return the path to the directory for intermediate configuration files + */ default Path configDir() { return buildRoot().resolve("config"); } + /** + * Creates an {@link OverridableResource} instance for the given resource name. + * + * @param defaultName the resource name + * @return the {@link OverridableResource} instance wrapping a resource with the + * given name + */ OverridableResource createResource(String defaultName); static BuildEnv withAppImageDir(BuildEnv env, Path appImageDir) { return ((Internal.DefaultBuildEnv)env).copyWithAppImageDir(appImageDir); } - static BuildEnv create(Path buildRoot, Optional resourceDir, boolean verbose, Class resourceLocator) { - return new Internal.DefaultBuildEnv(buildRoot, resourceDir, verbose, resourceLocator, Optional.empty()); + static BuildEnv withAppImageLayout(BuildEnv env, AppImageLayout appImageLayout) { + return ((Internal.DefaultBuildEnv)env).copyWithAppImageLayout(appImageLayout); + } + + static BuildEnv create(Path buildRoot, Optional resourceDir, boolean verbose, + Class resourceLocator, AppImageLayout appImageLayout) { + return new Internal.DefaultBuildEnv(buildRoot, resourceDir, verbose, + resourceLocator, appImageLayout); } static final class Internal { - private static record DefaultBuildEnv(Path buildRoot, Optional resourceDir, - boolean verbose, Class resourceLocator, Optional optAppImageDir) implements BuildEnv { + private record DefaultBuildEnv(Path buildRoot, Optional resourceDir, + boolean verbose, Class resourceLocator, + AppImageLayout appImageLayout) implements BuildEnv { DefaultBuildEnv { Objects.requireNonNull(buildRoot); Objects.requireNonNull(resourceDir); Objects.requireNonNull(resourceLocator); - Objects.requireNonNull(optAppImageDir); + Objects.requireNonNull(appImageLayout); } - DefaultBuildEnv copyWithAppImageDir(Path appImageDir) { - return new DefaultBuildEnv(buildRoot, resourceDir, verbose, resourceLocator, Optional.of(appImageDir)); + DefaultBuildEnv copyWithAppImageDir(Path v) { + return copyWithAppImageLayout(appImageLayout.unresolve().resolveAt(v)); } - @Override - public Path appImageDir() { - return optAppImageDir.orElseGet(BuildEnv.super::appImageDir); + DefaultBuildEnv copyWithAppImageLayout(AppImageLayout v) { + return new DefaultBuildEnv(buildRoot, resourceDir, verbose, resourceLocator, v); } @Override diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java index 9974e407ca7..ba70913eca9 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java @@ -29,8 +29,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.resources.ResourceLocator; final class BuildEnvBuilder { @@ -40,8 +42,6 @@ final class BuildEnvBuilder { } BuildEnv create() throws ConfigException { - Objects.requireNonNull(appImageDir); - var exceptionBuilder = I18N.buildConfigException("ERR_BuildRootInvalid", root); if (Files.isDirectory(root)) { try (var rootDirContents = Files.list(root)) { @@ -57,8 +57,8 @@ final class BuildEnvBuilder { throw exceptionBuilder.create(); } - return BuildEnv.withAppImageDir(BuildEnv.create(root, Optional.ofNullable(resourceDir), - verbose, ResourceLocator.class), appImageDir); + return BuildEnv.create(root, Optional.ofNullable(resourceDir), verbose, + ResourceLocator.class, resolvedAppImageLayout()); } BuildEnvBuilder verbose(boolean v) { @@ -76,21 +76,34 @@ final class BuildEnvBuilder { return this; } - BuildEnvBuilder appImageDirFor(Application app) { - appImageDir = defaultAppImageDir(root).resolve(app.appImageDirName()); + BuildEnvBuilder appImageLayout(AppImageLayout v) { + appImageLayout = v; return this; } - BuildEnvBuilder appImageDirForPackage() { - appImageDir = defaultAppImageDir(root); + BuildEnvBuilder appImageDirFor(Application app) { + appImageDir = defaultAppImageDir(root).resolve(app.appImageDirName()); + appImageLayout = app.imageLayout(); return this; } + BuildEnvBuilder appImageDirFor(Package pkg) { + appImageDir = defaultAppImageDir(root); + appImageLayout = pkg.appImageLayout(); + return this; + } + + private AppImageLayout resolvedAppImageLayout() { + Objects.requireNonNull(appImageLayout); + return Optional.ofNullable(appImageDir).map(appImageLayout.unresolve()::resolveAt).orElse(appImageLayout); + } + private static Path defaultAppImageDir(Path root) { return root.resolve("image"); } private Path appImageDir; + private AppImageLayout appImageLayout; private Path resourceDir; private boolean verbose; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java index 24ae249297e..6fb1a342fbf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java @@ -24,18 +24,25 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; +import static jdk.jpackage.internal.ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT; import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; +import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; import static jdk.jpackage.internal.StandardBundlerParam.TEMP_ROOT; import static jdk.jpackage.internal.StandardBundlerParam.VERBOSE; +import java.nio.file.Path; import java.util.Map; +import java.util.function.Function; +import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.RuntimeLayout; final class BuildEnvFromParams { - static BuildEnv create(Map params) throws ConfigException { + static BuildEnv create(Map params, + Function predefinedAppImageLayoutProvider, + Function predefinedRuntimeImageLayoutProvider) throws ConfigException { final var builder = new BuildEnvBuilder(TEMP_ROOT.fetchFrom(params)); @@ -47,11 +54,13 @@ final class BuildEnvFromParams { final var pkg = FromParams.getCurrentPackage(params); if (app.isRuntime()) { - PREDEFINED_RUNTIME_IMAGE.copyInto(params, builder::appImageDir); + var layout = predefinedRuntimeImageLayoutProvider.apply(PREDEFINED_RUNTIME_IMAGE.findIn(params).orElseThrow()); + builder.appImageLayout(layout); } else if (StandardBundlerParam.hasPredefinedAppImage(params)) { - PREDEFINED_APP_IMAGE.copyInto(params, builder::appImageDir); + var layout = predefinedAppImageLayoutProvider.apply(PREDEFINED_APP_IMAGE.findIn(params).orElseThrow()); + builder.appImageLayout(layout); } else if (pkg.isPresent()) { - builder.appImageDirForPackage(); + builder.appImageDirFor(pkg.orElseThrow()); } else { builder.appImageDirFor(app); } @@ -59,6 +68,7 @@ final class BuildEnvFromParams { return builder.create(); } - static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam( - BuildEnv.class, BuildEnvFromParams::create); + static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam(BuildEnv.class, params -> { + return create(params, PLATFORM_APPLICATION_LAYOUT::resolveAt, RuntimeLayout.DEFAULT::resolveAt); + }); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java index d4ea595969a..ec9718c5d24 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java @@ -79,12 +79,13 @@ final class FromParams { static ApplicationBuilder createApplicationBuilder(Map params, Function, Launcher> launcherMapper, ApplicationLayout appLayout) throws ConfigException, IOException { - return createApplicationBuilder(params, launcherMapper, appLayout, Optional.of(RuntimeLayout.DEFAULT)); + return createApplicationBuilder(params, launcherMapper, appLayout, RuntimeLayout.DEFAULT, Optional.of(RuntimeLayout.DEFAULT)); } static ApplicationBuilder createApplicationBuilder(Map params, Function, Launcher> launcherMapper, - ApplicationLayout appLayout, Optional predefinedRuntimeLayout) throws ConfigException, IOException { + ApplicationLayout appLayout, RuntimeLayout runtimeLayout, + Optional predefinedRuntimeLayout) throws ConfigException, IOException { final var appBuilder = new ApplicationBuilder(); @@ -104,7 +105,7 @@ final class FromParams { layout -> predefinedRuntimeImage.map(layout::resolveAt)).map(RuntimeLayout::runtimeDirectory); if (isRuntimeInstaller) { - appBuilder.appImageLayout(predefinedRuntimeLayout.orElseThrow()); + appBuilder.appImageLayout(runtimeLayout); } else { appBuilder.appImageLayout(appLayout); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java index c09c1ae4b1f..bd82b34d897 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java @@ -61,7 +61,7 @@ final class JLinkRuntimeBuilder implements RuntimeBuilder { } @Override - public void createRuntime(AppImageLayout appImageLayout) throws PackagerException { + public void create(AppImageLayout appImageLayout) throws PackagerException { var args = new ArrayList(); args.add("--output"); args.add(appImageLayout.runtimeDirectory().toString()); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java index 221e0dcac07..f93cd0eab14 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java @@ -29,6 +29,7 @@ import static jdk.jpackage.internal.I18N.buildConfigException; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.Package; @@ -86,9 +87,19 @@ final class PackageBuilder { Optional.ofNullable(aboutURL), Optional.ofNullable(licenseFile), Optional.ofNullable(predefinedAppImage), + validatedInstalledPackageLayout(relativeInstallDir), relativeInstallDir); } + PackageBuilder app(Application v) { + app = v; + return this; + } + + Application app() { + return app; + } + PackageBuilder name(String v) { name = v; return this; @@ -169,10 +180,34 @@ final class PackageBuilder { } } + PackageBuilder installedPackageLayout(AppImageLayout v) { + installedPackageLayout = v; + return this; + } + + Optional installedPackageLayout() { + return Optional.ofNullable(installedPackageLayout); + } + private String validatedName() { return name().orElseGet(app::name); } + private AppImageLayout validatedInstalledPackageLayout(Path relativeInstallDir) { + return installedPackageLayout().orElseGet(() -> { + var theInstallDir = relativeInstallDir; + if (type instanceof StandardPackageType stdType) { + switch (stdType) { + case LINUX_DEB, LINUX_RPM, MAC_DMG, MAC_PKG -> { + theInstallDir = Path.of("/").resolve(theInstallDir); + } + default -> {} + } + } + return app.imageLayout().resolveAt(theInstallDir).resetRootDirectory(); + }); + } + private static Path mapInstallDir(Path installDir, PackageType pkgType) throws ConfigException { var ex = buildConfigException("error.invalid-install-dir", installDir).create(); @@ -235,6 +270,7 @@ final class PackageBuilder { } } + private Application app; private String name; private Path fileName; private String description; @@ -243,7 +279,7 @@ final class PackageBuilder { private Path licenseFile; private Path predefinedAppImage; private Path installDir; + private AppImageLayout installedPackageLayout; private final PackageType type; - private final Application app; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index 276bed4f31c..64a37878bd8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java @@ -26,6 +26,7 @@ package jdk.jpackage.internal; import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; import java.io.IOException; import java.nio.file.LinkOption; @@ -38,9 +39,9 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; -import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; +import java.util.stream.Stream; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; @@ -62,7 +63,7 @@ final class PackagingPipeline { * @param app the application */ void execute(BuildEnv env, Application app) throws PackagerException { - execute(appContextMapper.apply(createTaskContext(env, app))); + execute(contextMapper.apply(createTaskContext(env, app))); } /** @@ -81,8 +82,7 @@ final class PackagingPipeline { * @param outputDir the output directory for the package file */ void execute(BuildEnv env, Package pkg, Path outputDir) throws PackagerException { - execute((StartupParameters)createPackagingTaskContext(env, pkg, outputDir, - taskConfig, appImageLayoutForPackaging.apply(pkg))); + execute((StartupParameters)createPackagingTaskContext(env, pkg, outputDir, taskConfig)); } /** @@ -92,7 +92,7 @@ final class PackagingPipeline { * @param startupParameters the pipeline startup parameters */ void execute(StartupParameters startupParameters) throws PackagerException { - execute(pkgContextMapper.apply(createTaskContext((PackagingTaskContext)startupParameters))); + execute(contextMapper.apply(createTaskContext((PackagingTaskContext)startupParameters))); } /** @@ -135,21 +135,27 @@ final class PackagingPipeline { void execute(TaskAction taskAction) throws IOException, PackagerException; } - record AppImageBuildEnv(BuildEnv env, T app, U envLayout) { + record AppImageBuildEnv(BuildEnv env, T app) { + @SuppressWarnings("unchecked") + U envLayout() { + return (U)app.imageLayout(); + } + @SuppressWarnings("unchecked") U resolvedLayout() { - return (U)envLayout.resolveAt(env.appImageDir()); + return (U)env.appImageLayout(); } } - record PackageBuildEnv(BuildEnv env, T pkg, U envLayout, Path outputDir) { + record PackageBuildEnv(BuildEnv env, T pkg, Path outputDir) { @SuppressWarnings("unchecked") - U resolvedLayout() { - return (U)envLayout.resolveAt(env.appImageDir()); + U envLayout() { + return (U)pkg.appImageLayout(); } - AppImageBuildEnv appImageBuildEnv() { - return new AppImageBuildEnv<>(env, pkg.app(), envLayout); + @SuppressWarnings("unchecked") + U resolvedLayout() { + return (U)env.appImageLayout(); } } @@ -165,7 +171,7 @@ final class PackagingPipeline { @FunctionalInterface interface CopyAppImageTaskAction extends TaskAction { - void execute(T pkg, AppImageDesc srcAppImage, AppImageDesc dstAppImage) throws IOException, PackagerException; + void execute(T pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException, PackagerException; } @FunctionalInterface @@ -195,6 +201,11 @@ final class PackagingPipeline { super(id); } + private TaskBuilder(TaskID id, TaskConfig config) { + this(id); + config.action().ifPresent(this::setAction); + } + private TaskBuilder setAction(TaskAction v) { action = v; return this; @@ -225,6 +236,10 @@ final class PackagingPipeline { return setAction(action); } + boolean hasAction() { + return action != null; + } + @Override public TaskBuilder addDependent(TaskID v) { super.addDependent(v); @@ -283,6 +298,12 @@ final class PackagingPipeline { return new TaskBuilder(id); } + Stream configuredTasks() { + return taskConfig.entrySet().stream().map(e -> { + return new TaskBuilder(e.getKey(), e.getValue()); + }); + } + Builder excludeDirFromCopying(Path path) { Objects.requireNonNull(path); excludeCopyDirs.add(path); @@ -290,23 +311,7 @@ final class PackagingPipeline { } Builder contextMapper(UnaryOperator v) { - appContextMapper(v); - pkgContextMapper(v); - return this; - } - - Builder appContextMapper(UnaryOperator v) { - appContextMapper = v; - return this; - } - - Builder pkgContextMapper(UnaryOperator v) { - pkgContextMapper = v; - return this; - } - - Builder appImageLayoutForPackaging(Function v) { - appImageLayoutForPackaging = v; + contextMapper = v; return this; } @@ -318,27 +323,18 @@ final class PackagingPipeline { } StartupParameters createStartupParameters(BuildEnv env, Package pkg, Path outputDir) { - return createPackagingTaskContext(env, pkg, outputDir, taskConfig, - validatedAppImageLayoutForPackaging().apply(pkg)); - } - - private Function validatedAppImageLayoutForPackaging() { - return Optional.ofNullable(appImageLayoutForPackaging).orElse(Package::packageLayout); + return createPackagingTaskContext(env, pkg, outputDir, taskConfig); } PackagingPipeline create() { return new PackagingPipeline(taskGraphSnapshot(), taskConfig, - Optional.ofNullable(appContextMapper).orElse(UnaryOperator.identity()), - Optional.ofNullable(pkgContextMapper).orElse(UnaryOperator.identity()), - validatedAppImageLayoutForPackaging()); + Optional.ofNullable(contextMapper).orElse(UnaryOperator.identity())); } private final FixedDAG.Builder taskGraphBuilder = FixedDAG.build(); private final List excludeCopyDirs = new ArrayList<>(); private final Map taskConfig = new HashMap<>(); - private UnaryOperator appContextMapper; - private UnaryOperator pkgContextMapper; - private Function appImageLayoutForPackaging; + private UnaryOperator contextMapper; private FixedDAG taskGraphSnapshot; } @@ -404,21 +400,20 @@ final class PackagingPipeline { return builder; } - static void copyAppImage(Package pkg, AppImageDesc srcAppImage, AppImageDesc dstAppImage) throws IOException { + static void copyAppImage(Package pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException { copyAppImage(srcAppImage, dstAppImage, true); } - static void copyAppImage(AppImageDesc srcAppImage, AppImageDesc dstAppImage, + static void copyAppImage(AppImageLayout srcAppImage, AppImageLayout dstAppImage, boolean removeAppImageFile) throws IOException { - final var srcLayout = srcAppImage.resolvedAppImagelayout(); - final var srcLayoutPathGroup = AppImageLayout.toPathGroup(srcLayout); + final var srcLayoutPathGroup = toPathGroup(srcAppImage); - if (removeAppImageFile && srcLayout instanceof ApplicationLayout appLayout) { + if (removeAppImageFile && srcAppImage instanceof ApplicationLayout appLayout) { // Copy app layout omitting application image info file. srcLayoutPathGroup.ghostPath(AppImageFile.getPathInAppImage(appLayout)); } - srcLayoutPathGroup.copy(AppImageLayout.toPathGroup(dstAppImage.resolvedAppImagelayout()), LinkOption.NOFOLLOW_LINKS); + srcLayoutPathGroup.copy(toPathGroup(dstAppImage), LinkOption.NOFOLLOW_LINKS); } static void runPostAppImageUserScript(PackageBuildEnv env) throws IOException { @@ -432,57 +427,66 @@ final class PackagingPipeline { } private PackagingPipeline(FixedDAG taskGraph, Map taskConfig, - UnaryOperator appContextMapper, UnaryOperator pkgContextMapper, - Function appImageLayoutForPackaging) { + UnaryOperator contextMapper) { this.taskGraph = Objects.requireNonNull(taskGraph); this.taskConfig = Objects.requireNonNull(taskConfig); - this.appContextMapper = Objects.requireNonNull(appContextMapper); - this.pkgContextMapper = Objects.requireNonNull(pkgContextMapper); - this.appImageLayoutForPackaging = Objects.requireNonNull(appImageLayoutForPackaging); + this.contextMapper = Objects.requireNonNull(contextMapper); } private TaskContext createTaskContext(BuildEnv env, Application app) { - return new DefaultTaskContext(taskGraph, env, app, app.asApplicationLayout(), Optional.empty()); + return new DefaultTaskContext(taskGraph, env, app, Optional.empty()); } private TaskContext createTaskContext(PackagingTaskContext packagingContext) { - final var pkgEnv = BuildEnv.withAppImageDir(packagingContext.env.env(), packagingContext.srcAppImage.path()); - return new DefaultTaskContext(taskGraph, pkgEnv, packagingContext.env.pkg.app(), - packagingContext.srcAppImage.asApplicationLayout(), Optional.of(packagingContext)); + return new DefaultTaskContext(taskGraph, packagingContext.env(), + packagingContext.pkg().app(), Optional.of(packagingContext)); } private static PackagingTaskContext createPackagingTaskContext(BuildEnv env, Package pkg, - Path outputDir, Map taskConfig, AppImageLayout appImageLayoutForPackaging) { + Path outputDir, Map taskConfig) { Objects.requireNonNull(env); Objects.requireNonNull(outputDir); Objects.requireNonNull(taskConfig); - Objects.requireNonNull(appImageLayoutForPackaging); + if (pkg.appImageLayout().isResolved()) { + throw new IllegalArgumentException(); + } - final AppImageDesc srcAppImageDesc; - final AppImageDesc dstAppImageDesc; + final AppImageLayout srcLayout; + final AppImageLayout dstLayout; if (pkg.app().runtimeBuilder().isPresent()) { // Runtime builder is present, will build application image. - // appImageDir() should point to a directory where the application image will be created. - srcAppImageDesc = new AppImageDesc(appImageLayoutForPackaging, env.appImageDir()); - dstAppImageDesc = srcAppImageDesc; + srcLayout = pkg.appImageLayout().resolveAt(env.appImageDir()); + dstLayout = srcLayout; } else { - srcAppImageDesc = new AppImageDesc(pkg.app().imageLayout(), - pkg.predefinedAppImage().orElseThrow(UnsupportedOperationException::new)); + srcLayout = pkg.predefinedAppImage().map(predefinedAppImage -> { + // Will create a package from the predefined app image. + if (predefinedAppImage.equals(env.appImageDir())) { + return env.appImageLayout(); + } else { + return pkg.appImageLayout().resolveAt(predefinedAppImage); + } + }).orElseGet(() -> { + // No predefined app image and no runtime builder. + // This should be runtime packaging. + if (pkg.isRuntimeInstaller()) { + return env.appImageLayout(); + } else { + // Can't create app image without runtime builder. + throw new UnsupportedOperationException(); + } + }); if (taskConfig.get(CopyAppImageTaskID.COPY).action().isEmpty()) { - // "copy app image" task action is undefined indicating - // the package will use provided app image as-is. - dstAppImageDesc = srcAppImageDesc; + // "copy app image" task action is empty indicating + // the package will use provided app image in place. + dstLayout = srcLayout; } else { - dstAppImageDesc = new AppImageDesc(appImageLayoutForPackaging, env.buildRoot().resolve("image")); + dstLayout = pkg.appImageLayout().resolveAt(env.buildRoot().resolve("image")); } } - final var pkgEnv = new PackageBuildEnv<>( - BuildEnv.withAppImageDir(env, dstAppImageDesc.path()), pkg, dstAppImageDesc.appImageLayout(), outputDir); - - return new PackagingTaskContext(pkgEnv, srcAppImageDesc); + return new PackagingTaskContext(BuildEnv.withAppImageLayout(env, dstLayout), pkg, outputDir, srcLayout); } private void execute(TaskContext context) throws PackagerException { @@ -500,28 +504,30 @@ final class PackagingPipeline { try { builder.create().call(); + } catch (ExceptionBox ex) { + throw new PackagerException(ex.getCause()); + } catch (RuntimeException ex) { + throw ex; + } catch (PackagerException ex) { + throw ex; } catch (Exception ex) { - if (ex instanceof PackagerException pex) { - throw pex; - } else if (ex instanceof ExceptionBox bex) { - throw new PackagerException(bex.getCause()); - } else { - throw new PackagerException(ex); - } + throw new PackagerException(ex); } } - private record PackagingTaskContext(PackageBuildEnv env, - AppImageDesc srcAppImage) implements TaskContext, StartupParameters { + private record PackagingTaskContext(BuildEnv env, Package pkg, Path outputDir, + AppImageLayout srcAppImage) implements TaskContext, StartupParameters { PackagingTaskContext { Objects.requireNonNull(env); + Objects.requireNonNull(pkg); + Objects.requireNonNull(outputDir); Objects.requireNonNull(srcAppImage); } @Override public BuildEnv packagingEnv() { - return env.env; + return env; } @Override @@ -538,28 +544,31 @@ final class PackagingPipeline { @Override public void execute(TaskAction taskAction) throws IOException, PackagerException { if (taskAction instanceof PackageTaskAction) { - ((PackageTaskAction)taskAction).execute(env); + ((PackageTaskAction)taskAction).execute(pkgBuildEnv()); } else if (taskAction instanceof CopyAppImageTaskAction) { - ((CopyAppImageTaskAction)taskAction).execute(env.pkg(), - srcAppImage, new AppImageDesc(env.envLayout(), env.env().appImageDir())); + ((CopyAppImageTaskAction)taskAction).execute(pkg(), + srcAppImage, env.appImageLayout()); } else { throw new IllegalArgumentException(); } } AppImageBuildEnv appImageBuildEnv() { - return env.appImageBuildEnv(); + return new AppImageBuildEnv<>(env, pkg.app()); + } + + PackageBuildEnv pkgBuildEnv() { + return new PackageBuildEnv<>(env, pkg, outputDir); } } private record DefaultTaskContext(FixedDAG taskGraph, BuildEnv env, Application app, - Optional appLayout, Optional pkg) implements TaskContext { + Optional pkg) implements TaskContext { DefaultTaskContext { Objects.requireNonNull(taskGraph); Objects.requireNonNull(env); Objects.requireNonNull(app); - Objects.requireNonNull(appLayout); Objects.requireNonNull(pkg); } @@ -571,11 +580,11 @@ final class PackagingPipeline { if (pkg.isPresent() && !pkg.orElseThrow().test(taskID)) { return false; - } else if (pkg.isEmpty() && isPackageTask) { - // Building application image, skip packaging tasks. + } else if (pkg.isEmpty() && (isPackageTask || isCopyAppImageTask)) { + // Building application image, skip packaging and copying app image tasks. return false; - } else if (app.runtimeBuilder().isEmpty() && isBuildApplicationImageTask && !isCopyAppImageTask) { - // Runtime builder is not present, skip building application image tasks. + } else if (pkg.isPresent() && app.runtimeBuilder().isEmpty() && isBuildApplicationImageTask && !isCopyAppImageTask) { + // Building a package, runtime builder is not present, skip building application image tasks. return false; } else if (app.runtimeBuilder().isPresent() && isCopyAppImageTask && !isBuildApplicationImageTask) { // Runtime builder is present, skip copying app image tasks. @@ -596,7 +605,7 @@ final class PackagingPipeline { } else if (taskAction instanceof NoArgTaskAction noArgAction) { noArgAction.execute(); } else { - pkg.orElseThrow().execute(taskAction); + pkg.orElseThrow(UnsupportedOperationException::new).execute(taskAction); } } @@ -612,7 +621,7 @@ final class PackagingPipeline { @SuppressWarnings("unchecked") private AppImageBuildEnv appBuildEnv() { - return new AppImageBuildEnv<>(env, app, (T)appLayout.orElseThrow()); + return new AppImageBuildEnv<>(env, app); } } @@ -622,11 +631,7 @@ final class PackagingPipeline { Objects.requireNonNull(config); return () -> { if (config.action.isPresent() && context.test(id)) { - try { - context.execute(config.action.orElseThrow()); - } catch (ExceptionBox ex) { - throw ExceptionBox.rethrowUnchecked(ex); - } + context.execute(config.action.orElseThrow()); } return null; }; @@ -634,7 +639,5 @@ final class PackagingPipeline { private final FixedDAG taskGraph; private final Map taskConfig; - private final Function appImageLayoutForPackaging; - private final UnaryOperator appContextMapper; - private final UnaryOperator pkgContextMapper; + private final UnaryOperator contextMapper; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java index 1cd0e0f6c2e..a64946c06d0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java @@ -25,15 +25,18 @@ package jdk.jpackage.internal.model; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; + import java.lang.reflect.Modifier; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.function.UnaryOperator; import java.util.stream.Stream; import jdk.jpackage.internal.util.PathGroup; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; /** @@ -41,9 +44,14 @@ import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; * * App image layout is a collection of files and directories with specific roles * (executables, configuration files, etc.) sharing the same root directory. - * - * The layout is "unresolved" if the root directory is an empty string and - * "resolved" otherwise. + *

      + * The layout is "unresolved" if the root directory is an empty path + * ({@code Path.of("")}) and "resolved" otherwise. + *

      + * The return value of the {@link #runtimeDirectory()} method call is always a + * path starting with the path returned by the {@link #rootDirectory()} method + * call. Public methods without parameters and with the return type {@link Path} + * in the derived interfaces must comply to this constrain. */ public interface AppImageLayout { @@ -56,29 +64,94 @@ public interface AppImageLayout { Path runtimeDirectory(); /** - * Root directory of this app image. + * Root directory of this app image layout. * It should normally be equal to Path.of("") for unresolved layout. * - * @return the root directory of this app image + * @return the root directory of this app image layout */ Path rootDirectory(); /** - * Creates a copy of this app image resolved at the given root directory. + * Returns a copy of this app image layout with the root directory set to an empty + * path ({@code Path.of("")}) or this instance if its root directory is already + * an empty path. + * + * @return an app image layout with the root directory set to an empty path + */ + AppImageLayout resetRootDirectory(); + + /** + * Returns true if the root directory of this app image layout is + * not an empty path, i.e, if it is not equal to Path.of(""). + * + * @return true if the root directory of this app image layout is + * not an empty path + */ + default boolean isResolved() { + return !rootDirectory().equals(Path.of("")); + } + + /** + * Creates a copy of this app image layout resolved at the given root directory. * * @param root path to a directory at which to resolve the layout - * @return a copy of this app image resolved at the given root directory + * @return a copy of this app image layout resolved at the given root directory */ - AppImageLayout resolveAt(Path root); + default AppImageLayout resolveAt(Path root) { + return map(root::resolve); + } + + /** + * Returns a copy of this app image layout resolved such that its root directory + * is set to an empty path ({@code Path.of("")}) or this instance if its root + * directory is already an empty path. + * + * @return an app image layout resolved at {@code Path.of("")} path + */ + default AppImageLayout unresolve() { + if (isResolved()) { + final var root = rootDirectory(); + return map(root::relativize); + } else { + return this; + } + } + + /** + * Returns a copy of this app image layout with the specified mapper applied to + * every path. + * + * @param mapper the mapper to use with every path in this app image layout. + * @return the copy of this app image layout with the specified mapper applied + * to every path + */ + AppImageLayout map(UnaryOperator mapper); /** * Default implementation of {@link AppImageLayout} interface. - */ + */ record Stub(Path rootDirectory, Path runtimeDirectory) implements AppImageLayout { + public Stub { + Objects.requireNonNull(rootDirectory); + } + + public Stub(Path runtimeDirectory) { + this(Path.of(""), runtimeDirectory); + } + @Override - public AppImageLayout resolveAt(Path base) { - return new Stub(resolveNullablePath(base, rootDirectory), resolveNullablePath(base, runtimeDirectory)); + public AppImageLayout resetRootDirectory() { + if (isResolved()) { + return new Stub(runtimeDirectory); + } else { + return this; + } + } + + @Override + public AppImageLayout map(UnaryOperator mapper) { + return new Stub(mapNullablePath(mapper, rootDirectory), mapNullablePath(mapper, runtimeDirectory)); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java index b8b2eda4437..013d2fc78cd 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java @@ -24,10 +24,11 @@ */ package jdk.jpackage.internal.model; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; import java.util.Objects; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.util.CompositeProxy; /** @@ -42,7 +43,26 @@ public interface ApplicationLayout extends AppImageLayout, ApplicationLayoutMixi @Override default ApplicationLayout resolveAt(Path root) { - return buildFrom(this).resolveAt(root).create(); + return (ApplicationLayout)AppImageLayout.super.resolveAt(root); + } + + @Override + default ApplicationLayout unresolve() { + return (ApplicationLayout)AppImageLayout.super.unresolve(); + } + + @Override + default ApplicationLayout resetRootDirectory() { + if (isResolved()) { + return buildFrom(this).rootDirectory("").create(); + } else { + return this; + } + } + + @Override + default ApplicationLayout map(UnaryOperator mapper) { + return buildFrom(this).mutate(mapper).create(); } /** @@ -113,14 +133,14 @@ public interface ApplicationLayout extends AppImageLayout, ApplicationLayoutMixi return this; } - public Builder resolveAt(Path base) { - rootDirectory(resolveNullablePath(base, rootDirectory)); - launchersDirectory(resolveNullablePath(base, launchersDirectory)); - appDirectory(resolveNullablePath(base, appDirectory)); - runtimeDirectory(resolveNullablePath(base, runtimeDirectory)); - appModsDirectory(resolveNullablePath(base, appModsDirectory)); - desktopIntegrationDirectory(resolveNullablePath(base, desktopIntegrationDirectory)); - contentDirectory(resolveNullablePath(base, contentDirectory)); + public Builder mutate(UnaryOperator mapper) { + rootDirectory(mapNullablePath(mapper, rootDirectory)); + launchersDirectory(mapNullablePath(mapper, launchersDirectory)); + appDirectory(mapNullablePath(mapper, appDirectory)); + runtimeDirectory(mapNullablePath(mapper, runtimeDirectory)); + appModsDirectory(mapNullablePath(mapper, appModsDirectory)); + desktopIntegrationDirectory(mapNullablePath(mapper, desktopIntegrationDirectory)); + contentDirectory(mapNullablePath(mapper, contentDirectory)); return this; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java index 6d2fdaf0bbb..33c707b776f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java @@ -30,19 +30,11 @@ import java.util.Optional; /** * Native application package. * - * The interface specifies the source app image layout with two transformations: - * package app image layout and installed app image layout. + * The interface specifies the source app image and the installed app image layouts. *

      * Use {@link #appImageLayout()} or {@link #asApplicationLayout()} to get the * unresolved source app image layout. *

      - * Package app image layout is the source app image layout resolved at the - * relative installation directory of the package. Additionally, to resolve the - * source layout, some packages may transform the source layout. - *

      - * Use {@link #packageLayout()} or {@link #asPackageApplicationLayout()} to get - * the package app image layout. - *

      * Installed app image layout is the layout of the installed app image. *

      * Use {@link #installedPackageLayout()} or @@ -55,25 +47,21 @@ import java.util.Optional; * * * Source app image layout - * Package app image layout * Installed app image layout * * * Windows * bin/foo.exe app/foo.jar * Duke/bin/foo.exe Duke/app/foo.jar - * Duke/bin/foo.exe Duke/app/foo.jar * * * Linux * bin/foo lib/app/foo.jar - * opt/duke/bin/foo opt/duke/lib/app/foo.jar * /opt/duke/bin/foo /opt/duke/lib/app/foo.jar * * * OSX * Contents/MacOS/foo Contents/app/foo.jar - * Applications/Duke.app/Contents/MacOS/foo Applications/Duke.app/Contents/app/foo.jar * /Applications/Duke.app/Contents/MacOS/foo /Applications/Duke.app/Contents/app/foo.jar * * @@ -169,7 +157,6 @@ public interface Package extends BundleSpec { * * @return the unresolved app image layout of the application of this package * - * @see #packageLayout * @see #installedPackageLayout */ default AppImageLayout appImageLayout() { @@ -193,68 +180,14 @@ public interface Package extends BundleSpec { return app().asApplicationLayout(); } - /** - * Gets the layout of the installed app image of the application resolved at the - * relative installation directory of this package. - * - * @return the layout of the installed app image of the application resolved at - * the relative installation directory of this package - * - * @see #relativeInstallDir - * @see #appImageLayout - * @see #installedPackageLayout - */ - default AppImageLayout packageLayout() { - return appImageLayout().resolveAt(relativeInstallDir()); - } - - /** - * Returns the layout of the installed app image of the application resolved at - * the relative installation directory of this package as - * {@link ApplicationLayout} type or an empty {@link Optional} instance if the - * layout object is of incompatible type. - *

      - * Returns an empty {@link Optional} instance if {@link #isRuntimeInstaller()} - * returns true. - * - * @return the layout of the installed app image of the application resolved at - * the relative installation directory of this package as - * {@link ApplicationLayout} type - * - * @see #packageLayout - */ - default Optional asPackageApplicationLayout() { - if (packageLayout() instanceof ApplicationLayout layout) { - return Optional.of(layout); - } else { - return Optional.empty(); - } - } - /** * Gets the layout of the installed app image of this package. * * @return the layout of the installed app image of this package * * @see #appImageLayout - * @see #packageLayout */ - default AppImageLayout installedPackageLayout() { - return asStandardPackageType().map(stdType -> { - switch (stdType) { - case LINUX_DEB, LINUX_RPM, MAC_DMG, MAC_PKG -> { - return packageLayout().resolveAt(Path.of("/")); - } - case WIN_EXE, WIN_MSI -> { - return packageLayout(); - } - default -> { - // Should never get here - throw new IllegalStateException(); - } - } - }).orElseThrow(UnsupportedOperationException::new); - } + AppImageLayout installedPackageLayout(); /** * Returns the layout of the installed app image of this package as @@ -334,6 +267,6 @@ public interface Package extends BundleSpec { */ record Stub(Application app, PackageType type, String packageName, String description, String version, Optional aboutURL, Optional licenseFile, Optional predefinedAppImage, - Path relativeInstallDir) implements Package { + AppImageLayout installedPackageLayout, Path relativeInstallDir) implements Package { } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java index bce3b417b7c..c989bcc8915 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java @@ -43,7 +43,7 @@ public interface RuntimeBuilder { * @param appImageLayout the app image where to create Java runtime. * @throws PackagerException if packaging error occurs */ - void createRuntime(AppImageLayout appImageLayout) throws PackagerException; + void create(AppImageLayout appImageLayout) throws PackagerException; /** * Gets the default set of paths where to find Java modules. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java index 733fc7a74c3..d995774af8f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java @@ -24,9 +24,10 @@ */ package jdk.jpackage.internal.model; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.util.CompositeProxy; /** @@ -39,8 +40,27 @@ public interface RuntimeLayout extends AppImageLayout { @Override default RuntimeLayout resolveAt(Path root) { - return create(new AppImageLayout.Stub(resolveNullablePath(root, rootDirectory()), - resolveNullablePath(root, runtimeDirectory()))); + return (RuntimeLayout)AppImageLayout.super.resolveAt(root); + } + + @Override + default RuntimeLayout resetRootDirectory() { + if (isResolved()) { + return create(runtimeDirectory()); + } else { + return this; + } + } + + @Override + default RuntimeLayout unresolve() { + return (RuntimeLayout)AppImageLayout.super.unresolve(); + } + + @Override + default RuntimeLayout map(UnaryOperator mapper) { + return create(new RuntimeLayout.Stub(mapNullablePath(mapper, rootDirectory()), + mapNullablePath(mapper, runtimeDirectory()))); } /** diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java index dfb5da7f829..93d317b0128 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java @@ -32,7 +32,7 @@ package jdk.jpackage.internal.resources; * to call getResourceAsStream() to get those resources. */ -public class ResourceLocator { - public ResourceLocator() { +public final class ResourceLocator { + private ResourceLocator() { } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java index 08a91175c1e..55144826efb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java @@ -470,5 +470,22 @@ public final class PathGroup { } } + @Override + public int hashCode() { + return Objects.hash(entries); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PathGroup other = (PathGroup) obj; + return Objects.equals(entries, other.entries); + } + private final Map entries; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java index 31019832b73..a8944a67ae0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java @@ -25,7 +25,9 @@ package jdk.jpackage.internal.util; import java.nio.file.Path; +import java.util.Objects; import java.util.Optional; +import java.util.function.UnaryOperator; public final class PathUtils { @@ -47,8 +49,13 @@ public final class PathUtils { return parent != null ? parent.resolve(filename) : Path.of(filename); } - public static Path resolveNullablePath(Path base, Path path) { - return Optional.ofNullable(path).map(base::resolve).orElse(null); + public static Path mapNullablePath(UnaryOperator mapper, Path path) { + Objects.requireNonNull(mapper); + if (path != null) { + return mapper.apply(path); + } else { + return null; + } } public static Path normalizedAbsolutePath(Path path) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java index be529bdc19e..2fa7dd895c3 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java @@ -34,7 +34,6 @@ import jdk.jpackage.internal.PackagingPipeline.CopyAppImageTaskID; import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.model.WinApplication; import jdk.jpackage.internal.model.WinLauncher; @@ -47,7 +46,6 @@ final class WinPackagingPipeline { static PackagingPipeline.Builder build() { return PackagingPipeline.buildStandard() - .appImageLayoutForPackaging(Package::appImageLayout) .task(CopyAppImageTaskID.COPY).noaction().add() .task(WinAppImageTaskID.REBRAND_LAUNCHERS) .addDependency(BuildApplicationTaskID.LAUNCHERS) diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java index 6244090b71f..f7715c5ac3d 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java @@ -46,6 +46,7 @@ public interface WinExePackage extends Package, WinExePackageMixin { pkg.aboutURL(), pkg.licenseFile(), pkg.predefinedAppImage(), + pkg.installedPackageLayout(), pkg.relativeInstallDir()); } } diff --git a/test/jdk/tools/jpackage/helpers-test/TEST.properties b/test/jdk/tools/jpackage/helpers-test/TEST.properties index 1830ea05443..ff5e0eef9de 100644 --- a/test/jdk/tools/jpackage/helpers-test/TEST.properties +++ b/test/jdk/tools/jpackage/helpers-test/TEST.properties @@ -1,6 +1,6 @@ JUnit.dirs = . -lib.dirs = /test/jdk/tools/jpackage/helpers /test/jdk/tools/jpackage/helpers-test +lib.dirs = /test/jdk/tools/jpackage/helpers /test/jdk/tools/jpackage/junit/tools modules=jdk.jpackage/jdk.jpackage.internal:+open \ jdk.jpackage/jdk.jpackage.internal.util:+open \ diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java index 50f1992f796..08d0ba76d1c 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java @@ -42,11 +42,10 @@ public class JavaAppDescTest extends JUnitAdapter { @Parameter({"Foo", "Foo.class"}) @Parameter({"com.bar.A", "com/bar/A.class"}) @Parameter({"module/com.bar.A", "com/bar/A.class"}) - public static void testClassFilePath(String... args) { - var appDesc = args[0]; - var expectedClassFilePath = Path.of(args[1]); - TKit.assertEquals(expectedClassFilePath.toString(), JavaAppDesc.parse( - appDesc).classFilePath().toString(), null); + public static void testClassFilePath(String appDesc, String expectedClassFile) { + var expectedClassFilePath = Path.of(expectedClassFile); + TKit.assertEquals(expectedClassFilePath.toString(), + JavaAppDesc.parse(appDesc).classFilePath().toString(), null); } public static List input() { @@ -55,6 +54,12 @@ public class JavaAppDescTest extends JUnitAdapter { createTestCase("foo.jar*", "foo.jar*hello.jar:Hello"), createTestCase("Bye", "hello.jar:Bye"), createTestCase("bye.jar:", "bye.jar:Hello"), + createTestCase("bye.jar:!", appDesc -> { + return appDesc + .setBundleFileName("bye.jar") + .setClassName("Hello") + .setWithMainClass(true); + }), createTestCase("duke.jar:com.other/com.other.foo.bar.Buz!@3.7", appDesc -> { return appDesc .setBundleFileName("duke.jar") diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 3a89ba28d26..3a423fd71ca 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1174,10 +1174,15 @@ public class JPackageCommand extends CommandArguments { "Check for unexpected value of property in app image file"); } - TKit.assertStringListEquals( - addLauncherNames().stream().sorted().toList(), - aif.addLaunchers().keySet().stream().sorted().toList(), - "Check additional launcher names"); + // Don't compare the add launchers configured on the command line with the + // add launchers listed in the `.jpackage.xml` file if the latter comes from + // a predefined app image. + if (!hasArgument("--app-image")) { + TKit.assertStringListEquals( + addLauncherNames().stream().sorted().toList(), + aif.addLaunchers().keySet().stream().sorted().toList(), + "Check additional launcher names"); + } } } diff --git a/test/jdk/tools/jpackage/junit/TEST.properties b/test/jdk/tools/jpackage/junit/TEST.properties index 34d66c3b8d7..c540eb76869 100644 --- a/test/jdk/tools/jpackage/junit/TEST.properties +++ b/test/jdk/tools/jpackage/junit/TEST.properties @@ -1,5 +1,7 @@ JUnit.dirs = share +lib.dirs = /test/jdk/tools/jpackage/helpers /test/jdk/tools/jpackage/junit/tools + modules=jdk.jpackage/jdk.jpackage.internal:+open \ jdk.jpackage/jdk.jpackage.internal.model:+open \ jdk.jpackage/jdk.jpackage.internal.pipeline:+open \ diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxApplicationLayoutTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxApplicationLayoutTest.java new file mode 100644 index 00000000000..0bb0fe5b73e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxApplicationLayoutTest.java @@ -0,0 +1,57 @@ +/* + * 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 jdk.jpackage.internal; + +import java.nio.file.Path; +import jdk.jpackage.internal.model.AppImageLayoutTest; +import jdk.jpackage.internal.model.ApplicationLayoutTest; +import org.junit.jupiter.api.Test; + +public class LinuxApplicationLayoutTest { + + @Test + public void testResolveAt() { + AppImageLayoutTest.testResolveAt(createLayout()); + } + + @Test + public void testResolveAtRepeat() { + AppImageLayoutTest.testResolveAtRepeat(createLayout()); + } + + @Test + public void testUnresolve() { + AppImageLayoutTest.testUnresolve(createLayout()); + } + + @Test + public void testEmptyRootDirectory() { + AppImageLayoutTest.testEmptyRootDirectory(createLayout()); + } + + public static LinuxApplicationLayout createLayout() { + return LinuxApplicationLayout.create(ApplicationLayoutTest.createLayout(), + Path.of("libapplauncher.so")); + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/junit.java b/test/jdk/tools/jpackage/junit/linux/junit.java new file mode 100644 index 00000000000..214812e951e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/junit.java @@ -0,0 +1,32 @@ +/* + * 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 + * @summary Test LinuxApplicationLayout + * @requires (os.family == "linux") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LinuxApplicationLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxApplicationLayoutTest + */ diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationLayoutTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationLayoutTest.java new file mode 100644 index 00000000000..7ee3b41b7f2 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationLayoutTest.java @@ -0,0 +1,57 @@ +/* + * 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 jdk.jpackage.internal; + +import java.nio.file.Path; +import jdk.jpackage.internal.model.AppImageLayoutTest; +import jdk.jpackage.internal.model.ApplicationLayoutTest; +import org.junit.jupiter.api.Test; + +public class MacApplicationLayoutTest { + + @Test + public void testResolveAt() { + AppImageLayoutTest.testResolveAt(createLayout()); + } + + @Test + public void testResolveAtRepeat() { + AppImageLayoutTest.testResolveAtRepeat(createLayout()); + } + + @Test + public void testUnresolve() { + AppImageLayoutTest.testUnresolve(createLayout()); + } + + @Test + public void testEmptyRootDirectory() { + AppImageLayoutTest.testEmptyRootDirectory(createLayout()); + } + + public static MacApplicationLayout createLayout() { + return MacApplicationLayout.create(ApplicationLayoutTest.createLayout(), + Path.of("Contents/runtime")); + } +} diff --git a/test/jdk/tools/jpackage/junit/macosx/junit.java b/test/jdk/tools/jpackage/junit/macosx/junit.java new file mode 100644 index 00000000000..1549aaa6cd7 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/junit.java @@ -0,0 +1,32 @@ +/* + * 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 + * @summary Test MacApplicationLayoutTest + * @requires (os.family == "mac") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/MacApplicationLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.MacApplicationLayoutTest + */ diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/BuildEnvTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/BuildEnvTest.java new file mode 100644 index 00000000000..c71ef307f0e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/BuildEnvTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Path; +import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.RuntimeLayout; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + + +public class BuildEnvTest { + + @ParameterizedTest + @ValueSource(strings = {"", "image"}) + public void testUnresolvedAppImageLayout(Path appImageDir) { + final var rootDir = Path.of(""); + + final var env = BuildEnv.create(rootDir, Optional.empty(), true, + BuildEnvTest.class, RuntimeLayout.DEFAULT.resolveAt(appImageDir).resetRootDirectory()); + + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(Path.of(""), env.appImageDir()); + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertTrue(env.verbose()); + } + + @Test + public void testResolvedAppImageLayout() { + final var rootDir = Path.of("/oof"); + final var appImageDir = Path.of("/foo/bar"); + + final var layout = RuntimeLayout.DEFAULT.resolveAt(appImageDir); + final var env = BuildEnv.create(rootDir, Optional.empty(), true, BuildEnvTest.class, layout); + + assertSame(layout, env.appImageLayout()); + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(Path.of("/foo/bar"), env.appImageDir()); + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertTrue(env.verbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"", "/foo/bar"}) + public void test_withAppImageDir(Path appImageDir) { + final var rootDir = Path.of("/oof"); + + final var layout = RuntimeLayout.DEFAULT; + final var env = BuildEnv.withAppImageDir(BuildEnv.create(rootDir, + Optional.empty(), false, BuildEnvTest.class, layout), appImageDir); + + assertNotSame(layout, env.appImageLayout()); + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(appImageDir, env.appImageDir()); + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertFalse(env.verbose()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_withAppImageLayout(boolean resolved) { + final var rootDir = Path.of("/oof"); + + final var appImageDir = Path.of("/foo/bar"); + + final AppImageLayout layout; + if (resolved) { + layout = RuntimeLayout.DEFAULT.resolveAt(appImageDir); + } else { + layout = RuntimeLayout.DEFAULT.resolveAt(appImageDir).resetRootDirectory(); + } + + final var env = BuildEnv.withAppImageLayout(BuildEnv.create(rootDir, + Optional.empty(), false, BuildEnvTest.class, RuntimeLayout.DEFAULT), layout); + + assertSame(layout, env.appImageLayout()); + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertFalse(env.verbose()); + } + + @Test + public void test_asApplicationLayout() { + final var rootDir = Path.of("r"); + + assertTrue(BuildEnv.create(rootDir, Optional.empty(), false, + BuildEnvTest.class, RuntimeLayout.DEFAULT).asApplicationLayout().isEmpty()); + + var layout = ApplicationLayout.build().setAll("foo").create(); + assertSame(layout, BuildEnv.create(rootDir, Optional.empty(), false, + BuildEnvTest.class, layout).asApplicationLayout().orElseThrow()); + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java new file mode 100644 index 00000000000..24c1d62c34f --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java @@ -0,0 +1,872 @@ +/* + * 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 jdk.jpackage.internal; + +import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +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.function.Consumer; +import java.util.stream.Collectors; +import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID; +import jdk.jpackage.internal.PackagingPipeline.CopyAppImageTaskID; +import jdk.jpackage.internal.PackagingPipeline.NoArgTaskAction; +import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; +import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID; +import jdk.jpackage.internal.PackagingPipeline.TaskAction; +import jdk.jpackage.internal.PackagingPipeline.TaskContext; +import jdk.jpackage.internal.PackagingPipeline.TaskID; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.PackagerException; +import jdk.jpackage.internal.model.RuntimeBuilder; +import jdk.jpackage.internal.model.RuntimeLayout; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + + +public class PackagingPipelineTest { + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testBuildApplication(boolean withRuntimeBuilder, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1, withRuntimeBuilder ? Optional.of(TestRuntimeBuilder.INSTANCE) : Optional.empty()); + final var env = buildEnv(workDir.resolve("build")).appImageDirFor(app).create(); + + // Build application image in `env.appImageDir()` directory. + final var builder = buildPipeline(); + if (app.runtimeBuilder().isEmpty()) { + builder.task(BuildApplicationTaskID.RUNTIME).noaction().add(); + } + + builder.create().execute(env, app); + + assertEquals(app.appImageDirName(), env.appImageDir().getFileName()); + + var executedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(env, app); + })); + + List expectedActions = new ArrayList<>(); + if (app.runtimeBuilder().isPresent()) { + expectedActions.add(BuildApplicationTaskID.RUNTIME); + } + expectedActions.addAll(List.of(BuildApplicationTaskID.LAUNCHERS, BuildApplicationTaskID.CONTENT)); + + assertEquals(expectedActions, executedTaskActions); + + final ExpectedAppImage expectedAppImage; + if (withRuntimeBuilder) { + expectedAppImage = ExpectedAppImage.build().dir("") + .file("launchers/my-launcher", TestLauncher.CONTENT) + .file("runtime/my-runtime", TestRuntimeBuilder.CONTENT); + } else { + expectedAppImage = ExpectedAppImage.build().dir("") + .file("launchers/my-launcher", TestLauncher.CONTENT); + } + + assertEquals(expectedAppImage, ExpectedAppImage.load(env.appImageDir())); + } + + @Test + void testCopyApplication(@TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var srcApp = createApp(TEST_LAYOUT_1, TestRuntimeBuilder.INSTANCE); + + final var srcEnv = buildEnv(workDir.resolve("build")).appImageDirFor(srcApp).create(); + + // Build application image in `srcEnv.appImageDir()` directory. + buildPipeline().create().execute(srcEnv, srcApp); + + final var dstApp = createApp(TEST_LAYOUT_2, TestRuntimeBuilder.INSTANCE); + + final var dstEnv = buildEnv(workDir.resolve("build-2")) + .appImageLayout(dstApp.imageLayout().resolveAt(workDir.resolve("a/b/c"))) + .create(); + + // Copy application image from `srcEnv.appImageDir()` into `dstEnv.appImageDir()` + // with layout transformation. + // This test exercises flexibility of the packaging pipeline. + final var builder = buildPipeline() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).applicationAction(cfg -> { + assertSame(dstApp, cfg.app()); + assertEquals(dstEnv.appImageDir(), cfg.env().appImageLayout().rootDirectory()); + assertFalse(Files.exists(dstEnv.appImageDir())); + PackagingPipeline.copyAppImage(srcEnv.appImageLayout(), cfg.env().appImageLayout(), false); + }).add(); + + // Disable the default "build application image" actions of the tasks which + // are the dependencies of `PrimaryTaskID.BUILD_APPLICATION_IMAGE` task as + // their output will be overwritten in the custom action of this task. + builder.taskGraphSnapshot().getAllTailsOf(PrimaryTaskID.BUILD_APPLICATION_IMAGE).forEach(taskId -> { + builder.task(taskId).noaction().add(); + }); + + builder.create().execute(dstEnv, dstApp); + + AppImageLayout.toPathGroup(dstEnv.appImageLayout()).paths().forEach(path -> { + assertTrue(Files.exists(path)); + }); + + assertEquals(Path.of("c"), dstEnv.appImageDir().getFileName()); + + var executedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(dstEnv, dstApp); + })); + + assertEquals(List.of(PrimaryTaskID.BUILD_APPLICATION_IMAGE), executedTaskActions); + + final ExpectedAppImage expectedAppImage = ExpectedAppImage.build().dir("") + .file("q/launchers/my-launcher", TestLauncher.CONTENT) + .file("qqq/runtime/my-runtime", TestRuntimeBuilder.CONTENT); + + assertEquals(expectedAppImage, ExpectedAppImage.load(dstEnv.appImageDir())); + } + + @Test + void testCreatePackage(@TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var outputDir = workDir.resolve("bundles"); + final var pkg = buildPackage(createApp(TEST_LAYOUT_1_WITH_INSTALL_DIR, TestRuntimeBuilder.INSTANCE)).create(); + final var env = buildEnv(workDir.resolve("build")).appImageDirFor(pkg).create(); + + final var builder = buildPipeline(); + + // Will create an app image in `env.appImageDir()` directory with `pkg.appImageLayout()` layout. + // Will convert the created app image into a package. + builder.create().execute(env, pkg, outputDir); + + final var expected = createTestPackageFileContents(env.appImageLayout()); + final var actual = Files.readString(outputDir.resolve(pkg.packageFileNameWithSuffix())); + + assertEquals(expected, actual); + System.out.println(String.format("testCreatePackage:\n---\n%s\n---", actual)); + + var executedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(env, pkg, outputDir); + })); + + assertEquals(List.of( + BuildApplicationTaskID.RUNTIME, + BuildApplicationTaskID.LAUNCHERS, + BuildApplicationTaskID.CONTENT, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE + ), executedTaskActions); + + final var expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("launchers/my-launcher"), TestLauncher.CONTENT) + .file(TEST_INSTALL_DIR.resolve("runtime/my-runtime"), TestRuntimeBuilder.CONTENT); + + assertEquals(expectedAppImage, ExpectedAppImage.load(env.appImageDir())); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testCreateRuntimeInstaller(boolean transformLayout, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final AppImageLayout srcLayout; + if (transformLayout) { + // Create an application layout such that the runtime directory doesn't + // have a common parent with other directories otherwise the runtime directory + // will be skipped when copying the app image layout as path groups because + // the destination app image layout is of type RuntimeLayout and have only + // the runtime directory. + srcLayout = ApplicationLayout.build() + .launchersDirectory("launchers") + .appDirectory("lib") + .runtimeDirectory("runtime") + .appModsDirectory("lib") + .contentDirectory("lib") + .desktopIntegrationDirectory("lib") + .create(); + } else { + srcLayout = RuntimeLayout.DEFAULT; + } + + // Create a runtime image in `env.appImageDir()` directory. + final var env = buildEnv(workDir.resolve("build")) + .appImageLayout(srcLayout) + .appImageDir(workDir.resolve("rt")) + .create(); + TestRuntimeBuilder.INSTANCE.create(env.appImageLayout()); + + final var pipeline = buildPackage(createApp( + RuntimeLayout.DEFAULT.resolveAt(TEST_INSTALL_DIR).resetRootDirectory())).create(); + + final var expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("my-runtime"), TestRuntimeBuilder.CONTENT); + + createAndVerifyPackage(buildPipeline(), pipeline, env, workDir.resolve("bundles"), + String.format("testCreateRuntimeInstaller(%s)", transformLayout), expectedAppImage, + CopyAppImageTaskID.COPY, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE); + } + + private enum ExternalAppImageMode { + + // Copy predefined app image from `BuildEnv.appImageDir()`. + // Layout of the predefined app image is `BuildEnv.appImageLayout()` and + // its unresolved variant equals to `Package.appImageLayout()`. + COPY_FROM_BUILD_ENV, + + // Copy predefined app image from some directory. + // Layout of the predefined app image is `Package.appImageLayout()`. + COPY, + + // Copy predefined app image from `BuildEnv.appImageDir()`. + // Layout of the predefined app image is `BuildEnv.appImageLayout()` and + // its unresolved variant is NOT equal to `Package.appImageLayout()`. + TRANSFORM_FROM_BUILD_ENV, + ; + + static final Set FROM_BUILD_ENV = Set.of( + COPY_FROM_BUILD_ENV, TRANSFORM_FROM_BUILD_ENV); + } + + @ParameterizedTest + @EnumSource(ExternalAppImageMode.class) + void testCreatePackageFromExternalAppImage(ExternalAppImageMode mode, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final ApplicationLayout appLayout; + final ExpectedAppImage expectedAppImage; + if (ExternalAppImageMode.TRANSFORM_FROM_BUILD_ENV == mode) { + appLayout = TEST_LAYOUT_2_WITH_INSTALL_DIR; + expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("q/launchers/my-launcher"), TestLauncher.CONTENT) + .file(TEST_INSTALL_DIR.resolve("qqq/runtime/my-runtime"), TestRuntimeBuilder.CONTENT); + } else { + appLayout = TEST_LAYOUT_1_WITH_INSTALL_DIR; + expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("launchers/my-launcher"), TestLauncher.CONTENT) + .file(TEST_INSTALL_DIR.resolve("runtime/my-runtime"), TestRuntimeBuilder.CONTENT); + } + + final BuildEnv env; + final Path predefinedAppImage; + if (ExternalAppImageMode.FROM_BUILD_ENV.contains(mode)) { + // External app image is stored in the build env app image directory. + env = setupBuildEnvForExternalAppImage(workDir); + predefinedAppImage = env.appImageDir(); + } else { + // External app image is stored outside of the build env app image directory + // and should have the same layout as the app's app image layout. + env = buildEnv(workDir.resolve("build")) + .appImageDir(workDir) + // Always need some app image layout. + .appImageLayout(new AppImageLayout.Stub(Path.of(""))) + .create(); + final var externalAppImageLayout = appLayout.resolveAt(workDir.resolve("app-image")); + TestRuntimeBuilder.INSTANCE.create(externalAppImageLayout); + TestLauncher.INSTANCE.create(externalAppImageLayout); + predefinedAppImage = externalAppImageLayout.rootDirectory(); + } + + final var pkg = buildPackage(createApp(appLayout)) + .predefinedAppImage(predefinedAppImage) + .create(); + + createAndVerifyPackage(buildPipeline(), pkg, env, workDir.resolve("bundles"), + String.format("testCreatePackageFromExternalAppImage(%s)", mode), expectedAppImage, + CopyAppImageTaskID.COPY, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE); + } + + @ParameterizedTest + @EnumSource(names={"COPY", "COPY_FROM_BUILD_ENV"}) + void testCreatePackageFromExternalAppImageNoCopyAction(ExternalAppImageMode mode, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final ApplicationLayout appLayout = TEST_LAYOUT_1_WITH_INSTALL_DIR; + + final BuildEnv env; + final ApplicationLayout predefinedAppImageLayout; + if (ExternalAppImageMode.FROM_BUILD_ENV.contains(mode)) { + // External app image is stored in the build env app image directory. + env = setupBuildEnvForExternalAppImage(workDir); + predefinedAppImageLayout = env.asApplicationLayout().orElseThrow(); + } else { + // External app image is stored outside of the build env app image directory + // and should have the same layout as the app's app image layout. + env = buildEnv(workDir.resolve("build")) + .appImageDir(workDir) + // Always need some app image layout. + .appImageLayout(new AppImageLayout.Stub(Path.of(""))) + .create(); + predefinedAppImageLayout = appLayout.resolveAt(workDir.resolve("app-image")); + TestRuntimeBuilder.INSTANCE.create(predefinedAppImageLayout); + TestLauncher.INSTANCE.create(predefinedAppImageLayout); + } + + final var pkg = buildPackage(createApp(appLayout)) + .predefinedAppImage(predefinedAppImageLayout.rootDirectory()) + .create(); + + final var outputDir = workDir.resolve("bundles"); + + final var builder = buildPipeline().configuredTasks().filter(task -> { + return CopyAppImageTaskID.COPY.equals(task.task()); + }).findFirst().orElseThrow().noaction().add(); + + final var startupParameters = builder.createStartupParameters(env, pkg, outputDir); + + builder.create().execute(startupParameters); + + final var expected = createTestPackageFileContents(predefinedAppImageLayout); + final var actual = Files.readString(outputDir.resolve(pkg.packageFileNameWithSuffix())); + assertEquals(expected, actual); + System.out.println(String.format("%s:\n---\n%s\n---", + String.format("testCreatePackageFromExternalAppImage(%s)", mode), actual)); + + final ExpectedAppImage expectedAppImage = ExpectedAppImage.build().dir("") + .file(predefinedAppImageLayout.unresolve().launchersDirectory().resolve("my-launcher"), TestLauncher.CONTENT) + .file(predefinedAppImageLayout.unresolve().runtimeDirectory().resolve("my-runtime"), TestRuntimeBuilder.CONTENT); + assertEquals(expectedAppImage, ExpectedAppImage.load(pkg.predefinedAppImage().orElseThrow())); + + var actualExecutedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(startupParameters); + })); + assertEquals(List.of( + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE), actualExecutedTaskActions); + } + + @Test + void testCreatePackageFromExternalAppImageWithoutExternalAppImageError(@TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var env = setupBuildEnvForExternalAppImage(workDir); + final var pkg = buildPackage(createApp(TEST_LAYOUT_1_WITH_INSTALL_DIR)).create(); + final var pipeline = buildPipeline().create(); + + assertThrowsExactly(UnsupportedOperationException.class, () -> pipeline.execute(env, pkg, workDir)); + } + + @Test + void testExceptionRethrow_RuntimeException() throws ConfigException, PackagerException, IOException { + + final var expectedException = new RuntimeException("foo"); + final var ex = testExceptionRethrow(expectedException, expectedException.getClass(), () -> { + throw expectedException; + }); + assertSame(expectedException, ex); + } + + @Test + void testExceptionRethrow_PackagerException() throws ConfigException, PackagerException, IOException { + + final var expectedException = new PackagerException("param.vendor.default"); + final var ex = testExceptionRethrow(expectedException, expectedException.getClass(), () -> { + throw expectedException; + }); + assertSame(expectedException, ex); + } + + @Test + void testExceptionRethrow_Exception() throws ConfigException, PackagerException, IOException { + + final var expectedException = new Exception("foo"); + final var ex = testExceptionRethrow(expectedException, PackagerException.class, () -> { + rethrowUnchecked(expectedException); + }); + assertSame(expectedException, ex.getCause()); + } + + @Test + void testAppImageAction() throws PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1); + final var env = dummyBuildEnv(); + + final var executed = new boolean[1]; + + PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE).add() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).appImageAction(ctx -> { + assertSame(app, ctx.app()); + assertSame(env, ctx.env()); + executed[0] = true; + }).add().create().execute(env, app); + + assertTrue(executed[0]); + } + + @Test + void testAppImageActionWithPackage() throws PackagerException, IOException { + + final var pkg = buildPackage(createApp(TEST_LAYOUT_1, TestRuntimeBuilder.INSTANCE)).create(); + final var env = dummyBuildEnv(); + + final var executed = new boolean[1]; + + final var builder = PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.PACKAGE).add(); + + final var startupParameters = builder.createStartupParameters(env, pkg, Path.of("")); + + builder.task(PrimaryTaskID.PACKAGE).appImageAction(ctx -> { + assertSame(pkg.app(), ctx.app()); + assertSame(startupParameters.packagingEnv(), ctx.env()); + executed[0] = true; + }).add().create().execute(startupParameters); + + assertTrue(executed[0]); + } + + @Test + void testPackageActionWithApplication() throws PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1); + final var env = dummyBuildEnv(); + + final var pipeline = PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE).add() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).packageAction(ctx -> { + throw new AssertionError(); + }).add().create(); + + // If the pipeline is building an application, it can not execute actions that take a package as an argument. + assertThrowsExactly(UnsupportedOperationException.class, () -> pipeline.execute(env, app)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testContextMapper(boolean allowAll) throws PackagerException, IOException { + + var builder = PackagingPipeline.buildStandard().contextMapper(ctx -> { + return new TaskContext() { + @Override + public boolean test(TaskID task) { + return allowAll; + } + + @Override + public void execute(TaskAction taskAction) throws IOException, PackagerException { + if (!allowAll) { + throw new AssertionError(); + } + ctx.execute(taskAction); + } + }; + }); + + var actualExecutedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(dummyBuildEnv(), createApp(TEST_LAYOUT_1)); + })); + + List expectedExecutedTaskActions; + + if (allowAll) { + expectedExecutedTaskActions = List.of( + BuildApplicationTaskID.RUNTIME, + BuildApplicationTaskID.LAUNCHERS, + BuildApplicationTaskID.CONTENT, + BuildApplicationTaskID.APP_IMAGE_FILE, + CopyAppImageTaskID.COPY, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT); + } else { + expectedExecutedTaskActions = List.of(); + } + + assertEquals(expectedExecutedTaskActions, actualExecutedTaskActions); + } + + public static List dryRun(PackagingPipeline.Builder builder, + Consumer callback) { + + List executedTaskActions = new ArrayList<>(); + builder.configuredTasks().filter(PackagingPipeline.Builder.TaskBuilder::hasAction).forEach(taskBuilder -> { + var taskId = taskBuilder.task(); + taskBuilder.action(() -> { + executedTaskActions.add(taskId); + }).add(); + }); + + callback.accept(builder); + + return executedTaskActions; + } + + private static Exception testExceptionRethrow(Exception expectedException, + Class expectedCatchExceptionType, + NoArgTaskAction throwAction) throws PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1); + final var env = dummyBuildEnv(); + + var pipeline = PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE).add() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).action(throwAction).add().create(); + + return assertThrowsExactly(expectedCatchExceptionType, () -> pipeline.execute(env, app)); + } + + private static BuildEnv setupBuildEnvForExternalAppImage(Path workDir) throws ConfigException { + // Create an app image in `env.appImageDir()` directory. + final var env = buildEnv(workDir.resolve("build")) + .appImageLayout(TEST_LAYOUT_1.resolveAt(Path.of("a/b/c")).resetRootDirectory()) + .appImageDir(workDir.resolve("app-image")) + .create(); + TestRuntimeBuilder.INSTANCE.create(env.appImageLayout()); + TestLauncher.INSTANCE.create((ApplicationLayout)env.appImageLayout()); + + return env; + } + + private static void createAndVerifyPackage(PackagingPipeline.Builder builder, Package pkg, + BuildEnv env, Path outputDir, String logMsgHeader, ExpectedAppImage expectedAppImage, + TaskID... expectedExecutedTaskActions) throws PackagerException, IOException { + Objects.requireNonNull(logMsgHeader); + + final var startupParameters = builder.createStartupParameters(env, pkg, outputDir); + + assertNotSameAppImageDirs(env, startupParameters.packagingEnv()); + + // Will create an app image in `startupParameters.packagingEnv().appImageDir()` directory + // with `pkg.appImageLayout()` layout using an app image (runtime image) from `env.appImageDir()` as input. + // Will convert the created app image into a package. + // Will not overwrite the contents of `env.appImageDir()` directory. + builder.create().execute(startupParameters); + + final var packagingAppImageDir = startupParameters.packagingEnv().appImageDir(); + + final var expected = createTestPackageFileContents(pkg.appImageLayout().resolveAt(packagingAppImageDir)); + + final var actual = Files.readString(outputDir.resolve(pkg.packageFileNameWithSuffix())); + + assertEquals(expected, actual); + System.out.println(String.format("%s:\n---\n%s\n---", logMsgHeader, actual)); + + assertEquals(expectedAppImage, ExpectedAppImage.load(packagingAppImageDir)); + + var actualExecutedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(startupParameters); + })); + + assertEquals(List.of(expectedExecutedTaskActions), actualExecutedTaskActions); + } + + private static Application createApp(AppImageLayout appImageLayout) { + return createApp(appImageLayout, Optional.empty()); + } + + private static Application createApp(AppImageLayout appImageLayout, RuntimeBuilder runtimeBuilder) { + return createApp(appImageLayout, Optional.of(runtimeBuilder)); + } + + private static Application createApp(AppImageLayout appImageLayout, Optional runtimeBuilder) { + Objects.requireNonNull(appImageLayout); + Objects.requireNonNull(runtimeBuilder); + if (appImageLayout.isResolved()) { + throw new IllegalArgumentException(); + } + + return new Application.Stub( + "foo", + "My app", + "1.0", + "Acme", + "copyright", + Optional.empty(), + List.of(), + appImageLayout, + runtimeBuilder, + List.of(), + Map.of()); + } + + + private static final class PackageBuilder { + PackageBuilder(Application app) { + this.app = Objects.requireNonNull(app); + } + + Package create() { + return new Package.Stub( + app, + new PackageType() {}, + "the-package", + "My package", + "1.0", + Optional.empty(), + Optional.empty(), + Optional.ofNullable(predefinedAppImage), + null, + TEST_INSTALL_DIR); + } + + PackageBuilder predefinedAppImage(Path v) { + predefinedAppImage = v; + return this; + } + + private Path predefinedAppImage; + private final Application app; + } + + + private static PackageBuilder buildPackage(Application app) { + return new PackageBuilder(app); + } + + private static BuildEnvBuilder buildEnv(Path rootDir) { + return new BuildEnvBuilder(rootDir); + } + + private static BuildEnv dummyBuildEnv() { + return BuildEnv.create(Path.of("foo"), Optional.empty(), false, PackagingPipeline.class, RuntimeLayout.DEFAULT); + } + + private static PackagingPipeline.Builder buildPipeline() { + return PackagingPipeline.buildStandard() + // Disable building the app image file (.jpackage.xml) as we don't have launchers in the test app. + .task(BuildApplicationTaskID.APP_IMAGE_FILE).noaction().add() + .task(BuildApplicationTaskID.LAUNCHERS).applicationAction(cfg -> { + TestLauncher.INSTANCE.create(cfg.resolvedLayout()); + }).add() + .task(PrimaryTaskID.PACKAGE).packageAction(cfg -> { + var str = createTestPackageFileContents(cfg.resolvedLayout()); + var packageFile = cfg.outputDir().resolve(cfg.pkg().packageFileNameWithSuffix()); + Files.createDirectories(packageFile.getParent()); + Files.writeString(packageFile, str); + }).add(); + } + + private static String createTestPackageFileContents(AppImageLayout pkgLayout) throws IOException { + return ExpectedAppImage.load(pkgLayout.rootDirectory()).toString(); + } + + private static void assertNotSameAppImageDirs(BuildEnv a, BuildEnv b) { + assertNotEquals(a.appImageDir(), b.appImageDir()); + assertEquals(a.buildRoot(), b.buildRoot()); + assertEquals(a.configDir(), b.configDir()); + assertEquals(a.resourceDir(), b.resourceDir()); + } + + + private static final class TestRuntimeBuilder implements RuntimeBuilder { + @Override + public void create(AppImageLayout appImageLayout) { + assertTrue(appImageLayout.isResolved()); + try { + Files.createDirectories(appImageLayout.runtimeDirectory()); + Files.writeString(runtimeFile(appImageLayout), CONTENT); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static Path runtimeFile(AppImageLayout appImageLayout) { + return appImageLayout.runtimeDirectory().resolve("my-runtime"); + } + + static final String CONTENT = "this is the runtime"; + + static final TestRuntimeBuilder INSTANCE = new TestRuntimeBuilder(); + } + + + private static final class TestLauncher { + public void create(ApplicationLayout appLayout) { + assertTrue(appLayout.isResolved()); + try { + Files.createDirectories(appLayout.launchersDirectory()); + Files.writeString(launcherFile(appLayout), CONTENT); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static Path launcherFile(ApplicationLayout appLayout) { + return appLayout.launchersDirectory().resolve("my-launcher"); + } + + static final String CONTENT = "this is the launcher"; + + static final TestLauncher INSTANCE = new TestLauncher(); + } + + + private static final class ExpectedAppImage { + + static ExpectedAppImage build() { + return new ExpectedAppImage(new HashSet<>()); + } + + static ExpectedAppImage load(Path appImageRoot) throws IOException { + try (var walk = Files.walk(appImageRoot)) { + return new ExpectedAppImage(walk.sorted().map(path -> { + var relativePath = appImageRoot.relativize(path); + if (Files.isDirectory(path)) { + return new Directory(relativePath); + } else { + return new File(relativePath, toSupplier(() -> Files.readString(path)).get()); + } + }).collect(Collectors.toSet())); + } + } + + ExpectedAppImage file(Path path, String content) { + return add(new File(path, content)); + } + + ExpectedAppImage file(String path, String content) { + return file(Path.of(path), content); + } + + ExpectedAppImage dir(Path path) { + return add(new Directory(path)); + } + + ExpectedAppImage dir(String path) { + return dir(Path.of(path)); + } + + @Override + public String toString() { + return items.stream().map(AppImageItem::toString).sorted().collect(Collectors.joining("\n")); + } + + @Override + public int hashCode() { + return Objects.hash(items); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + ExpectedAppImage other = (ExpectedAppImage) obj; + return Objects.equals(items, other.items); + } + + private ExpectedAppImage(Set items) { + this.items = Objects.requireNonNull(items); + } + + private ExpectedAppImage add(AppImageItem v) { + var path = v.path(); + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + + items.add(v); + while (path.getNameCount() > 1) { + items.add(new Directory(path = path.getParent())); + } + return this; + } + + private interface AppImageItem { + Path path(); + } + + private record File(Path path, String content) implements AppImageItem { + + File { + Objects.requireNonNull(path); + Objects.requireNonNull(content); + } + + @Override + public String toString() { + return String.format("%s[%s]", path, content); + } + } + + private record Directory(Path path) implements AppImageItem { + + Directory { + Objects.requireNonNull(path); + } + + @Override + public String toString() { + return path.toString(); + } + } + + private final Set items; + } + + + private static final ApplicationLayout TEST_LAYOUT_1 = ApplicationLayout.build() + .launchersDirectory("launchers") + .appDirectory("") + .runtimeDirectory("runtime") + .appModsDirectory("") + .contentDirectory("") + .desktopIntegrationDirectory("") + .create(); + + private static final ApplicationLayout TEST_LAYOUT_2 = ApplicationLayout.build() + .launchersDirectory("q/launchers") + .appDirectory("") + .runtimeDirectory("qqq/runtime") + .appModsDirectory("") + .contentDirectory("") + .desktopIntegrationDirectory("") + .create(); + + private static final Path TEST_INSTALL_DIR = Path.of("Acme/My app"); + + private static final ApplicationLayout TEST_LAYOUT_1_WITH_INSTALL_DIR = + TEST_LAYOUT_1.resolveAt(TEST_INSTALL_DIR).resetRootDirectory(); + + private static final ApplicationLayout TEST_LAYOUT_2_WITH_INSTALL_DIR = + TEST_LAYOUT_2.resolveAt(TEST_INSTALL_DIR).resetRootDirectory(); +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java index 1c863b653b1..5948d74079e 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java @@ -23,12 +23,14 @@ package jdk.jpackage.internal.model; +import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; import java.nio.file.Path; import java.util.List; import java.util.Set; +import jdk.jpackage.internal.util.PathGroup; import org.junit.jupiter.api.Test; @@ -48,7 +50,7 @@ public class AppImageLayoutTest { public void testPathGroup() { final var layout = new AppImageLayout.Stub(Path.of("root"), Path.of("runtime")); - final var pathGroup = AppImageLayout.toPathGroup(layout); + final var pathGroup = toPathGroup(layout); assertEquals(Set.of("runtimeDirectory"), pathGroup.keys()); assertEquals(List.of(layout.runtimeDirectory()), pathGroup.paths()); @@ -56,15 +58,88 @@ public class AppImageLayoutTest { @Test public void testResolveAt() { - final var dir = Path.of("foo/bar"); - - final var layout = new AppImageLayout.Stub(Path.of(""), Path.of("runtime")); - - final var resolvedLayout = layout.resolveAt(dir); - - assertNotSame(layout, resolvedLayout); - - assertEquals(dir.resolve(layout.rootDirectory()), resolvedLayout.rootDirectory()); - assertEquals(dir.resolve(layout.runtimeDirectory()), resolvedLayout.runtimeDirectory()); + testResolveAt(new AppImageLayout.Stub(Path.of("foo"))); } + + @Test + public void testResolveAtRepeat() { + testResolveAtRepeat(new AppImageLayout.Stub(Path.of("foo"))); + } + + @Test + public void testUnresolve() { + testUnresolve(new AppImageLayout.Stub(Path.of("runtime"))); + } + + @Test + public void testEmptyRootDirectory() { + testEmptyRootDirectory(new AppImageLayout.Stub(Path.of("rt"))); + } + + public static void testResolveAt(AppImageLayout testee) { + + var dir = Path.of("foo/bar"); + + assertLayout(testee.resolveAt(dir), true, testee, dir); + } + + public static void testResolveAtRepeat(AppImageLayout testee) { + + var resolvedLayout = testee.resolveAt(Path.of("b/c")).resolveAt(Path.of("a")); + + assertLayout(resolvedLayout, true, testee, Path.of("a/b/c")); + } + + public static void testUnresolve(AppImageLayout testee) { + if (testee.isResolved()) { + throw new IllegalArgumentException(); + } + + var resolvedLayout = testee.resolveAt(Path.of("foo/bar")); + var layout = resolvedLayout.unresolve(); + + assertLayout(layout, false, testee, Path.of("")); + + resolvedLayout = testee.resolveAt(Path.of("").toAbsolutePath()); + layout = resolvedLayout.unresolve(); + + assertLayout(layout, false, testee, Path.of("")); + + assertSame(testee, testee.unresolve()); + } + + public static void testEmptyRootDirectory(AppImageLayout testee) { + if (testee.isResolved()) { + throw new IllegalArgumentException(); + } + + assertEmptyRootDirectory(testee); + + final var resolved = testee.resolveAt(Path.of("t")); + + assertEmptyRootDirectory(resolved); + } + + private static void assertEmptyRootDirectory(AppImageLayout testee) { + if (testee.isResolved()) { + var newLayout = testee.resetRootDirectory(); + assertLayout(newLayout, false, Path.of(""), toPathGroup(testee)); + } else { + assertSame(testee, testee.resetRootDirectory()); + } + } + + private static void assertLayout(AppImageLayout actual, boolean expectedResolved, + AppImageLayout base, Path baseResolveAt) { + assertLayout(actual, expectedResolved, baseResolveAt.resolve(base.rootDirectory()), + toPathGroup(base).resolveAt(baseResolveAt)); + } + + private static void assertLayout(AppImageLayout actual, boolean expectedResolved, + Path expectedRootDir, PathGroup expectedPaths) { + assertEquals(expectedResolved, actual.isResolved()); + assertEquals(expectedRootDir, actual.rootDirectory()); + assertEquals(expectedPaths, toPathGroup(actual)); + } + } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java index 089a363e0e4..063b11ec589 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java @@ -23,20 +23,51 @@ package jdk.jpackage.internal.model; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ApplicationLayoutTest { - public void test(boolean move, Path tempDir) throws IOException { + @Test + public void testMove(@TempDir Path tempDir) throws IOException { + test(true, tempDir); + } + + @Test + public void testCopy(@TempDir Path tempDir) throws IOException { + test(false, tempDir); + } + + @Test + public void testResolveAt() { + AppImageLayoutTest.testResolveAt(createLayout()); + } + + @Test + public void testResolveAtRepeat() { + AppImageLayoutTest.testResolveAtRepeat(createLayout()); + } + + @Test + public void testUnresolve() { + AppImageLayoutTest.testUnresolve(createLayout()); + } + + @Test + public void testEmptyRootDirectory() { + AppImageLayoutTest.testEmptyRootDirectory(createLayout()); + } + + private static void test(boolean move, Path tempDir) throws IOException { final var srcAppImageRoot = tempDir.resolve("src"); Files.createDirectories(srcAppImageRoot); @@ -55,14 +86,7 @@ public class ApplicationLayoutTest { Files.createDirectories(path); } - final var layout = ApplicationLayout.build() - .launchersDirectory("bin") - .appDirectory("lib/app") - .runtimeDirectory("runtime") - .appModsDirectory("mods") - .contentDirectory("content") - .desktopIntegrationDirectory("lib/apps") - .create(); + final var layout = createLayout(); final var dstAppImageRoot = tempDir.resolve("dst"); Files.createDirectories(dstAppImageRoot); @@ -100,13 +124,14 @@ public class ApplicationLayoutTest { } } - @Test - public void testMove(@TempDir Path tempDir) throws IOException { - test(true, tempDir); - } - - @Test - public void testCopy(@TempDir Path tempDir) throws IOException { - test(false, tempDir); + public static ApplicationLayout createLayout() { + return ApplicationLayout.build() + .launchersDirectory("bin") + .appDirectory("lib/app") + .runtimeDirectory("runtime") + .appModsDirectory("mods") + .contentDirectory("content") + .desktopIntegrationDirectory("lib/apps") + .create(); } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java index 579cf5f083c..c5b5e271960 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java @@ -65,6 +65,14 @@ public class PathGroupTest { assertThrowsExactly(NullPointerException.class, () -> new PathGroup(Map.of()).getPath(null)); } + @Test + public void equals() { + assertEquals(new PathGroup(Map.of()), new PathGroup(Map.of())); + assertEquals(new PathGroup(Map.of("foo", Path.of("bar"))), new PathGroup(Map.of("foo", Path.of("bar")))); + assertNotEquals(new PathGroup(Map.of("foo", Path.of("bar"))), new PathGroup(Map.of("foo", Path.of("rab")))); + assertNotEquals(new PathGroup(Map.of("foo", Path.of("bar"))), new PathGroup(Map.of("Foo", Path.of("bar")))); + } + @Test public void testEmptyPathGroup() { PathGroup pg = new PathGroup(Map.of()); diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitAdapter.java b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java similarity index 90% rename from test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitAdapter.java rename to test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java index b7c50285797..5a605b1642d 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitAdapter.java +++ b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java @@ -40,10 +40,12 @@ import org.junit.jupiter.api.TestFactory; public class JUnitAdapter { - static { - if (System.getProperty("test.src") == null) { - // Was called by somebody else but not by jtreg - System.setProperty("test.src", Path.of("@@openJdkDir@@/test/jdk/tools/jpackage").toString()); + public static class TestSrcInitializer { + static { + if (System.getProperty("test.src") == null) { + // Was called by somebody else but not by jtreg + System.setProperty("test.src", Path.of("@@openJdkDir@@/test/jdk/tools/jpackage").toString()); + } } } @@ -84,5 +86,9 @@ public class JUnitAdapter { } } + static { + new TestSrcInitializer(); + } + private static final int LOG_MSG_TIMESTAMP_LENGTH = "[HH:mm:ss.SSS] ".length(); } diff --git a/test/jdk/tools/jpackage/junit/windows/junit.java b/test/jdk/tools/jpackage/junit/windows/junit.java index 38b5cd5ca0c..d27a282f0dc 100644 --- a/test/jdk/tools/jpackage/junit/windows/junit.java +++ b/test/jdk/tools/jpackage/junit/windows/junit.java @@ -24,6 +24,7 @@ /* @test * @summary Test function reading OS version from PE file * @requires (os.family == "windows") - * @compile/module=jdk.jpackage jdk/jpackage/internal/ExecutableOSVersionTest.java + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/ExecutableOSVersionTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.ExecutableOSVersionTest */ From bd65d483df4742bb7ce79b613f10f70a45117f84 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Sun, 17 Aug 2025 12:56:42 +0000 Subject: [PATCH 062/112] 8365245: Move size reducing operations to GrowableArrayWithAllocator Reviewed-by: jsjolen, stefank --- src/hotspot/share/utilities/growableArray.hpp | 131 +++++++++--------- test/hotspot/gtest/gc/z/test_zArray.cpp | 4 +- 2 files changed, 70 insertions(+), 65 deletions(-) diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 759b9451490..53403ca5cf1 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -93,12 +93,6 @@ public: bool is_empty() const { return _len == 0; } bool is_nonempty() const { return _len != 0; } bool is_full() const { return _len == _capacity; } - - void clear() { _len = 0; } - void trunc_to(int length) { - assert(length <= _len,"cannot increase length"); - _len = length; - } }; template class GrowableArrayIterator; @@ -189,11 +183,6 @@ public: return GrowableArrayIterator(this, length()); } - E pop() { - assert(_len > 0, "empty list"); - return _data[--_len]; - } - void at_put(int i, const E& elem) { assert(0 <= i && i < _len, "illegal index %d for length %d", i, _len); _data[i] = elem; @@ -247,59 +236,6 @@ public: return -1; } - // Order preserving remove operations. - - void remove(const E& elem) { - // Assuming that element does exist. - bool removed = remove_if_existing(elem); - if (removed) return; - ShouldNotReachHere(); - } - - bool remove_if_existing(const E& elem) { - // Returns TRUE if elem is removed. - for (int i = 0; i < _len; i++) { - if (_data[i] == elem) { - remove_at(i); - return true; - } - } - return false; - } - - void remove_at(int index) { - assert(0 <= index && index < _len, "illegal index %d for length %d", index, _len); - for (int j = index + 1; j < _len; j++) { - _data[j-1] = _data[j]; - } - _len--; - } - - // Remove all elements up to the index (exclusive). The order is preserved. - void remove_till(int idx) { - remove_range(0, idx); - } - - // Remove all elements in the range [start - end). The order is preserved. - void remove_range(int start, int end) { - assert(0 <= start, "illegal start index %d", start); - assert(start < end && end <= _len, "erase called with invalid range (%d, %d) for length %d", start, end, _len); - - for (int i = start, j = end; j < length(); i++, j++) { - at_put(i, at(j)); - } - trunc_to(length() - (end - start)); - } - - // The order is changed. - void delete_at(int index) { - assert(0 <= index && index < _len, "illegal index %d for length %d", index, _len); - if (index < --_len) { - // Replace removed element with last one. - _data[index] = _data[_len]; - } - } - void sort(int f(E*, E*)) { if (_data == nullptr) return; qsort(_data, length(), sizeof(E), (_sort_Fn)f); @@ -427,6 +363,11 @@ public: void push(const E& elem) { append(elem); } + E pop() { + assert(this->_len > 0, "empty list"); + return this->_data[--this->_len]; + } + E& at_grow(int i, const E& fill = E()) { assert(0 <= i, "negative index %d", i); if (i >= this->_len) { @@ -514,9 +455,71 @@ public: // Ensure capacity is at least new_capacity. void reserve(int new_capacity); + void trunc_to(int length) { + assert(length <= this->_len,"cannot increase length"); + this->_len = length; + } + + // Order preserving remove operations. + + void remove_at(int index) { + assert(0 <= index && index < this->_len, + "illegal index %d for length %d", index, this->_len); + for (int j = index + 1; j < this->_len; j++) { + this->_data[j-1] = this->_data[j]; + } + this->_len--; + } + + void remove(const E& elem) { + // Assuming that element does exist. + bool removed = this->remove_if_existing(elem); + if (removed) return; + ShouldNotReachHere(); + } + + bool remove_if_existing(const E& elem) { + // Returns TRUE if elem is removed. + for (int i = 0; i < this->_len; i++) { + if (this->_data[i] == elem) { + this->remove_at(i); + return true; + } + } + return false; + } + + // Remove all elements up to the index (exclusive). The order is preserved. + void remove_till(int idx) { + remove_range(0, idx); + } + + // Remove all elements in the range [start - end). The order is preserved. + void remove_range(int start, int end) { + assert(0 <= start, "illegal start index %d", start); + assert(start < end && end <= this->_len, + "erase called with invalid range (%d, %d) for length %d", + start, end, this->_len); + + for (int i = start, j = end; j < this->length(); i++, j++) { + this->at_put(i, this->at(j)); + } + this->_len -= (end - start); + } + + // Replaces the designated element with the last element and shrinks by 1. + void delete_at(int index) { + assert(0 <= index && index < this->_len, "illegal index %d for length %d", index, this->_len); + if (index < --this->_len) { + // Replace removed element with last one. + this->_data[index] = this->_data[this->_len]; + } + } + // Reduce capacity to length. void shrink_to_fit(); + void clear() { this->_len = 0; } void clear_and_deallocate(); }; diff --git a/test/hotspot/gtest/gc/z/test_zArray.cpp b/test/hotspot/gtest/gc/z/test_zArray.cpp index acc7688cb10..79d4b29b1f5 100644 --- a/test/hotspot/gtest/gc/z/test_zArray.cpp +++ b/test/hotspot/gtest/gc/z/test_zArray.cpp @@ -127,9 +127,11 @@ TEST_F(ZArrayTest, slice) { const auto check_reversed = [](ZArraySlice original, ZArraySlice reversed) { ASSERT_EQ(original.length(), reversed.length()); + int ri = reversed.length(); for (int e : original) { - ASSERT_EQ(e, reversed.pop()); + ASSERT_EQ(e, reversed.at(--ri)); } + ASSERT_EQ(ri, 0); }; ZArraySlice a_reversed = reverse(a, reverse); From f364fcab792ed5a14e5c2779fa85ecc9d6915ae3 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Mon, 18 Aug 2025 05:32:03 +0000 Subject: [PATCH 063/112] 8359119: Change Charset to use StableValue Reviewed-by: alanb, rriggs --- .../classes/java/nio/charset/Charset.java | 116 +++++++++--------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/src/java.base/share/classes/java/nio/charset/Charset.java b/src/java.base/share/classes/java/nio/charset/Charset.java index b36e11cdc36..1eb3c9c2094 100644 --- a/src/java.base/share/classes/java/nio/charset/Charset.java +++ b/src/java.base/share/classes/java/nio/charset/Charset.java @@ -37,13 +37,16 @@ import java.nio.charset.spi.CharsetProvider; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.ServiceLoader; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.function.Supplier; /** @@ -381,17 +384,7 @@ public abstract class Charset }; } - private static class ThreadTrackHolder { - static final ThreadTracker TRACKER = new ThreadTracker(); - } - - private static Object tryBeginLookup() { - return ThreadTrackHolder.TRACKER.tryBegin(); - } - - private static void endLookup(Object key) { - ThreadTrackHolder.TRACKER.end(key); - } + private static final ScopedValue IN_LOOKUP = ScopedValue.newInstance(); private static Charset lookupViaProviders(final String charsetName) { @@ -406,48 +399,54 @@ public abstract class Charset if (!VM.isBooted()) return null; - Object key = tryBeginLookup(); - if (key == null) { + if (IN_LOOKUP.isBound()) { // Avoid recursive provider lookups return null; } try { - for (Iterator i = providers(); i.hasNext();) { - CharsetProvider cp = i.next(); - Charset cs = cp.charsetForName(charsetName); - if (cs != null) - return cs; - } - return null; - } finally { - endLookup(key); + return ScopedValue.where(IN_LOOKUP, true).call( + new ScopedValue.CallableOp() { + @Override + public Charset call() { + for (Iterator i = providers(); i.hasNext(); ) { + CharsetProvider cp = i.next(); + Charset cs = cp.charsetForName(charsetName); + if (cs != null) + return cs; + } + return null; + } + } + ); + } catch (Exception t) { + // Should not happen + throw new RuntimeException(t); } + } /* The extended set of charsets */ - private static class ExtendedProviderHolder { - static final CharsetProvider[] extendedProviders = extendedProviders(); - // returns ExtendedProvider, if installed - private static CharsetProvider[] extendedProviders() { - CharsetProvider[] cps = new CharsetProvider[1]; - int n = 0; - ServiceLoader sl = + private static final Supplier> EXTENDED_PROVIDERS = StableValue.supplier( + new Supplier<>() { public List get() { return extendedProviders0(); }}); + + private static List extendedProviders0() { + CharsetProvider[] cps = new CharsetProvider[1]; + int n = 0; + final ServiceLoader sl = ServiceLoader.loadInstalled(CharsetProvider.class); - for (CharsetProvider cp : sl) { - if (n + 1 > cps.length) { - cps = Arrays.copyOf(cps, cps.length << 1); - } - cps[n++] = cp; + for (CharsetProvider cp : sl) { + if (n + 1 > cps.length) { + cps = Arrays.copyOf(cps, cps.length << 1); } - return n == cps.length ? cps : Arrays.copyOf(cps, n); + cps[n++] = cp; } + return List.of(n == cps.length ? cps : Arrays.copyOf(cps, n)); } private static Charset lookupExtendedCharset(String charsetName) { if (!VM.isBooted()) // see lookupViaProviders() return null; - CharsetProvider[] ecps = ExtendedProviderHolder.extendedProviders; - for (CharsetProvider cp : ecps) { + for (CharsetProvider cp : EXTENDED_PROVIDERS.get()) { Charset cs = cp.charsetForName(charsetName); if (cs != null) return cs; @@ -608,8 +607,7 @@ public abstract class Charset new TreeMap<>( String.CASE_INSENSITIVE_ORDER); put(standardProvider.charsets(), m); - CharsetProvider[] ecps = ExtendedProviderHolder.extendedProviders; - for (CharsetProvider ecp :ecps) { + for (CharsetProvider ecp : EXTENDED_PROVIDERS.get()) { put(ecp.charsets(), m); } for (Iterator i = providers(); i.hasNext();) { @@ -619,7 +617,16 @@ public abstract class Charset return Collections.unmodifiableSortedMap(m); } - private @Stable static Charset defaultCharset; + private static final Supplier defaultCharset = StableValue.supplier( + new Supplier<>() { public Charset get() { return defaultCharset0(); }}); + + private static Charset defaultCharset0() { + // do not look for providers other than the standard one + final Charset cs = standardProvider.charsetForName(StaticProperty.fileEncoding()); + return (cs == null) + ? sun.nio.cs.UTF_8.INSTANCE + : cs; + } /** * Returns the default charset of this Java virtual machine. @@ -640,25 +647,19 @@ public abstract class Charset * @since 1.5 */ public static Charset defaultCharset() { - if (defaultCharset == null) { - synchronized (Charset.class) { - // do not look for providers other than the standard one - Charset cs = standardProvider.charsetForName(StaticProperty.fileEncoding()); - if (cs != null) - defaultCharset = cs; - else - defaultCharset = sun.nio.cs.UTF_8.INSTANCE; - } - } - return defaultCharset; + return defaultCharset.get(); } /* -- Instance fields and methods -- */ - private final String name; // tickles a bug in oldjavac - private final String[] aliases; // tickles a bug in oldjavac - private Set aliasSet; + @Stable + private final String name; + @Stable + private final String[] aliases; + @Stable + private final Supplier> aliasSet = StableValue.supplier( + new Supplier<>() { public Set get() { return Set.of(aliases); }}); /** * Initializes a new charset with the given canonical name and alias @@ -710,12 +711,7 @@ public abstract class Charset * @return An immutable set of this charset's aliases */ public final Set aliases() { - Set set = this.aliasSet; - if (set == null) { - set = Set.of(aliases); - this.aliasSet = set; - } - return set; + return aliasSet.get(); } /** From e7ca8c7d55fa959cb43d49d63128420b05b7cc92 Mon Sep 17 00:00:00 2001 From: David Beaumont Date: Mon, 18 Aug 2025 07:08:19 +0000 Subject: [PATCH 064/112] 8365436: ImageReaderTest fails when jmods directory not present Reviewed-by: sgehwolf, alanb --- test/jdk/jdk/internal/jimage/ImageReaderTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/jdk/jdk/internal/jimage/ImageReaderTest.java b/test/jdk/jdk/internal/jimage/ImageReaderTest.java index 6bdf0cf479a..8db9a3768d4 100644 --- a/test/jdk/jdk/internal/jimage/ImageReaderTest.java +++ b/test/jdk/jdk/internal/jimage/ImageReaderTest.java @@ -25,11 +25,12 @@ import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReader.Node; import jdk.test.lib.compiler.InMemoryJavaCompiler; import jdk.test.lib.util.JarBuilder; +import jdk.tools.jlink.internal.LinkableRuntimeImage; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.opentest4j.TestSkippedException; import tests.Helper; import tests.JImageGenerator; @@ -54,6 +55,7 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; * @test * @summary Tests for ImageReader. * @modules java.base/jdk.internal.jimage + * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jimage * @library /test/jdk/tools/lib * /test/lib @@ -214,15 +216,15 @@ public class ImageReaderTest { /// Returns the helper for building JAR and jimage files. private static Helper getHelper() { + Helper helper; try { - Helper helper = Helper.newHelper(); - if (helper == null) { - throw new TestSkippedException("Cannot create test helper (exploded image?)"); - } - return helper; + boolean isLinkableRuntime = LinkableRuntimeImage.isLinkableRuntime(); + helper = Helper.newHelper(isLinkableRuntime); } catch (IOException e) { throw new RuntimeException(e); } + Assumptions.assumeTrue(helper != null, "Cannot create test helper, skipping test!"); + return helper; } /// Loads and performs actions on classes stored in a given `ImageReader`. From 166ea12d73c7a40a1a26dc586e3db9d9430c068f Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 18 Aug 2025 07:14:09 +0000 Subject: [PATCH 065/112] 8365543: UnixNativeDispatcher.init should lookup open64at and stat64at on AIX Co-authored-by: Joachim Kern Reviewed-by: jkern, stuefe, goetz, alanb --- .../unix/native/libnio/fs/UnixNativeDispatcher.c | 15 +++++++-------- .../java/nio/file/DirectoryStream/SecureDS.java | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index fbf996886ae..5f81241b7dd 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -343,22 +343,21 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) /* system calls that might not be available at run time */ -#if defined(_ALLBSD_SOURCE) - my_openat_func = (openat_func*) openat; - my_fstatat_func = (fstatat_func*) fstatat; -#else - // Make sure we link to the 64-bit version of the functions - my_openat_func = (openat_func*) dlsym(RTLD_DEFAULT, "openat64"); - my_fstatat_func = (fstatat_func*) dlsym(RTLD_DEFAULT, "fstatat64"); -#endif my_unlinkat_func = (unlinkat_func*) dlsym(RTLD_DEFAULT, "unlinkat"); my_renameat_func = (renameat_func*) dlsym(RTLD_DEFAULT, "renameat"); #if defined(_AIX) // Make sure we link to the 64-bit version of the function + my_openat_func = (openat_func*) dlsym(RTLD_DEFAULT, "open64at"); + my_fstatat_func = (fstatat_func*) dlsym(RTLD_DEFAULT, "stat64at"); my_fdopendir_func = (fdopendir_func*) dlsym(RTLD_DEFAULT, "fdopendir64"); #elif defined(_ALLBSD_SOURCE) + my_openat_func = (openat_func*) openat; + my_fstatat_func = (fstatat_func*) fstatat; my_fdopendir_func = (fdopendir_func*) fdopendir; #else + // Make sure we link to the 64-bit version of the functions + my_openat_func = (openat_func*) dlsym(RTLD_DEFAULT, "openat64"); + my_fstatat_func = (fstatat_func*) dlsym(RTLD_DEFAULT, "fstatat64"); my_fdopendir_func = (fdopendir_func*) dlsym(RTLD_DEFAULT, "fdopendir"); #endif diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 6199596b30c..d5d4b1904ea 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -24,7 +24,7 @@ /* @test * @bug 4313887 6838333 8343020 8357425 * @summary Unit test for java.nio.file.SecureDirectoryStream - * @requires (os.family == "linux" | os.family == "mac") + * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") * @library .. /test/lib * @build jdk.test.lib.Platform * @run main SecureDS From 190e113031bc6ece781fdf0d9f3c853ce324f170 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Mon, 18 Aug 2025 08:11:19 +0000 Subject: [PATCH 066/112] 8364263: HttpClient: Improve encapsulation of ProxyServer Reviewed-by: dfuchs, jpai --- test/jdk/java/net/httpclient/ProxyServer.java | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/test/jdk/java/net/httpclient/ProxyServer.java b/test/jdk/java/net/httpclient/ProxyServer.java index 747a20772d1..9ec84fe428f 100644 --- a/test/jdk/java/net/httpclient/ProxyServer.java +++ b/test/jdk/java/net/httpclient/ProxyServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,7 @@ import static java.util.stream.Collectors.toList; * Two threads are created per client connection. So, it's not * intended for large numbers of parallel connections. */ -public class ProxyServer extends Thread implements Closeable { +public final class ProxyServer implements Closeable { // could use the test library here - Platform.isWindows(), // but it would force all tests that use ProxyServer to @@ -97,9 +97,7 @@ public class ProxyServer extends Thread implements Closeable { this(port, debug, null); } - public ProxyServer(Integer port, - Boolean debug, - Credentials credentials) + private ProxyServer(Integer port, Boolean debug, Credentials credentials) throws IOException { this.debug = debug; @@ -108,15 +106,8 @@ public class ProxyServer extends Thread implements Closeable { listener.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), port)); this.port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); this.credentials = credentials; - setName("ProxyListener"); - setDaemon(true); - connections = new CopyOnWriteArrayList(); - start(); - } - - public ProxyServer(String s) { - credentials = null; connections = new CopyOnWriteArrayList(); + Thread.ofPlatform().name("ProxyListener").daemon().start(this::run); } /** @@ -148,7 +139,7 @@ public class ProxyServer extends Thread implements Closeable { volatile boolean done; - public void run() { + private void run() { int id = 0; try { while (!done) { @@ -656,10 +647,11 @@ public class ProxyServer extends Thread implements Closeable { int port = Integer.parseInt(args[0]); boolean debug = args.length > 1 && args[1].equals("-debug"); System.out.println("Debugging : " + debug); - ProxyServer ps = new ProxyServer(port, debug); - System.out.println("Proxy server listening on port " + ps.getPort()); - while (true) { - Thread.sleep(5000); + try (ProxyServer ps = new ProxyServer(port, debug)) { + System.out.println("Proxy server listening on port " + ps.getPort()); + while (true) { + Thread.sleep(5000); + } } } } From ca753ebad6681a76d18800d23898b7d6af83f567 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 18 Aug 2025 08:12:20 +0000 Subject: [PATCH 067/112] 8365165: Zap C-heap memory at delete/free Reviewed-by: kvn, kbarrett --- src/hotspot/share/nmt/mallocTracker.cpp | 6 ++++++ src/hotspot/share/runtime/globals.hpp | 3 +++ src/hotspot/share/runtime/os.cpp | 6 +++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp index 6a2da5f79cd..d919f3ce873 100644 --- a/src/hotspot/share/nmt/mallocTracker.cpp +++ b/src/hotspot/share/nmt/mallocTracker.cpp @@ -207,6 +207,12 @@ void* MallocTracker::record_free_block(void* memblock) { deaccount(header->free_info()); + if (ZapCHeap) { + // To do this zapping, we need to know the block size. + // This is why we have to do it here, and not in os::free. + memset(memblock, freeBlockPad, header->size()); + } + header->mark_block_as_dead(); return (void*)header; diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 24dda9ac6d8..ee3c37b471b 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -483,6 +483,9 @@ const int ObjectAlignmentInBytes = 8; develop(bool, ZapFillerObjects, trueInDebug, \ "Zap filler objects") \ \ + develop(bool, ZapCHeap, trueInDebug, \ + "Zap allocated/freed C heap space") \ + \ develop(bool, ZapTLAB, trueInDebug, \ "Zap allocated TLABs") \ develop(bool, TestingAsyncLoggingDeathTest, false, \ diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index db2256d70cb..04a363a7aab 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -666,8 +666,8 @@ void* os::malloc(size_t size, MemTag mem_tag, const NativeCallStack& stack) { if (CDSConfig::is_dumping_static_archive()) { // Need to deterministically fill all the alignment gaps in C++ structures. ::memset(inner_ptr, 0, size); - } else { - DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);) + } else if (ZapCHeap) { + ::memset(inner_ptr, uninitBlockPad, size); } DEBUG_ONLY(break_if_ptr_caught(inner_ptr);) return inner_ptr; @@ -740,7 +740,7 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS #ifdef ASSERT assert(old_size == free_info.size, "Sanity"); - if (old_size < size) { + if (ZapCHeap && old_size < size) { // We also zap the newly extended region. ::memset((char*)new_inner_ptr + old_size, uninitBlockPad, size - old_size); } From 2b756ab1e8cfacc5cf5d9c6dfdf1d1c9a6ecf4b1 Mon Sep 17 00:00:00 2001 From: Saranya Natarajan Date: Mon, 18 Aug 2025 08:16:32 +0000 Subject: [PATCH 068/112] 8358781: C2 fails with assert "bad profile data type" when TypeProfileCasts is disabled Reviewed-by: mhaessig, kvn, dfenacci --- src/hotspot/share/opto/graphKit.cpp | 18 ++++--- .../compiler/arguments/TestProfileCasts.java | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/arguments/TestProfileCasts.java diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 902968ef4d4..d65239ab0f8 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2309,16 +2309,18 @@ Node* GraphKit::record_profiled_receiver_for_speculation(Node* n) { if (!data->as_BitData()->null_seen()) { ptr_kind = ProfileNeverNull; } else { - assert(data->is_ReceiverTypeData(), "bad profile data type"); - ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData(); - uint i = 0; - for (; i < call->row_limit(); i++) { - ciKlass* receiver = call->receiver(i); - if (receiver != nullptr) { - break; + if (TypeProfileCasts) { + assert(data->is_ReceiverTypeData(), "bad profile data type"); + ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData(); + uint i = 0; + for (; i < call->row_limit(); i++) { + ciKlass* receiver = call->receiver(i); + if (receiver != nullptr) { + break; + } } + ptr_kind = (i == call->row_limit()) ? ProfileAlwaysNull : ProfileMaybeNull; } - ptr_kind = (i == call->row_limit()) ? ProfileAlwaysNull : ProfileMaybeNull; } } } diff --git a/test/hotspot/jtreg/compiler/arguments/TestProfileCasts.java b/test/hotspot/jtreg/compiler/arguments/TestProfileCasts.java new file mode 100644 index 00000000000..1a6bcf37919 --- /dev/null +++ b/test/hotspot/jtreg/compiler/arguments/TestProfileCasts.java @@ -0,0 +1,49 @@ +/* + * 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 8358781 + * @summary Regression test for -XX:-TypeProfileCasts crash + * @requires vm.debug + * @run main/othervm -XX:-TypeProfileCasts -XX:CompileThresholdScaling=0.01 + * compiler.arguments.TestProfileCasts + */ +package compiler.arguments; + +public class TestProfileCasts { + static class Foo { + } + + private static void test(Object o) { + if (o instanceof Foo) { + } + } + + public static void main(String[] args) { + for (int i = 0; i < 100; i++) { + test(new Foo()); + test(null); + } + } +} \ No newline at end of file From 6e91ccd1c3926094a9b6d8f9177d895aba3424a1 Mon Sep 17 00:00:00 2001 From: Pasam Soujanya Date: Mon, 18 Aug 2025 09:37:58 +0000 Subject: [PATCH 069/112] =?UTF-8?q?8365305:=20The=20ARIA=20role=20?= =?UTF-8?q?=E2=80=98contentinfo=E2=80=99=20is=20not=20valid=20for=20the=20?= =?UTF-8?q?element=20

      ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: hannesw --- .../jdk/javadoc/internal/html/HtmlTree.java | 3 +- .../TestHtmlLandmarkRegions.java | 6 ++-- .../testHtmlVersion/TestHtmlVersion.java | 34 +++++++++---------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java index 2d81a2914e6..f53a71a6999 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java @@ -586,8 +586,7 @@ public class HtmlTree extends Content { * @return the element */ public static HtmlTree FOOTER() { - return new HtmlTree(HtmlTag.FOOTER) - .setRole(HtmlAttr.Role.CONTENTINFO); + return new HtmlTree(HtmlTag.FOOTER); } /** diff --git a/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java b/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java index a65e474a251..784efa9767d 100644 --- a/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java +++ b/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java @@ -83,7 +83,7 @@ public class TestHtmlLandmarkRegions extends JavadocTester {

      Document Title

      """, """ -
      """, +
      """, """ bottom text""" ); @@ -113,7 +113,7 @@ public class TestHtmlLandmarkRegions extends JavadocTester {

      Document Title

      """, """ -
      """, +
      """, """ bottom text"""); } @@ -150,7 +150,7 @@ public class TestHtmlLandmarkRegions extends JavadocTester { """
      A sample doc file""", """ -
      """, +
      """, """ bottom text""" ); diff --git a/test/langtools/jdk/javadoc/doclet/testHtmlVersion/TestHtmlVersion.java b/test/langtools/jdk/javadoc/doclet/testHtmlVersion/TestHtmlVersion.java index 15c85765d68..e9d7f7a795d 100644 --- a/test/langtools/jdk/javadoc/doclet/testHtmlVersion/TestHtmlVersion.java +++ b/test/langtools/jdk/javadoc/doclet/testHtmlVersion/TestHtmlVersion.java @@ -85,7 +85,7 @@ public class TestHtmlVersion extends JavadocTester {
    *

    - * If both "nu" and "rg" are specified, the decimal digits from the "nu" - * extension supersedes the implicit one from the "rg" extension. + * For both "nu" and "cu", if they are specified in addition to "rg", the respective + * values from the "nu" and "cu" extension supersede the implicit ones from the "rg" extension. * Although Unicode extensions * defines various keys and values, actual locale-sensitive service implementations * in a Java Runtime Environment might not support any particular Unicode locale From ec7361e082eff3859d9f0dd732f39ae9abd5089a Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Mon, 18 Aug 2025 23:07:57 +0000 Subject: [PATCH 078/112] 8365660: test/jdk/sun/security/pkcs11/KeyAgreement/ tests skipped without SkipExceprion Reviewed-by: rhalade --- .../pkcs11/KeyAgreement/SupportedDHKeys.java | 7 ++-- .../security/pkcs11/KeyAgreement/TestDH.java | 6 ++-- .../pkcs11/KeyAgreement/TestInterop.java | 6 ++-- .../pkcs11/KeyAgreement/TestShort.java | 35 ++++--------------- .../KeyAgreement/UnsupportedDHKeys.java | 7 ++-- 5 files changed, 21 insertions(+), 40 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java b/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java index c4f8e189937..d044934fb59 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ * @run main/othervm SupportedDHKeys */ +import jtreg.SkippedException; + import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -62,8 +64,7 @@ public class SupportedDHKeys extends PKCS11Test { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyPairGenerator", "DiffieHellman") == null) { - System.out.println("No support of DH KeyPairGenerator, skipping"); - return; + throw new SkippedException("No support of DH KeyPairGenerator, skipping"); } for (SupportedKeySize keySize : SupportedKeySize.values()) { diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java index 560459c2b09..1140ba75e12 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,14 +38,14 @@ import java.util.Arrays; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import jdk.test.lib.security.SecurityUtils; +import jtreg.SkippedException; public class TestDH extends PKCS11Test { @Override public void main(Provider p) throws Exception { if (p.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } String kpgAlgorithm = "DH"; KeyPairGenerator kpg = KeyPairGenerator.getInstance(kpgAlgorithm, p); diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java index 810814ebd5d..da15c41132a 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 javax.crypto.spec.DHPrivateKeySpec; import javax.crypto.spec.DHPublicKeySpec; import jdk.test.lib.security.DiffieHellmanGroup; import jdk.test.lib.security.SecurityUtils; +import jtreg.SkippedException; public class TestInterop extends PKCS11Test { @@ -76,8 +77,7 @@ public class TestInterop extends PKCS11Test { @Override public void main(Provider prov) throws Exception { if (prov.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } try { System.out.println("testing generateSecret()"); diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java index 27e6bcbc8a3..d0e03c21f36 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ * @run main/othervm TestShort */ +import jtreg.SkippedException; + import java.math.BigInteger; import java.security.KeyFactory; import java.security.PrivateKey; @@ -90,12 +92,10 @@ public class TestShort extends PKCS11Test { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } + try { - DHPublicKeySpec publicSpec; - DHPrivateKeySpec privateSpec; KeyFactory kf = KeyFactory.getInstance("DH", provider); KeyAgreement ka = KeyAgreement.getInstance("DH", provider); @@ -106,7 +106,7 @@ public class TestShort extends PKCS11Test { ka.init(pr1); ka.doPhase(pu2, true); byte[] n2 = ka.generateSecret(); - if (Arrays.equals(s2, n2) == false) { + if (!Arrays.equals(s2, n2)) { throw new Exception("mismatch 2"); } System.out.println("short ok"); @@ -114,7 +114,7 @@ public class TestShort extends PKCS11Test { ka.init(pr1); ka.doPhase(pu3, true); byte[] n3 = ka.generateSecret(); - if (Arrays.equals(s3, n3) == false) { + if (!Arrays.equals(s3, n3)) { throw new Exception("mismatch 3"); } System.out.println("normal ok"); @@ -123,27 +123,6 @@ public class TestShort extends PKCS11Test { ex.printStackTrace(); throw ex; } - -/* - KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", provider); - kpg.initialize(512); -// KeyPair kp1 = kpg.generateKeyPair(); -// System.out.println(kp1.getPublic()); -// System.out.println(kp1.getPrivate()); - while (true) { - KeyAgreement ka = KeyAgreement.getInstance("DH", provider); - ka.init(pr1); - KeyPair kp2 = kpg.generateKeyPair(); - ka.doPhase(kp2.getPublic(), true); - byte[] sec = ka.generateSecret(); - if (sec.length == 64) { - System.out.println(kp2.getPrivate()); - System.out.println(kp2.getPublic()); - System.out.println(toString(sec)); - break; - } - } -/**/ } public static void main(String[] args) throws Exception { diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java b/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java index b2a2c29e8af..1cd224c2362 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ * @run main/othervm UnsupportedDHKeys */ +import jtreg.SkippedException; + import java.security.InvalidParameterException; import java.security.KeyPairGenerator; import java.security.Provider; @@ -59,8 +61,7 @@ public class UnsupportedDHKeys extends PKCS11Test { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyPairGenerator", "DiffieHellman") == null) { - System.out.println("No supported of DH KeyPairGenerator, skipping"); - return; + throw new SkippedException("DH (DiffieHellman) is not supported in KeyPairGenerator, skipping"); } for (UnsupportedKeySize keySize : UnsupportedKeySize.values()) { From e04a31037588217a246a8dca6ce9a83a031c343d Mon Sep 17 00:00:00 2001 From: Shawn M Emery Date: Mon, 18 Aug 2025 23:54:06 +0000 Subject: [PATCH 079/112] 8364806: Test sun/security/krb5/config/IncludeRandom.java times out on Windows Reviewed-by: mbaesken --- test/jdk/sun/security/krb5/config/IncludeRandom.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/sun/security/krb5/config/IncludeRandom.java b/test/jdk/sun/security/krb5/config/IncludeRandom.java index 11f93f17830..fe05917fd4c 100644 --- a/test/jdk/sun/security/krb5/config/IncludeRandom.java +++ b/test/jdk/sun/security/krb5/config/IncludeRandom.java @@ -53,7 +53,7 @@ public class IncludeRandom { public static void main(String[] args) throws Exception { System.setProperty("java.security.krb5.conf", "f"); - for (var i = 0; i < 10_000; i++) { + for (var i = 0; i < 1000; i++) { test(); } } From f2f7a490c091734ae1aa6cd402a117acbc1c699e Mon Sep 17 00:00:00 2001 From: Boris Ulasevich Date: Tue, 19 Aug 2025 04:40:45 +0000 Subject: [PATCH 080/112] 8365071: ARM32: JFR intrinsic jvm_commit triggers C2 regalloc assert Reviewed-by: mgronlun --- src/hotspot/share/opto/library_call.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 6c5efaafc21..ab1b75ae7e6 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -3275,7 +3275,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { lease_compare_io->init_req(_true_path, i_o()); lease_compare_io->init_req(_false_path, input_io_state); - lease_result_value->init_req(_true_path, null()); // if the lease was returned, return 0. + lease_result_value->init_req(_true_path, _gvn.longcon(0)); // if the lease was returned, return 0L. lease_result_value->init_req(_false_path, arg); // if not lease, return new updated position. RegionNode* result_rgn = new RegionNode(PATH_LIMIT); From 655dc516c22ac84fccee6b1fdc607c492465be6b Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Tue, 19 Aug 2025 05:06:50 +0000 Subject: [PATCH 081/112] 8361842: Move input validation checks to Java for java.lang.StringCoding intrinsics Reviewed-by: rriggs, liach, dfenacci, thartmann, redestad, jrose --- .../cpu/aarch64/macroAssembler_aarch64.cpp | 12 +- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 12 +- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 66 ++++++----- src/hotspot/share/classfile/vmIntrinsics.hpp | 6 +- src/hotspot/share/opto/c2_globals.hpp | 3 + src/hotspot/share/opto/library_call.cpp | 57 +++++++--- src/hotspot/share/opto/library_call.hpp | 3 +- .../share/classes/java/lang/String.java | 2 +- .../share/classes/java/lang/StringCoding.java | 93 ++++++++++++++-- .../share/classes/java/lang/System.java | 5 +- .../jdk/internal/access/JavaLangAccess.java | 19 ++-- .../share/classes/sun/nio/cs/CESU_8.java | 4 +- .../share/classes/sun/nio/cs/DoubleByte.java | 4 +- .../share/classes/sun/nio/cs/ISO_8859_1.java | 40 +++---- .../share/classes/sun/nio/cs/SingleByte.java | 2 +- .../share/classes/sun/nio/cs/US_ASCII.java | 2 +- .../share/classes/sun/nio/cs/UTF_8.java | 2 +- .../sun/nio/cs/ext/EUC_JP.java.template | 2 +- .../intrinsics/TestVerifyIntrinsicChecks.java | 104 ++++++++++++++++++ .../intrinsics/string/TestCountPositives.java | 30 ++++- .../string/TestEncodeIntrinsics.java | 22 +++- .../intrinsics/string/TestHasNegatives.java | 25 ++++- .../patches/java.base/java/lang/Helper.java | 5 + 23 files changed, 413 insertions(+), 107 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index e51b3c530de..fb307c8831a 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -6421,10 +6421,14 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) // Intrinsic for // -// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray -// return the number of characters copied. -// - java/lang/StringUTF16.compress -// return index of non-latin1 character if copy fails, otherwise 'len'. +// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ASCII // // This version always returns the number of characters copied, and does not // clobber the 'len' register. A successful copy will complete with the post- diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index aac2c98fc86..e91af885d78 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2593,10 +2593,14 @@ void C2_MacroAssembler::char_array_compress_v(Register src, Register dst, Regist // Intrinsic for // -// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray -// return the number of characters copied. -// - java/lang/StringUTF16.compress -// return index of non-latin1 character if copy fails, otherwise 'len'. +// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ASCII // // This version always returns the number of characters copied. A successful // copy will complete with the post-condition: 'res' == 'len', while an diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 2d1005b8438..0b41d594194 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -6027,32 +6027,46 @@ void MacroAssembler::evpbroadcast(BasicType type, XMMRegister dst, Register src, } } -// encode char[] to byte[] in ISO_8859_1 or ASCII - //@IntrinsicCandidate - //private static int implEncodeISOArray(byte[] sa, int sp, - //byte[] da, int dp, int len) { - // int i = 0; - // for (; i < len; i++) { - // char c = StringUTF16.getChar(sa, sp++); - // if (c > '\u00FF') - // break; - // da[dp++] = (byte)c; - // } - // return i; - //} - // - //@IntrinsicCandidate - //private static int implEncodeAsciiArray(char[] sa, int sp, - // byte[] da, int dp, int len) { - // int i = 0; - // for (; i < len; i++) { - // char c = sa[sp++]; - // if (c >= '\u0080') - // break; - // da[dp++] = (byte)c; - // } - // return i; - //} +// Encode given char[]/byte[] to byte[] in ISO_8859_1 or ASCII +// +// @IntrinsicCandidate +// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0( +// char[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = sa[sp++]; +// if (c > '\u00FF') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } +// +// @IntrinsicCandidate +// int java.lang.StringCoding.encodeISOArray0( +// byte[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = StringUTF16.getChar(sa, sp++); +// if (c > '\u00FF') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } +// +// @IntrinsicCandidate +// int java.lang.StringCoding.encodeAsciiArray0( +// char[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = sa[sp++]; +// if (c >= '\u0080') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1Reg, XMMRegister tmp2Reg, XMMRegister tmp3Reg, XMMRegister tmp4Reg, diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 3d110c5706b..c9c5c925f86 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -415,18 +415,18 @@ class methodHandle; \ do_class(java_lang_StringCoding, "java/lang/StringCoding") \ do_intrinsic(_countPositives, java_lang_StringCoding, countPositives_name, countPositives_signature, F_S) \ - do_name( countPositives_name, "countPositives") \ + do_name( countPositives_name, "countPositives0") \ do_signature(countPositives_signature, "([BII)I") \ \ do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \ do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeISOArray_name, "implEncodeISOArray") \ + do_name( encodeISOArray_name, "encodeISOArray0") \ do_signature(encodeISOArray_signature, "([CI[BII)I") \ \ do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \ \ do_intrinsic(_encodeAsciiArray, java_lang_StringCoding, encodeAsciiArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeAsciiArray_name, "implEncodeAsciiArray") \ + do_name( encodeAsciiArray_name, "encodeAsciiArray0") \ \ do_class(java_math_BigInteger, "java/math/BigInteger") \ do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \ diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 540b6600a0f..3a2d4cbdf96 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -666,6 +666,9 @@ product(bool, PrintIntrinsics, false, DIAGNOSTIC, \ "prints attempted and successful inlining of intrinsics") \ \ + develop(bool, VerifyIntrinsicChecks, false, \ + "Verify in intrinsic that Java level checks work as expected") \ + \ develop(bool, StressReflectiveCode, false, \ "Use inexact types at allocations, etc., to test reflection") \ \ diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index ab1b75ae7e6..da04d6b01ac 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -939,7 +939,11 @@ inline Node* LibraryCallKit::generate_limit_guard(Node* offset, } // Emit range checks for the given String.value byte array -void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count) { +void LibraryCallKit::generate_string_range_check(Node* array, + Node* offset, + Node* count, + bool char_count, + bool halt_on_oob) { if (stopped()) { return; // already stopped } @@ -957,10 +961,17 @@ void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node generate_limit_guard(offset, count, load_array_length(array), bailout); if (bailout->req() > 1) { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); + if (halt_on_oob) { + bailout = _gvn.transform(bailout)->as_Region(); + Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); + Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); + C->root()->add_req(halt); + } else { + PreserveJVMState pjvms(this); + set_control(_gvn.transform(bailout)); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } } } @@ -1118,6 +1129,7 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { //------------------------------inline_countPositives------------------------------ +// int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len) bool LibraryCallKit::inline_countPositives() { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; @@ -1129,13 +1141,14 @@ bool LibraryCallKit::inline_countPositives() { Node* offset = argument(1); Node* len = argument(2); - ba = must_be_not_null(ba, true); - - // Range checks - generate_string_range_check(ba, offset, len, false); - if (stopped()) { - return true; + if (VerifyIntrinsicChecks) { + ba = must_be_not_null(ba, true); + generate_string_range_check(ba, offset, len, false, true); + if (stopped()) { + return true; + } } + Node* ba_start = array_element_address(ba, offset, T_BYTE); Node* result = new CountPositivesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len); set_result(_gvn.transform(result)); @@ -6128,6 +6141,9 @@ CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* no } //-------------inline_encodeISOArray----------------------------------- +// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// int java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// int java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) // encode char[] to byte[] in ISO_8859_1 or ASCII bool LibraryCallKit::inline_encodeISOArray(bool ascii) { assert(callee()->signature()->size() == 5, "encodeISOArray has 5 parameters"); @@ -6138,8 +6154,14 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { Node *dst_offset = argument(3); Node *length = argument(4); - src = must_be_not_null(src, true); - dst = must_be_not_null(dst, true); + // Cast source & target arrays to not-null + if (VerifyIntrinsicChecks) { + src = must_be_not_null(src, true); + dst = must_be_not_null(dst, true); + if (stopped()) { + return true; + } + } const TypeAryPtr* src_type = src->Value(&_gvn)->isa_aryptr(); const TypeAryPtr* dst_type = dst->Value(&_gvn)->isa_aryptr(); @@ -6156,6 +6178,15 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { return false; } + // Check source & target bounds + if (VerifyIntrinsicChecks) { + generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true); + generate_string_range_check(dst, dst_offset, length, false, true); + if (stopped()) { + return true; + } + } + Node* src_start = array_element_address(src, src_offset, T_CHAR); Node* dst_start = array_element_address(dst, dst_offset, dst_elem); // 'src_start' points to src array + scaled offset diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index fbc6007d4e1..fbac1363dae 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -163,7 +163,8 @@ class LibraryCallKit : public GraphKit { Node* array_length, RegionNode* region); void generate_string_range_check(Node* array, Node* offset, - Node* length, bool char_count); + Node* length, bool char_count, + bool halt_on_oob = false); Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset, bool is_immutable); Node* generate_current_thread(Node* &tls_output); diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index eac8a1355b7..01c26538642 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -1019,7 +1019,7 @@ public final class String int sp = 0; int sl = len; while (sp < sl) { - int ret = StringCoding.implEncodeISOArray(val, sp, dst, dp, len); + int ret = StringCoding.encodeISOArray(val, sp, dst, dp, len); sp = sp + ret; dp = dp + ret; if (ret != len) { diff --git a/src/java.base/share/classes/java/lang/StringCoding.java b/src/java.base/share/classes/java/lang/StringCoding.java index c02af28c37d..545f216b755 100644 --- a/src/java.base/share/classes/java/lang/StringCoding.java +++ b/src/java.base/share/classes/java/lang/StringCoding.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. * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,11 @@ package java.lang; +import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; +import java.util.function.BiFunction; + /** * Utility class for string encoding and decoding. */ @@ -38,7 +41,7 @@ class StringCoding { /** * Count the number of leading non-zero ascii chars in the range. */ - public static int countNonZeroAscii(String s) { + static int countNonZeroAscii(String s) { byte[] value = s.value(); if (s.isLatin1()) { return countNonZeroAsciiLatin1(value, 0, value.length); @@ -50,7 +53,7 @@ class StringCoding { /** * Count the number of non-zero ascii chars in the range. */ - public static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { + private static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] <= 0) { @@ -63,7 +66,7 @@ class StringCoding { /** * Count the number of leading non-zero ascii chars in the range. */ - public static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { + private static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { int limit = off + strlen; for (int i = off; i < limit; i++) { char c = StringUTF16.charAt(ba, i); @@ -74,7 +77,7 @@ class StringCoding { return strlen; } - public static boolean hasNegatives(byte[] ba, int off, int len) { + static boolean hasNegatives(byte[] ba, int off, int len) { return countPositives(ba, off, len) != len; } @@ -85,9 +88,24 @@ class StringCoding { * bytes in the range. If there are negative bytes, the implementation must return * a value that is less than or equal to the index of the first negative byte * in the range. + * + * @param ba a byte array + * @param off the index of the first byte to start reading from + * @param len the total number of bytes to read + * @throws NullPointerException if {@code ba} is null + * @throws ArrayIndexOutOfBoundsException if the provided sub-range is + * {@linkplain Preconditions#checkFromIndexSize(int, int, int, BiFunction) out of bounds} */ + static int countPositives(byte[] ba, int off, int len) { + Preconditions.checkFromIndexSize( + off, len, + ba.length, // Implicit null check on `ba` + Preconditions.AIOOBE_FORMATTER); + return countPositives0(ba, off, len); + } + @IntrinsicCandidate - public static int countPositives(byte[] ba, int off, int len) { + private static int countPositives0(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] < 0) { @@ -97,9 +115,37 @@ class StringCoding { return len; } + /** + * Encodes as many ISO-8859-1 codepoints as possible from the source byte + * array containing characters encoded in UTF-16, into the destination byte + * array, assuming that the encoding is ISO-8859-1 compatible. + * + * @param sa the source byte array containing characters encoded in UTF-16 + * @param sp the index of the character (not byte!) from the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters (not bytes!) to be encoded + * @return the total number of characters (not bytes!) successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ + static int encodeISOArray(byte[] sa, int sp, + byte[] da, int dp, int len) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + int sl; + if ((sp | dp | len) < 0 || + // Halving the length of `sa` to obtain the number of characters: + sp >= (sl = sa.length >>> 1) || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` + return 0; + } + int minLen = Math.min(len, Math.min(sl - sp, da.length - dp)); + return encodeISOArray0(sa, sp, da, dp, minLen); + } + @IntrinsicCandidate - public static int implEncodeISOArray(byte[] sa, int sp, - byte[] da, int dp, int len) { + private static int encodeISOArray0(byte[] sa, int sp, + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = StringUTF16.getChar(sa, sp++); @@ -110,10 +156,35 @@ class StringCoding { return i; } + /** + * Encodes as many ASCII codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ASCII compatible. + * + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ + static int encodeAsciiArray(char[] sa, int sp, + byte[] da, int dp, int len) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + if ((sp | dp | len) < 0 || + sp >= sa.length || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` + return 0; + } + int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); + return encodeAsciiArray0(sa, sp, da, dp, minLen); + } + @IntrinsicCandidate - public static int implEncodeAsciiArray(char[] sa, int sp, - byte[] da, int dp, int len) - { + static int encodeAsciiArray0(char[] sa, int sp, + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = sa[sp++]; diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 1d62d698896..cc90ca0d235 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -55,7 +55,6 @@ import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2156,8 +2155,8 @@ public final class System { return String.decodeASCII(src, srcOff, dst, dstOff, len); } - public int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len) { - return StringCoding.implEncodeAsciiArray(src, srcOff, dst, dstOff, len); + public int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len) { + return StringCoding.encodeAsciiArray(sa, sp, da, dp, len); } public InputStream initialSystemIn() { diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index efa36b5b2d8..5ceeff99002 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -45,6 +45,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; +import java.util.function.BiFunction; import java.util.stream.Stream; import jdk.internal.loader.NativeLibraries; @@ -422,15 +423,19 @@ public interface JavaLangAccess { PrintStream initialSystemErr(); /** - * Encodes as many ASCII codepoints as possible from the source array into - * the destination byte array, assuming that the encoding is ASCII - * compatible. - *

    - * WARNING: This method does not perform any bound checks. + * Encodes as many ASCII codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ASCII compatible. * - * @return the number of bytes successfully encoded, or 0 if none + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the total number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null */ - int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len); + int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len); /** * Set the cause of Throwable diff --git a/src/java.base/share/classes/sun/nio/cs/CESU_8.java b/src/java.base/share/classes/sun/nio/cs/CESU_8.java index a9a25e151ad..9b907bcbc65 100644 --- a/src/java.base/share/classes/sun/nio/cs/CESU_8.java +++ b/src/java.base/share/classes/sun/nio/cs/CESU_8.java @@ -446,7 +446,7 @@ class CESU_8 extends Unicode int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; @@ -551,7 +551,7 @@ class CESU_8 extends Unicode int dp = 0; // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(len, da.length)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(len, da.length)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 4738f51515b..165e1e21c0f 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -600,7 +600,7 @@ public class DoubleByte { try { if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } @@ -686,7 +686,7 @@ public class DoubleByte { int dp = 0; int sl = sp + len; if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(src, sp, dst, dp, len); + int n = JLA.encodeASCII(src, sp, dst, dp, len); sp += n; dp += n; } diff --git a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java index 39215bfa93d..ff5970e92a8 100644 --- a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java @@ -35,7 +35,6 @@ import java.util.Objects; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; public class ISO_8859_1 @@ -142,20 +141,34 @@ public class ISO_8859_1 private final Surrogate.Parser sgp = new Surrogate.Parser(); - // Method possible replaced with a compiler intrinsic. + /** + * Encodes as many ISO-8859-1 codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ISO-8859-1 compatible. + * + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ private static int encodeISOArray(char[] sa, int sp, byte[] da, int dp, int len) { - if (len <= 0) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + if ((sp | dp | len) < 0 || + sp >= sa.length || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` return 0; } - encodeISOArrayCheck(sa, sp, da, dp, len); - return implEncodeISOArray(sa, sp, da, dp, len); + int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); + return encodeISOArray0(sa, sp, da, dp, minLen); } @IntrinsicCandidate - private static int implEncodeISOArray(char[] sa, int sp, - byte[] da, int dp, int len) - { + private static int encodeISOArray0(char[] sa, int sp, byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = sa[sp++]; @@ -166,17 +179,6 @@ public class ISO_8859_1 return i; } - private static void encodeISOArrayCheck(char[] sa, int sp, - byte[] da, int dp, int len) { - Objects.requireNonNull(sa); - Objects.requireNonNull(da); - Preconditions.checkIndex(sp, sa.length, Preconditions.AIOOBE_FORMATTER); - Preconditions.checkIndex(dp, da.length, Preconditions.AIOOBE_FORMATTER); - - Preconditions.checkIndex(sp + len - 1, sa.length, Preconditions.AIOOBE_FORMATTER); - Preconditions.checkIndex(dp + len - 1, da.length, Preconditions.AIOOBE_FORMATTER); - } - private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index d802cc85aa8..8efa6b295ff 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -217,7 +217,7 @@ public class SingleByte int len = Math.min(dl - dp, sl - sp); if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, len); + int n = JLA.encodeASCII(sa, sp, da, dp, len); sp += n; dp += n; len -= n; diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index bb84ab1bd4b..3886978d209 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -159,7 +159,7 @@ public class US_ASCII assert (dp <= dl); dp = (dp <= dl ? dp : dl); - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 54e479f838a..d2d6d7e485d 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -452,7 +452,7 @@ public final class UTF_8 extends Unicode { int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template index 4fc0b2796ee..f3d03a9e9c7 100644 --- a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template +++ b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template @@ -309,7 +309,7 @@ public class EUC_JP try { if (enc0201.isASCIICompatible()) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java b/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java new file mode 100644 index 00000000000..392ca35e2fd --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java @@ -0,0 +1,104 @@ +/* + * 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 8361842 + * @summary Verify the effectiveness of the `VerifyIntrinsicChecks` VM flag + * through (bypassing `StringCoding::encodeAsciiArray`, and) feeding + * invalid input to an intrinsified `StringCoding::encodeAsciiArray0` + * (note the `0` suffix!). + * @library /compiler/patches + * @library /test/lib + * @build java.base/java.lang.Helper + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true & vm.flavor == "server" & !vm.graal.enabled + * @run main/othervm compiler.intrinsics.TestVerifyIntrinsicChecks verify + */ + +package compiler.intrinsics; + +import java.lang.Helper; +import java.time.Instant; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public final class TestVerifyIntrinsicChecks { + + public static void main(String[] args) throws Exception { + switch (args[0]) { + case "verify" -> { + log("Starting JVM in a separate process to verify the crash"); + OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava( + "-Xcomp", + "-XX:-TieredCompilation", + "-XX:CompileCommand=inline,java.lang.StringCoding::encodeAsciiArray0", + "-XX:+VerifyIntrinsicChecks", + "--patch-module", "java.base=%s/java.base".formatted(System.getProperty("test.patch.path")), + "compiler.intrinsics.TestVerifyIntrinsicChecks", + "crash"); + outputAnalyzer.shouldContain("unexpected null in intrinsic"); + outputAnalyzer.shouldNotHaveExitValue(0); + } + case "crash" -> { + log("Triggering the crash"); + warmUpIntrinsicMethod(); + violateIntrinsicMethodContract(); + } + default -> throw new IllegalArgumentException(); + } + } + + private static void warmUpIntrinsicMethod() { + log("Warming up the intrinsic method"); + char[] sa = createAsciiChars(8192); + byte[] sp = new byte[4096]; + for (int i = 0; i < 1_000; i++) { + Helper.StringCodingEncodeAsciiArray0(sa, i, sp, 0, sp.length - i); + } + } + + private static char[] createAsciiChars(int length) { + char[] buffer = new char[length]; + for (int i = 0; i < length; i++) { + buffer[i] = (char) (i % '\u0080'); + } + return buffer; + } + + private static void violateIntrinsicMethodContract() { + log("Violating the intrinsic method contract (sa=null)"); + Helper.StringCodingEncodeAsciiArray0(null, 1, null, 1, 1); + } + + private synchronized static void log(String format, Object... args) { + Object[] extendedArgs = new Object[2 + args.length]; + extendedArgs[0] = Instant.now(); + extendedArgs[1] = Thread.currentThread().getName(); + System.arraycopy(args, 0, extendedArgs, extendedArgs.length - args.length, args.length); + String extendedFormat = "%%s [%%s] %s%%n".formatted(format); + System.out.printf(extendedFormat, extendedArgs); + } + +} diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java index 76ef4766159..1c20a49d281 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestCountPositives */ + /* * @test * @bug 8281146 8318509 @@ -46,17 +47,38 @@ * @run main/othervm/timeout=1200 -XX:UseAVX=3 compiler.intrinsics.string.TestCountPositives * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestCountPositives */ -/** - * This test was derived from compiler.intrinsics.string.TestHasNegatives + +/* + * @test + * @bug 8281146 + * @summary Verify `StringCoding::countPositives` intrinsic Java wrapper checks + * by enabling the ones in the VM intrinsic using + * `-XX:+VerifyIntrinsicChecks` + * @comment This does not check out-of-range conditions. The + * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures + * that the VM intrinsic will produce no spurious errors. + * @key randomness + * @library /compiler/patches + * @library /test/lib + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true + * @build java.base/java.lang.Helper + * @run main/othervm + * -XX:+VerifyIntrinsicChecks + * compiler.intrinsics.string.TestCountPositives */ + package compiler.intrinsics.string; import java.lang.Helper; import java.util.Random; -import java.util.stream.IntStream; import jdk.test.lib.Utils; +/** + * This test was derived from {@link TestHasNegatives}. + */ public class TestCountPositives { private static byte[] bytes = new byte[4096 + 32]; diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java index 38a516e7521..bb343b246ff 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, 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 @@ -31,6 +31,26 @@ * @run main/othervm/timeout=1200 --add-opens=java.base/sun.nio.cs=ALL-UNNAMED -Xbatch -Xmx256m compiler.intrinsics.string.TestEncodeIntrinsics */ +/* + * @test + * @bug 6896617 8274242 + * @summary Verify `sun.nio.cs.ISO_8859_1.Encoder::encodeISOArray` intrinsic + * Java wrapper checks by enabling the ones in the VM intrinsic using + * `-XX:+VerifyIntrinsicChecks` + * @comment This does not check out-of-range conditions. The + * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures + * that the VM intrinsic will produce no spurious errors. + * @key randomness + * @library /test/lib + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true + * @run main/othervm/timeout=1200 + * -XX:+VerifyIntrinsicChecks + * --add-opens=java.base/sun.nio.cs=ALL-UNNAMED -Xbatch -Xmx256m + * compiler.intrinsics.string.TestEncodeIntrinsics + */ + package compiler.intrinsics.string; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java index 6edf2dc2e56..a15f6aade2e 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestHasNegatives */ + /* * @test * @bug 8054307 8318509 @@ -46,11 +47,31 @@ * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestHasNegatives */ +/* + * @test + * @bug 8054307 + * @summary Verify `StringCoding::hasNegatives` intrinsic Java wrapper checks + * by enabling the ones in the VM intrinsic using + * `-XX:+VerifyIntrinsicChecks` + * @comment This does not check out-of-range conditions. The + * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures + * that the VM intrinsic will produce no spurious errors. + * @key randomness + * @library /compiler/patches + * @library /test/lib + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true + * @build java.base/java.lang.Helper + * @run main/othervm + * -XX:+VerifyIntrinsicChecks + * compiler.intrinsics.string.TestHasNegatives + */ + package compiler.intrinsics.string; import java.lang.Helper; import java.util.Random; -import java.util.stream.IntStream; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index e6c8b68fc6f..3985ad8ea90 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java @@ -39,6 +39,11 @@ public class Helper { return StringCoding.countPositives(ba, off, len); } + @jdk.internal.vm.annotation.ForceInline + public static int StringCodingEncodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) { + return StringCoding.encodeAsciiArray0(sa, sp, da, dp, len); + } + @jdk.internal.vm.annotation.ForceInline public static byte[] compressByte(byte[] src, int srcOff, int dstSize, int dstOff, int len) { byte[] dst = new byte[dstSize]; From 4c80780f6a3fef688d932bdad04e98eb1bd16563 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Tue, 19 Aug 2025 06:33:12 +0000 Subject: [PATCH 082/112] 8359380: Rework deferral profile logic after JDK-8346465 Reviewed-by: prr --- .../classes/java/awt/color/ICC_Profile.java | 63 ++++----- .../java/awt/color/ICC_ProfileGray.java | 8 +- .../java/awt/color/ICC_ProfileRGB.java | 10 +- ...erralInfo.java => BuiltinProfileInfo.java} | 16 +-- .../sun/java2d/cmm/lcms/LCMSTransform.java | 4 +- .../awt/color/CheckDefaultProperties.java | 38 ++--- .../BuiltInProfileCheck.java | 2 +- .../GetProfileDataAfterTransform.java | 131 ++++++++++++++++++ 8 files changed, 196 insertions(+), 76 deletions(-) rename src/java.desktop/share/classes/sun/java2d/cmm/{ProfileDeferralInfo.java => BuiltinProfileInfo.java} (74%) create mode 100644 test/jdk/java/awt/color/ICC_Profile/GetProfileDataAfterTransform.java diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java index 3793b138a96..8bf09195627 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java @@ -51,11 +51,11 @@ import java.util.Objects; import java.util.StringTokenizer; import sun.awt.AWTAccessor; +import sun.java2d.cmm.BuiltinProfileInfo; import sun.java2d.cmm.CMSManager; import sun.java2d.cmm.PCMM; import sun.java2d.cmm.Profile; import sun.java2d.cmm.ProfileDataVerifier; -import sun.java2d.cmm.ProfileDeferralInfo; import static sun.java2d.cmm.ProfileDataVerifier.HEADER_SIZE; @@ -102,20 +102,11 @@ public sealed class ICC_Profile implements Serializable private transient volatile Profile cmmProfile; /** - * Stores some information about {@code ICC_Profile} without causing a - * deferred profile to be loaded. Note that we can defer the loading of - * standard profiles only. If this field is null, then {@link #cmmProfile} - * should be used to access profile information. + * Stores information about a built-in profile without triggering profile + * loading. If this field is null, {@link #cmmProfile} should be used to + * access profile data. If not null, the profile is considered immutable. */ - private transient volatile ProfileDeferralInfo deferralInfo; - - - /** - * Set to {@code true} for {@code BuiltInProfile}, {@code false} otherwise. - * This flag is used in {@link #setData(int, byte[])} to prevent modifying - * built-in profiles. - */ - private final transient boolean builtIn; + private transient final BuiltinProfileInfo builtInInfo; /** * The lazy registry of singleton profile objects for specific built-in @@ -124,22 +115,22 @@ public sealed class ICC_Profile implements Serializable */ private interface BuiltInProfile { /* - * ProfileDeferralInfo is used for built-in profile creation only, + * BuiltinProfileInfo is used for built-in profile creation only, * and all built-in profiles should be constructed using it. */ - ICC_Profile SRGB = new ICC_ProfileRGB(new ProfileDeferralInfo( + ICC_Profile SRGB = new ICC_ProfileRGB(new BuiltinProfileInfo( "sRGB.pf", ColorSpace.TYPE_RGB, 3, CLASS_DISPLAY)); - ICC_Profile LRGB = new ICC_ProfileRGB(new ProfileDeferralInfo( + ICC_Profile LRGB = new ICC_ProfileRGB(new BuiltinProfileInfo( "LINEAR_RGB.pf", ColorSpace.TYPE_RGB, 3, CLASS_DISPLAY)); - ICC_Profile XYZ = new ICC_Profile(new ProfileDeferralInfo( + ICC_Profile XYZ = new ICC_Profile(new BuiltinProfileInfo( "CIEXYZ.pf", ColorSpace.TYPE_XYZ, 3, CLASS_ABSTRACT)); - ICC_Profile PYCC = new ICC_Profile(new ProfileDeferralInfo( + ICC_Profile PYCC = new ICC_Profile(new BuiltinProfileInfo( "PYCC.pf", ColorSpace.TYPE_3CLR, 3, CLASS_COLORSPACECONVERSION)); - ICC_Profile GRAY = new ICC_ProfileGray(new ProfileDeferralInfo( + ICC_Profile GRAY = new ICC_ProfileGray(new BuiltinProfileInfo( "GRAY.pf", ColorSpace.TYPE_GRAY, 1, CLASS_DISPLAY)); } @@ -771,7 +762,7 @@ public sealed class ICC_Profile implements Serializable */ ICC_Profile(Profile p) { cmmProfile = p; - builtIn = false; + builtInInfo = null; } /** @@ -779,12 +770,11 @@ public sealed class ICC_Profile implements Serializable * The ID will be 0 until the profile is loaded. * *

    - * Note: {@code ProfileDeferralInfo} is used for built-in profile + * Note: {@code BuiltinProfileInfo} is used for built-in profile * creation only, and all built-in profiles should be constructed using it. */ - ICC_Profile(ProfileDeferralInfo pdi) { - deferralInfo = pdi; - builtIn = true; + ICC_Profile(BuiltinProfileInfo bpi) { + builtInInfo = bpi; } /** @@ -934,7 +924,7 @@ public sealed class ICC_Profile implements Serializable if (cmmProfile != null) { return cmmProfile; } - var is = getStandardProfileInputStream(deferralInfo.filename); + var is = getStandardProfileInputStream(builtInInfo.filename); if (is == null) { return null; } @@ -942,8 +932,6 @@ public sealed class ICC_Profile implements Serializable byte[] data = getProfileDataFromStream(is); if (data != null) { p = cmmProfile = CMSManager.getModule().loadProfile(data); - // from now we cannot use the deferred value, drop it - deferralInfo = null; } } catch (CMMException | IOException ignore) { } @@ -975,9 +963,8 @@ public sealed class ICC_Profile implements Serializable * @return one of the predefined profile class constants */ public int getProfileClass() { - ProfileDeferralInfo info = deferralInfo; - if (info != null) { - return info.profileClass; + if (builtInInfo != null) { + return builtInInfo.profileClass; } byte[] theHeader = getData(cmmProfile(), icSigHead); return getProfileClass(theHeader); @@ -1012,9 +999,8 @@ public sealed class ICC_Profile implements Serializable * {@code ColorSpace} class */ public int getColorSpaceType() { - ProfileDeferralInfo info = deferralInfo; - if (info != null) { - return info.colorSpaceType; + if (builtInInfo != null) { + return builtInInfo.colorSpaceType; } byte[] theHeader = getData(cmmProfile(), icSigHead); return getColorSpaceType(theHeader); @@ -1160,8 +1146,8 @@ public sealed class ICC_Profile implements Serializable * @see ColorSpace */ public void setData(int tagSignature, byte[] tagData) { - if (builtIn) { - throw new IllegalArgumentException("Built-in profile cannot be modified"); + if (builtInInfo != null) { + throw new IllegalArgumentException("Can't modify built-in profile"); } if (tagSignature == ICC_Profile.icSigHead) { @@ -1205,9 +1191,8 @@ public sealed class ICC_Profile implements Serializable * @throws ProfileDataException if color space is in the profile is invalid */ public int getNumComponents() { - ProfileDeferralInfo info = deferralInfo; - if (info != null) { - return info.numComponents; + if (builtInInfo != null) { + return builtInInfo.numComponents; } byte[] theHeader = getData(cmmProfile(), icSigHead); int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace); diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java index 523668d37ec..f8de7d1581d 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java @@ -37,8 +37,8 @@ package java.awt.color; import java.io.Serial; +import sun.java2d.cmm.BuiltinProfileInfo; import sun.java2d.cmm.Profile; -import sun.java2d.cmm.ProfileDeferralInfo; /** * The {@code ICC_ProfileGray} class is a subclass of the {@code ICC_Profile} @@ -87,10 +87,10 @@ public final class ICC_ProfileGray extends ICC_Profile { /** * Constructs a new {@code ICC_ProfileGray} from a - * {@code ProfileDeferralInfo} object. + * {@code BuiltinProfileInfo} object. */ - ICC_ProfileGray(ProfileDeferralInfo pdi) { - super(pdi); + ICC_ProfileGray(BuiltinProfileInfo bpi) { + super(bpi); } /** diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java index d46852380f3..1c7fab265ac 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java @@ -37,8 +37,8 @@ package java.awt.color; import java.io.Serial; +import sun.java2d.cmm.BuiltinProfileInfo; import sun.java2d.cmm.Profile; -import sun.java2d.cmm.ProfileDeferralInfo; /** * The {@code ICC_ProfileRGB} class is a subclass of the {@code ICC_Profile} @@ -122,12 +122,10 @@ public final class ICC_ProfileRGB extends ICC_Profile { /** * Constructs a new {@code ICC_ProfileRGB} from a - * {@code ProfileDeferralInfo} object. - * - * @param pdi + * {@code BuiltinProfileInfo} object. */ - ICC_ProfileRGB(ProfileDeferralInfo pdi) { - super(pdi); + ICC_ProfileRGB(BuiltinProfileInfo bpi) { + super(bpi); } /** diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralInfo.java b/src/java.desktop/share/classes/sun/java2d/cmm/BuiltinProfileInfo.java similarity index 74% rename from src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralInfo.java rename to src/java.desktop/share/classes/sun/java2d/cmm/BuiltinProfileInfo.java index 522ffa21453..f9f96235d3a 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralInfo.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/BuiltinProfileInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,15 +26,15 @@ package sun.java2d.cmm; /** - * A class to pass information about a profile to be loaded from a file to the - * static getInstance(int cspace) method of ICC_Profile. Loading of the profile - * data and initialization of the CMM is to be deferred as long as possible. + * Stores information about a built-in profile used by + * ICC_Profile.getInstance(int cspace) to defer the loading of profile data and + * CMM initialization. Since built-in profiles are immutable, this information + * is always valid. */ -public final class ProfileDeferralInfo { +public final class BuiltinProfileInfo { /** - * Need to have this info for ICC_ColorSpace without causing a deferred - * profile to be loaded. + * Used by ICC_ColorSpace without triggering built-in profile loading. */ public final int colorSpaceType, numComponents, profileClass; @@ -43,7 +43,7 @@ public final class ProfileDeferralInfo { */ public final String filename; - public ProfileDeferralInfo(String fn, int type, int ncomp, int pclass) { + public BuiltinProfileInfo(String fn, int type, int ncomp, int pclass) { filename = fn; colorSpaceType = type; numComponents = ncomp; diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java index 59f023a9aa2..881a6556ace 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, 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 @@ -82,7 +82,7 @@ final class LCMSTransform implements ColorTransform { lcmsProfiles = new LCMSProfile[profiles.length]; for (int i = 0; i < profiles.length; i++) { lcmsProfiles[i] = LCMS.getLcmsProfile(acc.cmmProfile(profiles[i])); - profiles[i].getNumComponents(); // force header initialization + profiles[i].getMajorVersion(); // force header initialization } this.renderingIntent = (renderingIntent == ColorTransform.Any) ? ICC_Profile.icPerceptual : renderingIntent; diff --git a/test/jdk/java/awt/color/CheckDefaultProperties.java b/test/jdk/java/awt/color/CheckDefaultProperties.java index 583c46da7e1..fd44a7e669b 100644 --- a/test/jdk/java/awt/color/CheckDefaultProperties.java +++ b/test/jdk/java/awt/color/CheckDefaultProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,8 +34,9 @@ import static java.awt.color.ICC_Profile.CLASS_DISPLAY; /** * @test - * @bug 8256321 - * @summary Verifies profile properties are the same before/after activation + * @bug 8256321 8359380 + * @summary Verifies built-in profile properties are the same before and after + * activation and in copies of built-in profiles */ public final class CheckDefaultProperties { @@ -46,21 +47,26 @@ public final class CheckDefaultProperties { ICC_Profile lrgb = ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB); ICC_Profile pycc = ICC_Profile.getInstance(ColorSpace.CS_PYCC); - // check default values, before profile activation - test(srgb, TYPE_RGB, 3, CLASS_DISPLAY); - test(gray, TYPE_GRAY, 1, CLASS_DISPLAY); - test(xyz, TYPE_XYZ, 3, CLASS_ABSTRACT); - test(lrgb, TYPE_RGB, 3, CLASS_DISPLAY); - test(pycc, TYPE_3CLR, 3, CLASS_COLORSPACECONVERSION); + // checks default values before built-in profiles are activated + test(srgb, gray, xyz, lrgb, pycc); - // activate profiles - srgb.getData(); - gray.getData(); - xyz.getData(); - lrgb.getData(); - pycc.getData(); + // activates built-in profiles and creates copies + ICC_Profile srgbCopy = ICC_Profile.getInstance(srgb.getData()); + ICC_Profile grayCopy = ICC_Profile.getInstance(gray.getData()); + ICC_Profile xyzCopy = ICC_Profile.getInstance(xyz.getData()); + ICC_Profile lrgbCopy = ICC_Profile.getInstance(lrgb.getData()); + ICC_Profile pyccCopy = ICC_Profile.getInstance(pycc.getData()); - // check default values, after profile activation + // checks default values after profile activation + test(srgb, gray, xyz, lrgb, pycc); + + // checks default values in copies of the built-in profiles + test(srgbCopy, grayCopy, xyzCopy, lrgbCopy, pyccCopy); + } + + private static void test(ICC_Profile srgb, ICC_Profile gray, + ICC_Profile xyz, ICC_Profile lrgb, + ICC_Profile pycc) { test(srgb, TYPE_RGB, 3, CLASS_DISPLAY); test(gray, TYPE_GRAY, 1, CLASS_DISPLAY); test(xyz, TYPE_XYZ, 3, CLASS_ABSTRACT); diff --git a/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java index 8f00ab10747..6cc10c47550 100644 --- a/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java +++ b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java @@ -39,7 +39,7 @@ import java.util.Map; public class BuiltInProfileCheck { private static final int HEADER_TAG = ICC_Profile.icSigHead; private static final int INDEX = ICC_Profile.icHdrDeviceClass; - private static final String EXCEPTION_MSG = "Built-in profile cannot be modified"; + private static final String EXCEPTION_MSG = "Can't modify built-in profile"; /** * {@link #prepareTestProfile(String, boolean, int)} * stores the profile to test in testProfile. diff --git a/test/jdk/java/awt/color/ICC_Profile/GetProfileDataAfterTransform.java b/test/jdk/java/awt/color/ICC_Profile/GetProfileDataAfterTransform.java new file mode 100644 index 00000000000..e6a436ba81e --- /dev/null +++ b/test/jdk/java/awt/color/ICC_Profile/GetProfileDataAfterTransform.java @@ -0,0 +1,131 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorConvertOp; +import java.util.ArrayList; +import java.util.List; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +import static java.awt.image.BufferedImage.TYPE_INT_RGB; + +/** + * @test + * @bug 8272860 8359380 + * @summary Verifies that ICC_Profile methods work correctly after a + * ColorConvertOp transformation + * @library /test/lib + */ +public final class GetProfileDataAfterTransform { + + private static final int[] CSS = { + ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, + ColorSpace.CS_LINEAR_RGB, ColorSpace.CS_PYCC, ColorSpace.CS_sRGB + }; + + /** + * The main process records expected get*() values before conversion and + * passes them to a subprocess, which verifies it after the transform. + * + * @param args If empty, the main process runs all color space pairs and + * spawns subprocesses. If not empty, args[0] and args[1] are source + * and target color space constants, followed by expected profile + * values to validate in the subprocess. + */ + public static void main(String[] args) throws Exception { + if (args.length == 0) { + for (int csFrom : CSS) { + for (int csTo : CSS) { + var from = (ICC_ColorSpace) ColorSpace.getInstance(csFrom); + var to = (ICC_ColorSpace) ColorSpace.getInstance(csTo); + + ICC_Profile profileFrom = from.getProfile(); + ICC_Profile profileTo = to.getProfile(); + + List cmd = new ArrayList<>(); + cmd.add(GetProfileDataAfterTransform.class.getSimpleName()); + + cmd.add(String.valueOf(csFrom)); + cmd.add(String.valueOf(csTo)); + + for (ICC_Profile p : List.of(profileFrom, profileTo)) { + cmd.add(String.valueOf(p.getPCSType())); + cmd.add(String.valueOf(p.getProfileClass())); + cmd.add(String.valueOf(p.getMinorVersion())); + cmd.add(String.valueOf(p.getMajorVersion())); + cmd.add(String.valueOf(p.getColorSpaceType())); + cmd.add(String.valueOf(p.getNumComponents())); + } + + OutputAnalyzer output = ProcessTools.executeTestJava(cmd); + output.shouldHaveExitValue(0).stdoutShouldBeEmpty() + .stderrShouldBeEmpty(); + } + } + } else { + int csFrom = Integer.parseInt(args[0]); + int csTo = Integer.parseInt(args[1]); + var from = (ICC_ColorSpace) ColorSpace.getInstance(csFrom); + var to = (ICC_ColorSpace) ColorSpace.getInstance(csTo); + + BufferedImageOp op = new ColorConvertOp(from, to, null); + // Note from.getProfile() and to.getProfile() are not loaded yet! + op.filter(new BufferedImage(10, 10, TYPE_INT_RGB), + new BufferedImage(10, 10, TYPE_INT_RGB)); + + test(from.getProfile(), args, 2); + test(to.getProfile(), args, 8); + } + } + + private static void test(ICC_Profile profile, String[] args, int offset) { + // Uncomment when JDK-8272860 is fixed + // if (profile.getData() == null) { + // throw new RuntimeException("Profile data is null"); + // } + if (profile.getPCSType() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong PCStype"); + } + if (profile.getProfileClass() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong ProfileClass"); + } + if (profile.getMinorVersion() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong MinorVersion"); + } + if (profile.getMajorVersion() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong MajorVersion"); + } + if (profile.getColorSpaceType() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong ColorSpaceType"); + } + if (profile.getNumComponents() != Integer.parseInt(args[offset])) { + throw new RuntimeException("Wrong NumComponents"); + } + } +} From 626bea80abf1660757a12462ebc8313ef6d41f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Tue, 19 Aug 2025 06:37:52 +0000 Subject: [PATCH 083/112] 8356176: C2 MemorySegment: missing RCE with byteSize() in Loop Exit Check inside the for Expression Co-authored-by: Quan Anh Mai Co-authored-by: Emanuel Peter Co-authored-by: Christian Hagedorn Co-authored-by: Tobias Hartmann Reviewed-by: epeter, qamai --- src/hotspot/share/opto/loopnode.hpp | 58 ++++++++++++++ src/hotspot/share/opto/loopopts.cpp | 20 +++-- .../InvariantCodeMotionReassociateAddSub.java | 10 +-- .../loopopts/superword/TestMemorySegment.java | 36 +++------ ...estMemorySegmentByteSizeLongLoopLimit.java | 76 +++++++++++++++++++ .../superword/TestMemorySegmentField.java | 69 +++++++++++++++++ .../runner/ArrayIndexFillTest.java | 10 +-- 7 files changed, 238 insertions(+), 41 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentByteSizeLongLoopLimit.java create mode 100644 test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentField.java diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 730f0750159..8863f37699a 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1630,6 +1630,64 @@ public: // same block. Split thru the Region. void do_split_if(Node *iff, RegionNode** new_false_region = nullptr, RegionNode** new_true_region = nullptr); +private: + // Class to keep track of wins in split_thru_phi. + class SplitThruPhiWins { + private: + // Region containing the phi we are splitting through. + const Node* _region; + + // Sum of all wins regardless of where they happen. This applies to Loops phis as well as non-loop phis. + int _total_wins; + + // For Loops, wins have different impact depending on if they happen on loop entry or on the backedge. + // Number of wins on a loop entry edge if the split is through a loop head, + // otherwise 0. Entry edge wins only pay dividends once on loop entry. + int _loop_entry_wins; + // Number of wins on a loop back-edge, which pay dividends on every iteration. + int _loop_back_wins; + + public: + SplitThruPhiWins(const Node* region) : + _region(region), + _total_wins(0), + _loop_entry_wins(0), + _loop_back_wins(0) {}; + + void reset() {_total_wins = 0; _loop_entry_wins = 0; _loop_back_wins = 0;} + void add_win(int ctrl_index) { + if (_region->is_Loop() && ctrl_index == LoopNode::EntryControl) { + _loop_entry_wins++; + } else if (_region->is_Loop() && ctrl_index == LoopNode::LoopBackControl) { + _loop_back_wins++; + } + _total_wins++; + } + // Is this split profitable with respect to the policy? + bool profitable(int policy) const { + assert(_region->is_Loop() || (_loop_entry_wins == 0 && _loop_back_wins == 0), "wins on loop edges without a loop"); + assert(!_region->is_Loop() || _total_wins == _loop_entry_wins + _loop_back_wins, "missed some win"); + // In general this means that the split has to have more wins than specified + // in the policy. However, for loops we need to take into account where the + // wins happen. We need to be careful when splitting, because splitting nodes + // related to the iv through the phi can sufficiently rearrange the loop + // structure to prevent RCE and thus vectorization. Thus, we only deem splitting + // profitable if the win of a split is not on the entry edge, as such wins + // only pay off once and have a high chance of messing up the loop structure. + return (_loop_entry_wins == 0 && _total_wins > policy) || + // If there are wins on the entry edge but the backadge also has sufficient wins, + // there is sufficient profitability to spilt regardless of the risk of messing + // up the loop structure. + _loop_back_wins > policy || + // If the policy is less than 0, a split is always profitable, i.e. we always + // split. This is needed when we split a node and then must also split a + // dependant node, i.e. spliting a Bool node after splitting a Cmp node. + policy < 0; + } + }; + +public: + // Conversion of fill/copy patterns into intrinsic versions bool do_intrinsify_fill(); bool intrinsify_fill(IdealLoopTree* lpt); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index e2696336238..a09eef0bb81 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -41,6 +41,7 @@ #include "opto/subtypenode.hpp" #include "opto/superword.hpp" #include "opto/vectornode.hpp" +#include "utilities/checkedCast.hpp" #include "utilities/macros.hpp" //============================================================================= @@ -66,7 +67,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { return nullptr; } - int wins = 0; + SplitThruPhiWins wins(region); assert(!n->is_CFG(), ""); assert(region->is_Region(), ""); @@ -119,7 +120,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { } if (singleton) { - wins++; + wins.add_win(i); x = makecon(t); } else { // We now call Identity to try to simplify the cloned node. @@ -134,7 +135,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { x->raise_bottom_type(t); Node* y = x->Identity(&_igvn); if (y != x) { - wins++; + wins.add_win(i); x = y; } else { y = _igvn.hash_find(x); @@ -142,7 +143,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { y = similar_subtype_check(x, region->in(i)); } if (y) { - wins++; + wins.add_win(i); x = y; } else { // Else x is a new node we are keeping @@ -165,12 +166,12 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { n->is_Load() && can_move_to_inner_loop(n, region->as_Loop(), x)) { // it is not a win if 'x' moved from an outer to an inner loop // this edge case can only happen for Load nodes - wins = 0; + wins.reset(); break; } } // Too few wins? - if (wins <= policy) { + if (!wins.profitable(policy)) { _igvn.remove_dead_node(phi); return nullptr; } @@ -227,6 +228,13 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { } } +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("Split %d %s through %d Phi in %d %s", + n->_idx, n->Name(), phi->_idx, region->_idx, region->Name()); + } +#endif // !PRODUCT + return phi; } diff --git a/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java index 4e8daf3d163..11f37c1f7e7 100644 --- a/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java +++ b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java @@ -345,7 +345,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.ADD_I, "2"}) @IR(counts = {IRNode.SUB_I, "1"}) public int addSubInt(int inv1, int inv2, int size) { int result = -1; @@ -381,7 +381,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.ADD_I, "2"}) @IR(counts = {IRNode.SUB_I, "1"}) public int addSubInt2(int inv1, int inv2, int size) { int result = -1; @@ -417,7 +417,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.ADD_I, "2"}) @IR(counts = {IRNode.SUB_I, "1"}) public int addSubInt3(int inv1, int inv2, int size) { int result = -1; @@ -453,7 +453,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.ADD_I, "1"}) @IR(counts = {IRNode.SUB_I, "2"}) public int subSubInt(int inv1, int inv2, int size) { int result = -1; @@ -489,7 +489,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.ADD_I, "1"}) @IR(counts = {IRNode.SUB_I, "2"}) public int subSubInt2(int inv1, int inv2, int size) { int result = -1; diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java index 7ecc14e8980..502fad5be96 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java @@ -706,9 +706,11 @@ class TestMemorySegmentImpl { } @Test - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 - // Interestingly, it now vectorizes for native, but not for arrays. + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] testLongLoop_longIndex_intInvar_byte(MemorySegment a, int invar) { for (long i = 0; i < a.byteSize(); i++) { long adr1 = (long)(i) + (long)(invar); @@ -720,9 +722,11 @@ class TestMemorySegmentImpl { } @Test - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 - // Interestingly, it now vectorizes for native, but not for arrays. + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] testLongLoop_longIndex_longInvar_byte(MemorySegment a, long invar) { for (long i = 0; i < a.byteSize(); i++) { long adr1 = (long)(i) + (long)(invar); @@ -798,20 +802,12 @@ class TestMemorySegmentImpl { } @Test - @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", - IRNode.ADD_VI, "= 0", - IRNode.STORE_VECTOR, "= 0"}, - applyIfAnd = { "ShortRunningLongLoop", "false", "AlignVector", "false" }, - applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfAnd = { "ShortRunningLongLoop", "true", "AlignVector", "false" }, + applyIf = { "AlignVector", "false" }, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 static Object[] testLongLoop_longIndex_intInvar_int(MemorySegment a, int invar) { for (long i = 0; i < a.byteSize()/4; i++) { long adr1 = 4L * (long)(i) + 4L * (long)(invar); @@ -823,20 +819,12 @@ class TestMemorySegmentImpl { } @Test - @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", - IRNode.ADD_VI, "= 0", - IRNode.STORE_VECTOR, "= 0"}, - applyIfAnd = { "ShortRunningLongLoop", "false", "AlignVector", "false" }, - applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfAnd = { "ShortRunningLongLoop", "true", "AlignVector", "false" }, + applyIf = { "AlignVector", "false" }, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 static Object[] testLongLoop_longIndex_longInvar_int(MemorySegment a, long invar) { for (long i = 0; i < a.byteSize()/4; i++) { long adr1 = 4L * (long)(i) + 4L * (long)(invar); diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentByteSizeLongLoopLimit.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentByteSizeLongLoopLimit.java new file mode 100644 index 00000000000..4a449d94ca9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentByteSizeLongLoopLimit.java @@ -0,0 +1,76 @@ +/* + * 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 compiler.loopopts.superword; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.Scenario; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; + +/* + * @test + * @bug 8356176 + * @summary Test vectorization of loops over MemorySegment with unaligned access + * @library /test/lib / + * @run driver compiler.loopopts.superword.TestMemorySegmentByteSizeLongLoopLimit + */ + + +public class TestMemorySegmentByteSizeLongLoopLimit { + public static int SIZE = 10_000; + + public static int[] a = new int[SIZE]; + public static long[] b = new long[SIZE]; + + public static MemorySegment msA = MemorySegment.ofArray(a); + public static MemorySegment msB = MemorySegment.ofArray(b); + + public static void main(String[] args) { + TestFramework f = new TestFramework(); + f.addFlags("-XX:+IgnoreUnrecognizedVMOptions"); + f.addScenarios(new Scenario(0, "-XX:-AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(1, "-XX:+AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(2, "-XX:-AlignVector", "-XX:+ShortRunningLongLoop"), + new Scenario(3, "-XX:+AlignVector", "-XX:+ShortRunningLongLoop")); + f.start(); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIf = {"AlignVector", "false"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + public static void test() { + for (long i = 0; i < msA.byteSize() / 8L; i++) { + int v = msA.get(ValueLayout.JAVA_INT_UNALIGNED, i * 4L); + msB.set(ValueLayout.JAVA_LONG_UNALIGNED, i * 8L, v + 1); + } + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentField.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentField.java new file mode 100644 index 00000000000..6f83b93d055 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentField.java @@ -0,0 +1,69 @@ +/* + * 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 compiler.loopopts.superword; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Utils; + +import java.util.Random; +import java.lang.foreign.*; + +/* + * @test + * @bug 8356176 + * @summary Test vectorization of loops over MemorySegment stored in field + * @library /test/lib / + * @run driver compiler.loopopts.superword.TestMemorySegmentField + */ + +public class TestMemorySegmentField { + static int SIZE = 10_000; + static MemorySegment MS = Arena.ofAuto().allocate(SIZE * 4); + + public static void main(String[] args) { + TestFramework f = new TestFramework(); + f.addFlags("-XX:+IgnoreUnrecognizedVMOptions"); + f.addScenarios(new Scenario(0, "-XX:-AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(1, "-XX:+AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(2, "-XX:-AlignVector", "-XX:+ShortRunningLongLoop"), + new Scenario(3, "-XX:+AlignVector", "-XX:+ShortRunningLongLoop")); + f.start(); + } + + static int zeroInvarI = 0; + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + public static void testFields() { + int invar = zeroInvarI; + for (long i = 0; i < MS.byteSize(); i++) { + long adr = (long)(i) + (long)(invar); + byte v = MS.get(ValueLayout.JAVA_BYTE, adr); + MS.set(ValueLayout.JAVA_BYTE, adr, (byte)(v + 1)); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java index 16334103614..8d0ba2be589 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -106,7 +106,7 @@ public class ArrayIndexFillTest extends VectorizationTestRunner { @Test @IR(applyIfCPUFeatureOr = {"sve", "true", "avx2", "true", "rvv", "true"}, counts = {IRNode.POPULATE_INDEX, "=0"}) - // The ConvI2L can be split through the AddI, creating a mix of + // The ConvI2L can be pushed through the AddI, creating a mix of // ConvI2L(AddI) and AddL(ConvI2L) cases, which do not vectorize. // See: JDK-8332878 public long[] fillLongArray() { @@ -132,8 +132,7 @@ public class ArrayIndexFillTest extends VectorizationTestRunner { @Test @IR(applyIfCPUFeatureOr = {"sve", "true", "avx2", "true", "rvv", "true"}, - counts = {IRNode.POPULATE_INDEX, "=0"}) - // See: JDK-8332878 + counts = {IRNode.POPULATE_INDEX, ">0"}) public float[] fillFloatArray() { float[] res = new float[SIZE]; for (int i = 0; i < SIZE; i++) { @@ -155,8 +154,7 @@ public class ArrayIndexFillTest extends VectorizationTestRunner { @Test @IR(applyIfCPUFeatureOr = {"sve", "true", "avx2", "true", "rvv", "true"}, - counts = {IRNode.POPULATE_INDEX, "=0"}) - // See: JDK-8332878 + counts = {IRNode.POPULATE_INDEX, ">0"}) public double[] fillDoubleArray() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { From 812434c42072ce4cfc91117a3187df7930500a86 Mon Sep 17 00:00:00 2001 From: Manjunath Matti Date: Tue, 19 Aug 2025 07:57:00 +0000 Subject: [PATCH 084/112] 8359114: [s390x] Add z17 detection code Reviewed-by: amitkumar, aph --- src/hotspot/cpu/s390/vm_version_s390.cpp | 20 ++++++++++++++------ src/hotspot/cpu/s390/vm_version_s390.hpp | 10 +++++++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index 8261fbd083a..ed925aa23b4 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -68,12 +68,13 @@ unsigned int VM_Version::_Icache_lineSize = DEFAULT // z14: 2017-09 // z15: 2019-09 // z16: 2022-05 +// z17: 2025-04 -static const char* z_gen[] = {" ", "G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", "G10" }; -static const char* z_machine[] = {" ", "2064", "2084", "2094", "2097", "2817", "2827", "2964", "3906", "8561", "3931" }; -static const char* z_name[] = {" ", "z900", "z990", "z9 EC", "z10 EC", "z196 EC", "ec12", "z13", "z14", "z15", "z16" }; -static const char* z_WDFM[] = {" ", "2006-06-30", "2008-06-30", "2010-06-30", "2012-06-30", "2014-06-30", "2016-12-31", "2019-06-30", "2021-06-30", "2024-12-31", "tbd" }; -static const char* z_EOS[] = {" ", "2014-12-31", "2014-12-31", "2017-10-31", "2019-12-31", "2021-12-31", "2023-12-31", "2024-12-31", "tbd", "tbd", "tbd" }; +static const char* z_gen[] = {" ", "G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", "G10", "G11" }; +static const char* z_machine[] = {" ", "2064", "2084", "2094", "2097", "2817", "2827", "2964", "3906", "8561", "3931", "9175" }; +static const char* z_name[] = {" ", "z900", "z990", "z9 EC", "z10 EC", "z196 EC", "ec12", "z13", "z14", "z15", "z16", "z17" }; +static const char* z_WDFM[] = {" ", "2006-06-30", "2008-06-30", "2010-06-30", "2012-06-30", "2014-06-30", "2016-12-31", "2019-06-30", "2021-06-30", "2024-12-31", "tbd", "tbd" }; +static const char* z_EOS[] = {" ", "2014-12-31", "2014-12-31", "2017-10-31", "2019-12-31", "2021-12-31", "2023-12-31", "2024-12-31", "tbd", "tbd", "tbd", "tbd" }; static const char* z_features[] = {" ", "system-z, g1-z900, ldisp", "system-z, g2-z990, ldisp_fast", @@ -85,7 +86,9 @@ static const char* z_features[] = {" ", "system-z, g8-z14, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1", "system-z, g9-z15, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, venh2", "system-z, g10-z16, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, venh2," - "bear_enh, sort_enh, nnpa_assist, storage_key_removal, vpack_decimal_enh" + "bear_enh, sort_enh, nnpa_assist, storage_key_removal, vpack_decimal_enh", + "system-z, g11-z17, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, venh2," + "bear_enh, sort_enh, nnpa_assist, storage_key_removal, vpack_decimal_enh, concurrent_function" }; void VM_Version::initialize() { @@ -339,6 +342,11 @@ int VM_Version::get_model_index() { // is the index of the oldest detected model. int ambiguity = 0; int model_ix = 0; + if (is_z17()) { + model_ix = 11; + ambiguity++; + } + if (is_z16()) { model_ix = 10; ambiguity++; diff --git a/src/hotspot/cpu/s390/vm_version_s390.hpp b/src/hotspot/cpu/s390/vm_version_s390.hpp index 6c6eb76bf7b..04005ff2cf9 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.hpp +++ b/src/hotspot/cpu/s390/vm_version_s390.hpp @@ -100,7 +100,7 @@ class VM_Version: public Abstract_VM_Version { #define CryptoExtension4Mask 0x0004000000000000UL // z196 (aka message-security assist extension 4, for KMF, KMCTR, KMO) #define DFPPackedConversionMask 0x0000800000000000UL // z13 // ---------------------------------------------- -// --- FeatureBitString Bits 128..192 (DW[2]) --- +// --- FeatureBitString Bits 128..191 (DW[2]) --- // ---------------------------------------------- // 11111111111111111 // 23344455666778889 @@ -118,9 +118,10 @@ class VM_Version: public Abstract_VM_Version { #define NNPAssistFacilityMask 0x0000000004000000UL // z16, Neural-network-processing-assist facility, Bit: 165 // ---------------------------------------------- -// --- FeatureBitString Bits 193..200 (DW[3]) --- +// --- FeatureBitString Bits 192..255 (DW[3]) --- // ---------------------------------------------- #define BEAREnhFacilityMask 0x4000000000000000UL // z16, BEAR-enhancement facility, Bit: 193 +#define ConcurrentFunFacilityMask 0x0040000000000000UL // z17, Concurrent-functions facility, Bit: 201 enum { _max_cache_levels = 8, // As limited by ECAG instruction. @@ -189,7 +190,8 @@ class VM_Version: public Abstract_VM_Version { static bool is_z13() { return has_CryptoExt5() && !has_MiscInstrExt2(); } static bool is_z14() { return has_MiscInstrExt2() && !has_MiscInstrExt3(); } static bool is_z15() { return has_MiscInstrExt3() && !has_BEAR_Enh_Facility(); } - static bool is_z16() { return has_BEAR_Enh_Facility(); } + static bool is_z16() { return has_BEAR_Enh_Facility() && !has_Concurrent_Fun_Facility(); } + static bool is_z17() { return has_Concurrent_Fun_Facility();} // Need to use nested class with unscoped enum. // C++11 declaration "enum class Cipher { ... } is not supported. @@ -497,6 +499,7 @@ class VM_Version: public Abstract_VM_Version { static bool has_BEAR_Enh_Facility() { return (_features[3] & BEAREnhFacilityMask) == BEAREnhFacilityMask; } static bool has_NNP_Assist_Facility() { return (_features[2] & NNPAssistFacilityMask) == NNPAssistFacilityMask; } + static bool has_Concurrent_Fun_Facility() { return (_features[3] & ConcurrentFunFacilityMask) == ConcurrentFunFacilityMask; } // Crypto features query functions. static bool has_Crypto_AES_GCM128() { return has_Crypto() && test_feature_bit(&_cipher_features_KMA[0], Cipher::_AES128, Cipher::_featureBits); } @@ -573,6 +576,7 @@ class VM_Version: public Abstract_VM_Version { static void set_has_VectorPackedDecimalEnh() { _features[2] |= VectorPackedDecimalEnhMask; } static void set_has_BEAR_Enh_Facility() { _features[3] |= BEAREnhFacilityMask;} static void set_has_NNP_Assist_Facility() { _features[2] |= NNPAssistFacilityMask;} + static void set_has_Concurrent_Fun_Facility() { _features[3] |= ConcurrentFunFacilityMask;} static void reset_has_VectorFacility() { _features[2] &= ~VectorFacilityMask; } From 999761d0f6d37c9cd6ec482620800b694c5fb9ad Mon Sep 17 00:00:00 2001 From: Fei Gao Date: Tue, 19 Aug 2025 08:22:40 +0000 Subject: [PATCH 085/112] 8365312: GCC 12 cannot compile SVE on aarch64 with auto-var-init pattern Reviewed-by: kbarrett, ihse, erikj --- make/autoconf/flags-cflags.m4 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 056334e99c4..1e12b04219f 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -954,6 +954,17 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP], [ AC_MSG_RESULT([yes]) $2SVE_CFLAGS="-march=armv8-a+sve" + # Switching the initialization mode with gcc from 'pattern' to 'zero' + # avoids the use of unsupported `__builtin_clear_padding` for variable + # length aggregates + if test "x$DEBUG_LEVEL" != xrelease && test "x$TOOLCHAIN_TYPE" = xgcc ; then + INIT_ZERO_FLAG="-ftrivial-auto-var-init=zero" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [$INIT_ZERO_FLAG], + IF_TRUE: [ + $2SVE_CFLAGS="${$2SVE_CFLAGS} $INIT_ZERO_FLAG" + ] + ) + fi ], [ AC_MSG_RESULT([no]) From 0b2d0817f14895102600744670e4a6d4764b0259 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Tue, 19 Aug 2025 14:45:37 +0000 Subject: [PATCH 086/112] 8365636: JFR: Minor cleanup Reviewed-by: shade --- .../classes/jdk/jfr/AnnotationElement.java | 2 +- .../share/classes/jdk/jfr/Configuration.java | 4 +-- .../share/classes/jdk/jfr/Enabled.java | 4 +-- .../share/classes/jdk/jfr/EventType.java | 6 ++--- .../jdk/jfr/FlightRecorderListener.java | 4 +-- .../share/classes/jdk/jfr/Recording.java | 12 ++++----- .../classes/jdk/jfr/consumer/EventStream.java | 2 +- .../jdk/jfr/consumer/MetadataEvent.java | 4 +-- .../jdk/jfr/consumer/RecordedMethod.java | 6 ++--- .../jdk/jfr/consumer/RecordedObject.java | 2 +- .../jdk/jfr/consumer/RecordingFile.java | 2 +- .../jdk/jfr/consumer/RecordingStream.java | 4 +-- .../jdk/jfr/consumer/package-info.java | 4 +-- .../jdk/jfr/internal/EventControl.java | 2 +- .../classes/jdk/jfr/internal/LongMap.java | 2 +- .../jdk/jfr/internal/PlatformEventType.java | 1 - .../jdk/jfr/internal/PrivateAccess.java | 1 - .../internal/management/ChunkFilename.java | 1 - .../jdk/jfr/internal/periodic/FlushTask.java | 1 - .../jdk/jfr/internal/query/Histogram.java | 8 +++--- .../jdk/jfr/internal/query/QueryParser.java | 1 - .../internal/settings/CPUThrottleSetting.java | 6 +---- .../jfr/internal/settings/MethodSetting.java | 2 +- .../jdk/jfr/internal/settings/Throttler.java | 2 -- .../jdk/jfr/internal/tool/Filters.java | 4 +-- .../jdk/jfr/internal/tool/Metadata.java | 4 +-- .../classes/jdk/jfr/internal/tool/Print.java | 4 +-- .../classes/jdk/jfr/internal/tool/Scrub.java | 2 +- .../jfr/internal/tracing/PlatformTracer.java | 21 +++++++-------- .../jdk/jfr/internal/util/TimespanRate.java | 4 +-- .../share/classes/jdk/jfr/package-info.java | 4 +-- .../jdk/management/jfr/EventTypeInfo.java | 4 +-- .../management/jfr/FlightRecorderMXBean.java | 27 ++++++++++--------- .../management/jfr/SettingDescriptorInfo.java | 4 +-- 34 files changed, 73 insertions(+), 88 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java b/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java index 046709fa92e..155e9098d91 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java @@ -69,7 +69,7 @@ public final class AnnotationElement { } StringJoiner values = new StringJoiner(",", "[", "]"); for (Object object : objects) { - descriptors.add(String.valueOf(object)); + values.add(String.valueOf(object)); } throw new IllegalArgumentException("Annotation " + descriptors + " for " + type.getName() + " doesn't match number of values " + values); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java b/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java index 097f069c895..a423af114a9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,7 @@ public final class Configuration { } /** - * Returns the settings that specifies how a recording is configured. + * Returns the settings that specify how a recording is configured. *

    * Modifying the returned {@code Map} object doesn't change the * configuration. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java b/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java index 328826356a1..3f3270d7fc0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,7 +64,7 @@ public @interface Enabled { /** * Returns {@code true} if by default the event should be enabled, {@code false} otherwise. * - * @return {@code true} if by default the event should be enabled by default, {@code false} otherwise + * @return {@code true} if by default the event should be enabled, {@code false} otherwise */ boolean value() default true; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/EventType.java b/src/jdk.jfr/share/classes/jdk/jfr/EventType.java index d9b4e50fca8..0f2057a7981 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/EventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/EventType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -125,7 +125,7 @@ public final class EventType { */ public String getLabel() { if (label == UNKNOWN) { - label = platformEventType.getLabel();; + label = platformEventType.getLabel(); } return label; } @@ -226,7 +226,7 @@ public final class EventType { } /** - * Returns the list of human-readable names that makes up the categories for + * Returns the list of human-readable names that make up the categories for * this event type (for example, {@code "Java Application"}, {@code "Statistics"}). * * @return an immutable list of category names, or a list with the name diff --git a/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java b/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java index 55798f89de4..e3a172ca3fd 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ public interface FlightRecorderListener { /** * Receives notification when Flight Recorder is initialized. *

    - * This method is also be invoked when a listener is added to an already + * This method is also invoked when a listener is added to an already * initialized Flight Recorder. *

    * This method allows clients to implement their own initialization mechanism diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java index 333645f1731..089a5ed37d8 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java @@ -44,7 +44,7 @@ import jdk.jfr.internal.WriteablePath; /** * Provides means to configure, start, stop and dump recording data to disk. *

    - * The following example shows how configure, start, stop and dump recording data to disk. + * The following example shows how to configure, start, stop and dump recording data to disk. * * {@snippet class="Snippets" region="RecordingOverview"} * @@ -138,7 +138,7 @@ public final class Recording implements Closeable { * The newly created recording is in the {@link RecordingState#NEW} state. To * start the recording, invoke the {@link Recording#start()} method. * - * @param configuration configuration that contains the settings to be use, not + * @param configuration configuration that contains the settings to be used, not * {@code null} * * @throws IllegalStateException if Flight Recorder can't be created (for @@ -339,7 +339,7 @@ public final class Recording implements Closeable { *

    * Clones are useful for dumping data without stopping the recording. After * a clone is created, the amount of data to copy is constrained - * with the {@link #setMaxAge(Duration)} method and the {@link #setMaxSize(long)}method. + * with the {@link #setMaxAge(Duration)} method and the {@link #setMaxSize(long)} method. * * @param stop {@code true} if the newly created copy should be stopped * immediately, {@code false} otherwise @@ -615,9 +615,9 @@ public final class Recording implements Closeable { /** * Disables event with the specified name. *

    - * If multiple events with same name (for example, the same class is loaded + * If multiple events with the same name (for example, the same class is loaded * in different class loaders), then all events that match the - * name is disabled. To disable a specific class, use the + * name are disabled. To disable a specific class, use the * {@link #disable(Class)} method or a {@code String} representation of the event * type ID. * @@ -653,7 +653,7 @@ public final class Recording implements Closeable { /** * Disables event. * - * @param eventClass the event to enable, not {@code null} + * @param eventClass the event to disable, not {@code null} * * @throws IllegalArgumentException if {@code eventClass} is an abstract * class or not a subclass of {@link Event} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java index ac325f19f22..d7de775ea3c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java @@ -295,7 +295,7 @@ public interface EventStream extends AutoCloseable { void setReuse(boolean reuse); /** - * Specifies that events arrives in chronological order, sorted by the time + * Specifies that events arrive in chronological order, sorted by the time * they were committed to the stream. * * @param ordered if event objects arrive in chronological order to diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java index 153d4f343f7..df1e1edc236 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,7 +82,7 @@ public final class MetadataEvent { * The delta will be from the last metadata event. If no metadata event has been * emitted earlier, the list will be empty. * - * @return an immutable list of added event types, not {@code null} + * @return an immutable list of removed event types, not {@code null} */ public final List getRemovedEventTypes() { if (removed == null) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java index 6c8f23aa02a..7f0dbbf7318 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ public final class RecordedMethod extends RecordedObject { } /** - * Returns the class this method belongs to, if it belong to a Java frame. + * Returns the class this method belongs to, if it belongs to a Java frame. *

    * To ensure this is a Java frame, use the {@link RecordedFrame#isJavaFrame()} * method. @@ -99,7 +99,7 @@ public final class RecordedMethod extends RecordedObject { /** * Returns whether this method is hidden (for example, wrapper code in a lambda - * expressions). + * expression). * * @return {@code true} if method is hidden, {@code false} otherwise */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java index 8d4a69a92d7..a229709ecaa 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java @@ -183,7 +183,7 @@ public sealed class RecordedObject return defaultValue; } T object = getValue(name); - if (object == null || object.getClass().isAssignableFrom(clazz)) { + if (object == null || object.getClass() == clazz) { return object; } else { return defaultValue; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java index a3880da33fa..2e839e93068 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java @@ -51,7 +51,7 @@ import jdk.jfr.internal.consumer.filter.ChunkWriter.RemovedEvents; /** * A recording file. *

    - * The following example shows how read and print all events in a recording file. + * The following example shows how to read and print all events in a recording file. * * {@snippet class="Snippets" region="RecordingFileOverview"} * diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java index 3d6c8de3015..5ec794bdf7c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java @@ -232,8 +232,8 @@ public final class RecordingStream implements AutoCloseable, EventStream { * that is older than the specified length of time is removed by the Java * Virtual Machine (JVM). *

    - * If neither maximum limit or the maximum age is set, the size of the - * recording may grow indefinitely if events are on + * If neither the maximum limit nor the maximum age is set, the size of the + * recording may grow indefinitely if events are not consumed. * * @param maxAge the length of time that data is kept, or {@code null} if * infinite diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.java index 81f7977deba..56f47b4e483 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.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 @@ * Typically this is expressed as {@code "not null"}. If a {@code null} * parameter is used where it is not allowed, a * {@code java.lang.NullPointerException} is thrown. If a {@code null} - * parameters is passed to a method that throws other exceptions, such as + * parameter is passed to a method that throws other exceptions, such as * {@code java.io.IOException}, the {@code java.lang.NullPointerException} takes * precedence, unless the Javadoc for the method explicitly states how * {@code null} is handled, i.e. by throwing {@code java.lang.IllegalArgumentException}. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java index 7a8d490d6e5..621c879f712 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -97,7 +97,7 @@ public final class EventControl { } if (eventType.hasThrottle()) { addControl(Throttle.NAME, defineThrottle(eventType)); - eventType.setThrottler(new Throttler(eventType)); + eventType.setThrottler(new Throttler()); } if (eventType.hasLevel()) { addControl(Level.NAME, defineLevel(eventType)); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java index dfb549b5778..3f02e71acdd 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java @@ -273,7 +273,7 @@ public final class LongMap { for (int i = 0; i < keys.length; i++) { T o = objects[i]; if (o != null) { - list.add(o); + list.add(o == NULL_OBJECT ? null : o); } } return list; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java index dfe87509e14..c4f0a8e9a0e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java @@ -30,7 +30,6 @@ import java.util.List; import java.util.Objects; import jdk.jfr.SettingDescriptor; -import jdk.jfr.events.ActiveSettingEvent; import jdk.jfr.internal.periodic.PeriodicEvents; import jdk.jfr.internal.util.ImplicitFields; import jdk.jfr.internal.util.TimespanRate; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java index 95dbd0986dc..fe5d5aea327 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java @@ -32,7 +32,6 @@ import jdk.jfr.AnnotationElement; import jdk.jfr.Configuration; import jdk.jfr.EventSettings; import jdk.jfr.EventType; -import jdk.jfr.FlightRecorderPermission; import jdk.jfr.Recording; import jdk.jfr.SettingDescriptor; import jdk.jfr.ValueDescriptor; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java index 0d8290b206e..47e8de74037 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java @@ -24,7 +24,6 @@ */ package jdk.jfr.internal.management; -import java.nio.file.Paths; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDateTime; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java index 739f32bf634..bed92a2f283 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java @@ -25,7 +25,6 @@ package jdk.jfr.internal.periodic; import jdk.jfr.internal.JVM; -import jdk.jfr.internal.MetadataRepository; import jdk.jfr.internal.PlatformRecorder; import jdk.jfr.internal.PrivateAccess; import jdk.jfr.internal.util.Utils; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.java index 0eca381cca4..d3d596dc4b9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.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 @@ -50,7 +50,7 @@ final class Histogram { private static final class LookupKey { private Object keys; - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked"}) public void add(Object o) { // One key, fast path if (keys == null) { @@ -58,8 +58,8 @@ final class Histogram { return; } // Three or more keys - if (keys instanceof Set set) { - set.add(o); + if (keys instanceof Set set) { + ((Set) set).add(o); return; } // Two keys diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java index 51cb2cb03e7..3c7d1e58b35 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java @@ -28,7 +28,6 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.function.BiConsumer; import java.util.function.Consumer; import jdk.jfr.internal.query.Configuration.Truncate; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java index 997f4a84949..944827f6d6f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Datadog, Inc. All rights reserved. * Copyright (c) 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -27,9 +27,6 @@ package jdk.jfr.internal.settings; -import static jdk.jfr.internal.util.TimespanUnit.SECONDS; -import static jdk.jfr.internal.util.TimespanUnit.MILLISECONDS; - import java.util.Objects; import java.util.Set; @@ -41,7 +38,6 @@ import jdk.jfr.Name; import jdk.jfr.internal.PlatformEventType; import jdk.jfr.internal.Type; import jdk.jfr.internal.util.TimespanRate; -import jdk.jfr.internal.util.Utils; @MetadataDefinition @Label("CPUThrottleSetting") diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java index 0f2751b6ac7..b369172d7b6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java @@ -42,7 +42,7 @@ import jdk.jfr.internal.tracing.PlatformTracer; @Name(Type.SETTINGS_PREFIX + "Filter") public final class MethodSetting extends FilterSetting { private final Modification modification; - private volatile static boolean initialized; + private static volatile boolean initialized; public MethodSetting(PlatformEventType eventType, Modification modification, String defaultValue) { super(eventType, defaultValue); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java index df8f5a58700..e5093de781b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java @@ -55,8 +55,6 @@ public final class Throttler { private boolean disabled; private boolean update = true; - public Throttler(PlatformEventType t) { - } // Not synchronized in fast path, but uses volatile reads. public boolean sample(long ticks) { if (disabled) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.java index e63fd34f8b8..ed983467dc6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.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 @@ -91,7 +91,7 @@ public class Filters { return !types.stream().anyMatch(f); } - public static Predicate matchAny(List> filters) { + public static Predicate matchAll(List> filters) { if (filters.isEmpty()) { return t -> true; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java index e456b14362f..2d6f55dd4b3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -203,7 +203,7 @@ final class Metadata extends Command { try (PrintWriter pw = new PrintWriter(System.out, false, UTF_8)) { MetadataWriter metadataWriter = new MetadataWriter(pw, showIds); if (!filters.isEmpty()) { - filter = Filters.matchAny(filters); + filter = Filters.matchAll(filters); } List types = findTypes(file); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java index 79dd04c379f..0bc6ceeb18f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -164,7 +164,7 @@ final class Print extends Command { } eventWriter.setStackDepth(stackDepth); if (!eventFilters.isEmpty()) { - eventWriter.setEventFilter(Filters.matchAny(eventFilters)); + eventWriter.setEventFilter(Filters.matchAll(eventFilters)); } try { eventWriter.print(file); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java index 8a02ded1125..6043e841b50 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java @@ -214,7 +214,7 @@ final class Scrub extends Command { } optionCount = options.size(); } - return Filters.matchAny(filters); + return Filters.matchAll(filters); } private void ensureUsableOutput(Path input, Path output) throws UserSyntaxException, UserDataException { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java index 2d40e496dc1..a77123b30f3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java @@ -35,7 +35,6 @@ import jdk.jfr.internal.LogTag; import jdk.jfr.internal.Logger; import jdk.jfr.internal.MetadataRepository; import jdk.jfr.internal.SecuritySupport; -import jdk.jfr.internal.Type; import jdk.jfr.internal.util.Bytecode; import jdk.jfr.tracing.MethodTracer; @@ -264,18 +263,16 @@ public final class PlatformTracer { // provided a user has specified a non-empty filter for the MethodTrace or // MethodTiming event. public static void initialize() { - try { - Logger.log(LogTag.JFR_METHODTRACE, LogLevel.DEBUG, "Method tracer initialization started."); - Thread current = Thread.currentThread(); - JVM.exclude(current); - long methodId = 16384126; - long classId = methodId >> 16; - ClassLoader cl = null; - String className = " java/lang/String"; - Module m = String.class.getModule(); - var is = ClassLoader.getSystemClassLoader().getResourceAsStream("java/lang/String.class"); + Logger.log(LogTag.JFR_METHODTRACE, LogLevel.DEBUG, "Method tracer initialization started."); + Thread current = Thread.currentThread(); + JVM.exclude(current); + long methodId = 16384126; + long classId = methodId >> 16; + ClassLoader cl = null; + String className = "java/lang/String"; + Module m = String.class.getModule(); + try (var is = ClassLoader.getSystemClassLoader().getResourceAsStream("java/lang/String.class")) { byte[] oldBytecode = is.readAllBytes(); - is.close(); long[] ids = { methodId }; String[] names = { "" }; String[] signatures = { "()V" }; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java index c35432d5d6b..e4606059822 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2025 SAP SE. All rights reserved. - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,6 @@ */ package jdk.jfr.internal.util; -import jdk.jfr.internal.settings.CPUThrottleSetting; - /** * A rate or fixed period, see {@link jdk.jfr.internal.Rate} */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/package-info.java b/src/jdk.jfr/share/classes/jdk/jfr/package-info.java index 54866ec508f..812525e0ff4 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/package-info.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/package-info.java @@ -33,7 +33,7 @@ * running application up to the failure or crash. *

    * To define a Flight Recorder event, extend {@link jdk.jfr.Event} and add - * fields that matches the data types of the payload. Metadata about fields, + * fields that match the data types of the payload. Metadata about fields, * such as labels, descriptions and units, can be added by using the annotations * available in the {@code jdk.jfr} package, or by using a user-defined * annotation that has the {@link jdk.jfr.MetadataDefinition} annotation. @@ -232,7 +232,7 @@ * Typically this is expressed as {@code "not null"}. If a {@code null} * parameter is used where it is not allowed, a * {@code java.lang.NullPointerException} is thrown. If a {@code null} - * parameters is passed to a method that throws other exceptions, such as + * parameters are passed to a method that throws other exceptions, such as * {@code java.io.IOException}, the {@code java.lang.NullPointerException} takes * precedence, unless the Javadoc for the method explicitly states how * {@code null} is handled, i.e. by throwing diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java index 15739829ee4..9be2ad90f62 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -163,7 +163,7 @@ public final class EventTypeInfo { /** * Returns a short sentence or two describing the event type associated with * this {@code EventTypeInfo}, for example - * {@code "Garbage collection performed by the JVM""}. + * {@code "Garbage collection performed by the JVM"}. * * @return the description, or {@code null} if no description exists * diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java index a0648a0b1a6..9f61cc3f358 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ package jdk.management.jfr; import java.io.IOException; import java.lang.management.PlatformManagedObject; +import java.nio.file.Paths; import java.time.Instant; import java.util.List; import java.util.Map; @@ -47,7 +48,7 @@ import jdk.jfr.Recording; * Specify how long a recording should last, and where and when data * should be dumped. *

  • Settings
    - * Specify which events should be enabled and what kind information each + * Specify which events should be enabled and what kind of information each * event should capture.
  • *
  • Configurations
    * Predefined sets of settings, typically derived from a settings file, @@ -59,7 +60,7 @@ import jdk.jfr.Recording; * *

    Recording options

    *

    - * The following table shows the options names to use with {@link #setRecordingOptions(long, Map)} + * The following table shows the option names to use with {@link #setRecordingOptions(long, Map)} * and {@link #getRecordingOptions(long)}. * * @@ -67,7 +68,7 @@ import jdk.jfr.Recording; * * * - * + * * * * @@ -107,7 +108,7 @@ import jdk.jfr.Recording; * * * - * * @@ -131,8 +132,8 @@ import jdk.jfr.Recording; * - * + * * * @@ -145,7 +146,7 @@ import jdk.jfr.Recording; * {@code "false"} * * - * + * * * * * - * + * * * * @@ -337,7 +338,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { * * + * To be able to read from a running recording the value must be set to * * @@ -355,7 +356,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { * * @return a unique ID for the stream. * - * @throws IllegalArgumentException if a recording with the iD doesn't + * @throws IllegalArgumentException if a recording with the ID doesn't * exist, or if {@code options} contains invalid values * * @throws IOException if the recording is closed, an I/O error occurs, or @@ -425,7 +426,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { * is a textual representation of the settings value (for example, * {@code "60 s"}). * - * @param recordingId the ID of the recordings to get settings for + * @param recordingId the ID of the recording to get settings for * * @return a map that describes the recording settings, not {@code null} * @@ -522,7 +523,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { void setRecordingOptions(long recordingId, Map options) throws IllegalArgumentException; /** - * Returns the list of the available recordings, not necessarily running. + * Returns the list of available recordings, not necessarily running. *

    * MBeanServer access:
    * The mapped type of {@code RecordingInfo} is {@code CompositeData} with diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java index 6c13dd24a64..bece634dcca 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ public final class SettingDescriptorInfo { /** * Returns the description of the setting associated this * {@code SettingDescriptorInfo} (for example, - * {@code "The duration an event must exceed to be be recorded"}). + * {@code "The duration an event must exceed to be recorded"}). * * @return the description of this setting, not null */ From 4ed268ff9aa6059221d76291c031b0fd29e20b95 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Tue, 19 Aug 2025 15:05:25 +0000 Subject: [PATCH 087/112] 8362304: Fix JDWP spec w.r.t. OPAQUE_FRAME and INVALID_SLOT errors Reviewed-by: sspitsyn, alanb, amenkov --- src/java.se/share/data/jdwp/jdwp.spec | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/java.se/share/data/jdwp/jdwp.spec b/src/java.se/share/data/jdwp/jdwp.spec index 5206a0c6ae9..11e3160ec97 100644 --- a/src/java.se/share/data/jdwp/jdwp.spec +++ b/src/java.se/share/data/jdwp/jdwp.spec @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2148,12 +2148,8 @@ JDWP "Java(tm) Debug Wire Protocol" "the thread is not alive.") (Error INVALID_OBJECT "Thread or value is not a known ID.") (Error THREAD_NOT_SUSPENDED) - (Error OPAQUE_FRAME "Attempted to return early from a frame " - "corresponding to a native method, " - "the thread is a suspended virtual thread and the target " - "VM is unable to force its current frame to return, " - "or the implementation is unable to provide this " - "functionality on this frame.") + (Error OPAQUE_FRAME "Unable to force the current frame to return " + "(e.g. the current frame is executing a native method).") (Error NO_MORE_FRAMES) (Error NOT_IMPLEMENTED) (Error TYPE_MISMATCH "Value is not an appropriate type for the " @@ -2625,6 +2621,8 @@ JDWP "Java(tm) Debug Wire Protocol" (Error INVALID_OBJECT) (Error INVALID_FRAMEID) (Error INVALID_SLOT) + (Error OPAQUE_FRAME "Unable to get the value of local variables in the frame " + "(e.g. the frame is executing a native method).") (Error VM_DEAD) ) ) @@ -2662,9 +2660,9 @@ JDWP "Java(tm) Debug Wire Protocol" (Error INVALID_THREAD) (Error INVALID_OBJECT) (Error INVALID_FRAMEID) - (Error OPAQUE_FRAME "The thread is a suspended virtual thread and the target VM " - "does not support setting the value of local " - "variables in the frame.") + (Error INVALID_SLOT) + (Error OPAQUE_FRAME "Unable to set the value of local variables in the frame " + "(e.g. the frame is executing a native method).") (Error VM_DEAD) ) ) @@ -2713,10 +2711,9 @@ JDWP "Java(tm) Debug Wire Protocol" (Error INVALID_FRAMEID) (Error THREAD_NOT_SUSPENDED) (Error NO_MORE_FRAMES) - (Error OPAQUE_FRAME "If one or more of the frames to pop is a native " - "method or its caller is a native method, or the " - "thread is a suspended virtual thread and the implementation " - "was unable to pop the frames.") + (Error OPAQUE_FRAME "Unable to pop one or more of the frames " + "(e.g. one or more of the frames to pop is a native " + "method or its caller is a native method).") (Error NOT_IMPLEMENTED) (Error VM_DEAD) ) From 0755477c9a06cc773f307c7119efb97df797d23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Tue, 19 Aug 2025 16:01:12 +0000 Subject: [PATCH 088/112] 8342705: Add dark mode for docs Reviewed-by: liach --- .../doclets/formats/html/HtmlDoclet.java | 2 + .../doclets/formats/html/HtmlIds.java | 5 + .../doclets/formats/html/Navigation.java | 41 ++- .../doclets/formats/html/resources/glass.svg | 6 +- .../formats/html/resources/highlight.css | 185 ++++++++++ .../doclets/formats/html/resources/moon.svg | 15 + .../formats/html/resources/script.js.template | 52 ++- .../html/resources/standard.properties | 4 + .../formats/html/resources/stylesheet.css | 336 ++++++++++++++++-- .../doclets/formats/html/resources/sun.svg | 22 ++ .../doclets/formats/html/resources/x.svg | 6 +- .../doclets/toolkit/util/DocPaths.java | 6 + .../jdk/javadoc/internal/html/HtmlAttr.java | 1 + .../CheckStylesheetClasses.java | 3 + .../testNavigation/TestModuleNavigation.java | 26 +- .../doclet/testNavigation/TestNavigation.java | 34 ++ .../doclet/testSpecTag/TestSpecTag.java | 15 +- .../doclet/testStylesheet/TestStylesheet.java | 3 +- .../jdk/javadoc/tool/api/basic/APITest.java | 2 + 19 files changed, 697 insertions(+), 67 deletions(-) create mode 100644 src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/moon.svg create mode 100644 src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sun.svg diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index 47b2322226e..d00d7b1becf 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -327,6 +327,8 @@ public class HtmlDoclet extends AbstractDoclet { copyResource(DocPaths.RIGHT_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.RIGHT_SVG), true); copyResource(DocPaths.CLIPBOARD_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.CLIPBOARD_SVG), true); copyResource(DocPaths.LINK_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.LINK_SVG), true); + copyResource(DocPaths.MOON_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.MOON_SVG), true); + copyResource(DocPaths.SUN_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.SUN_SVG), true); if (options.createIndex()) { copyResource(DocPaths.SEARCH_JS_TEMPLATE, DocPaths.SCRIPT_FILES.resolve(DocPaths.SEARCH_JS), true); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java index 42d93c41e11..c257e1e153c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java @@ -114,6 +114,11 @@ public class HtmlIds { static final HtmlId SEARCH_INPUT = HtmlId.of("search-input"); static final HtmlId SERVICES = HtmlId.of("services-summary"); static final HtmlId SKIP_NAVBAR_TOP = HtmlId.of("skip-navbar-top"); + static final HtmlId THEME_BUTTON = HtmlId.of("theme-button"); + static final HtmlId THEME_DARK = HtmlId.of("theme-dark"); + static final HtmlId THEME_LIGHT = HtmlId.of("theme-light"); + static final HtmlId THEME_OS = HtmlId.of("theme-os"); + static final HtmlId THEME_PANEL = HtmlId.of("theme-panel"); static final HtmlId UNNAMED_PACKAGE_ANCHOR = HtmlId.of("unnamed-package"); private static final String FIELDS_INHERITANCE = "fields-inherited-from-class-"; private static final String METHODS_INHERITANCE = "methods-inherited-from-class-"; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java index 61af26259af..1938a4838f5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java @@ -137,6 +137,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case MODULE: addOverviewLink(target); @@ -148,6 +149,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case PACKAGE: addOverviewLink(target); @@ -166,6 +168,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case CLASS: addOverviewLink(target); @@ -184,6 +187,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case USE: addOverviewLink(target); @@ -208,6 +212,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case TREE: addOverviewLink(target); @@ -226,6 +231,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case DEPRECATED: case INDEX: @@ -268,6 +274,7 @@ public class Navigation { } else { addHelpLink(target); } + addThemeSwitcher(target); break; case ALL_CLASSES: case ALL_PACKAGES: @@ -285,6 +292,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case DOC_FILE: addOverviewLink(target); @@ -311,16 +319,13 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; default: break; } } - private void addContentToList(List listContents, Content source) { - listContents.add(HtmlTree.LI(source)); - } - private void addItemToList(Content list, Content item) { list.add(HtmlTree.LI(item)); } @@ -492,6 +497,17 @@ public class Navigation { } } + private void addThemeSwitcher(Content target) { + var selectTheme = contents.getContent("doclet.theme.select_theme"); + target.add(HtmlTree.LI(HtmlTree.BUTTON(HtmlIds.THEME_BUTTON) + .add(HtmlTree.IMG(pathToRoot.resolve(DocPaths.RESOURCE_FILES).resolve(DocPaths.SUN_SVG), + selectTheme.toString()).addStyle(HtmlIds.THEME_LIGHT.name())) + .add(HtmlTree.IMG(pathToRoot.resolve(DocPaths.RESOURCE_FILES).resolve(DocPaths.MOON_SVG), + selectTheme.toString()).addStyle(HtmlIds.THEME_DARK.name())) + .put(HtmlAttr.ARIA_LABEL, selectTheme.toString()) + .put(HtmlAttr.TITLE, selectTheme.toString()))); + } + private void addSearch(Content target) { var resources = configuration.getDocResources(); var inputText = HtmlTree.INPUT(HtmlAttr.InputType.TEXT, HtmlIds.SEARCH_INPUT) @@ -545,6 +561,7 @@ public class Navigation { navContent.add(aboutDiv); navigationBar.add(HtmlTree.DIV(HtmlStyles.topNav, navContent).setId(HtmlIds.NAVBAR_TOP)); + var subNavContent = HtmlTree.DIV(HtmlStyles.navContent); List subNavLinks = new ArrayList<>(); switch (documentedPage) { @@ -557,6 +574,22 @@ public class Navigation { breadcrumbNav.addAll(subNavLinks, HtmlTree::LI); subNavContent.addUnchecked(breadcrumbNav); + var selectTheme = contents.getContent("doclet.theme.select_theme"); + subNavContent.add(HtmlTree.DIV(HtmlIds.THEME_PANEL) + .add(HtmlTree.DIV(selectTheme)) + .add(HtmlTree.DIV(HtmlTree.LABEL(HtmlIds.THEME_LIGHT.name(), Text.EMPTY) + .add(HtmlTree.INPUT(HtmlAttr.InputType.RADIO, HtmlIds.THEME_LIGHT) + .put(HtmlAttr.NAME, "theme").put(HtmlAttr.VALUE, HtmlIds.THEME_LIGHT.name())) + .add(HtmlTree.SPAN(contents.getContent("doclet.theme.light")))) + .add(HtmlTree.LABEL(HtmlIds.THEME_DARK.name(), Text.EMPTY) + .add(HtmlTree.INPUT(HtmlAttr.InputType.RADIO, HtmlIds.THEME_DARK) + .put(HtmlAttr.NAME, "theme").put(HtmlAttr.VALUE, HtmlIds.THEME_DARK.name())) + .add(HtmlTree.SPAN(contents.getContent("doclet.theme.dark")))) + .add(HtmlTree.LABEL(HtmlIds.THEME_OS.name(), Text.EMPTY) + .add(HtmlTree.INPUT(HtmlAttr.InputType.RADIO, HtmlIds.THEME_OS) + .put(HtmlAttr.NAME, "theme").put(HtmlAttr.VALUE, HtmlIds.THEME_OS.name())) + .add(HtmlTree.SPAN(contents.getContent("doclet.theme.system")))))); + if (options.createIndex() && documentedPage != PageMode.SEARCH) { addSearch(subNavContent); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg index ff7df85e8f6..c979c9f3d83 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg @@ -8,6 +8,6 @@ --> - - - \ No newline at end of file + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css index 145c1c7f5d2..77c32543049 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css @@ -65,3 +65,188 @@ /* ignored */ } +body.theme-dark { + .hljs-title.function_, + .hljs-template-variable { + color: #66bcce; + } + .hljs-code, + .hljs-comment, + .hljs-quote { + color:#9d9d9d; + font-style: italic; + } + .hljs-meta { + color: #836F00; + } + .hljs-symbol, + .hljs-template-tag, + .hljs-keyword, + .hljs-literal, + .hljs-name, + .hljs-built_in, + .hljs-char.escape_ { + color: #88aece; + } + .hljs-variable, + .hljs-property, + .hljs-attr, + .hljs-section { + color: #c59bc1; + } + .hljs-attribute { + color: #c59bc1; + } + .hljs-regexp, + .hljs-number { + color: #cfe374; + } + .hljs-link { + color: #b5bd68; + } + .hljs-string { + color: #b5bd68; + } + .hljs-doctag { + text-decoration: underline; + } + .hljs-emphasis { + font-style: italic; + } + .hljs-strong { + font-weight: bold; + } + .hljs-subst, + .hljs-title, + .hljs-params, + .hljs-bullet, + .hljs-formula, + .hljs-tag, + .hljs-type { + /* ignored */ + } +} + +@media (prefers-color-scheme: dark) { + .hljs-title.function_, + .hljs-template-variable { + color: #66bcce; + } + .hljs-code, + .hljs-comment, + .hljs-quote { + color:#9d9d9d; + font-style: italic; + } + .hljs-meta { + color: #836F00; + } + .hljs-symbol, + .hljs-template-tag, + .hljs-keyword, + .hljs-literal, + .hljs-name, + .hljs-built_in, + .hljs-char.escape_ { + color: #88aece; + } + .hljs-variable, + .hljs-property, + .hljs-attr, + .hljs-section { + color: #c59bc1; + } + .hljs-attribute { + color: #c59bc1; + } + .hljs-regexp, + .hljs-number { + color: #cfe374; + } + .hljs-link { + color: #b5bd68; + } + .hljs-string { + color: #b5bd68; + } + .hljs-doctag { + text-decoration: underline; + } + .hljs-emphasis { + font-style: italic; + } + .hljs-strong { + font-weight: bold; + } + .hljs-subst, + .hljs-title, + .hljs-params, + .hljs-bullet, + .hljs-formula, + .hljs-tag, + .hljs-type { + /* ignored */ + } + + body.theme-light { + .hljs-title.function_, + .hljs-template-variable { + color: #00738F; + } + .hljs-code, + .hljs-comment, + .hljs-quote { + color: #6e6e71; + font-style: italic; + } + .hljs-meta { + color: #836F00; + } + .hljs-symbol, + .hljs-template-tag, + .hljs-keyword, + .hljs-literal, + .hljs-name, + .hljs-built_in, + .hljs-char.escape_ { + color: #0C40C2; + } + .hljs-variable, + .hljs-property, + .hljs-attr, + .hljs-section { + color: #841191; + } + .hljs-attribute { + color: #164ad9; + } + .hljs-regexp, + .hljs-number { + color: #104BEB; + } + .hljs-link { + color: #47688a; + } + .hljs-string { + color: #008313; + } + .hljs-doctag { + text-decoration: underline; + } + .hljs-emphasis { + font-style: italic; + } + .hljs-strong { + font-weight: bold; + } + .hljs-subst, + .hljs-title, + .hljs-params, + .hljs-bullet, + .hljs-formula, + .hljs-tag, + .hljs-type { + /* ignored */ + } + } +} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/moon.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/moon.svg new file mode 100644 index 00000000000..856a8ffeeec --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/moon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template index d3b59f4f40b..a32c44b8043 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template @@ -328,11 +328,7 @@ document.addEventListener("readystatechange", (e) => { }); document.addEventListener("DOMContentLoaded", function(e) { setTopMargin(); - // Make sure current element is visible in breadcrumb navigation on small displays const subnav = document.querySelector("ol.sub-nav-list"); - if (subnav && subnav.lastElementChild) { - subnav.lastElementChild.scrollIntoView({ behavior: "instant", inline: "start", block: "nearest" }); - } const keymap = new Map(); const searchInput = document.getElementById("search-input") || document.getElementById("page-search-input"); @@ -351,6 +347,49 @@ document.addEventListener("DOMContentLoaded", function(e) { const mainnav = navbar.querySelector("ul.nav-list"); const toggleButton = document.querySelector("button#navbar-toggle-button"); const tocMenu = sidebar ? sidebar.cloneNode(true) : null; + const themeButton = document.querySelector("button#theme-button"); + const themePanel = document.querySelector("div#theme-panel"); + var themePanelVisible = false; + themeButton.addEventListener("click", e => { + if (!themePanelVisible) { + let {x, y} = themeButton.getBoundingClientRect(); + themePanel.style.display = "block"; + if (window.innerHeight - 85 < y) { + themePanel.style.top = ""; + themePanel.style.bottom = "4px"; + } else { + themePanel.style.top = y + (expanded ? 0 : 36) + "px"; + themePanel.style.bottom = ""; + } + themePanel.style.left = x + (expanded ? 36 : 0) + "px"; + themeButton.setAttribute("aria-expanded", "true"); + themePanelVisible = true; + e.stopPropagation(); + } + }); + function closeThemePanel(e) { + if (themePanelVisible && (!e || !themePanel.contains(e.target))) { + themePanel.style.removeProperty("display"); + themeButton.setAttribute("aria-expanded", "false"); + themePanelVisible = false; + } + } + themePanel.querySelectorAll("input").forEach(input => { + input.removeAttribute("disabled"); + input.addEventListener("change", e => { + setTheme(e.target.value); + }) + }); + const THEMES = ["theme-light", "theme-dark", "theme-os"]; + function setTheme(theme) { + THEMES.forEach(t => { + if (t !== theme) document.body.classList.remove(t); + }); + document.body.classList.add(theme); + localStorage.setItem("theme", theme); + document.getElementById(theme).checked = true; + } + setTheme(localStorage.getItem("theme") || THEMES[0]); makeFilterWidget(sidebar, updateToc); if (tocMenu) { navbar.appendChild(tocMenu); @@ -362,6 +401,7 @@ document.addEventListener("DOMContentLoaded", function(e) { return; } if (!isInput(e.target) && keymap.has(e.key)) { + closeThemePanel(); var elem = keymap.get(e.key); if (elem === filterInput && !elem.offsetParent) { elem = getVisibleFilterInput(true); @@ -370,6 +410,7 @@ document.addEventListener("DOMContentLoaded", function(e) { elem.select(); e.preventDefault(); } else if (e.key === "Escape") { + closeThemePanel(); if (expanded) { collapse(); e.preventDefault(); @@ -389,6 +430,7 @@ document.addEventListener("DOMContentLoaded", function(e) { var windowWidth; var bodyHeight; function collapse() { + closeThemePanel(); if (expanded) { mainnav.removeAttribute("style"); if (tocMenu) { @@ -403,6 +445,7 @@ document.addEventListener("DOMContentLoaded", function(e) { } } function expand() { + closeThemePanel(); expanded = true; mainnav.style.display = "block"; mainnav.style.removeProperty("height"); @@ -457,6 +500,7 @@ document.addEventListener("DOMContentLoaded", function(e) { }); } document.querySelector("main").addEventListener("click", collapse); + document.querySelector("body").addEventListener("click", closeThemePanel); document.querySelectorAll("h1, h2, h3, h4, h5, h6") .forEach((hdr, idx) => { // Create anchor links for headers with an associated id attribute diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index f49174f6dcd..2281a469c1c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -193,6 +193,10 @@ doclet.Window_Help_title=API Help doclet.references={0} references doclet.Window_Search_title=Search doclet.search.main_heading=Search +doclet.theme.select_theme=Select Theme +doclet.theme.light=Light +doclet.theme.dark=Dark +doclet.theme.system=System Setting # label for link/button element to show the information below doclet.search.show_more=Additional resources diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index f7994c7f7ed..587121ad582 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -28,13 +28,35 @@ /* Line height for continuous text blocks */ --block-line-height: 1.5; --code-line-height: 1.6; + /* Navigation bar and content area dimensions */ + --top-nav-height: 44px; + --sub-nav-height: 36px; + --nav-height: calc(var(--top-nav-height) + var(--sub-nav-height)); + --max-content-width: 1500px; + --content-margin: 0 auto; + /* Inline SVG icons for dark theme */ + --right-svg-dark: url('data:image/svg+xml; utf8, \ + \ + \ + '); + --glass-svg-dark: url('data:image/svg+xml; utf8, \ + \ + \ + '); + --x-svg-dark: url('data:image/svg+xml; utf8, \ + \ + \ + '); +} +body { /* Text colors for body and block elements */ --body-text-color: #181818; --block-text-color: #181818; /* Background colors for various elements */ --body-background-color: #ffffff; --section-background-color: var(--body-background-color); - --detail-background-color: #ffffff; + --detail-background-color: var(--body-background-color); --code-background-color: #f5f5f5; --mark-background-color: #f7f7f7; --detail-block-color: #f4f4f4; @@ -76,9 +98,10 @@ /* Search input colors */ --search-input-background-color: #ffffff; --search-input-text-color: #000000; - --search-input-placeholder-color: #909090; + --search-input-placeholder-color: #757575; /* Highlight color for active search tag target */ - --search-tag-highlight-color: #ffff66; + --search-tag-background-color: #ffff66; + --search-tag-text-color: var(--block-text-color); /* Copy button colors and filters */ --button-border-color: #b0b8c8; --button-active-filter: brightness(96%); @@ -86,12 +109,158 @@ /* Colors for invalid tag notifications */ --invalid-tag-background-color: #ffe6e6; --invalid-tag-text-color: #000000; - /* Navigation bar dimensions */ - --top-nav-height: 44px; - --sub-nav-height: 36px; - --nav-height: calc(var(--top-nav-height) + var(--sub-nav-height)); - --max-content-width: 1500px; - --content-margin: 0 auto; +} + +body.theme-dark { + --body-text-color: #e8e8e8; + --block-text-color: #e8e8e8; + --body-background-color: #222528; + --section-background-color: var(--body-background-color); + --detail-background-color: var(--body-background-color); + --code-background-color: #303940; + --mark-background-color: #313131; + --detail-block-color: #f4f4f4; + --navbar-background-color: #395A6F; + --navbar-text-color: #ffffff; + --subnav-background-color: #3d454d; + --subnav-link-color: #d8dcdf; + --member-heading-background-color: var(--subnav-background-color); + --selected-background-color: #f8981d; + --selected-text-color: #253441; + --selected-link-color: #4a698a; + --table-header-color: #38444d; + --even-row-color: #222528; + --odd-row-color: #2d3135; + --title-color: #fff; + --link-color: #94badb; + --link-color-active: #ffb45b; + --toc-background-color: #31363c; + --toc-highlight-color: var(--subnav-background-color); + --toc-hover-color: #3f4146; + --snippet-background-color: #2d363c; + --snippet-text-color: var(--block-text-color); + --snippet-highlight-color: #f7c590; + --pre-background-color: var(--snippet-background-color); + --pre-text-color: var(--snippet-text-color); + --border-color: #444444; + --table-border-color: #717171; + --tab-border-radius: 2px 2px 0 0; + --search-input-background-color: #303030; + --search-input-text-color: #d0d0d0; + --search-input-placeholder-color: #979797; + --search-tag-background-color: #c6c61e; + --search-tag-text-color: #282828; + --button-border-color: #909090; + --button-active-filter: brightness(96%); + --button-focus-filter: brightness(104%); + --invalid-tag-background-color: #ffe6e6; + --invalid-tag-text-color: #000000; + div.main-grid img { + filter: invert(100%) brightness(160%); + } +} + +/* + * Dark theme + */ +@media (prefers-color-scheme: dark) { + body { + --body-text-color: #e8e8e8; + --block-text-color: #e8e8e8; + --body-background-color: #222528; + --section-background-color: var(--body-background-color); + --detail-background-color: var(--body-background-color); + --code-background-color: #303940; + --mark-background-color: #313131; + --detail-block-color: #f4f4f4; + --navbar-background-color: #395A6F; + --navbar-text-color: #ffffff; + --subnav-background-color: #3d454d; + --subnav-link-color: #d8dcdf; + --member-heading-background-color: var(--subnav-background-color); + --selected-background-color: #f8981d; + --selected-text-color: #253441; + --selected-link-color: #4a698a; + --table-header-color: #38444d; + --even-row-color: #222528; + --odd-row-color: #2d3135; + --title-color: #fff; + --link-color: #94badb; + --link-color-active: #ffb45b; + --toc-background-color: #31363c; + --toc-highlight-color: var(--subnav-background-color); + --toc-hover-color: #3f4146; + --snippet-background-color: #2d363c; + --snippet-text-color: var(--block-text-color); + --snippet-highlight-color: #f7c590; + --pre-background-color: var(--snippet-background-color); + --pre-text-color: var(--snippet-text-color); + --border-color: #444444; + --table-border-color: #717171; + --tab-border-radius: 2px 2px 0 0; + --search-input-background-color: #303030; + --search-input-text-color: #d0d0d0; + --search-input-placeholder-color: #979797; + --search-tag-background-color: #c6c61e; + --search-tag-text-color: #282828; + --button-border-color: #909090; + --button-active-filter: brightness(96%); + --button-focus-filter: brightness(104%); + --invalid-tag-background-color: #ffe6e6; + --invalid-tag-text-color: #000000; + div.main-grid img { + filter: invert(100%) brightness(160%); + } + } + + body.theme-light { + --body-text-color: #282828; + --block-text-color: #282828; + --body-background-color: #ffffff; + --section-background-color: var(--body-background-color); + --detail-background-color: var(--body-background-color); + --code-background-color: #f5f5f5; + --mark-background-color: #f7f7f7; + --detail-block-color: #f4f4f4; + --navbar-background-color: #4D7A97; + --navbar-text-color: #ffffff; + --subnav-background-color: #dee3e9; + --subnav-link-color: #47688a; + --member-heading-background-color: var(--subnav-background-color); + --selected-background-color: #f8981d; + --selected-text-color: #253441; + --selected-link-color: #4a698a; + --table-header-color: #ebeff4; + --even-row-color: #ffffff; + --odd-row-color: #f0f0f2; + --title-color: #2c4557; + --link-color: #437291; + --link-color-active: #bb7a2a; + --toc-background-color: #f8f8f8; + --toc-highlight-color: var(--subnav-background-color); + --toc-hover-color: #e9ecf0; + --snippet-background-color: #f2f2f4; + --snippet-text-color: var(--block-text-color); + --snippet-highlight-color: #f7c590; + --pre-background-color: var(--snippet-background-color); + --pre-text-color: var(--snippet-text-color); + --border-color: #e6e6e6; + --table-border-color: #000000; + --tab-border-radius: 2px 2px 0 0; + --search-input-background-color: #ffffff; + --search-input-text-color: #000000; + --search-input-placeholder-color: #757575; + --search-tag-background-color: #ffff66; + --search-tag-text-color: var(--block-text-color); + --button-border-color: #b0b8c8; + --button-active-filter: brightness(96%); + --button-focus-filter: brightness(104%); + --invalid-tag-background-color: #ffe6e6; + --invalid-tag-text-color: #000000; + div.main-grid img { + filter: none; + } + } } /* * Styles for individual HTML elements. @@ -186,7 +355,7 @@ hr { */ .about-language { flex: 0 0 auto; - padding:0 20px; + padding:0 20px 0 10px; margin:0; font-size:0.915em; max-width: 50%; @@ -215,6 +384,7 @@ hr { height: 100%; max-width: var(--max-content-width); margin: var(--content-margin); + position: relative; } .top-nav { background-color:var(--navbar-background-color); @@ -236,6 +406,68 @@ hr { button#navbar-toggle-button { display:none; } +button#theme-button { + position: relative; + top: -9.5px; + width: 32px; + height: 32px; + padding: 6px; + margin-left: -6px; + background-color: transparent; + border: 1px solid transparent; + border-radius: 6px; + cursor: pointer; +} +button#theme-button:hover, +button#theme-button:focus-visible { + border: 1px solid var(--button-border-color); +} +button#theme-button img { + display: none; + width: 18px; +} +body.theme-light button#theme-button img.theme-light { + display: inline; +} +body.theme-dark button#theme-button img.theme-dark { + display: inline; +} +@media (prefers-color-scheme: dark) { + body.theme-os button#theme-button img.theme-dark { + display: inline; + } +} +@media (prefers-color-scheme: light) { + body.theme-os button#theme-button img.theme-light { + display: inline; + } +} +div#theme-panel { + display: none; + position: fixed; + z-index: 8; + background-color: var(--toc-background-color); + color: var(--body-text-color); + padding: 4px; + white-space: nowrap; + border: 1px solid var(--border-color); + border-radius: 4px; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +div#theme-panel div:first-child { + font-weight: bold; + font-size: 16px; +} +div#theme-panel div { + padding: 8px; +} +div#theme-panel label { + margin-right: 8px; + cursor: pointer; +} +div#theme-panel input { + margin-right: 4px; +} ul.nav-list { display:inline-flex; margin:0; @@ -252,28 +484,25 @@ ul.nav-list li { div.sub-nav { background-color:var(--subnav-background-color); width:100%; - overflow:hidden; font-size:var(--nav-font-size); - height: var(--sub-nav-height); + min-height: var(--sub-nav-height); } ol.sub-nav-list { flex: 1 1 90%; - line-height: 1.8; - display: inline-flex; + line-height: 1.7; overflow: auto; - scroll-snap-type: x mandatory; - scroll-padding-left: 13px; - scrollbar-width: none; padding-left:6px; - white-space: nowrap; - margin:0; + margin: 4px 0; + min-height: 28px; } ol.sub-nav-list::-webkit-scrollbar { display: none; } ol.sub-nav-list li { list-style:none; - scroll-snap-align: start; + display: inline-block; + padding: 2px 0; + margin-right: -4px; } ol.sub-nav-list li:not(:first-child) { background: url("right.svg") no-repeat 3px; @@ -281,6 +510,17 @@ ol.sub-nav-list li:not(:first-child) { padding-left: 17px; list-style: none; } +body.theme-dark ol.sub-nav-list li:not(:first-child) { + background-image: var(--right-svg-dark); +} +@media (prefers-color-scheme: dark) { + ol.sub-nav-list li:not(:first-child) { + background-image: var(--right-svg-dark); + } + body.theme-light ol.sub-nav-list li:not(:first-child) { + background-image: url("right.svg"); + } +} ol.sub-nav-list a { padding: 3px; } @@ -290,7 +530,8 @@ ol.sub-nav-list a.current-selection { } .sub-nav .nav-list-search { flex: 1 1 10%; - margin: 0 15px; + margin: 4.5px 15px auto 5px; + padding: 1px 0; position:relative; white-space: nowrap; } @@ -362,7 +603,7 @@ body.class-declaration-page section.detail > h3:target { } body.class-declaration-page section.detail:target > h3 > a.anchor-link > img, body.class-declaration-page section.detail > h3:target > a.anchor-link > img { - filter: invert(100%) sepia(4%) saturate(98%) hue-rotate(212deg) brightness(160%) contrast(160%); + filter: invert(100%) brightness(160%); } h1 > sup { font-size: small; @@ -1015,7 +1256,7 @@ li.ui-static-link a, li.ui-static-link a:visited { .ui-autocomplete .search-result-desc { font-size: var(--nav-font-size); padding: 2px 4px; - color: #404040; + color: var(--block-text-color); overflow: hidden; text-overflow: ellipsis; } @@ -1023,7 +1264,7 @@ li.ui-static-link a, li.ui-static-link a:visited { font-weight:bold; } .ui-menu .ui-state-active .search-result-desc { - color: #383838; + color: var(--selected-text-color); } .ui-menu .ui-menu-item-wrapper { padding: 3px 4px; @@ -1042,6 +1283,17 @@ input[type="text"] { font-size: var(--nav-font-size); height: 19px; } +body.theme-dark input[type="text"] { + background-image: var(--glass-svg-dark); +} +@media (prefers-color-scheme: dark) { + input[type="text"] { + background-image: var(--glass-svg-dark); + } + body.theme-light input[type="text"] { + background-image: url('glass.svg'); + } +} input#page-search-input { width: calc(180px + 10vw); margin: 10px 0; @@ -1069,6 +1321,21 @@ input#reset-search, input.reset-filter, input#page-search-reset { font-size:0; visibility:hidden; } +body.theme-dark input#reset-search, +body.theme-dark input.reset-filter, +body.theme-dark input#page-search-reset { + background-image: var(--x-svg-dark); +} +@media (prefers-color-scheme: dark) { + input#reset-search, input.reset-filter, input#page-search-reset { + background-image: var(--x-svg-dark); + } + body.theme-light input#reset-search, + body.theme-light input.reset-filter, + body.theme-light input#page-search-reset { + background-image: url('x.svg'); + } +} input#reset-search { position:absolute; right:5px; @@ -1101,6 +1368,7 @@ select#search-modules { } kbd { background-color: #eeeeee; + color: #282828; border: 1px solid #b0b0b0; border-radius: 3px; padding: 0 4px; @@ -1109,11 +1377,13 @@ kbd { font-weight: bold; } .search-tag-result:target { - background-color:var(--search-tag-highlight-color); + background-color:var(--search-tag-background-color); + color:var(--search-tag-text-color); } dd > span:target, h1 > span:target { - background-color: var(--search-tag-highlight-color); + background-color: var(--search-tag-background-color); + color:var(--search-tag-text-color); } section.class-description dd > span:target, section.class-description h1 > span:target { @@ -1160,6 +1430,7 @@ div#result-container { #result-container div.result-table > a.search-result-link:focus-visible, #result-container div.result-table > a.search-result-link.selected { background-color: var(--selected-background-color); + color: var(--selected-text-color); outline: none; } #result-container div.result-table > a.search-result-link .search-result-label { @@ -1168,7 +1439,6 @@ div#result-container { } #result-container div.result-table > a.search-result-link .search-result-desc { font-size: var(--nav-font-size); - color: #404040; overflow: hidden; text-overflow: ellipsis; } @@ -1502,17 +1772,6 @@ table.striped > tbody > tr > th { .top-nav nav.toc ol.toc-list li { font-size: 1.04em; } - nav.toc a:link, nav.toc a:visited { - text-decoration:none; - color:var(--link-color); - } - nav.toc a[href]:hover, nav.toc a[href]:focus { - text-decoration:none; - color:var(--link-color-active); - } - :root { - scroll-behavior: auto; - } header { max-height: 100vh; overflow-y: visible; @@ -1603,7 +1862,6 @@ table.striped > tbody > tr > th { } @media screen and (max-width: 800px) { .about-language { - padding: 0 16px; max-width: 90%; } ul.nav-list li { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sun.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sun.svg new file mode 100644 index 00000000000..e8fa03499ac --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sun.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg index 1efb4109241..34a942ffff1 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg @@ -8,6 +8,6 @@ --> - - - \ No newline at end of file + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java index 21f12673619..7bc4da60419 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java @@ -115,6 +115,12 @@ public class DocPaths { /** The name of the right pointing angle icon. */ public static final DocPath RIGHT_SVG = DocPath.create("right.svg"); + /** The name of the moon icon for the dark theme. */ + public static final DocPath MOON_SVG = DocPath.create("moon.svg"); + + /** The name of the sun icon for the light theme. */ + public static final DocPath SUN_SVG = DocPath.create("sun.svg"); + /** The name of the syntax highlighting style sheet. */ public static final DocPath HIGHLIGHT_CSS = DocPath.create("highlight.css"); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java index 10aa7c0c57b..2ef3ae9db2a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java @@ -203,6 +203,7 @@ public enum HtmlAttr { public enum InputType { CHECKBOX, + RADIO, RESET, TEXT; diff --git a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java index ee1fa538a57..ba846c293a2 100644 --- a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java +++ b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java @@ -143,6 +143,9 @@ public class CheckStylesheetClasses { "search-result-desc", "search-result-label", "search-result-link", "selected", "sort-asc", "sort-desc", "two-column-search-results", "visible"); + // used for themes + removeAll(styleSheetNames, "theme-dark", "theme-light", "theme-os"); + // very JDK specific styleSheetNames.remove("module-graph"); styleSheetNames.remove("sealed-graph"); diff --git a/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java b/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java index 2614b352b71..b4f28b36f3f 100644 --- a/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java +++ b/test/langtools/jdk/javadoc/doclet/testNavigation/TestModuleNavigation.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8196027 8196202 8320458 + * @bug 8196027 8196202 8320458 8342705 * @summary test navigation links * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -87,6 +87,9 @@ public class TestModuleNavigation extends JavadocTester {

  • Index
  • Search
  • Help
  • +
  • """); checkOutput("overview-tree.html", true, @@ -98,6 +101,9 @@ public class TestModuleNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("deprecated-list.html", true, @@ -109,6 +115,9 @@ public class TestModuleNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("index-all.html", true, @@ -120,6 +129,9 @@ public class TestModuleNavigation extends JavadocTester {
  • Search
  • Help
  • +
  • """); checkOutput("search.html", true, @@ -131,6 +143,9 @@ public class TestModuleNavigation extends JavadocTester {
  • Index
  • Help
  • +
  • """); checkOutput("help-doc.html", true, @@ -142,6 +157,9 @@ public class TestModuleNavigation extends JavadocTester {
  • Index
  • Search
  • +
  • """); checkOutput("m/p1/package-summary.html", true, @@ -154,6 +172,9 @@ public class TestModuleNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("m/p1/A.html", true, @@ -166,6 +187,9 @@ public class TestModuleNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); } diff --git a/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java b/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java index f7cbb78fbeb..5be754a3163 100644 --- a/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java +++ b/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java @@ -25,6 +25,7 @@ * @test * @bug 7025314 8023700 7198273 8025633 8026567 8081854 8196027 8182765 * 8196200 8196202 8223378 8258659 8261976 8320458 8329537 8350638 + * 8342705 * @summary Make sure the Next/Prev Class links iterate through all types. * Make sure the navagation is 2 columns, not 3. * @library /tools/lib ../../lib @@ -70,6 +71,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/package-summary.html", true, @@ -81,6 +85,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/A.html", true, @@ -92,6 +99,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/C.html", true, @@ -103,6 +113,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/E.html", true, @@ -114,6 +127,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/I.html", true, @@ -339,6 +355,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/A.html", true, @@ -350,6 +369,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/C.html", true, @@ -361,6 +383,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/E.html", true, @@ -372,6 +397,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """); checkOutput("pkg/I.html", true, @@ -411,6 +439,9 @@ public class TestNavigation extends JavadocTester {
  • Index
  • Search
  • Help
  • +
  • """, """
    - + - + diff --git a/doc/building.md b/doc/building.md index 32c6ae46540..047255d1848 100644 --- a/doc/building.md +++ b/doc/building.md @@ -1258,11 +1258,11 @@ toolchain and a sysroot environment which can easily be used together with the following command: ``` -bash configure --with-devkit= --openjdk-target=ppc64-linux-gnu && make +bash configure --with-devkit= --openjdk-target=ppc64le-linux-gnu && make ``` -will configure and build the JDK for Linux/ppc64 assuming that `` -points to a Linux/x86_64 to Linux/ppc64 devkit. +will configure and build the JDK for Linux/ppc64le assuming that `` +points to a Linux/x86_64 to Linux/ppc64le devkit. Devkits can be created from the `make/devkit` directory by executing: @@ -1281,8 +1281,8 @@ at least the following targets are known to work: | x86_64-linux-gnu | | aarch64-linux-gnu | | arm-linux-gnueabihf | -| ppc64-linux-gnu | | ppc64le-linux-gnu | +| riscv64-linux-gnu | | s390x-linux-gnu | `BASE_OS` must be one of `OL` for Oracle Enterprise Linux or `Fedora`. If the diff --git a/make/devkit/Makefile b/make/devkit/Makefile index 5ffa5265aa3..d2167bf33fa 100644 --- a/make/devkit/Makefile +++ b/make/devkit/Makefile @@ -39,7 +39,7 @@ # # make TARGETS="aarch64-linux-gnu" BASE_OS=Fedora # or -# make TARGETS="arm-linux-gnueabihf ppc64-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=17 +# make TARGETS="arm-linux-gnueabihf ppc64le-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=17 # # to build several devkits for a specific OS version at once. # You can find the final results under ../../build/devkit/result/-to- @@ -50,7 +50,7 @@ # makefile again for cross compilation. Ex: # # PATH=$PWD/../../build/devkit/result/x86_64-linux-gnu-to-x86_64-linux-gnu/bin:$PATH \ -# make TARGETS="arm-linux-gnueabihf,ppc64-linux-gnu" BASE_OS=Fedora +# make TARGETS="arm-linux-gnueabihf ppc64le-linux-gnu" BASE_OS=Fedora # # This is the makefile which iterates over all host and target platforms. # diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk index 1b9240df49c..f27d47b822c 100644 --- a/make/devkit/Tools.gmk +++ b/make/devkit/Tools.gmk @@ -69,15 +69,26 @@ else ifeq ($(BASE_OS), Fedora) ifeq ($(BASE_OS_VERSION), ) BASE_OS_VERSION := $(DEFAULT_OS_VERSION) endif + ifeq ($(filter aarch64 armhfp ppc64le riscv64 s390x x86_64, $(ARCH)), ) + $(error Only "aarch64 armhfp ppc64le riscv64 s390x x86_64" architectures are supported for Fedora, but "$(ARCH)" was requested) + endif ifeq ($(ARCH), riscv64) + ifeq ($(filter 38 39 40 41, $(BASE_OS_VERSION)), ) + $(error Only Fedora 38-41 are supported for "$(ARCH)", but Fedora $(BASE_OS_VERSION) was requested) + endif BASE_URL := http://fedora.riscv.rocks/repos-dist/f$(BASE_OS_VERSION)/latest/$(ARCH)/Packages/ else - LATEST_ARCHIVED_OS_VERSION := 35 - ifeq ($(filter x86_64 armhfp, $(ARCH)), ) + LATEST_ARCHIVED_OS_VERSION := 36 + ifeq ($(filter aarch64 armhfp x86_64, $(ARCH)), ) FEDORA_TYPE := fedora-secondary else FEDORA_TYPE := fedora/linux endif + ifeq ($(ARCH), armhfp) + ifneq ($(BASE_OS_VERSION), 36) + $(error Fedora 36 is the last release supporting "armhfp", but $(BASE_OS) was requested) + endif + endif NOT_ARCHIVED := $(shell [ $(BASE_OS_VERSION) -gt $(LATEST_ARCHIVED_OS_VERSION) ] && echo true) ifeq ($(NOT_ARCHIVED),true) BASE_URL := https://dl.fedoraproject.org/pub/$(FEDORA_TYPE)/releases/$(BASE_OS_VERSION)/Everything/$(ARCH)/os/Packages/ @@ -464,7 +475,7 @@ ifeq ($(ARCH), armhfp) $(BUILDDIR)/$(gcc_ver)/Makefile : CONFIG += --with-float=hard endif -ifneq ($(filter riscv64 ppc64 ppc64le s390x, $(ARCH)), ) +ifneq ($(filter riscv64 ppc64le s390x, $(ARCH)), ) # We only support 64-bit on these platforms anyway CONFIG += --disable-multilib endif From e912977a6687917ed45520c4d8558ebe630e3f52 Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Wed, 20 Aug 2025 13:40:13 +0000 Subject: [PATCH 109/112] 8353444: NMT: rename 'category' to 'MemTag' in malloc tracker Reviewed-by: jsjolen --- src/hotspot/share/nmt/mallocLimit.cpp | 37 +++++++++---------- src/hotspot/share/nmt/mallocLimit.hpp | 8 ++-- .../share/nmt/mallocTracker.inline.hpp | 4 +- .../gtest/nmt/test_nmt_malloclimit.cpp | 6 +-- test/hotspot/gtest/nmt/test_nmt_totals.cpp | 4 +- 5 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/hotspot/share/nmt/mallocLimit.cpp b/src/hotspot/share/nmt/mallocLimit.cpp index ed479725cf9..2bce2e459e0 100644 --- a/src/hotspot/share/nmt/mallocLimit.cpp +++ b/src/hotspot/share/nmt/mallocLimit.cpp @@ -61,7 +61,7 @@ public: // Check if string at position matches a malloclimit_mode_t. // Advance position on match. - bool match_mode_flag(MallocLimitMode* out) { + bool match_mode(MallocLimitMode* out) { if (eof()) { return false; } @@ -77,9 +77,9 @@ public: return false; } - // Check if string at position matches a category name. + // Check if string at position matches a MemTag name. // Advances position on match. - bool match_category(MemTag* out) { + bool match_mem_tag(MemTag* out) { if (eof()) { return false; } @@ -130,9 +130,9 @@ void MallocLimitSet::set_global_limit(size_t s, MallocLimitMode flag) { _glob.sz = s; _glob.mode = flag; } -void MallocLimitSet::set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode flag) { +void MallocLimitSet::set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode mode) { const int i = NMTUtil::tag_to_index(mem_tag); - _cat[i].sz = s; _cat[i].mode = flag; + _mtag[i].sz = s; _mtag[i].mode = mode; } void MallocLimitSet::reset() { @@ -144,16 +144,15 @@ void MallocLimitSet::reset() { } void MallocLimitSet::print_on(outputStream* st) const { - static const char* flagnames[] = { MODE_FATAL, MODE_OOM }; if (_glob.sz > 0) { st->print_cr("MallocLimit: total limit: " PROPERFMT " (%s)", PROPERFMTARGS(_glob.sz), mode_to_name(_glob.mode)); } else { for (int i = 0; i < mt_number_of_tags; i++) { - if (_cat[i].sz > 0) { + if (_mtag[i].sz > 0) { st->print_cr("MallocLimit: category \"%s\" limit: " PROPERFMT " (%s)", NMTUtil::tag_to_enum_name(NMTUtil::index_to_tag(i)), - PROPERFMTARGS(_cat[i].sz), mode_to_name(_cat[i].mode)); + PROPERFMTARGS(_mtag[i].sz), mode_to_name(_mtag[i].mode)); } } } @@ -164,10 +163,10 @@ bool MallocLimitSet::parse_malloclimit_option(const char* v, const char** err) { #define BAIL_UNLESS(condition, errormessage) if (!(condition)) { *err = errormessage; return false; } // Global form: - // MallocLimit=[:flag] + // MallocLimit=[:mode] - // Category-specific form: - // MallocLimit=:[:flag][,:[:flag]...] + // MemTag-specific form: + // MallocLimit=:[:mode][,:[:mode]...] reset(); @@ -177,29 +176,29 @@ bool MallocLimitSet::parse_malloclimit_option(const char* v, const char** err) { // Global form? if (sst.match_size(&_glob.sz)) { - // Match optional mode flag (e.g. 1g:oom) + // Match optional mode (e.g. 1g:oom) if (!sst.eof()) { BAIL_UNLESS(sst.match_char(':'), "Expected colon"); - BAIL_UNLESS(sst.match_mode_flag(&_glob.mode), "Expected flag"); + BAIL_UNLESS(sst.match_mode(&_glob.mode), "Expected mode"); } } - // Category-specific form? + // MemTag-specific form? else { while (!sst.eof()) { MemTag mem_tag; - // Match category, followed by : - BAIL_UNLESS(sst.match_category(&mem_tag), "Expected category name"); + // Match MemTag, followed by : + BAIL_UNLESS(sst.match_mem_tag(&mem_tag), "Expected category name"); BAIL_UNLESS(sst.match_char(':'), "Expected colon following category"); - malloclimit* const modified_limit = &_cat[NMTUtil::tag_to_index(mem_tag)]; + malloclimit* const modified_limit = &_mtag[NMTUtil::tag_to_index(mem_tag)]; // Match size BAIL_UNLESS(sst.match_size(&modified_limit->sz), "Expected size"); - // Match optional flag + // Match optional mode if (!sst.eof() && sst.match_char(':')) { - BAIL_UNLESS(sst.match_mode_flag(&modified_limit->mode), "Expected flag"); + BAIL_UNLESS(sst.match_mode(&modified_limit->mode), "Expected mode"); } // More to come? diff --git a/src/hotspot/share/nmt/mallocLimit.hpp b/src/hotspot/share/nmt/mallocLimit.hpp index ec6799b41a3..d41eaabea46 100644 --- a/src/hotspot/share/nmt/mallocLimit.hpp +++ b/src/hotspot/share/nmt/mallocLimit.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2023 SAP SE. All rights reserved. - * Copyright (c) 2023, 2024, 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 @@ -46,7 +46,7 @@ class outputStream; class MallocLimitSet { malloclimit _glob; // global limit - malloclimit _cat[mt_number_of_tags]; // per-category limit + malloclimit _mtag[mt_number_of_tags]; // per-memtag limit public: MallocLimitSet(); @@ -57,7 +57,7 @@ public: void set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode mode); const malloclimit* global_limit() const { return &_glob; } - const malloclimit* category_limit(MemTag mem_tag) const { return &_cat[(int)mem_tag]; } + const malloclimit* mem_tag_limit(MemTag mem_tag) const { return &_mtag[(int)mem_tag]; } void print_on(outputStream* st) const; }; @@ -69,7 +69,7 @@ class MallocLimitHandler : public AllStatic { public: static const malloclimit* global_limit() { return _limits.global_limit(); } - static const malloclimit* category_limit(MemTag mem_tag) { return _limits.category_limit(mem_tag); } + static const malloclimit* mem_tag_limit(MemTag mem_tag) { return _limits.mem_tag_limit(mem_tag); } static void initialize(const char* options); static void print_on(outputStream* st); diff --git a/src/hotspot/share/nmt/mallocTracker.inline.hpp b/src/hotspot/share/nmt/mallocTracker.inline.hpp index b2aec345eff..51a7f28cf99 100644 --- a/src/hotspot/share/nmt/mallocTracker.inline.hpp +++ b/src/hotspot/share/nmt/mallocTracker.inline.hpp @@ -49,8 +49,8 @@ inline bool MallocMemorySummary::check_exceeds_limit(size_t s, MemTag mem_tag) { return total_limit_reached(s, so_far, l); } } else { - // Category Limit? - l = MallocLimitHandler::category_limit(mem_tag); + // MemTag Limit? + l = MallocLimitHandler::mem_tag_limit(mem_tag); if (l->sz > 0) { const MallocMemory* mm = as_snapshot()->by_tag(mem_tag); size_t so_far = mm->malloc_size() + mm->arena_size(); diff --git a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp index 83d4bd6235b..da357285148 100644 --- a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp @@ -42,8 +42,8 @@ static bool compare_limits(const malloclimit* a, const malloclimit* b) { static bool compare_sets(const MallocLimitSet* a, const MallocLimitSet* b) { if (compare_limits(a->global_limit(), b->global_limit())) { for (int i = 0; i < mt_number_of_tags; i++) { - if (!compare_limits(a->category_limit(NMTUtil::index_to_tag(i)), - b->category_limit(NMTUtil::index_to_tag(i)))) { + if (!compare_limits(a->mem_tag_limit(NMTUtil::index_to_tag(i)), + b->mem_tag_limit(NMTUtil::index_to_tag(i)))) { return false; } } @@ -92,7 +92,7 @@ TEST(NMT, MallocLimitPerCategory) { test("metaspace:1m,compiler:2m:oom,thread:3m:oom,threadstack:4m:oom,class:5m,classshared:6m", expected); } -TEST(NMT, MallocLimitCategoryEnumNames) { +TEST(NMT, MallocLimitMemTagEnumNames) { MallocLimitSet expected; stringStream option; for (int i = 0; i < mt_number_of_tags; i++) { diff --git a/test/hotspot/gtest/nmt/test_nmt_totals.cpp b/test/hotspot/gtest/nmt/test_nmt_totals.cpp index 61c591fa0bb..690751ede54 100644 --- a/test/hotspot/gtest/nmt/test_nmt_totals.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_totals.cpp @@ -88,8 +88,8 @@ TEST_VM(NMTNumbers, totals) { void* p[NUM_ALLOCS]; for (int i = 0; i < NUM_ALLOCS; i ++) { // spread over categories - int category = i % (mt_number_of_tags - 1); - p[i] = NEW_C_HEAP_ARRAY(char, ALLOC_SIZE, (MemTag)category); + int mtag = i % (mt_number_of_tags - 1); + p[i] = NEW_C_HEAP_ARRAY(char, ALLOC_SIZE, (MemTag)mtag); } const totals_t t2 = get_totals(); From ebf5ae8435e27e4315e43237b1167a1e99150393 Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Wed, 20 Aug 2025 14:49:16 +0000 Subject: [PATCH 110/112] 8359222: [asan] jvmti/vthread/ToggleNotifyJvmtiTest/ToggleNotifyJvmtiTest triggers stack-buffer-overflow error Reviewed-by: dholmes, fbredberg, coleenp --- .../continuationFreezeThaw_aarch64.inline.hpp | 16 +++++++--- .../arm/continuationFreezeThaw_arm.inline.hpp | 4 +++ .../ppc/continuationFreezeThaw_ppc.inline.hpp | 3 ++ .../continuationFreezeThaw_riscv.inline.hpp | 3 ++ .../continuationFreezeThaw_s390.inline.hpp | 4 +++ .../x86/continuationFreezeThaw_x86.inline.hpp | 16 +++++++--- .../continuationFreezeThaw_zero.inline.hpp | 4 +++ .../share/runtime/continuationFreezeThaw.cpp | 29 ++++++++++++++++--- .../share/runtime/continuationHelper.hpp | 5 +++- 9 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp index e82162df4ab..1fd59b81d9e 100644 --- a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,9 +101,12 @@ frame FreezeBase::new_heap_frame(frame& f, frame& caller) { *hf.addr_at(frame::interpreter_frame_locals_offset) = locals_offset; return hf; } else { - // We need to re-read fp out of the frame because it may be an oop and we might have - // had a safepoint in finalize_freeze, after constructing f. - fp = *(intptr_t**)(f.sp() - frame::sender_sp_offset); + // For a compiled frame we need to re-read fp out of the frame because it may be an + // oop and we might have had a safepoint in finalize_freeze, after constructing f. + // For stub/native frames the value is not used while frozen, and will be constructed again + // when thawing the frame (see ThawBase::new_stack_frame). We use a special bad address to + // help with debugging, particularly when inspecting frames and identifying invalid accesses. + fp = FKind::compiled ? *(intptr_t**)(f.sp() - frame::sender_sp_offset) : (intptr_t*)badAddressVal; int fsize = FKind::size(f); sp = caller.unextended_sp() - fsize; @@ -192,6 +195,11 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { } } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + intptr_t* fp_addr = sp - frame::sender_sp_offset; + *fp_addr = badAddressVal; +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp index b276a7d70b6..389cf4dc936 100644 --- a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp @@ -60,6 +60,10 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { Unimplemented(); } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + Unimplemented(); +} + inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { Unimplemented(); } diff --git a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp index 8eb33c3cd0b..fa878b07d43 100644 --- a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp @@ -334,6 +334,9 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { #endif } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp index 78975b8df89..461dc19f383 100644 --- a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp @@ -194,6 +194,9 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { } } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp index bee7bb72cbf..d39821bd034 100644 --- a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp @@ -60,6 +60,10 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { Unimplemented(); } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + Unimplemented(); +} + inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { Unimplemented(); } diff --git a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp index 9591c9f2c96..126f4043cad 100644 --- a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,9 +98,12 @@ frame FreezeBase::new_heap_frame(frame& f, frame& caller) { *hf.addr_at(frame::interpreter_frame_locals_offset) = locals_offset; return hf; } else { - // We need to re-read fp out of the frame because it may be an oop and we might have - // had a safepoint in finalize_freeze, after constructing f. - fp = *(intptr_t**)(f.sp() - frame::sender_sp_offset); + // For a compiled frame we need to re-read fp out of the frame because it may be an + // oop and we might have had a safepoint in finalize_freeze, after constructing f. + // For stub/native frames the value is not used while frozen, and will be constructed again + // when thawing the frame (see ThawBase::new_stack_frame). We use a special bad address to + // help with debugging, particularly when inspecting frames and identifying invalid accesses. + fp = FKind::compiled ? *(intptr_t**)(f.sp() - frame::sender_sp_offset) : (intptr_t*)badAddressVal; int fsize = FKind::size(f); sp = caller.unextended_sp() - fsize; @@ -183,6 +186,11 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { } } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + intptr_t* fp_addr = sp - frame::sender_sp_offset; + *fp_addr = badAddressVal; +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp index de2769fb376..eb6be8772f9 100644 --- a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp @@ -60,6 +60,10 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { Unimplemented(); } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + Unimplemented(); +} + inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { Unimplemented(); } diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index a928b0443ee..1027b4447c5 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -460,6 +460,7 @@ private: template frame new_heap_frame(frame& f, frame& caller); inline void set_top_frame_metadata_pd(const frame& hf); inline void patch_pd(frame& callee, const frame& caller); + inline void patch_pd_unused(intptr_t* sp); void adjust_interpreted_frame_unextended_sp(frame& f); static inline void prepare_freeze_interpreted_top_frame(frame& f); static inline void relativize_interpreted_frame_metadata(const frame& f, const frame& hf); @@ -783,9 +784,24 @@ void FreezeBase::freeze_fast_copy(stackChunkOop chunk, int chunk_start_sp CONT_J log_develop_trace(continuations)("freeze_fast start: " INTPTR_FORMAT " sp: %d chunk_top: " INTPTR_FORMAT, p2i(chunk->start_address()), chunk_new_sp, p2i(chunk_top)); - intptr_t* from = _cont_stack_top - frame::metadata_words_at_bottom; - intptr_t* to = chunk_top - frame::metadata_words_at_bottom; - copy_to_chunk(from, to, cont_size() + frame::metadata_words_at_bottom); + + int adjust = frame::metadata_words_at_bottom; +#if INCLUDE_ASAN && defined(AARCH64) + // Reading at offset frame::metadata_words_at_bottom from _cont_stack_top + // will accesss memory at the callee frame, which on preemption cases will + // be the VM native method being called. The Arm 64-bit ABI doesn't specify + // a location where the frame record (returnpc+fp) has to be stored within + // a stack frame, and GCC currently chooses to save it at the top of the + // frame (lowest address). ASan treats this memory access in the callee as + // an overflow access to one of the locals stored in that frame. For these + // preemption cases we don't need to read these words anyways so we avoid it. + if (_preempt) { + adjust = 0; + } +#endif + intptr_t* from = _cont_stack_top - adjust; + intptr_t* to = chunk_top - adjust; + copy_to_chunk(from, to, cont_size() + adjust); // Because we're not patched yet, the chunk is now in a bad state // patch return pc of the bottom-most frozen frame (now in the chunk) @@ -816,6 +832,11 @@ void FreezeBase::freeze_fast_copy(stackChunkOop chunk, int chunk_start_sp CONT_J address last_pc = _last_frame.pc(); ContinuationHelper::patch_return_address_at(chunk_top - frame::sender_sp_ret_address_offset(), last_pc); chunk->set_pc(last_pc); + // For stub/native frames the fp is not used while frozen, and will be constructed + // again when thawing the frame (see ThawBase::handle_preempted_continuation). We + // patch it with a special bad address to help with debugging, particularly when + // inspecting frames and identifying invalid accesses. + patch_pd_unused(chunk_top); } else { chunk->set_pc(ContinuationHelper::return_address_at( _cont_stack_top - frame::sender_sp_ret_address_offset())); @@ -2484,7 +2505,7 @@ intptr_t* ThawBase::handle_preempted_continuation(intptr_t* sp, Continuation::pr if (fast_case) { // If we thawed in the slow path the runtime stub/native wrapper frame already // has the correct fp (see ThawBase::new_stack_frame). On the fast path though, - // we copied the original fp at the time of freeze which now will have to be fixed. + // we copied the fp patched during freeze, which will now have to be fixed. assert(top.is_runtime_frame() || top.is_native_frame(), ""); int fsize = top.cb()->frame_size(); patch_pd(top, sp + fsize); diff --git a/src/hotspot/share/runtime/continuationHelper.hpp b/src/hotspot/share/runtime/continuationHelper.hpp index 0b3e6c72452..dffbf333bcc 100644 --- a/src/hotspot/share/runtime/continuationHelper.hpp +++ b/src/hotspot/share/runtime/continuationHelper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,6 +65,7 @@ public: class ContinuationHelper::Frame : public AllStatic { public: static const bool interpreted = false; + static const bool compiled = false; static const bool stub = false; static const bool native = false; @@ -127,6 +128,8 @@ public: class ContinuationHelper::CompiledFrame : public ContinuationHelper::NonInterpretedFrame { public: + static const bool compiled = true; + static bool is_instance(const frame& f); #ifdef ASSERT From 5ca8d7c2a79ed3d9a6def61ba0fbd0acd5d404dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Wed, 20 Aug 2025 14:52:04 +0000 Subject: [PATCH 111/112] 8284499: Add the ability to right-click and open in new tab JavaDoc Search results Reviewed-by: liach --- .../doclets/formats/html/resources/search.js.template | 10 ++++++++-- .../doclets/formats/html/resources/stylesheet.css | 10 ++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template index 5a36ee06819..16ae5a4d221 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template @@ -423,7 +423,8 @@ $.widget("custom.catcomplete", $.ui.autocomplete, { var label = getResultLabel(item); var resultDesc = getResultDescription(item); return $("
  • ") - .append($("
    ") + .append($("") + .attr("href", item.indexItem ? pathtoroot + getURL(item.indexItem, item.category) : null) .append($("").addClass("search-result-label").html(label)) .append($("").addClass("search-result-desc").html(resultDesc))) .appendTo(ul); @@ -515,7 +516,7 @@ $(function() { this.menu.previousFilter = "_"; this.menu.filterTimer = this.menu._delay(function() { delete this.previousFilter; - }, 1000); + }, 500); return doSearch(request, response); }, response: function(event, ui) { @@ -531,6 +532,11 @@ $(function() { collision: "flip" }, select: function(event, ui) { + for (var e = event.originalEvent; e != null; e = e.originalEvent) { + if (e.type === "click") { + return; + } + } if (ui.item.indexItem) { var url = getURL(ui.item.indexItem, ui.item.category); window.location.href = pathtoroot + url; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index 587121ad582..778ee5f03b3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -1235,11 +1235,11 @@ li.ui-static-link a, li.ui-static-link a:visited { grid-template-columns: auto auto; } .ui-autocomplete > li, -.ui-autocomplete > li > div { +.ui-autocomplete > li > a { grid-column: 1 / 3; } .ui-autocomplete > li.result-item, -.ui-autocomplete > li.result-item > div { +.ui-autocomplete > li.result-item > a { display: grid; grid-template-columns: subgrid; } @@ -1250,6 +1250,7 @@ li.ui-static-link a, li.ui-static-link a:visited { } .ui-autocomplete .search-result-label { padding: 1px 4px; + color: var(--block-text-color); overflow: hidden; text-overflow: ellipsis; } @@ -1263,6 +1264,7 @@ li.ui-static-link a, li.ui-static-link a:visited { .ui-autocomplete .result-highlight { font-weight:bold; } +.ui-menu .ui-state-active .search-result-label, .ui-menu .ui-state-active .search-result-desc { color: var(--selected-text-color); } @@ -1843,9 +1845,9 @@ table.striped > tbody > tr > th { grid-template-columns: none; } .ui-autocomplete > li, - .ui-autocomplete > li > div, + .ui-autocomplete > li > a, .ui-autocomplete > li.result-item, - .ui-autocomplete > li.result-item > div { + .ui-autocomplete > li.result-item > a { grid-column: unset; display: block; grid-template-columns: none; From 3e60ab51fea17098d852931a06f4f5a827ae0e78 Mon Sep 17 00:00:00 2001 From: Archie Cobbs Date: Wed, 20 Aug 2025 15:04:48 +0000 Subject: [PATCH 112/112] 8348611: Eliminate DeferredLintHandler and emit warnings after attribution 8224228: No way to locally suppress lint warnings in parser/tokenizer or preview features 8353758: Missing calls to Log.useSource() in JavacTrees Reviewed-by: mcimadamore, vromero, jlahoda --- .../sun/tools/javac/api/JavacTaskImpl.java | 7 +- .../sun/tools/javac/api/JavacTaskPool.java | 2 + .../com/sun/tools/javac/api/JavacTrees.java | 13 +- .../tools/javac/code/DeferredLintHandler.java | 176 --------- .../com/sun/tools/javac/code/Lint.java | 26 +- .../com/sun/tools/javac/code/LintMapper.java | 328 +++++++++++++++++ .../com/sun/tools/javac/code/Preview.java | 8 +- .../com/sun/tools/javac/comp/Annotate.java | 60 +--- .../com/sun/tools/javac/comp/Attr.java | 40 +-- .../com/sun/tools/javac/comp/Check.java | 180 ++++------ .../com/sun/tools/javac/comp/Flow.java | 335 ++++++++---------- .../com/sun/tools/javac/comp/MemberEnter.java | 44 +-- .../com/sun/tools/javac/comp/Modules.java | 33 +- .../tools/javac/comp/ThisEscapeAnalyzer.java | 86 ++--- .../com/sun/tools/javac/comp/TypeEnter.java | 32 +- .../sun/tools/javac/file/BaseFileManager.java | 2 +- .../com/sun/tools/javac/file/Locations.java | 23 +- .../com/sun/tools/javac/jvm/ClassReader.java | 17 +- .../com/sun/tools/javac/main/Arguments.java | 29 +- .../sun/tools/javac/main/JavaCompiler.java | 10 + .../sun/tools/javac/parser/JavaTokenizer.java | 40 +-- .../sun/tools/javac/parser/JavacParser.java | 29 +- .../com/sun/tools/javac/parser/Lexer.java | 12 +- .../sun/tools/javac/parser/ParserFactory.java | 5 +- .../com/sun/tools/javac/parser/Scanner.java | 9 +- .../tools/javac/parser/ScannerFactory.java | 5 +- .../sun/tools/javac/parser/VirtualParser.java | 10 +- .../tools/javac/processing/JavacFiler.java | 27 +- .../JavacProcessingEnvironment.java | 29 +- .../tools/javac/resources/compiler.properties | 42 ++- .../sun/tools/javac/util/JCDiagnostic.java | 34 ++ .../classes/com/sun/tools/javac/util/Log.java | 169 ++++++++- .../tools/javac/6304921/TestLog.java | 13 +- test/langtools/tools/javac/ImportModule.java | 2 +- .../OverrideChecks/6400189/T6400189a.out | 2 +- .../OverrideChecks/6400189/T6400189b.out | 2 +- .../DanglingDocCommentsClass.enabled.out | 4 +- .../DanglingDocCommentsClass_Line.enabled.out | 4 +- ...DanglingDocCommentsClass_Mixed.enabled.out | 2 +- .../DanglingDocCommentsEnum.enabled.out | 6 +- .../generics/diamond/7188968/T7188968.out | 6 +- .../tools/javac/lambda/TargetType22.out | 2 +- .../tools/javac/lint/LexicalLintNesting.java | 170 +++++++++ .../tools/javac/lint/LexicalLintNesting.out | 10 + .../tools/javac/lint/TextBlockSuppress.java | 61 ++++ .../tools/javac/lint/TextBlockSuppress.out | 5 + .../mandatoryWarnings/deprecated/Test5.out | 2 +- .../mandatoryWarnings/deprecated/Test5b.out | 2 +- .../javac/modules/AnnotationsOnModules.java | 4 +- .../tools/javac/preview/PreviewErrors.java | 6 +- .../tools/javac/preview/PreviewTest.java | 97 ++++- .../tools/javac/varargs/7097436/T7097436.out | 4 +- .../javac/warnings/6594914/T6594914a.out | 2 +- .../tools/javac/warnings/7090499/T7090499.out | 8 +- .../UnneededStrictfpWarningToolBox.java | 4 +- .../javac/warnings/suppress/T6480588.out | 12 +- 56 files changed, 1344 insertions(+), 948 deletions(-) delete mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java create mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java create mode 100644 test/langtools/tools/javac/lint/LexicalLintNesting.java create mode 100644 test/langtools/tools/javac/lint/LexicalLintNesting.out create mode 100644 test/langtools/tools/javac/lint/TextBlockSuppress.java create mode 100644 test/langtools/tools/javac/lint/TextBlockSuppress.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java index 980ce060c33..65ce640ef76 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, 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 @@ -412,6 +412,7 @@ public class JavacTaskImpl extends BasicJavacTask { f.run(compiler.todo, classes); } } finally { + compiler.log.reportOutstandingWarnings(); compiler.log.flush(); } return results; @@ -483,8 +484,10 @@ public class JavacTaskImpl extends BasicJavacTask { } } finally { - if (compiler != null) + if (compiler != null) { + compiler.log.reportOutstandingWarnings(); compiler.log.flush(); + } } return results; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java index 6214c3775d3..65772cdccb3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java @@ -55,6 +55,7 @@ import com.sun.source.util.TaskEvent.Kind; import com.sun.source.util.TaskListener; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symtab; @@ -268,6 +269,7 @@ public class JavacTaskPool { if (ht.get(Log.logKey) instanceof ReusableLog) { //log already inited - not first round Log.instance(this).clear(); + LintMapper.instance(this).clear(); Enter.instance(this).newRound(); ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear(); Types.instance(this).newRound(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 4ced466d938..2eca26de838 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -360,9 +360,14 @@ public class JavacTrees extends DocTrees { Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); try { Env env = getAttrContext(path.getTreePath()); - Type t = attr.attribType(dcReference.qualifierExpression, env); - if (t != null && !t.isErroneous()) { - return t; + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); + try { + Type t = attr.attribType(dcReference.qualifierExpression, env); + if (t != null && !t.isErroneous()) { + return t; + } + } finally { + log.useSource(prevSource); } } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; @@ -388,6 +393,7 @@ public class JavacTrees extends DocTrees { return null; } Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { final TypeSymbol tsym; final Name memberName; @@ -509,6 +515,7 @@ public class JavacTrees extends DocTrees { } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; } finally { + log.useSource(prevSource); log.popDiagnosticHandler(deferredDiagnosticHandler); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java deleted file mode 100644 index 11544b92b7f..00000000000 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.tools.javac.code; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Optional; -import java.util.function.Consumer; - -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.Tag; -import com.sun.tools.javac.util.Assert; -import com.sun.tools.javac.util.Context; - -/** - * Holds pending {@link Lint} warnings until the {@lint Lint} instance associated with the containing - * module, package, class, method, or variable declaration is known so that {@link @SupressWarnings} - * suppressions may be applied. - * - *

    - * Warnings are regsistered at any time prior to attribution via {@link #report}. The warning will be - * associated with the declaration placed in context by the most recent invocation of {@link #push push()} - * not yet {@link #pop}'d. Warnings are actually emitted later, during attribution, via {@link #flush}. - * - *

    - * There is also an "immediate" mode, where warnings are emitted synchronously; see {@link #pushImmediate}. - * - *

    - * Deferred warnings are grouped by the innermost containing module, package, class, method, or variable - * declaration (represented by {@link JCTree} nodes), so that the corresponding {@link Lint} configuration - * can be applied when the warning is eventually generated. - * - *

    This is NOT part of any supported API. - * If you write code that depends on this, you do so at your own risk. - * This code and its internal interfaces are subject to change or - * deletion without notice. - */ -public class DeferredLintHandler { - - protected static final Context.Key deferredLintHandlerKey = new Context.Key<>(); - - public static DeferredLintHandler instance(Context context) { - DeferredLintHandler instance = context.get(deferredLintHandlerKey); - if (instance == null) - instance = new DeferredLintHandler(context); - return instance; - } - - /** - * Registered {@link LintLogger}s grouped by the innermost containing module, package, class, - * method, or variable declaration. - */ - private final HashMap> deferralMap = new HashMap<>(); - - /** - * The current "reporter" stack, reflecting calls to {@link #push} and {@link #pop}. - * - *

    - * The top of the stack determines how calls to {@link #report} are handled. - */ - private final ArrayDeque> reporterStack = new ArrayDeque<>(); - - @SuppressWarnings("this-escape") - protected DeferredLintHandler(Context context) { - context.put(deferredLintHandlerKey, this); - Lint rootLint = Lint.instance(context); - pushImmediate(rootLint); // default to "immediate" mode - } - -// LintLogger - - /**An interface for deferred lint reporting - loggers passed to - * {@link #report(LintLogger) } will be called when - * {@link #flush(DiagnosticPosition) } is invoked. - */ - public interface LintLogger { - - /** - * Generate a warning if appropriate. - * - * @param lint the applicable lint configuration - */ - void report(Lint lint); - } - -// Reporter Stack - - /** - * Defer {@link #report}ed warnings until the given declaration is flushed. - * - * @param decl module, package, class, method, or variable declaration - * @see #pop - */ - public void push(JCTree decl) { - Assert.check(decl.getTag() == Tag.MODULEDEF - || decl.getTag() == Tag.PACKAGEDEF - || decl.getTag() == Tag.CLASSDEF - || decl.getTag() == Tag.METHODDEF - || decl.getTag() == Tag.VARDEF); - reporterStack.push(logger -> deferralMap - .computeIfAbsent(decl, s -> new ArrayList<>()) - .add(logger)); - } - - /** - * Enter "immediate" mode so that {@link #report}ed warnings are emitted synchonously. - * - * @param lint lint configuration to use for reported warnings - */ - public void pushImmediate(Lint lint) { - reporterStack.push(logger -> logger.report(lint)); - } - - /** - * Revert to the previous configuration in effect prior to the most recent invocation - * of {@link #push} or {@link #pushImmediate}. - * - * @see #pop - */ - public void pop() { - Assert.check(reporterStack.size() > 1); // the bottom stack entry should never be popped - reporterStack.pop(); - } - - /** - * Report a warning. - * - *

    - * In immediate mode, the warning is emitted synchronously. Otherwise, the warning is emitted later - * when the current declaration is flushed. - */ - public void report(LintLogger logger) { - Assert.check(!reporterStack.isEmpty()); - reporterStack.peek().accept(logger); - } - -// Warning Flush - - /** - * Emit deferred warnings encompassed by the given declaration. - * - * @param decl module, package, class, method, or variable declaration - * @param lint lint configuration corresponding to {@code decl} - */ - public void flush(JCTree decl, Lint lint) { - Optional.of(decl) - .map(deferralMap::remove) - .stream() - .flatMap(ArrayList::stream) - .forEach(logger -> logger.report(lint)); - } -} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 6d83f95fce4..8eab238f82a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -374,11 +374,8 @@ public class Lint { /** * Warn about issues relating to use of text blocks - * - *

    - * This category is not supported by {@code @SuppressWarnings} (yet - see JDK-8224228). */ - TEXT_BLOCKS("text-blocks", false), + TEXT_BLOCKS("text-blocks"), /** * Warn about possible 'this' escapes before subclass instance is fully initialized. @@ -476,27 +473,6 @@ public class Lint { return suppressedValues.contains(lc); } - /** - * Helper method. Log a lint warning if its lint category is enabled. - * - * @param warning key for the localized warning message - */ - public void logIfEnabled(LintWarning warning) { - logIfEnabled(null, warning); - } - - /** - * Helper method. Log a lint warning if its lint category is enabled. - * - * @param pos source position at which to report the warning - * @param warning key for the localized warning message - */ - public void logIfEnabled(DiagnosticPosition pos, LintWarning warning) { - if (isEnabled(warning.getLintCategory())) { - log.warning(pos, warning); - } - } - /** * Obtain the set of recognized lint warning categories suppressed at the given symbol's declaration. * diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java new file mode 100644 index 00000000000..b15ddae02e1 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.code; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileObject; + +import com.sun.tools.javac.tree.EndPosTable; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeScanner; +import com.sun.tools.javac.util.Assert; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; + +/** + * Maps source code positions to the applicable {@link Lint} instance. + * + *

    + * Because {@code @SuppressWarnings} is a Java symbol, in general this mapping can't be + * calculated until after attribution. As each top-level declaration (class, package, or module) + * is attributed, this singleton is notified and the {@link Lint}s that apply to every source + * position within that top-level declaration are calculated. + * + *

    + * The method {@link #lintAt} returns the {@link Lint} instance applicable to source position; + * if it can't be determined yet, an empty {@link Optional} is returned. + * + *

    This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class LintMapper { + + // The key for the context singleton + private static final Context.Key CONTEXT_KEY = new Context.Key<>(); + + // Per-source file information. Note: during the parsing of a file, an entry exists but the FileInfo value is null + private final Map fileInfoMap = new HashMap<>(); + + // Compiler context + private final Context context; + + // These are initialized lazily; see initializeIfNeeded() + private Lint rootLint; + + /** + * Obtain the {@link LintMapper} context singleton. + */ + public static LintMapper instance(Context context) { + LintMapper instance = context.get(CONTEXT_KEY); + if (instance == null) + instance = new LintMapper(context); + return instance; + } + + /** + * Constructor. + */ + @SuppressWarnings("this-escape") + protected LintMapper(Context context) { + context.put(CONTEXT_KEY, this); + this.context = context; + } + + // Lazy initialization to avoid dependency loops + private void initializeIfNeeded() { + if (rootLint == null) + rootLint = Lint.instance(context); + } + +// Lint Operations + + /** + * Determine if the given file is known to this instance. + * + * @param sourceFile source file + * @return true if file is recognized + */ + public boolean isKnown(JavaFileObject sourceFile) { + return fileInfoMap.containsKey(sourceFile); + } + + /** + * Obtain the {@link Lint} configuration that applies at the given position, if known. + * + * @param sourceFile source file + * @param pos source position + * @return the applicable {@link Lint}, if known, otherwise empty + */ + public Optional lintAt(JavaFileObject sourceFile, DiagnosticPosition pos) { + initializeIfNeeded(); + return Optional.of(sourceFile) + .map(fileInfoMap::get) + .flatMap(fileInfo -> fileInfo.lintAt(pos)); + } + + /** + * Calculate {@lint Lint} configurations for all positions within the given top-level declaration. + * + * @param sourceFile source file + * @param tree top-level declaration (class, package, or module) + */ + public void calculateLints(JavaFileObject sourceFile, JCTree tree, EndPosTable endPositions) { + Assert.check(rootLint != null); + fileInfoMap.get(sourceFile).afterAttr(tree, endPositions); + } + + /** + * Reset this instance. + */ + public void clear() { + fileInfoMap.clear(); + } + +// Parsing Notifications + + /** + * Invoked when file parsing starts to create an entry for the new file (but with a null value). + */ + public void startParsingFile(JavaFileObject sourceFile) { + initializeIfNeeded(); + fileInfoMap.put(sourceFile, null); + } + + /** + * Invoked when file parsing completes to put in place a corresponding {@link FileInfo}. + */ + public void finishParsingFile(JCCompilationUnit tree) { + Assert.check(rootLint != null); + fileInfoMap.put(tree.sourcefile, new FileInfo(rootLint, tree)); + } + +// FileInfo + + /** + * Holds {@link Lint} information for a fully parsed source file. + * + *

    + * Initially (immediately after parsing), "unmappedDecls" contains a {@link Span} corresponding to each + * top-level declaration in the source file. As each top-level declaration is attributed, the corresponding + * {@link Span} is removed and the corresponding {@link LintRange} subtree is populated under "rootRange". + */ + private static class FileInfo { + + final LintRange rootRange; // the root LintRange (covering the entire source file) + final List unmappedDecls = new ArrayList<>(); // unmapped top-level declarations awaiting attribution + + // After parsing: Add top-level declarations to our "unmappedDecls" list + FileInfo(Lint rootLint, JCCompilationUnit tree) { + rootRange = new LintRange(rootLint); + tree.defs.stream() + .filter(this::isTopLevelDecl) + .map(decl -> new Span(decl, tree.endPositions)) + .forEach(unmappedDecls::add); + } + + // After attribution: Discard the span from "unmappedDecls" and populate the declaration's subtree under "rootRange" + void afterAttr(JCTree tree, EndPosTable endPositions) { + for (Iterator i = unmappedDecls.iterator(); i.hasNext(); ) { + if (i.next().contains(tree.pos())) { + rootRange.populateSubtree(tree, endPositions); + i.remove(); + return; + } + } + throw new AssertionError("top-level declaration not found"); + } + + // Find the most specific Lint configuration applying to the given position, unless the position has not been mapped yet + Optional lintAt(DiagnosticPosition pos) { + boolean mapped = unmappedDecls.stream().noneMatch(span -> span.contains(pos)); + return mapped ? Optional.of(rootRange.bestMatch(pos).lint) : Optional.empty(); + } + + boolean isTopLevelDecl(JCTree tree) { + return tree.getTag() == Tag.MODULEDEF + || tree.getTag() == Tag.PACKAGEDEF + || tree.getTag() == Tag.CLASSDEF; + } + } + +// Span + + /** + * A lexical range. + */ + private record Span(int startPos, int endPos) { + + static final Span MAXIMAL = new Span(Integer.MIN_VALUE, Integer.MAX_VALUE); + + Span(JCTree tree, EndPosTable endPositions) { + this(TreeInfo.getStartPos(tree), TreeInfo.getEndPos(tree, endPositions)); + } + + boolean contains(DiagnosticPosition pos) { + int offset = pos.getLintPosition(); + return offset == startPos || (offset > startPos && offset < endPos); + } + + boolean contains(Span that) { + return this.startPos <= that.startPos && this.endPos >= that.endPos; + } + } + +// LintRange + + /** + * A tree of nested lexical ranges and the {@link Lint} configurations that apply therein. + */ + private record LintRange( + Span span, // declaration's lexical range + Lint lint, // the Lint configuration that applies at this declaration + List children // the nested declarations one level below this node + ) { + + // Create a node representing the entire file, using the root lint configuration + LintRange(Lint rootLint) { + this(Span.MAXIMAL, rootLint, new ArrayList<>()); + } + + // Create a node representing the given declaration and its corresponding Lint configuration + LintRange(JCTree tree, EndPosTable endPositions, Lint lint) { + this(new Span(tree, endPositions), lint, new ArrayList<>()); + } + + // Find the most specific node in this tree (including me) that contains the given position, if any + LintRange bestMatch(DiagnosticPosition pos) { + return children.stream() + .map(child -> child.bestMatch(pos)) + .filter(Objects::nonNull) + .reduce((a, b) -> a.span.contains(b.span) ? b : a) + .orElseGet(() -> span.contains(pos) ? this : null); + } + + // Populate a sparse subtree corresponding to the given nested declaration. + // Only when the Lint configuration differs from the parent is a node added. + void populateSubtree(JCTree tree, EndPosTable endPositions) { + new TreeScanner() { + + private LintRange currentNode = LintRange.this; + + @Override + public void visitModuleDef(JCModuleDecl tree) { + scanDecl(tree, tree.sym, super::visitModuleDef); + } + @Override + public void visitPackageDef(JCPackageDecl tree) { + scanDecl(tree, tree.packge, super::visitPackageDef); + } + @Override + public void visitClassDef(JCClassDecl tree) { + scanDecl(tree, tree.sym, super::visitClassDef); + } + @Override + public void visitMethodDef(JCMethodDecl tree) { + scanDecl(tree, tree.sym, super::visitMethodDef); + } + @Override + public void visitVarDef(JCVariableDecl tree) { + scanDecl(tree, tree.sym, super::visitVarDef); + } + + private void scanDecl(T tree, Symbol symbol, Consumer recursor) { + + // The "symbol" can be null if there were earlier errors; skip this declaration if so + if (symbol == null) { + recursor.accept(tree); + return; + } + + // Update the Lint using the declaration; if there's no change, then we don't need a new node here + Lint newLint = currentNode.lint.augment(symbol); + if (newLint == currentNode.lint) { // note: lint.augment() returns the same instance if there's no change + recursor.accept(tree); + return; + } + + // Add a new node here and proceed + final LintRange previousNode = currentNode; + currentNode = new LintRange(tree, endPositions, newLint); + previousNode.children.add(currentNode); + try { + recursor.accept(tree); + } finally { + currentNode = previousNode; + } + } + }.scan(tree); + } + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index e11a9c6754e..1c93c37698a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -67,9 +67,6 @@ public class Preview { /** flag: are preview features enabled */ private final boolean enabled; - /** flag: is the "preview" lint category enabled? */ - private final boolean verbose; - /** test flag: should all features be considered as preview features? */ private final boolean forcePreview; @@ -100,7 +97,6 @@ public class Preview { enabled = options.isSet(PREVIEW); log = Log.instance(context); source = Source.instance(context); - verbose = Lint.instance(context).isEnabled(LintCategory.PREVIEW); forcePreview = options.isSet("forcePreview"); majorVersionToSource = initMajorVersionToSourceMap(); } @@ -184,9 +180,7 @@ public class Preview { */ public void warnPreview(JavaFileObject classfile, int majorVersion) { Assert.check(isEnabled()); - if (verbose) { - log.warning(LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); - } + log.warning(LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 3da83248436..9f56bec4cca 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -89,9 +89,7 @@ public class Annotate { private final Attr attr; private final Check chk; private final ConstFold cfolder; - private final DeferredLintHandler deferredLintHandler; private final Enter enter; - private final Lint lint; private final Log log; private final Names names; private final Resolve resolve; @@ -110,10 +108,8 @@ public class Annotate { attr = Attr.instance(context); chk = Check.instance(context); cfolder = ConstFold.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); enter = Enter.instance(context); log = Log.instance(context); - lint = Lint.instance(context); make = TreeMaker.instance(context); names = Names.instance(context); resolve = Resolve.instance(context); @@ -235,10 +231,8 @@ public class Annotate { * @param annotations the list of JCAnnotations to attribute and enter * @param localEnv the enclosing env * @param s the Symbol on which to enter the annotations - * @param deferDecl enclosing declaration for DeferredLintHandler, or null for no deferral */ - public void annotateLater(List annotations, Env localEnv, - Symbol s, JCTree deferDecl) + public void annotateLater(List annotations, Env localEnv, Symbol s) { if (annotations.isEmpty()) { return; @@ -256,8 +250,6 @@ public class Annotate { // been handled, meaning that the set of annotations pending completion is now empty. Assert.check(s.kind == PCK || s.annotationsPendingCompletion()); JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); - Assert.check(deferDecl != null); - deferredLintHandler.push(deferDecl); try { if (s.hasAnnotations() && annotations.nonEmpty()) log.error(annotations.head.pos, Errors.AlreadyAnnotated(Kinds.kindName(s), s)); @@ -268,7 +260,6 @@ public class Annotate { // never called for a type parameter annotateNow(s, annotations, localEnv, false, false); } finally { - deferredLintHandler.pop(); log.useSource(prev); } }); @@ -285,16 +276,13 @@ public class Annotate { /** Queue processing of an attribute default value. */ - public void annotateDefaultValueLater(JCExpression defaultValue, Env localEnv, - MethodSymbol m, JCTree deferDecl) + public void annotateDefaultValueLater(JCExpression defaultValue, Env localEnv, MethodSymbol m) { normal(() -> { JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); - deferredLintHandler.push(deferDecl); try { enterDefaultValue(defaultValue, localEnv, m); } finally { - deferredLintHandler.pop(); log.useSource(prev); } }); @@ -682,7 +670,7 @@ public class Annotate { // Scan the annotation element value and then attribute nested annotations if present if (tree.type != null && tree.type.tsym != null) { - queueScanTreeAndTypeAnnotate(tree, env, tree.type.tsym, null); + queueScanTreeAndTypeAnnotate(tree, env, tree.type.tsym); } result = cfolder.coerce(result, expectedElementType); @@ -1034,20 +1022,14 @@ public class Annotate { /** * Attribute the list of annotations and enter them onto s. */ - public void enterTypeAnnotations(List annotations, Env env, - Symbol s, JCTree deferDecl, boolean isTypeParam) + public void enterTypeAnnotations(List annotations, Env env, Symbol s, boolean isTypeParam) { Assert.checkNonNull(s, "Symbol argument to actualEnterTypeAnnotations is nul/"); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); - if (deferDecl != null) { - deferredLintHandler.push(deferDecl); - } try { annotateNow(s, annotations, env, true, isTypeParam); } finally { - if (deferDecl != null) - deferredLintHandler.pop(); log.useSource(prev); } } @@ -1055,10 +1037,10 @@ public class Annotate { /** * Enqueue tree for scanning of type annotations, attaching to the Symbol sym. */ - public void queueScanTreeAndTypeAnnotate(JCTree tree, Env env, Symbol sym, JCTree deferDecl) + public void queueScanTreeAndTypeAnnotate(JCTree tree, Env env, Symbol sym) { Assert.checkNonNull(sym); - normal(() -> tree.accept(new TypeAnnotate(env, sym, deferDecl))); + normal(() -> tree.accept(new TypeAnnotate(env, sym))); } /** @@ -1093,32 +1075,30 @@ public class Annotate { private class TypeAnnotate extends TreeScanner { private final Env env; private final Symbol sym; - private JCTree deferDecl; - public TypeAnnotate(Env env, Symbol sym, JCTree deferDecl) { + public TypeAnnotate(Env env, Symbol sym) { this.env = env; this.sym = sym; - this.deferDecl = deferDecl; } @Override public void visitAnnotatedType(JCAnnotatedType tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, false); + enterTypeAnnotations(tree.annotations, env, sym, false); scan(tree.underlyingType); } @Override public void visitTypeParameter(JCTypeParameter tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, true); + enterTypeAnnotations(tree.annotations, env, sym, true); scan(tree.bounds); } @Override public void visitNewArray(JCNewArray tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, false); + enterTypeAnnotations(tree.annotations, env, sym, false); for (List dimAnnos : tree.dimAnnotations) - enterTypeAnnotations(dimAnnos, env, sym, deferDecl, false); + enterTypeAnnotations(dimAnnos, env, sym, false); scan(tree.elemtype); scan(tree.elems); } @@ -1137,19 +1117,13 @@ public class Annotate { @Override public void visitVarDef(JCVariableDecl tree) { - JCTree prevDecl = deferDecl; - deferDecl = tree; - try { - if (sym != null && sym.kind == VAR) { - // Don't visit a parameter once when the sym is the method - // and once when the sym is the parameter. - scan(tree.mods); - scan(tree.vartype); - } - scan(tree.init); - } finally { - deferDecl = prevDecl; + if (sym != null && sym.kind == VAR) { + // Don't visit a parameter once when the sym is the method + // and once when the sym is the parameter. + scan(tree.mods); + scan(tree.vartype); } + scan(tree.init); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index ea3b08e0338..45ece909ad7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -41,6 +41,7 @@ import com.sun.source.tree.TreeVisitor; import com.sun.source.util.SimpleTreeVisitor; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.*; @@ -98,6 +99,7 @@ public class Attr extends JCTree.Visitor { final Names names; final Log log; + final LintMapper lintMapper; final Symtab syms; final Resolve rs; final Operators operators; @@ -116,7 +118,6 @@ public class Attr extends JCTree.Visitor { final Preview preview; final JCDiagnostic.Factory diags; final TypeAnnotations typeAnnotations; - final DeferredLintHandler deferredLintHandler; final TypeEnvs typeEnvs; final Dependencies dependencies; final Annotate annotate; @@ -137,6 +138,7 @@ public class Attr extends JCTree.Visitor { names = Names.instance(context); log = Log.instance(context); + lintMapper = LintMapper.instance(context); syms = Symtab.instance(context); rs = Resolve.instance(context); operators = Operators.instance(context); @@ -156,7 +158,6 @@ public class Attr extends JCTree.Visitor { diags = JCDiagnostic.Factory.instance(context); annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); argumentAttr = ArgumentAttr.instance(context); @@ -853,7 +854,6 @@ public class Attr extends JCTree.Visitor { Env enclosingEnv, JCVariableDecl variable, Type type) { - deferredLintHandler.push(variable); final JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { doQueueScanTreeAndTypeAnnotateForVarInit(variable, enclosingEnv); @@ -869,7 +869,6 @@ public class Attr extends JCTree.Visitor { } } finally { log.useSource(prevSource); - deferredLintHandler.pop(); } } @@ -998,7 +997,6 @@ public class Attr extends JCTree.Visitor { Assert.check(!env.info.ctorPrologue); MethodSymbol prevMethod = chk.setMethod(m); try { - deferredLintHandler.flush(tree, lint); chk.checkDeprecatedAnnotation(tree.pos(), m); @@ -1233,7 +1231,7 @@ public class Attr extends JCTree.Visitor { } // Attribute all type annotations in the body - annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null); + annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m); annotate.flush(); // Start of constructor prologue (if not in java.lang.Object constructor) @@ -1297,7 +1295,6 @@ public class Attr extends JCTree.Visitor { try { v.getConstValue(); // ensure compile-time constant initializer is evaluated - deferredLintHandler.flush(tree, lint); chk.checkDeprecatedAnnotation(tree.pos(), v); if (tree.init != null) { @@ -1342,7 +1339,7 @@ public class Attr extends JCTree.Visitor { env.info.scope.owner.kind != MTH && env.info.scope.owner.kind != VAR) { tree.mods.flags |= Flags.FIELD_INIT_TYPE_ANNOTATIONS_QUEUED; // Field initializer expression need to be entered. - annotate.queueScanTreeAndTypeAnnotate(tree.init, env, tree.sym, tree); + annotate.queueScanTreeAndTypeAnnotate(tree.init, env, tree.sym); annotate.flush(); } } @@ -1439,7 +1436,7 @@ public class Attr extends JCTree.Visitor { if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++; // Attribute all type annotations in the block - annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, localEnv.info.scope.owner, null); + annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, localEnv.info.scope.owner); annotate.flush(); attribStats(tree.stats, localEnv); @@ -1952,7 +1949,7 @@ public class Attr extends JCTree.Visitor { public void visitSynchronized(JCSynchronized tree) { chk.checkRefType(tree.pos(), attribExpr(tree.lock, env)); if (tree.lock.type != null && tree.lock.type.isValueBased()) { - env.info.lint.logIfEnabled(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); + log.warning(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); } attribStat(tree.body, env); result = null; @@ -2054,7 +2051,7 @@ public class Attr extends JCTree.Visitor { if (close.kind == MTH && close.overrides(syms.autoCloseableClose, resource.tsym, types, true) && chk.isHandled(syms.interruptedExceptionType, types.memberType(resource, close).getThrownTypes())) { - env.info.lint.logIfEnabled(pos, LintWarnings.TryResourceThrowsInterruptedExc(resource)); + log.warning(pos, LintWarnings.TryResourceThrowsInterruptedExc(resource)); } } } @@ -4226,9 +4223,9 @@ public class Attr extends JCTree.Visitor { setSyntheticVariableType(tree.var, type == Type.noType ? syms.errType : type); } - annotate.annotateLater(tree.var.mods.annotations, env, v, tree.var); + annotate.annotateLater(tree.var.mods.annotations, env, v); if (!tree.var.isImplicitlyTyped()) { - annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v, tree.var); + annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v); } annotate.flush(); result = tree.type; @@ -4466,7 +4463,7 @@ public class Attr extends JCTree.Visitor { sym.kind == MTH && sym.name.equals(names.close) && sym.overrides(syms.autoCloseableClose, sitesym.type.tsym, types, true)) { - env.info.lint.logIfEnabled(tree, LintWarnings.TryExplicitCloseCall); + log.warning(tree, LintWarnings.TryExplicitCloseCall); } // Disallow selecting a type from an expression @@ -4493,9 +4490,9 @@ public class Attr extends JCTree.Visitor { // If the qualified item is not a type and the selected item is static, report // a warning. Make allowance for the class of an array type e.g. Object[].class) if (!sym.owner.isAnonymous()) { - chk.lint.logIfEnabled(tree, LintWarnings.StaticNotQualifiedByType(sym.kind.kindName(), sym.owner)); + log.warning(tree, LintWarnings.StaticNotQualifiedByType(sym.kind.kindName(), sym.owner)); } else { - chk.lint.logIfEnabled(tree, LintWarnings.StaticNotQualifiedByType2(sym.kind.kindName())); + log.warning(tree, LintWarnings.StaticNotQualifiedByType2(sym.kind.kindName())); } } @@ -5297,6 +5294,9 @@ public class Attr extends JCTree.Visitor { } annotate.flush(); + + // Now that this tree is attributed, we can calculate the Lint configuration everywhere within it + lintMapper.calculateLints(env.toplevel.sourcefile, env.tree, env.toplevel.endPositions); } public void attribPackage(DiagnosticPosition pos, PackageSymbol p) { @@ -5339,7 +5339,6 @@ public class Attr extends JCTree.Visitor { JavaFileObject prev = log.useSource(env.toplevel.sourcefile); try { - deferredLintHandler.flush(env.tree, lint); attrib.accept(env); } finally { log.useSource(prev); @@ -5523,7 +5522,6 @@ public class Attr extends JCTree.Visitor { } } - deferredLintHandler.flush(env.tree, env.info.lint); env.info.returnResult = null; // java.lang.Enum may not be subclassed by a non-enum if (st.tsym == syms.enumSym && @@ -5569,11 +5567,9 @@ public class Attr extends JCTree.Visitor { ModuleSymbol msym = tree.sym; Lint lint = env.outer.info.lint = env.outer.info.lint.augment(msym); Lint prevLint = chk.setLint(lint); - chk.checkModuleName(tree); - chk.checkDeprecatedAnnotation(tree, msym); - try { - deferredLintHandler.flush(tree, lint); + chk.checkModuleName(tree); + chk.checkDeprecatedAnnotation(tree, msym); } finally { chk.setLint(prevLint); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index ec328f391f9..3d9eff107da 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -121,7 +121,7 @@ public class Check { // The set of lint options currently in effect. It is initialized // from the context, and then is set/reset as needed by Attr as it // visits all the various parts of the trees during attribution. - Lint lint; + private Lint lint; // The method being analyzed in Attr - it is set/reset as needed by // Attr as it visits new method declarations. @@ -164,8 +164,6 @@ public class Check { profile = Profile.instance(context); preview = Preview.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); - allowModules = Feature.MODULES.allowedInSource(source); allowRecords = Feature.RECORDS.allowedInSource(source); allowSealed = Feature.SEALED_CLASSES.allowedInSource(source); @@ -180,10 +178,6 @@ public class Check { */ private Map,ClassSymbol> compiled = new HashMap<>(); - /** A handler for deferred lint warnings. - */ - private DeferredLintHandler deferredLintHandler; - /** Are modules allowed */ private final boolean allowModules; @@ -229,24 +223,15 @@ public class Check { * @param sym The deprecated symbol. */ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { - LintWarning warningKey = null; - if (sym.isDeprecatedForRemoval()) { - if (!lint.isSuppressed(LintCategory.REMOVAL)) { - if (sym.kind == MDL) { - warningKey = LintWarnings.HasBeenDeprecatedForRemovalModule(sym); - } else { - warningKey = LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location()); - } - } - } else if (!lint.isSuppressed(LintCategory.DEPRECATION)) { - if (sym.kind == MDL) { - warningKey = LintWarnings.HasBeenDeprecatedModule(sym); - } else { - warningKey = LintWarnings.HasBeenDeprecated(sym, sym.location()); - } - } - if (warningKey != null) - log.warning(pos, warningKey); + Assert.check(!importSuppression); + LintWarning warningKey = sym.isDeprecatedForRemoval() ? + (sym.kind == MDL ? + LintWarnings.HasBeenDeprecatedForRemovalModule(sym) : + LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location())) : + (sym.kind == MDL ? + LintWarnings.HasBeenDeprecatedModule(sym) : + LintWarnings.HasBeenDeprecated(sym, sym.location())); + log.warning(pos, warningKey); } /** Log a preview warning. @@ -254,25 +239,16 @@ public class Check { * @param msg A Warning describing the problem. */ public void warnPreviewAPI(DiagnosticPosition pos, LintWarning warnKey) { - if (!importSuppression && !lint.isSuppressed(LintCategory.PREVIEW)) + if (!importSuppression) log.warning(pos, warnKey); } - /** Log a preview warning. - * @param pos Position to be used for error reporting. - * @param msg A Warning describing the problem. - */ - public void warnRestrictedAPI(DiagnosticPosition pos, Symbol sym) { - lint.logIfEnabled(pos, LintWarnings.RestrictedMethod(sym.enclClass(), sym)); - } - /** Warn about unchecked operation. * @param pos Position to be used for error reporting. * @param msg A string describing the problem. */ public void warnUnchecked(DiagnosticPosition pos, LintWarning warnKey) { - if (!lint.isSuppressed(LintCategory.UNCHECKED)) - log.warning(pos, warnKey); + log.warning(pos, warnKey); } /** Report a failure to complete a class. @@ -608,9 +584,7 @@ public class Check { && types.isSameType(tree.expr.type, tree.clazz.type) && !(ignoreAnnotatedCasts && TreeInfo.containsTypeAnnotation(tree.clazz)) && !is292targetTypeCast(tree)) { - deferredLintHandler.report(_l -> { - lint.logIfEnabled(tree.pos(), LintWarnings.RedundantCast(tree.clazz.type)); - }); + log.warning(tree.pos(), LintWarnings.RedundantCast(tree.clazz.type)); } } //where @@ -914,7 +888,7 @@ public class Check { } } else if (hasTrustMeAnno && varargElemType != null && types.isReifiable(varargElemType)) { - lint.logIfEnabled(tree, LintWarnings.VarargsRedundantTrustmeAnno( + log.warning(tree.pos(), LintWarnings.VarargsRedundantTrustmeAnno( syms.trustMeType.tsym, diags.fragment(Fragments.VarargsTrustmeOnReifiableVarargs(varargElemType)))); } @@ -1173,7 +1147,7 @@ public class Check { mask = MethodFlags; } if ((flags & STRICTFP) != 0) { - warnOnExplicitStrictfp(tree); + log.warning(tree.pos(), LintWarnings.Strictfp); } // Imply STRICTFP if owner has STRICTFP set. if (((flags|implicit) & Flags.ABSTRACT) == 0 || @@ -1217,7 +1191,7 @@ public class Check { implicit |= FINAL; } if ((flags & STRICTFP) != 0) { - warnOnExplicitStrictfp(tree); + log.warning(tree.pos(), LintWarnings.Strictfp); } // Imply STRICTFP if owner has STRICTFP set. implicit |= sym.owner.flags_field & STRICTFP; @@ -1281,16 +1255,6 @@ public class Check { return flags & (mask | ~ExtendedStandardFlags) | implicit; } - private void warnOnExplicitStrictfp(JCTree tree) { - deferredLintHandler.push(tree); - try { - deferredLintHandler.report(_ -> lint.logIfEnabled(tree.pos(), LintWarnings.Strictfp)); - } finally { - deferredLintHandler.pop(); - } - } - - /** Determine if this enum should be implicitly final. * * If the enum has no specialized enum constants, it is final. @@ -1503,7 +1467,7 @@ public class Check { !TreeInfo.isDiamond(tree) && !withinAnonConstr(env) && tree.type.isRaw()) { - lint.logIfEnabled(tree.pos(), LintWarnings.RawClassUse(tree.type, tree.type.tsym.type)); + log.warning(tree.pos(), LintWarnings.RawClassUse(tree.type, tree.type.tsym.type)); } } //where @@ -1827,7 +1791,7 @@ public class Check { // Optional warning if varargs don't agree if ((((m.flags() ^ other.flags()) & Flags.VARARGS) != 0)) { - lint.logIfEnabled(TreeInfo.diagnosticPositionFor(m, tree), + log.warning(TreeInfo.diagnosticPositionFor(m, tree), ((m.flags() & Flags.VARARGS) != 0) ? LintWarnings.OverrideVarargsMissing(varargsOverrides(m, other)) : LintWarnings.OverrideVarargsExtra(varargsOverrides(m, other))); @@ -1841,12 +1805,7 @@ public class Check { // Warn if a deprecated method overridden by a non-deprecated one. if (!isDeprecatedOverrideIgnorable(other, origin)) { - Lint prevLint = setLint(lint.augment(m)); - try { - checkDeprecated(() -> TreeInfo.diagnosticPositionFor(m, tree), m, other); - } finally { - setLint(prevLint); - } + checkDeprecated(() -> TreeInfo.diagnosticPositionFor(m, tree), m, other); } } // where @@ -2915,42 +2874,33 @@ public class Check { // Apply special flag "-XDwarnOnAccessToMembers" which turns on just this particular warning for all types of access void checkAccessFromSerializableElement(final JCTree tree, boolean isLambda) { - final Lint prevLint = setLint(warnOnAnyAccessToMembers ? lint.enable(LintCategory.SERIAL) : lint); - try { - if (warnOnAnyAccessToMembers || isLambda) - checkAccessFromSerializableElementInner(tree, isLambda); - } finally { - setLint(prevLint); - } + if (warnOnAnyAccessToMembers || isLambda) + checkAccessFromSerializableElementInner(tree, isLambda); } private void checkAccessFromSerializableElementInner(final JCTree tree, boolean isLambda) { - if (lint.isEnabled(LintCategory.SERIAL)) { - Symbol sym = TreeInfo.symbol(tree); - if (!sym.kind.matches(KindSelector.VAL_MTH)) { + Symbol sym = TreeInfo.symbol(tree); + if (!sym.kind.matches(KindSelector.VAL_MTH)) { + return; + } + + if (sym.kind == VAR) { + if ((sym.flags() & PARAMETER) != 0 || + sym.isDirectlyOrIndirectlyLocal() || + sym.name == names._this || + sym.name == names._super) { return; } + } - if (sym.kind == VAR) { - if ((sym.flags() & PARAMETER) != 0 || - sym.isDirectlyOrIndirectlyLocal() || - sym.name == names._this || - sym.name == names._super) { - return; - } - } - - if (!types.isSubtype(sym.owner.type, syms.serializableType) && - isEffectivelyNonPublic(sym)) { - if (isLambda) { - if (belongsToRestrictedPackage(sym)) { - log.warning(tree.pos(), - LintWarnings.AccessToMemberFromSerializableLambda(sym)); - } - } else { - log.warning(tree.pos(), - LintWarnings.AccessToMemberFromSerializableElement(sym)); + if (!types.isSubtype(sym.owner.type, syms.serializableType) && isEffectivelyNonPublic(sym)) { + DiagnosticFlag flag = warnOnAnyAccessToMembers ? DiagnosticFlag.DEFAULT_ENABLED : null; + if (isLambda) { + if (belongsToRestrictedPackage(sym)) { + log.warning(flag, tree.pos(), LintWarnings.AccessToMemberFromSerializableLambda(sym)); } + } else { + log.warning(flag, tree.pos(), LintWarnings.AccessToMemberFromSerializableElement(sym)); } } } @@ -3737,8 +3687,7 @@ public class Check { // Note: @Deprecated has no effect on local variables, parameters and package decls. if (lint.isEnabled(LintCategory.DEPRECATION) && !s.isDeprecatableViaAnnotation()) { if (!syms.deprecatedType.isErroneous() && s.attribute(syms.deprecatedType.tsym) != null) { - log.warning(pos, - LintWarnings.DeprecatedAnnotationHasNoEffect(Kinds.kindName(s))); + log.warning(pos, LintWarnings.DeprecatedAnnotationHasNoEffect(Kinds.kindName(s))); } } } @@ -3752,15 +3701,13 @@ public class Check { && (s.isDeprecatedForRemoval() || s.isDeprecated() && !other.isDeprecated()) && (s.outermostClass() != other.outermostClass() || s.outermostClass() == null) && s.kind != Kind.PCK) { - deferredLintHandler.report(_l -> warnDeprecated(pos.get(), s)); + warnDeprecated(pos.get(), s); } } void checkSunAPI(final DiagnosticPosition pos, final Symbol s) { if ((s.flags() & PROPRIETARY) != 0) { - deferredLintHandler.report(_l -> { - log.warning(pos, Warnings.SunProprietary(s)); - }); + log.warning(pos, Warnings.SunProprietary(s)); } } @@ -3817,7 +3764,7 @@ public class Check { void checkRestricted(DiagnosticPosition pos, Symbol s) { if (s.kind == MTH && (s.flags() & RESTRICTED) != 0) { - deferredLintHandler.report(_l -> warnRestrictedAPI(pos, s)); + log.warning(pos, LintWarnings.RestrictedMethod(s.enclClass(), s)); } } @@ -4089,7 +4036,7 @@ public class Check { int opc = ((OperatorSymbol)operator).opcode; if (opc == ByteCodes.idiv || opc == ByteCodes.imod || opc == ByteCodes.ldiv || opc == ByteCodes.lmod) { - deferredLintHandler.report(_ -> lint.logIfEnabled(pos, LintWarnings.DivZero)); + log.warning(pos, LintWarnings.DivZero); } } } @@ -4102,8 +4049,7 @@ public class Check { */ void checkLossOfPrecision(final DiagnosticPosition pos, Type found, Type req) { if (found.isNumeric() && req.isNumeric() && !types.isAssignable(found, req)) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.PossibleLossOfPrecision(found, req))); + log.warning(pos, LintWarnings.PossibleLossOfPrecision(found, req)); } } @@ -4112,7 +4058,7 @@ public class Check { */ void checkEmptyIf(JCIf tree) { if (tree.thenpart.hasTag(SKIP) && tree.elsepart == null) { - lint.logIfEnabled(tree.thenpart.pos(), LintWarnings.EmptyIf); + log.warning(tree.thenpart.pos(), LintWarnings.EmptyIf); } } @@ -4259,8 +4205,7 @@ public class Check { rs.isAccessible(env, c) && !fileManager.isSameFile(c.sourcefile, env.toplevel.sourcefile)) { - lint.logIfEnabled(pos, - LintWarnings.AuxiliaryClassAccessedFromOutsideOfItsSourceFile(c, c.sourcefile)); + log.warning(pos, LintWarnings.AuxiliaryClassAccessedFromOutsideOfItsSourceFile(c, c.sourcefile)); } } @@ -4302,8 +4247,7 @@ public class Check { // Warning may be suppressed by // annotations; check again for being // enabled in the deferred context. - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.MissingExplicitCtor(c, pkg, modle))); + log.warning(pos, LintWarnings.MissingExplicitCtor(c, pkg, modle)); } else { return; } @@ -4339,7 +4283,7 @@ public class Check { method.attribute(syms.trustMeType.tsym) != null && isTrustMeAllowedOnMethod(method) && !types.isReifiable(method.type.getParameterTypes().last())) { - Check.this.lint.logIfEnabled(pos(), LintWarnings.VarargsUnsafeUseVarargsParam(method.params.last())); + log.warning(pos(), LintWarnings.VarargsUnsafeUseVarargsParam(method.params.last())); } break; default: @@ -4637,28 +4581,24 @@ public class Check { void checkModuleExists(final DiagnosticPosition pos, ModuleSymbol msym) { if (msym.kind != MDL) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.ModuleNotFound(msym))); + log.warning(pos, LintWarnings.ModuleNotFound(msym)); } } void checkPackageExistsForOpens(final DiagnosticPosition pos, PackageSymbol packge) { if (packge.members().isEmpty() && ((packge.flags() & Flags.HAS_RESOURCE) == 0)) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.PackageEmptyOrNotFound(packge))); + log.warning(pos, LintWarnings.PackageEmptyOrNotFound(packge)); } } void checkModuleRequires(final DiagnosticPosition pos, final RequiresDirective rd) { if ((rd.module.flags() & Flags.AUTOMATIC_MODULE) != 0) { - deferredLintHandler.report(_ -> { - if (rd.isTransitive() && lint.isEnabled(LintCategory.REQUIRES_TRANSITIVE_AUTOMATIC)) { - log.warning(pos, LintWarnings.RequiresTransitiveAutomatic); - } else { - lint.logIfEnabled(pos, LintWarnings.RequiresAutomatic); - } - }); + if (rd.isTransitive()) { // see comment in Log.applyLint() for special logic that applies + log.warning(pos, LintWarnings.RequiresTransitiveAutomatic); + } else { + log.warning(pos, LintWarnings.RequiresAutomatic); + } } } @@ -5693,14 +5633,14 @@ public class Check { VarSymbol lastParam = ms.params.head; for (VarSymbol param: ms.params) { if ((param.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + log.warning(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } lastParam = param; argExps = argExps.tail; } while (argExps != null && !argExps.isEmpty() && lastParam != null) { if ((lastParam.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) { - lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + log.warning(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); } argExps = argExps.tail; } @@ -5727,7 +5667,7 @@ public class Check { // we need to avoid recursion due to self referencing type vars or captures, this is why we need a set requiresIdentityVisitor.visit(t, new HashSet<>()); if (requiresIdentityVisitor.requiresWarning) { - lint.logIfEnabled(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); + log.warning(pos, LintWarnings.AttemptToUseValueBasedWhereIdentityExpected); return true; } } @@ -5804,7 +5744,7 @@ public class Check { .filter(ta -> isRequiresIdentityAnnotation(ta.type.tsym) && typeParamTrees.get(ta.position.parameter_index).type != null && typeParamTrees.get(ta.position.parameter_index).type.isValueBased()) - .forEach(ta -> lint.logIfEnabled(typeParamTrees.get(ta.position.parameter_index).pos(), + .forEach(ta -> log.warning(typeParamTrees.get(ta.position.parameter_index).pos(), CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected)); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index e685f139b68..3bbd007c66a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -214,7 +214,6 @@ public class Flow { private final Resolve rs; private final JCDiagnostic.Factory diags; private Env attrEnv; - private Lint lint; private final Infer infer; public static Flow instance(Context context) { @@ -337,7 +336,6 @@ public class Flow { syms = Symtab.instance(context); types = Types.instance(context); chk = Check.instance(context); - lint = Lint.instance(context); infer = Infer.instance(context); rs = Resolve.instance(context); diags = JCDiagnostic.Factory.instance(context); @@ -562,10 +560,8 @@ public class Flow { if (tree.sym == null) return; Liveness alivePrev = alive; ListBuffer pendingExitsPrev = pendingExits; - Lint lintPrev = lint; pendingExits = new ListBuffer<>(); - lint = lint.augment(tree.sym); try { // process all the nested classes @@ -596,30 +592,22 @@ public class Flow { } finally { pendingExits = pendingExitsPrev; alive = alivePrev; - lint = lintPrev; } } public void visitMethodDef(JCMethodDecl tree) { if (tree.body == null) return; - Lint lintPrev = lint; - - lint = lint.augment(tree.sym); Assert.check(pendingExits.isEmpty()); - try { - alive = Liveness.ALIVE; - scanStat(tree.body); - tree.completesNormally = alive != Liveness.DEAD; + alive = Liveness.ALIVE; + scanStat(tree.body); + tree.completesNormally = alive != Liveness.DEAD; - if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) - log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); + if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) + log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); - clearPendingExits(true); - } finally { - lint = lintPrev; - } + clearPendingExits(true); } private void clearPendingExits(boolean inMethod) { @@ -634,15 +622,7 @@ public class Flow { } public void visitVarDef(JCVariableDecl tree) { - if (tree.init != null) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - scan(tree.init); - } finally { - lint = lintPrev; - } - } + scan(tree.init); } public void visitBlock(JCBlock tree) { @@ -724,8 +704,7 @@ public class Flow { // Warn about fall-through if lint switch fallthrough enabled. if (alive == Liveness.ALIVE && c.stats.nonEmpty() && l.tail.nonEmpty()) - lint.logIfEnabled(l.tail.head.pos(), - LintWarnings.PossibleFallThroughIntoCase); + log.warning(l.tail.head.pos(), LintWarnings.PossibleFallThroughIntoCase); } tree.isExhaustive = tree.hasUnconditionalPattern || TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases); @@ -1232,7 +1211,7 @@ public class Flow { scanStat(tree.finalizer); tree.finallyCanCompleteNormally = alive != Liveness.DEAD; if (alive == Liveness.DEAD) { - lint.logIfEnabled(TreeInfo.diagEndPos(tree.finalizer), + log.warning(TreeInfo.diagEndPos(tree.finalizer), LintWarnings.FinallyCannotComplete); } else { while (exits.nonEmpty()) { @@ -1453,7 +1432,6 @@ public class Flow { List thrownPrev = thrown; List caughtPrev = caught; ListBuffer pendingExitsPrev = pendingExits; - Lint lintPrev = lint; boolean anonymousClass = tree.name == names.empty; pendingExits = new ListBuffer<>(); if (!anonymousClass) { @@ -1461,7 +1439,6 @@ public class Flow { } classDef = tree; thrown = List.nil(); - lint = lint.augment(tree.sym); try { // process all the nested classes @@ -1510,7 +1487,6 @@ public class Flow { pendingExits = pendingExitsPrev; caught = caughtPrev; classDef = classDefPrev; - lint = lintPrev; } } @@ -1519,9 +1495,6 @@ public class Flow { List caughtPrev = caught; List mthrown = tree.sym.type.getThrownTypes(); - Lint lintPrev = lint; - - lint = lint.augment(tree.sym); Assert.check(pendingExits.isEmpty()); @@ -1554,20 +1527,11 @@ public class Flow { } } finally { caught = caughtPrev; - lint = lintPrev; } } public void visitVarDef(JCVariableDecl tree) { - if (tree.init != null) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - scan(tree.init); - } finally { - lint = lintPrev; - } - } + scan(tree.init); } public void visitBlock(JCBlock tree) { @@ -2387,82 +2351,76 @@ public class Flow { return; } - Lint lintPrev = lint; - lint = lint.augment(tree.sym); + JCClassDecl classDefPrev = classDef; + int firstadrPrev = firstadr; + int nextadrPrev = nextadr; + ListBuffer pendingExitsPrev = pendingExits; + + pendingExits = new ListBuffer<>(); + if (tree.name != names.empty) { + firstadr = nextadr; + } + classDef = tree; try { - JCClassDecl classDefPrev = classDef; - int firstadrPrev = firstadr; - int nextadrPrev = nextadr; - ListBuffer pendingExitsPrev = pendingExits; - - pendingExits = new ListBuffer<>(); - if (tree.name != names.empty) { - firstadr = nextadr; + // define all the static fields + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(VARDEF)) { + JCVariableDecl def = (JCVariableDecl)l.head; + if ((def.mods.flags & STATIC) != 0) { + VarSymbol sym = def.sym; + if (trackable(sym)) { + newVar(def); + } + } + } } - classDef = tree; - try { - // define all the static fields - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(VARDEF)) { - JCVariableDecl def = (JCVariableDecl)l.head; - if ((def.mods.flags & STATIC) != 0) { - VarSymbol sym = def.sym; - if (trackable(sym)) { - newVar(def); - } + + // process all the static initializers + forEachInitializer(tree, true, def -> { + scan(def); + clearPendingExits(false); + }); + + // verify all static final fields got initialized + for (int i = firstadr; i < nextadr; i++) { + JCVariableDecl vardecl = vardecls[i]; + VarSymbol var = vardecl.sym; + if (var.owner == classDef.sym && var.isStatic()) { + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); + } + } + + // define all the instance fields + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(VARDEF)) { + JCVariableDecl def = (JCVariableDecl)l.head; + if ((def.mods.flags & STATIC) == 0) { + VarSymbol sym = def.sym; + if (trackable(sym)) { + newVar(def); } } } + } - // process all the static initializers - forEachInitializer(tree, true, def -> { - scan(def); - clearPendingExits(false); - }); - - // verify all static final fields got initialized - for (int i = firstadr; i < nextadr; i++) { - JCVariableDecl vardecl = vardecls[i]; - VarSymbol var = vardecl.sym; - if (var.owner == classDef.sym && var.isStatic()) { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); - } + // process all the methods + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(METHODDEF)) { + scan(l.head); } + } - // define all the instance fields - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(VARDEF)) { - JCVariableDecl def = (JCVariableDecl)l.head; - if ((def.mods.flags & STATIC) == 0) { - VarSymbol sym = def.sym; - if (trackable(sym)) { - newVar(def); - } - } - } + // process all the nested classes + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(CLASSDEF)) { + scan(l.head); } - - // process all the methods - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(METHODDEF)) { - scan(l.head); - } - } - - // process all the nested classes - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(CLASSDEF)) { - scan(l.head); - } - } - } finally { - pendingExits = pendingExitsPrev; - nextadr = nextadrPrev; - firstadr = firstadrPrev; - classDef = classDefPrev; } } finally { - lint = lintPrev; + pendingExits = pendingExitsPrev; + nextadr = nextadrPrev; + firstadr = firstadrPrev; + classDef = classDefPrev; } } @@ -2477,87 +2435,81 @@ public class Flow { return; } - Lint lintPrev = lint; - lint = lint.augment(tree.sym); + final Bits initsPrev = new Bits(inits); + final Bits uninitsPrev = new Bits(uninits); + int nextadrPrev = nextadr; + int firstadrPrev = firstadr; + int returnadrPrev = returnadr; + + Assert.check(pendingExits.isEmpty()); + boolean isConstructorPrev = isConstructor; try { - final Bits initsPrev = new Bits(inits); - final Bits uninitsPrev = new Bits(uninits); - int nextadrPrev = nextadr; - int firstadrPrev = firstadr; - int returnadrPrev = returnadr; + isConstructor = TreeInfo.isConstructor(tree); - Assert.check(pendingExits.isEmpty()); - boolean isConstructorPrev = isConstructor; - try { - isConstructor = TreeInfo.isConstructor(tree); + // We only track field initialization inside constructors + if (!isConstructor) { + firstadr = nextadr; + } - // We only track field initialization inside constructors - if (!isConstructor) { - firstadr = nextadr; - } + // Mark all method parameters as DA + for (List l = tree.params; l.nonEmpty(); l = l.tail) { + JCVariableDecl def = l.head; + scan(def); + Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); + /* If we are executing the code from Gen, then there can be + * synthetic or mandated variables, ignore them. + */ + initParam(def); + } + // else we are in an instance initializer block; + // leave caught unchanged. + scan(tree.body); - // Mark all method parameters as DA - for (List l = tree.params; l.nonEmpty(); l = l.tail) { - JCVariableDecl def = l.head; - scan(def); - Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); - /* If we are executing the code from Gen, then there can be - * synthetic or mandated variables, ignore them. - */ - initParam(def); - } - // else we are in an instance initializer block; - // leave caught unchanged. - scan(tree.body); - - boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || - (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); - if (isConstructor) { - boolean isSynthesized = (tree.sym.flags() & - GENERATEDCONSTR) != 0; - for (int i = firstadr; i < nextadr; i++) { - JCVariableDecl vardecl = vardecls[i]; - VarSymbol var = vardecl.sym; - if (var.owner == classDef.sym && !var.isStatic()) { - // choose the diagnostic position based on whether - // the ctor is default(synthesized) or not - if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), - var, Errors.VarNotInitializedInDefaultConstructor(var)); - } else if (isCompactOrGeneratedRecordConstructor) { - boolean isInstanceRecordField = var.enclClass().isRecord() && - (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && - var.owner.kind == TYP; - if (isInstanceRecordField) { - boolean notInitialized = !inits.isMember(var.adr); - if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { - /* this way we indicate Lower that it should generate an initialization for this field - * in the compact constructor - */ - var.flags_field |= UNINITIALIZED_FIELD; - } else { - checkInit(TreeInfo.diagEndPos(tree.body), var); - } + boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || + (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); + if (isConstructor) { + boolean isSynthesized = (tree.sym.flags() & + GENERATEDCONSTR) != 0; + for (int i = firstadr; i < nextadr; i++) { + JCVariableDecl vardecl = vardecls[i]; + VarSymbol var = vardecl.sym; + if (var.owner == classDef.sym && !var.isStatic()) { + // choose the diagnostic position based on whether + // the ctor is default(synthesized) or not + if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), + var, Errors.VarNotInitializedInDefaultConstructor(var)); + } else if (isCompactOrGeneratedRecordConstructor) { + boolean isInstanceRecordField = var.enclClass().isRecord() && + (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && + var.owner.kind == TYP; + if (isInstanceRecordField) { + boolean notInitialized = !inits.isMember(var.adr); + if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { + /* this way we indicate Lower that it should generate an initialization for this field + * in the compact constructor + */ + var.flags_field |= UNINITIALIZED_FIELD; } else { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); + checkInit(TreeInfo.diagEndPos(tree.body), var); } } else { - checkInit(TreeInfo.diagEndPos(tree.body), var); + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); } + } else { + checkInit(TreeInfo.diagEndPos(tree.body), var); } } } - clearPendingExits(true); - } finally { - inits.assign(initsPrev); - uninits.assign(uninitsPrev); - nextadr = nextadrPrev; - firstadr = firstadrPrev; - returnadr = returnadrPrev; - isConstructor = isConstructorPrev; } + clearPendingExits(true); } finally { - lint = lintPrev; + inits.assign(initsPrev); + uninits.assign(uninitsPrev); + nextadr = nextadrPrev; + firstadr = firstadrPrev; + returnadr = returnadrPrev; + isConstructor = isConstructorPrev; } } @@ -2585,21 +2537,15 @@ public class Flow { } public void visitVarDef(JCVariableDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - boolean track = trackable(tree.sym); - if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { - newVar(tree); + boolean track = trackable(tree.sym); + if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { + newVar(tree); + } + if (tree.init != null) { + scanExpr(tree.init); + if (track) { + letInit(tree.pos(), tree.sym); } - if (tree.init != null) { - scanExpr(tree.init); - if (track) { - letInit(tree.pos(), tree.sym); - } - } - } finally { - lint = lintPrev; } } @@ -2851,8 +2797,7 @@ public class Flow { final Bits uninitsEnd = new Bits(uninits); int nextadrCatch = nextadr; - if (!resourceVarDecls.isEmpty() && - lint.isEnabled(Lint.LintCategory.TRY)) { + if (!resourceVarDecls.isEmpty()) { for (JCVariableDecl resVar : resourceVarDecls) { if (unrefdResources.includes(resVar.sym) && !resVar.sym.isUnnamedVariable()) { log.warning(resVar.pos(), diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java index b726cc7a61d..d63ba1677d6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java @@ -66,7 +66,6 @@ public class MemberEnter extends JCTree.Visitor { private final Annotate annotate; private final Types types; private final Names names; - private final DeferredLintHandler deferredLintHandler; public static MemberEnter instance(Context context) { MemberEnter instance = context.get(memberEnterKey); @@ -87,7 +86,6 @@ public class MemberEnter extends JCTree.Visitor { types = Types.instance(context); source = Source.instance(context); names = Names.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); } /** Construct method type from method signature. @@ -194,16 +192,11 @@ public class MemberEnter extends JCTree.Visitor { } Env localEnv = methodEnv(tree, env); - deferredLintHandler.push(tree); - try { - // Compute the method type - m.type = signature(m, tree.typarams, tree.params, - tree.restype, tree.recvparam, - tree.thrown, - localEnv); - } finally { - deferredLintHandler.pop(); - } + // Compute the method type + m.type = signature(m, tree.typarams, tree.params, + tree.restype, tree.recvparam, + tree.thrown, + localEnv); if (types.isSignaturePolymorphic(m)) { m.flags_field |= SIGNATURE_POLYMORPHIC; @@ -227,14 +220,14 @@ public class MemberEnter extends JCTree.Visitor { enclScope.enter(m); } - annotate.annotateLater(tree.mods.annotations, localEnv, m, tree); + annotate.annotateLater(tree.mods.annotations, localEnv, m); // Visit the signature of the method. Note that // TypeAnnotate doesn't descend into the body. - annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, m, tree); + annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, m); if (tree.defaultValue != null) { m.defaultValue = annotate.unfinishedDefaultValue(); // set it to temporary sentinel for now - annotate.annotateDefaultValueLater(tree.defaultValue, localEnv, m, tree); + annotate.annotateDefaultValueLater(tree.defaultValue, localEnv, m); } } @@ -263,18 +256,13 @@ public class MemberEnter extends JCTree.Visitor { localEnv = env.dup(tree, env.info.dup()); localEnv.info.staticLevel++; } - deferredLintHandler.push(tree); - try { - if (TreeInfo.isEnumInit(tree)) { - attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); - } else if (!tree.isImplicitlyTyped()) { - attr.attribType(tree.vartype, localEnv); - if (TreeInfo.isReceiverParam(tree)) - checkReceiver(tree, localEnv); - } - } finally { - deferredLintHandler.pop(); + if (TreeInfo.isEnumInit(tree)) { + attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); + } else if (!tree.isImplicitlyTyped()) { + attr.attribType(tree.vartype, localEnv); + if (TreeInfo.isReceiverParam(tree)) + checkReceiver(tree, localEnv); } if ((tree.mods.flags & VARARGS) != 0) { @@ -315,9 +303,9 @@ public class MemberEnter extends JCTree.Visitor { } } - annotate.annotateLater(tree.mods.annotations, localEnv, v, tree); + annotate.annotateLater(tree.mods.annotations, localEnv, v); if (!tree.isImplicitlyTyped()) { - annotate.queueScanTreeAndTypeAnnotate(tree.vartype, localEnv, v, tree); + annotate.queueScanTreeAndTypeAnnotate(tree.vartype, localEnv, v); } v.pos = tree.pos; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 4d0af014d83..0cef9cc6602 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -52,7 +52,6 @@ import javax.tools.JavaFileObject.Kind; import javax.tools.StandardLocation; import com.sun.source.tree.ModuleTree.ModuleKind; -import com.sun.tools.javac.code.DeferredLintHandler; import com.sun.tools.javac.code.Directive; import com.sun.tools.javac.code.Directive.ExportsDirective; import com.sun.tools.javac.code.Directive.ExportsFlag; @@ -141,7 +140,6 @@ public class Modules extends JCTree.Visitor { private final Attr attr; private final Check chk; private final Preview preview; - private final DeferredLintHandler deferredLintHandler; private final TypeEnvs typeEnvs; private final Types types; private final JavaFileManager fileManager; @@ -169,8 +167,6 @@ public class Modules extends JCTree.Visitor { private final String moduleVersionOpt; private final boolean sourceLauncher; - private final boolean lintOptions; - private Set rootModules = null; private final Set warnedMissing = new HashSet<>(); @@ -193,7 +189,6 @@ public class Modules extends JCTree.Visitor { attr = Attr.instance(context); chk = Check.instance(context); preview = Preview.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); moduleFinder = ModuleFinder.instance(context); types = Types.instance(context); @@ -205,8 +200,6 @@ public class Modules extends JCTree.Visitor { allowAccessIntoSystem = options.isUnset(Option.RELEASE); - lintOptions = !options.isLintDisabled(LintCategory.OPTIONS); - multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); ClassWriter classWriter = ClassWriter.instance(context); classWriter.multiModuleMode = multiModuleMode; @@ -746,7 +739,6 @@ public class Modules extends JCTree.Visitor { ModuleVisitor v = new ModuleVisitor(); JavaFileObject prev = log.useSource(tree.sourcefile); JCModuleDecl moduleDecl = tree.getModuleDecl(); - deferredLintHandler.push(moduleDecl); try { moduleDecl.accept(v); @@ -754,7 +746,6 @@ public class Modules extends JCTree.Visitor { checkCyclicDependencies(moduleDecl); } finally { log.useSource(prev); - deferredLintHandler.pop(); msym.flags_field &= ~UNATTRIBUTED; } } @@ -991,13 +982,11 @@ public class Modules extends JCTree.Visitor { UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); JCModuleDecl decl = env.toplevel.getModuleDecl(); - deferredLintHandler.push(decl); try { decl.accept(v); } finally { log.useSource(prev); - deferredLintHandler.pop(); } }; } @@ -1263,12 +1252,9 @@ public class Modules extends JCTree.Visitor { } observable = computeTransitiveClosure(limitMods, rootModules, null); observable.addAll(rootModules); - if (lintOptions) { - for (ModuleSymbol msym : limitMods) { - if (!observable.contains(msym)) { - log.warning( - LintWarnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym)); - } + for (ModuleSymbol msym : limitMods) { + if (!observable.contains(msym)) { + log.warning(LintWarnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym)); } } } @@ -1721,10 +1707,7 @@ public class Modules extends JCTree.Visitor { } if (!unknownModules.contains(msym)) { - if (lintOptions) { - log.warning( - LintWarnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym)); unknownModules.add(msym); } return false; @@ -1760,9 +1743,7 @@ public class Modules extends JCTree.Visitor { ModuleSymbol msym = syms.enterModule(names.fromString(sourceName)); if (!allModules.contains(msym)) { - if (lintOptions) { - log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, msym)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, msym)); continue; } @@ -1780,9 +1761,7 @@ public class Modules extends JCTree.Visitor { continue; targetModule = syms.enterModule(names.fromString(targetName)); if (!allModules.contains(targetModule)) { - if (lintOptions) { - log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule)); continue; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 9119278660f..6fb1feed08d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -45,6 +45,7 @@ import java.util.stream.Stream; import com.sun.tools.javac.code.Directive; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; @@ -57,6 +58,7 @@ import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; @@ -67,6 +69,7 @@ import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.Lint.LintCategory.THIS_ESCAPE; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; +import static com.sun.tools.javac.util.Position.NOPOS; /** * Looks for possible 'this' escapes and generates corresponding warnings. @@ -156,7 +159,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { private final Types types; private final Resolve rs; private final Log log; - private Lint lint; + private final LintMapper lintMapper; // These fields are scoped to the entire compilation unit @@ -168,10 +171,6 @@ public class ThisEscapeAnalyzer extends TreeScanner { */ private final Map methodMap = new LinkedHashMap<>(); - /** Contains symbols of fields and constructors that have warnings suppressed. - */ - private final Set suppressed = new HashSet<>(); - /** Contains classes whose outer instance (if any) is non-public. */ private final Set nonPublicOuters = new HashSet<>(); @@ -231,7 +230,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { syms = Symtab.instance(context); types = Types.instance(context); rs = Resolve.instance(context); - lint = Lint.instance(context); + lintMapper = LintMapper.instance(context); } // @@ -262,8 +261,8 @@ public class ThisEscapeAnalyzer extends TreeScanner { Assert.check(checkInvariants(false, false)); Assert.check(methodMap.isEmpty()); // we are not prepared to be used more than once - // Short circuit if warnings are totally disabled - if (!lint.isEnabled(THIS_ESCAPE)) + // Short circuit if this calculation is unnecessary + if (!lintMapper.lintAt(env.toplevel.sourcefile, env.tree.pos()).get().isEnabled(THIS_ESCAPE)) return; // Determine which packages are exported by the containing module, if any. @@ -278,11 +277,9 @@ public class ThisEscapeAnalyzer extends TreeScanner { // Build a mapping from symbols of methods to their declarations. // Classify all ctors and methods as analyzable and/or invokable. - // Track which constructors and fields have warnings suppressed. // Record classes whose outer instance (if any) is non-public. new TreeScanner() { - private Lint lint = ThisEscapeAnalyzer.this.lint; private JCClassDecl currentClass; private boolean nonPublicOuter; @@ -290,8 +287,6 @@ public class ThisEscapeAnalyzer extends TreeScanner { public void visitClassDef(JCClassDecl tree) { JCClassDecl currentClassPrev = currentClass; boolean nonPublicOuterPrev = nonPublicOuter; - Lint lintPrev = lint; - lint = lint.augment(tree.sym); try { currentClass = tree; @@ -306,57 +301,29 @@ public class ThisEscapeAnalyzer extends TreeScanner { } finally { currentClass = currentClassPrev; nonPublicOuter = nonPublicOuterPrev; - lint = lintPrev; - } - } - - @Override - public void visitVarDef(JCVariableDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - - // Track warning suppression of fields - if (tree.sym.owner.kind == TYP && !lint.isEnabled(THIS_ESCAPE)) - suppressed.add(tree.sym); - - // Recurse - super.visitVarDef(tree); - } finally { - lint = lintPrev; } } @Override public void visitMethodDef(JCMethodDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - // Track warning suppression of constructors - if (TreeInfo.isConstructor(tree) && !lint.isEnabled(THIS_ESCAPE)) - suppressed.add(tree.sym); + // Gather some useful info + boolean constructor = TreeInfo.isConstructor(tree); + boolean extendableClass = currentClassIsExternallyExtendable(); + boolean nonPrivate = (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0; + boolean finalish = (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; - // Gather some useful info - boolean constructor = TreeInfo.isConstructor(tree); - boolean extendableClass = currentClassIsExternallyExtendable(); - boolean nonPrivate = (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0; - boolean finalish = (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; + // Determine if this is a constructor we should analyze + boolean analyzable = extendableClass && constructor && nonPrivate; - // Determine if this is a constructor we should analyze - boolean analyzable = extendableClass && constructor && nonPrivate; + // Determine if it's safe to "invoke" the method in an analysis (i.e., it can't be overridden) + boolean invokable = !extendableClass || constructor || finalish; - // Determine if it's safe to "invoke" the method in an analysis (i.e., it can't be overridden) - boolean invokable = !extendableClass || constructor || finalish; + // Add this method or constructor to our map + methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable)); - // Add this method or constructor to our map - methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable)); - - // Recurse - super.visitMethodDef(tree); - } finally { - lint = lintPrev; - } + // Recurse + super.visitMethodDef(tree); } // Determines if the current class could be extended in some other package/module @@ -401,7 +368,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { for (Warning warning : warningList) { LintWarning key = LintWarnings.PossibleThisEscape; for (StackFrame frame : warning.stack) { - log.warning(frame.site.pos(), key); + log.warning(frame.warningPos(), key); key = LintWarnings.PossibleThisEscapeLocation; } } @@ -1746,9 +1713,16 @@ public class ThisEscapeAnalyzer extends TreeScanner { this.suppressible = initializer != null || (method.constructor && method.declaringClass == targetClass); } + DiagnosticPosition warningPos() { + return site.pos().withLintPosition(NOPOS); // disable normal Lint suppression + } + + Lint lint() { + return lintMapper.lintAt(topLevelEnv.toplevel.sourcefile, site.pos()).get(); + } + boolean isSuppressed() { - return suppressible && - suppressed.contains(initializer instanceof JCVariableDecl v ? v.sym : method.declaration.sym); + return suppressible && !lint().isEnabled(THIS_ESCAPE); } int comparePos(StackFrame that) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index eb4ab96dfd2..8648b929a04 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -34,7 +34,6 @@ import javax.tools.JavaFileObject; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Directive.ExportsDirective; import com.sun.tools.javac.code.Directive.RequiresDirective; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.ImportFilter; import com.sun.tools.javac.code.Scope.NamedImportScope; import com.sun.tools.javac.code.Scope.StarImportScope; @@ -108,8 +107,6 @@ public class TypeEnter implements Completer { private final Annotate annotate; private final TypeAnnotations typeAnnotations; private final Types types; - private final DeferredLintHandler deferredLintHandler; - private final Lint lint; private final TypeEnvs typeEnvs; private final Dependencies dependencies; @@ -135,8 +132,6 @@ public class TypeEnter implements Completer { annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); types = Types.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); - lint = Lint.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); Source source = Source.instance(context); @@ -274,7 +269,6 @@ public class TypeEnter implements Completer { queue.add(env); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); - deferredLintHandler.push(tree); try { dependencies.push(env.enclClass.sym, phaseName); runPhase(env); @@ -282,7 +276,6 @@ public class TypeEnter implements Completer { chk.completionError(tree.pos(), ex); } finally { dependencies.pop(); - deferredLintHandler.pop(); log.useSource(prev); } } @@ -351,8 +344,6 @@ public class TypeEnter implements Completer { ImportFilter prevStaticImportFilter = staticImportFilter; ImportFilter prevTypeImportFilter = typeImportFilter; - deferredLintHandler.pushImmediate(lint); - Lint prevLint = chk.setLint(lint); Env prevEnv = this.env; try { this.env = env; @@ -376,20 +367,13 @@ public class TypeEnter implements Completer { handleImports(tree.getImports()); if (decl != null) { - deferredLintHandler.push(decl); - try { - //check @Deprecated: - markDeprecated(decl.sym, decl.mods.annotations, env); - } finally { - deferredLintHandler.pop(); - } + //check for @Deprecated annotations + markDeprecated(decl.sym, decl.mods.annotations, env); // process module annotations - annotate.annotateLater(decl.mods.annotations, env, env.toplevel.modle, decl); + annotate.annotateLater(decl.mods.annotations, env, env.toplevel.modle); } } finally { this.env = prevEnv; - chk.setLint(prevLint); - deferredLintHandler.pop(); this.staticImportFilter = prevStaticImportFilter; this.typeImportFilter = prevTypeImportFilter; } @@ -422,7 +406,7 @@ public class TypeEnter implements Completer { } } // process package annotations - annotate.annotateLater(tree.annotations, env, env.toplevel.packge, tree); + annotate.annotateLater(tree.annotations, env, env.toplevel.packge); } private void doImport(JCImport tree, boolean fromModuleImport) { @@ -914,9 +898,9 @@ public class TypeEnter implements Completer { Env baseEnv = baseEnv(tree, env); if (tree.extending != null) - annotate.queueScanTreeAndTypeAnnotate(tree.extending, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(tree.extending, baseEnv, sym); for (JCExpression impl : tree.implementing) - annotate.queueScanTreeAndTypeAnnotate(impl, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(impl, baseEnv, sym); annotate.flush(); attribSuperTypes(env, baseEnv); @@ -931,11 +915,11 @@ public class TypeEnter implements Completer { chk.checkNotRepeated(iface.pos(), types.erasure(it), interfaceSet); } - annotate.annotateLater(tree.mods.annotations, baseEnv, sym, tree); + annotate.annotateLater(tree.mods.annotations, baseEnv, sym); attr.attribTypeVariables(tree.typarams, baseEnv, false); for (JCTypeParameter tp : tree.typarams) - annotate.queueScanTreeAndTypeAnnotate(tp, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(tp, baseEnv, sym); // check that no package exists with same fully qualified name, // but admit classes in the unnamed package which have the same diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java index 73f86239713..5964c16c151 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java @@ -92,7 +92,7 @@ public abstract class BaseFileManager implements JavaFileManager { options = Options.instance(context); // Initialize locations - locations.update(log, lint, FSInfo.instance(context)); + locations.update(log, FSInfo.instance(context)); // Apply options options.whenReady(this::applyOptions); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java index ff02de705fb..c4573b9a364 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java @@ -77,12 +77,11 @@ import javax.tools.StandardJavaFileManager; import javax.tools.StandardJavaFileManager.PathFactory; import javax.tools.StandardLocation; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import jdk.internal.jmod.JmodFile; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.resources.CompilerProperties.Errors; +import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.ListBuffer; @@ -123,11 +122,6 @@ public class Locations { */ private FSInfo fsInfo; - /** - * The root {@link Lint} instance. - */ - private Lint lint; - private ModuleNameReader moduleNameReader; private PathFactory pathFactory = Paths::get; @@ -168,9 +162,8 @@ public class Locations { } } - void update(Log log, Lint lint, FSInfo fsInfo) { + void update(Log log, FSInfo fsInfo) { this.log = log; - this.lint = lint; this.fsInfo = fsInfo; } @@ -221,7 +214,7 @@ public class Locations { try { entries.add(getPath(s)); } catch (IllegalArgumentException e) { - lint.logIfEnabled(LintWarnings.InvalidPath(s)); + log.warning(LintWarnings.InvalidPath(s)); } } } @@ -316,7 +309,7 @@ public class Locations { private void addDirectory(Path dir, boolean warn) { if (!Files.isDirectory(dir)) { if (warn) { - lint.logIfEnabled(LintWarnings.DirPathElementNotFound(dir)); + log.warning(LintWarnings.DirPathElementNotFound(dir)); } return; } @@ -361,7 +354,7 @@ public class Locations { if (!fsInfo.exists(file)) { /* No such file or directory exists */ if (warn) { - lint.logIfEnabled(LintWarnings.PathElementNotFound(file)); + log.warning(LintWarnings.PathElementNotFound(file)); } super.add(file); return; @@ -383,12 +376,12 @@ public class Locations { try { FileSystems.newFileSystem(file, (ClassLoader)null).close(); if (warn) { - lint.logIfEnabled(LintWarnings.UnexpectedArchiveFile(file)); + log.warning(LintWarnings.UnexpectedArchiveFile(file)); } } catch (IOException | ProviderNotFoundException e) { // FIXME: include e.getLocalizedMessage in warning if (warn) { - lint.logIfEnabled(LintWarnings.InvalidArchiveFile(file)); + log.warning(LintWarnings.InvalidArchiveFile(file)); } return; } @@ -1651,7 +1644,7 @@ public class Locations { void add(Map> map, Path prefix, Path suffix) { if (!Files.isDirectory(prefix)) { - lint.logIfEnabled(Files.exists(prefix) ? + log.warning(Files.exists(prefix) ? LintWarnings.DirPathElementNotDirectory(prefix) : LintWarnings.DirPathElementNotFound(prefix)); return; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index cf751ff6b30..6f1ad28586d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -51,7 +51,6 @@ import com.sun.tools.javac.comp.Annotate; import com.sun.tools.javac.comp.Annotate.AnnotationTypeCompleter; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Directive.*; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; @@ -139,9 +138,6 @@ public class ClassReader { /** The symbol table. */ Symtab syms; - /** The root Lint config. */ - Lint lint; - Types types; /** The name table. */ @@ -303,8 +299,6 @@ public class ClassReader { typevars = WriteableScope.create(syms.noSymbol); - lint = Lint.instance(context); - initAttributeReaders(); } @@ -854,8 +848,7 @@ public class ClassReader { if (!warnedAttrs.contains(name)) { JavaFileObject prev = log.useSource(currentClassFile); try { - lint.logIfEnabled( - LintWarnings.FutureAttr(name, version.major, version.minor, majorVersion, minorVersion)); + log.warning(LintWarnings.FutureAttr(name, version.major, version.minor, majorVersion, minorVersion)); } finally { log.useSource(prev); } @@ -1609,7 +1602,7 @@ public class ClassReader { } else if (parameterAnnotations.length != numParameters) { //the RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations //provide annotations for a different number of parameters, ignore: - lint.logIfEnabled(LintWarnings.RuntimeVisibleInvisibleParamAnnotationsMismatch(currentClassFile)); + log.warning(LintWarnings.RuntimeVisibleInvisibleParamAnnotationsMismatch(currentClassFile)); for (int pnum = 0; pnum < numParameters; pnum++) { readAnnotations(); } @@ -2075,9 +2068,9 @@ public class ClassReader { JavaFileObject prevSource = log.useSource(requestingOwner.classfile); try { if (failure == null) { - lint.logIfEnabled(LintWarnings.AnnotationMethodNotFound(container, name)); + log.warning(LintWarnings.AnnotationMethodNotFound(container, name)); } else { - lint.logIfEnabled(LintWarnings.AnnotationMethodNotFoundReason(container, + log.warning(LintWarnings.AnnotationMethodNotFoundReason(container, name, failure.getDetailValue()));//diagnostic, if present } @@ -2954,7 +2947,7 @@ public class ClassReader { private void dropParameterAnnotations() { parameterAnnotations = null; - lint.logIfEnabled(LintWarnings.RuntimeInvisibleParameterAnnotations(currentClassFile)); + log.warning(LintWarnings.RuntimeInvisibleParameterAnnotations(currentClassFile)); } /** * Creates the parameter at the position {@code mpIndex} in the parameter list of the owning method. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 7aa1cc473b5..58beee78af2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -52,7 +52,6 @@ import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import com.sun.tools.doclint.DocLint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.file.BaseFileManager; import com.sun.tools.javac.file.JavacFileManager; @@ -503,12 +502,9 @@ public class Arguments { } } else { // single-module or legacy mode - boolean lintPaths = !options.isLintDisabled(LintCategory.PATH); - if (lintPaths) { - Path outDirParent = outDir.getParent(); - if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { - log.warning(LintWarnings.OutdirIsInExplodedModule(outDir)); - } + Path outDirParent = outDir.getParent(); + if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { + log.warning(LintWarnings.OutdirIsInExplodedModule(outDir)); } } } @@ -576,15 +572,14 @@ public class Arguments { reportDiag(Errors.SourcepathModulesourcepathConflict); } - boolean lintOptions = !options.isLintDisabled(LintCategory.OPTIONS); - if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { + if (source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { if (fm instanceof BaseFileManager baseFileManager) { if (source.compareTo(Source.JDK8) <= 0) { - if (baseFileManager.isDefaultBootClassPath()) + if (baseFileManager.isDefaultBootClassPath()) { log.warning(LintWarnings.SourceNoBootclasspath(source.name, releaseNote(source, targetString))); - } else { - if (baseFileManager.isDefaultSystemModulesPath()) - log.warning(LintWarnings.SourceNoSystemModulesPath(source.name, releaseNote(source, targetString))); + } + } else if (baseFileManager.isDefaultSystemModulesPath()) { + log.warning(LintWarnings.SourceNoSystemModulesPath(source.name, releaseNote(source, targetString))); } } } @@ -593,14 +588,14 @@ public class Arguments { if (source.compareTo(Source.MIN) < 0) { log.error(Errors.OptionRemovedSource(source.name, Source.MIN.name)); - } else if (source == Source.MIN && lintOptions) { + } else if (source == Source.MIN) { log.warning(LintWarnings.OptionObsoleteSource(source.name)); obsoleteOptionFound = true; } if (target.compareTo(Target.MIN) < 0) { log.error(Errors.OptionRemovedTarget(target, Target.MIN)); - } else if (target == Target.MIN && lintOptions) { + } else if (target == Target.MIN) { log.warning(LintWarnings.OptionObsoleteTarget(target)); obsoleteOptionFound = true; } @@ -634,7 +629,7 @@ public class Arguments { log.error(Errors.ProcessorpathNoProcessormodulepath); } - if (obsoleteOptionFound && lintOptions) { + if (obsoleteOptionFound) { log.warning(LintWarnings.OptionObsoleteSuppression); } @@ -645,7 +640,7 @@ public class Arguments { validateLimitModules(sv); validateDefaultModuleForCreatedFiles(sv); - if (lintOptions && options.isSet(Option.ADD_OPENS)) { + if (options.isSet(Option.ADD_OPENS)) { log.warning(LintWarnings.AddopensIgnored); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 83106ff2278..ee11304dce9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -258,6 +258,10 @@ public class JavaCompiler { */ protected JNIWriter jniWriter; + /** The Lint mapper. + */ + protected LintMapper lintMapper; + /** The module for the symbol table entry phases. */ protected Enter enter; @@ -384,6 +388,7 @@ public class JavaCompiler { names = Names.instance(context); log = Log.instance(context); + lintMapper = LintMapper.instance(context); diagFactory = JCDiagnostic.Factory.instance(context); finder = ClassFinder.instance(context); reader = ClassReader.instance(context); @@ -575,6 +580,7 @@ public class JavaCompiler { /** The number of errors reported so far. */ public int errorCount() { + log.reportOutstandingWarnings(); if (werror && log.nerrors == 0 && log.nwarnings > 0) { log.error(Errors.WarningsAndWerror); } @@ -625,6 +631,7 @@ public class JavaCompiler { private JCCompilationUnit parse(JavaFileObject filename, CharSequence content, boolean silent) { long msec = now(); JCCompilationUnit tree = make.TopLevel(List.nil()); + lintMapper.startParsingFile(filename); if (content != null) { if (verbose) { log.printVerbose("parsing.started", filename); @@ -644,6 +651,7 @@ public class JavaCompiler { } tree.sourcefile = filename; + lintMapper.finishParsingFile(tree); if (content != null && !taskListener.isEmpty() && !silent) { TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, tree); @@ -1843,6 +1851,7 @@ public class JavaCompiler { else log.warning(Warnings.ProcUseProcOrImplicit); } + log.reportOutstandingWarnings(); log.reportOutstandingNotes(); if (log.compressedOutput) { log.note(Notes.CompressedDiags); @@ -1916,6 +1925,7 @@ public class JavaCompiler { attr = null; chk = null; gen = null; + lintMapper = null; flow = null; transTypes = null; lower = null; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index f3d0837398c..f40cb0fb6b7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -25,8 +25,6 @@ package com.sun.tools.javac.parser; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Source.Feature; @@ -83,7 +81,7 @@ public class JavaTokenizer extends UnicodeReader { /** * The log to be used for error reporting. Copied from scanner factory. */ - private final Log log; + protected final Log log; /** * The token factory. Copied from scanner factory. @@ -135,13 +133,6 @@ public class JavaTokenizer extends UnicodeReader { */ protected boolean hasEscapeSequences; - /** - * The set of lint options currently in effect. It is initialized - * from the context, and then is set/reset as needed by Attr as it - * visits all the various parts of the trees during attribution. - */ - protected final Lint lint; - /** * Construct a Java token scanner from the input character buffer. * @@ -168,7 +159,6 @@ public class JavaTokenizer extends UnicodeReader { this.source = fac.source; this.preview = fac.preview; this.enableLineDocComments = fac.enableLineDocComments; - this.lint = fac.lint; this.sb = new StringBuilder(256); } @@ -205,17 +195,6 @@ public class JavaTokenizer extends UnicodeReader { errPos = pos; } - /** - * Report a warning at the given position using the provided arguments. - * - * @param pos position in input buffer. - * @param key error key to report. - */ - protected void lexWarning(int pos, JCDiagnostic.LintWarning key) { - DiagnosticPosition dp = new SimpleDiagnosticPosition(pos) ; - log.warning(dp, key); - } - /** * Add a character to the literal buffer. * @@ -1060,17 +1039,12 @@ public class JavaTokenizer extends UnicodeReader { // If a text block. if (isTextBlock) { // Verify that the incidental indentation is consistent. - if (lint.isEnabled(LintCategory.TEXT_BLOCKS)) { - Set checks = - TextBlockSupport.checkWhitespace(string); - if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { - lexWarning(pos, - LintWarnings.InconsistentWhiteSpaceIndentation); - } - if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) { - lexWarning(pos, - LintWarnings.TrailingWhiteSpaceWillBeRemoved); - } + Set checks = TextBlockSupport.checkWhitespace(string); + if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { + log.warning(pos, LintWarnings.InconsistentWhiteSpaceIndentation); + } + if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) { + log.warning(pos, LintWarnings.TrailingWhiteSpaceWillBeRemoved); } // Remove incidental indentation. try { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index b63374d9c6d..4a990701315 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -116,8 +116,6 @@ public class JavacParser implements Parser { /** A map associating "other nearby documentation comments" * with the preferred documentation comment for a declaration. */ protected Map> danglingComments = new HashMap<>(); - /** Handler for deferred diagnostics. */ - protected final DeferredLintHandler deferredLintHandler; // Because of javac's limited lookahead, some contexts are ambiguous in // the presence of type annotations even though they are not ambiguous @@ -190,7 +188,6 @@ public class JavacParser implements Parser { this.names = fac.names; this.source = fac.source; this.preview = fac.preview; - this.deferredLintHandler = fac.deferredLintHandler; this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true); this.keepDocComments = keepDocComments; this.parseModuleInfo = parseModuleInfo; @@ -216,7 +213,6 @@ public class JavacParser implements Parser { this.names = parser.names; this.source = parser.source; this.preview = parser.preview; - this.deferredLintHandler = parser.deferredLintHandler; this.allowStringFolding = parser.allowStringFolding; this.keepDocComments = parser.keepDocComments; this.parseModuleInfo = false; @@ -591,8 +587,7 @@ public class JavacParser implements Parser { * 4. When the tree node for the declaration is finally * available, and the primary comment, if any, * is "attached", (in {@link #attach}) any related - * dangling comments are also attached to the tree node - * by registering them using the {@link #deferredLintHandler}. + * dangling comments are reported to the log as warnings. * 5. (Later) Warnings may be generated for the dangling * comments, subject to the {@code -Xlint} and * {@code @SuppressWarnings}. @@ -653,32 +648,22 @@ public class JavacParser implements Parser { void reportDanglingComments(JCTree tree, Comment dc) { var list = danglingComments.remove(dc); if (list != null) { - deferredLintHandler.push(tree); - try { - list.forEach(this::reportDanglingDocComment); - } finally { - deferredLintHandler.pop(); - } + list.forEach(c -> reportDanglingDocComment(tree, c)); } } /** - * Reports an individual dangling comment using the {@link #deferredLintHandler}. + * Reports an individual dangling comment as a warning to the log. * The comment may or not may generate an actual diagnostic, depending on * the settings for {@code -Xlint} and/or {@code @SuppressWarnings}. * * @param c the comment */ - void reportDanglingDocComment(Comment c) { + void reportDanglingDocComment(JCTree tree, Comment c) { var pos = c.getPos(); - if (pos != null) { - deferredLintHandler.report(lint -> { - if (lint.isEnabled(Lint.LintCategory.DANGLING_DOC_COMMENTS) && - !shebang(c, pos)) { - log.warning( - pos, LintWarnings.DanglingDocComment); - } - }); + if (pos != null && !shebang(c, pos)) { + pos = pos.withLintPosition(tree.getStartPosition()); + S.lintWarning(pos, LintWarnings.DanglingDocComment); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java index bff4e027db7..1d1e08194f7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.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 @@ -28,6 +28,8 @@ package com.sun.tools.javac.parser; import java.util.Queue; import com.sun.tools.javac.parser.Tokens.*; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Position.LineMap; /** @@ -103,4 +105,12 @@ public interface Lexer { * token. */ Queue getDocComments(); + + /** + * Report a warning that is subject to possible suppression by {@code @SuppressWarnings}. + * + * @param pos the lexical position at which the warning occurs + * @param key the warning to report + */ + void lintWarning(DiagnosticPosition pos, LintWarning key); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java index c873c6f31b7..f9e187315ba 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.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 @@ -28,7 +28,6 @@ package com.sun.tools.javac.parser; import java.util.Locale; import com.sun.tools.javac.api.JavacTrees; -import com.sun.tools.javac.code.DeferredLintHandler; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; @@ -70,7 +69,6 @@ public class ParserFactory { final Options options; final ScannerFactory scannerFactory; final Locale locale; - final DeferredLintHandler deferredLintHandler; private final JavacTrees trees; @@ -88,7 +86,6 @@ public class ParserFactory { this.options = Options.instance(context); this.scannerFactory = ScannerFactory.instance(context); this.locale = context.get(Locale.class); - this.deferredLintHandler = DeferredLintHandler.instance(context); this.trees = JavacTrees.instance(context); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java index a24e73a4141..7fcb87eac7a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.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 @@ -31,6 +31,8 @@ import java.util.List; import java.util.ArrayList; import java.util.Queue; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Position.LineMap; import static com.sun.tools.javac.parser.Tokens.*; @@ -150,6 +152,11 @@ public class Scanner implements Lexer { return docComments; } + @Override + public void lintWarning(DiagnosticPosition pos, LintWarning key) { + tokenizer.log.warning(pos, key); + } + public int errPos() { return tokenizer.errPos(); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java index 188fe838b18..66f2b66fabf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.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 @@ -27,7 +27,6 @@ package com.sun.tools.javac.parser; import java.nio.CharBuffer; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.main.Option; @@ -62,7 +61,6 @@ public class ScannerFactory { final Source source; final Preview preview; final Tokens tokens; - final Lint lint; final boolean enableLineDocComments; /** Create a new scanner factory. */ @@ -74,7 +72,6 @@ public class ScannerFactory { this.source = Source.instance(context); this.preview = Preview.instance(context); this.tokens = Tokens.instance(context); - this.lint = Lint.instance(context); var options = Options.instance(context); this.enableLineDocComments = !options.isSet(Option.DISABLE_LINE_DOC_COMMENTS); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java index ec3a373ab4e..2a819152eed 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, 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 @@ -30,6 +30,7 @@ import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCErroneous; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Position.LineMap; @@ -167,10 +168,9 @@ public class VirtualParser extends JavacParser { return S.getLineMap(); } - public void commit() { - for (int i = 0 ; i < offset ; i++) { - S.nextToken(); // advance underlying lexer until position matches - } + @Override + public void lintWarning(DiagnosticPosition pos, LintWarning key) { + // ignore } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java index 012ac628ecd..dc25de30b36 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java @@ -51,7 +51,6 @@ import javax.tools.JavaFileManager.Location; import static javax.tools.StandardLocation.SOURCE_OUTPUT; import static javax.tools.StandardLocation.CLASS_OUTPUT; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symtab; @@ -62,7 +61,6 @@ import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; -import static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.main.Option; @@ -338,7 +336,6 @@ public class JavacFiler implements Filer, Closeable { JavaFileManager fileManager; JavacElements elementUtils; Log log; - Lint lint; Modules modules; Names names; Symtab syms; @@ -421,8 +418,6 @@ public class JavacFiler implements Filer, Closeable { aggregateGeneratedClassNames = new LinkedHashSet<>(); initialClassNames = new LinkedHashSet<>(); - lint = Lint.instance(context); - Options options = Options.instance(context); defaultTargetModule = options.get(Option.DEFAULT_MODULE_FOR_CREATED_FILES); @@ -486,14 +481,12 @@ public class JavacFiler implements Filer, Closeable { private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name, Element... originatingElements) throws IOException { Assert.checkNonNull(mod); - if (lint.isEnabled(PROCESSING)) { - int periodIndex = name.lastIndexOf("."); - if (periodIndex != -1) { - String base = name.substring(periodIndex); - String extn = (isSourceFile ? ".java" : ".class"); - if (base.equals(extn)) - log.warning(LintWarnings.ProcSuspiciousClassName(name, extn)); - } + int periodIndex = name.lastIndexOf("."); + if (periodIndex != -1) { + String base = name.substring(periodIndex); + String extn = (isSourceFile ? ".java" : ".class"); + if (base.equals(extn)) + log.warning(LintWarnings.ProcSuspiciousClassName(name, extn)); } checkNameAndExistence(mod, name, isSourceFile); Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT); @@ -707,7 +700,7 @@ public class JavacFiler implements Filer, Closeable { private void checkName(String name, boolean allowUnnamedPackageInfo) throws FilerException { if (!SourceVersion.isName(name) && !isPackageInfo(name, allowUnnamedPackageInfo)) { - lint.logIfEnabled(LintWarnings.ProcIllegalFileName(name)); + log.warning(LintWarnings.ProcIllegalFileName(name)); throw new FilerException("Illegal name " + name); } } @@ -735,11 +728,11 @@ public class JavacFiler implements Filer, Closeable { initialClassNames.contains(typename) || containedInInitialInputs(typename); if (alreadySeen) { - lint.logIfEnabled(LintWarnings.ProcTypeRecreate(typename)); + log.warning(LintWarnings.ProcTypeRecreate(typename)); throw new FilerException("Attempt to recreate a file for type " + typename); } if (existing != null) { - lint.logIfEnabled(LintWarnings.ProcTypeAlreadyExists(typename)); + log.warning(LintWarnings.ProcTypeAlreadyExists(typename)); } if (!mod.isUnnamed() && !typename.contains(".")) { throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename); @@ -768,7 +761,7 @@ public class JavacFiler implements Filer, Closeable { */ private void checkFileReopening(FileObject fileObject, boolean forWriting) throws FilerException { if (isInFileObjectHistory(fileObject, forWriting)) { - lint.logIfEnabled(LintWarnings.ProcFileReopening(fileObject.getName())); + log.warning(LintWarnings.ProcFileReopening(fileObject.getName())); throw new FilerException("Attempt to reopen a file for path " + fileObject.getName()); } if (forWriting) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java index 7bc538a1d1e..b28f19bd3af 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -123,7 +123,6 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea private final Modules modules; private final Types types; private final Annotate annotate; - private final Lint lint; /** * Holds relevant state history of which processors have been @@ -206,7 +205,6 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea printProcessorInfo = options.isSet(Option.XPRINTPROCESSORINFO); printRounds = options.isSet(Option.XPRINTROUNDS); verbose = options.isSet(Option.VERBOSE); - lint = Lint.instance(context); compiler = JavaCompiler.instance(context); if (options.isSet(Option.PROC, "only") || options.isSet(Option.XPRINT)) { compiler.shouldStopPolicyIfNoError = CompileState.PROCESS; @@ -626,7 +624,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea private Set supportedOptionNames; ProcessorState(Processor p, Log log, Source source, DeferredCompletionFailureHandler dcfh, - boolean allowModules, ProcessingEnvironment env, Lint lint) { + boolean allowModules, ProcessingEnvironment env) { processor = p; contributed = false; @@ -647,10 +645,9 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea boolean patternAdded = supportedAnnotationStrings.add(annotationPattern); supportedAnnotationPatterns. - add(importStringToPattern(allowModules, annotationPattern, - processor, log, lint)); + add(importStringToPattern(allowModules, annotationPattern, processor, log)); if (!patternAdded) { - lint.logIfEnabled(LintWarnings.ProcDuplicateSupportedAnnotation(annotationPattern, + log.warning(LintWarnings.ProcDuplicateSupportedAnnotation(annotationPattern, p.getClass().getName())); } } @@ -663,7 +660,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea // and "foo.bar.*". if (supportedAnnotationPatterns.contains(MatchingUtils.validImportStringToPattern("*")) && supportedAnnotationPatterns.size() > 1) { - lint.logIfEnabled(LintWarnings.ProcRedundantTypesWithWildcard(p.getClass().getName())); + log.warning(LintWarnings.ProcRedundantTypesWithWildcard(p.getClass().getName())); } supportedOptionNames = new LinkedHashSet<>(); @@ -671,8 +668,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea if (checkOptionName(optionName, log)) { boolean optionAdded = supportedOptionNames.add(optionName); if (!optionAdded) { - lint.logIfEnabled(LintWarnings.ProcDuplicateOptionName(optionName, - p.getClass().getName())); + log.warning(LintWarnings.ProcDuplicateOptionName(optionName, p.getClass().getName())); } } } @@ -759,8 +755,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea ProcessorState ps = new ProcessorState(psi.processorIterator.next(), log, source, dcfh, Feature.MODULES.allowedInSource(source), - JavacProcessingEnvironment.this, - lint); + JavacProcessingEnvironment.this); psi.procStateList.add(ps); return ps; } else @@ -888,7 +883,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea } unmatchedAnnotations.remove(""); - if (lint.isEnabled(PROCESSING) && unmatchedAnnotations.size() > 0) { + if (unmatchedAnnotations.size() > 0) { // Remove annotations processed by javac unmatchedAnnotations.keySet().removeAll(platformAnnotations); if (unmatchedAnnotations.size() > 0) { @@ -1649,7 +1644,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea * regex matching that string. If the string is not a valid * import-style string, return a regex that won't match anything. */ - private static Pattern importStringToPattern(boolean allowModules, String s, Processor p, Log log, Lint lint) { + private static Pattern importStringToPattern(boolean allowModules, String s, Processor p, Log log) { String module; String pkg; int slash = s.indexOf('/'); @@ -1662,7 +1657,7 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea } else { String moduleName = s.substring(0, slash); if (!SourceVersion.isName(moduleName)) { - return warnAndNoMatches(s, p, log, lint); + return warnAndNoMatches(s, p, log); } module = Pattern.quote(moduleName + "/"); // And warn if module is specified if modules aren't supported, conditional on -Xlint:proc? @@ -1671,12 +1666,12 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea if (MatchingUtils.isValidImportString(pkg)) { return Pattern.compile(module + MatchingUtils.validImportStringToPatternString(pkg)); } else { - return warnAndNoMatches(s, p, log, lint); + return warnAndNoMatches(s, p, log); } } - private static Pattern warnAndNoMatches(String s, Processor p, Log log, Lint lint) { - lint.logIfEnabled(LintWarnings.ProcMalformedSupportedString(s, p.getClass().getName())); + private static Pattern warnAndNoMatches(String s, Processor p, Log log) { + log.warning(LintWarnings.ProcMalformedSupportedString(s, p.getClass().getName())); return noMatches; // won't match any valid identifier } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 7dde6cc963f..a226b2f5960 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -1600,6 +1600,7 @@ compiler.err.multi-module.outdir.cannot.be.exploded.module=\ # 0: path # lint: path +# flags: default-enabled compiler.warn.outdir.is.in.exploded.module=\ the output directory is within an exploded module: {0} @@ -1924,19 +1925,19 @@ compiler.warn.incubating.modules=\ # 0: symbol, 1: symbol # lint: deprecation -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.has.been.deprecated=\ {0} in {1} has been deprecated # 0: symbol, 1: symbol # lint: removal -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.has.been.deprecated.for.removal=\ {0} in {1} has been deprecated and marked for removal # 0: symbol # lint: preview -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.is.preview=\ {0} is a preview API and may be removed in a future release. @@ -1947,7 +1948,7 @@ compiler.err.is.preview=\ # 0: symbol # lint: preview -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.is.preview.reflective=\ {0} is a reflective preview API and may be removed in a future release. @@ -1959,13 +1960,13 @@ compiler.warn.restricted.method=\ # 0: symbol # lint: deprecation -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.has.been.deprecated.module=\ module {0} has been deprecated # 0: symbol # lint: removal -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.has.been.deprecated.for.removal.module=\ module {0} has been deprecated and marked for removal @@ -2194,11 +2195,13 @@ compiler.warn.static.not.qualified.by.type2=\ # 0: string, 1: fragment # lint: options +# flags: default-enabled compiler.warn.source.no.bootclasspath=\ bootstrap class path is not set in conjunction with -source {0}\n{1} # 0: string, 1: fragment # lint: options +# flags: default-enabled compiler.warn.source.no.system.modules.path=\ location of system modules is not set in conjunction with -source {0}\n{1} @@ -2224,11 +2227,13 @@ compiler.misc.source.no.system.modules.path.with.target=\ # 0: string # lint: options +# flags: default-enabled compiler.warn.option.obsolete.source=\ source value {0} is obsolete and will be removed in a future release # 0: target # lint: options +# flags: default-enabled compiler.warn.option.obsolete.target=\ target value {0} is obsolete and will be removed in a future release @@ -2241,6 +2246,7 @@ compiler.err.option.removed.target=\ Target option {0} is no longer supported. Use {1} or later. # lint: options +# flags: default-enabled compiler.warn.option.obsolete.suppression=\ To suppress warnings about obsolete options, use -Xlint:-options. @@ -2365,13 +2371,13 @@ compiler.warn.unchecked.assign=\ # 0: symbol, 1: type # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.unchecked.assign.to.var=\ unchecked assignment to variable {0} as member of raw type {1} # 0: symbol, 1: type # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.unchecked.call.mbr.of.raw.type=\ unchecked call to {0} as a member of the raw type {1} @@ -2381,7 +2387,7 @@ compiler.warn.unchecked.cast.to.type=\ # 0: kind name, 1: name, 2: object, 3: object, 4: kind name, 5: symbol # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.unchecked.meth.invocation.applied=\ unchecked method invocation: {0} {1} in {4} {5} is applied to given types\n\ required: {2}\n\ @@ -2389,13 +2395,13 @@ compiler.warn.unchecked.meth.invocation.applied=\ # 0: type # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.unchecked.generic.array.creation=\ unchecked generic array creation for varargs parameter of type {0} # 0: type # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.unchecked.varargs.non.reifiable.type=\ Possible heap pollution from parameterized vararg type {0} @@ -2794,7 +2800,7 @@ compiler.misc.prob.found.req=\ # 0: message segment, 1: type, 2: type # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.prob.found.req=\ {0}\n\ required: {2}\n\ @@ -3191,14 +3197,14 @@ compiler.err.override.incompatible.ret=\ # 0: message segment, 1: type, 2: type # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.override.unchecked.ret=\ {0}\n\ return type requires unchecked conversion from {1} to {2} # 0: message segment, 1: type # lint: unchecked -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.override.unchecked.thrown=\ {0}\n\ overridden method does not throw {1} @@ -3302,13 +3308,13 @@ compiler.err.preview.feature.disabled.classfile=\ # 0: message segment (feature) # lint: preview -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.preview.feature.use=\ {0} is a preview feature and may be removed in a future release. # 0: message segment (feature) # lint: preview -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.preview.feature.use.plural=\ {0} are a preview feature and may be removed in a future release. @@ -3879,6 +3885,7 @@ compiler.err.bad.name.for.option=\ # 0: option name, 1: symbol # lint: options +# flags: default-enabled compiler.warn.module.for.option.not.found=\ module name in {0} option not found: {1} @@ -3895,6 +3902,7 @@ compiler.err.add.reads.with.release=\ adding read edges for system module {0} is not allowed with --release # lint: options +# flags: default-enabled compiler.warn.addopens.ignored=\ --add-opens has no effect at compile time @@ -4272,7 +4280,7 @@ compiler.err.incorrect.number.of.nested.patterns=\ # 0: kind name, 1: symbol # lint: preview -# flags: aggregate, mandatory +# flags: aggregate, mandatory, default-enabled compiler.warn.declared.using.preview=\ {0} {1} is declared using a preview feature, which may be removed in a future release. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index 06a1d9fc7a3..c9f529eae55 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -368,6 +368,36 @@ public class JCDiagnostic implements Diagnostic { * the end position of the tree node. Otherwise, just returns the * same as getPreferredPosition(). */ int getEndPosition(EndPosTable endPosTable); + /** Get the position that determines which Lint configuration applies. */ + default int getLintPosition() { + return getStartPosition(); + } + /** Create a new instance from this instance and the given lint position. */ + default DiagnosticPosition withLintPosition(int lintPos) { + DiagnosticPosition orig = this; + return new DiagnosticPosition() { + @Override + public JCTree getTree() { + return orig.getTree(); + } + @Override + public int getStartPosition() { + return orig.getStartPosition(); + } + @Override + public int getPreferredPosition() { + return orig.getPreferredPosition(); + } + @Override + public int getEndPosition(EndPosTable endPosTable) { + return orig.getEndPosition(endPosTable); + } + @Override + public int getLintPosition() { + return lintPos; + } + }; + } } /** @@ -405,6 +435,10 @@ public class JCDiagnostic implements Diagnostic { RECOVERABLE, NON_DEFERRABLE, COMPRESSED, + /** Flag for lint diagnostics that should be emitted even when their category + * is not explicitly enabled, as long as it is not explicitly suppressed. + */ + DEFAULT_ENABLED, /** Flags mandatory warnings that should pass through a mandatory warning aggregator. */ AGGREGATE, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 576344a3d2a..95458f339a1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -32,29 +32,45 @@ import java.util.Comparator; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; import com.sun.tools.javac.api.DiagnosticFormatter; +import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Source; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.main.Main; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.tree.EndPosTable; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticInfo; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import static com.sun.tools.javac.main.Option.*; import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*; +import static com.sun.tools.javac.code.Lint.LintCategory.*; +import static com.sun.tools.javac.resources.CompilerProperties.LintWarnings.RequiresAutomatic; +import static com.sun.tools.javac.resources.CompilerProperties.LintWarnings.RequiresTransitiveAutomatic; +import static com.sun.tools.javac.tree.JCTree.Tag.*; /** A class for error logs. Reports errors and warnings, and * keeps track of error numbers and positions. @@ -103,6 +119,11 @@ public class Log extends AbstractLog { */ protected final DiagnosticHandler prev; + /** + * Diagnostics waiting for an applicable {@link Lint} instance. + */ + protected Map> lintWaitersMap = new LinkedHashMap<>(); + /** * Install this diagnostic handler as the current one, * recording the previous one. @@ -113,9 +134,92 @@ public class Log extends AbstractLog { } /** - * Handle a diagnostic. + * Step 1: Handle a diagnostic for which the applicable Lint instance (if any) may not be known yet. */ - public abstract void report(JCDiagnostic diag); + public final void report(JCDiagnostic diag) { + Lint lint = null; + LintCategory category = diag.getLintCategory(); + if (category != null) { // this is a lint warning; find the applicable Lint + DiagnosticPosition pos = diag.getDiagnosticPosition(); + if (pos != null && category.annotationSuppression) { // we should apply the Lint from the warning's position + if ((lint = lintFor(diag)) == null) { + addLintWaiter(currentSourceFile(), diag); // ...but we don't know it yet, so defer + return; + } + } else // we should apply the root Lint + lint = rootLint(); + } + reportWithLint(diag, lint); + } + + /** + * Step 2: Handle a diagnostic for which the applicable Lint instance (if any) is known and provided. + */ + public final void reportWithLint(JCDiagnostic diag, Lint lint) { + + // Apply hackery for REQUIRES_TRANSITIVE_AUTOMATIC (see also Check.checkModuleRequires()) + if (diag.getCode().equals(RequiresTransitiveAutomatic.key()) && !lint.isEnabled(REQUIRES_TRANSITIVE_AUTOMATIC)) { + reportWithLint( + diags.warning(null, diag.getDiagnosticSource(), diag.getDiagnosticPosition(), RequiresAutomatic), lint); + return; + } + + // Apply the lint configuration (if any) and discard the warning if it gets filtered out + if (lint != null) { + LintCategory category = diag.getLintCategory(); + boolean emit = !diag.isFlagSet(DEFAULT_ENABLED) ? // is the warning not enabled by default? + lint.isEnabled(category) : // then emit if the category is enabled + category.annotationSuppression ? // else emit if the category is not suppressed, where + !lint.isSuppressed(category) : // ...suppression happens via @SuppressWarnings + !options.isLintDisabled(category); // ...suppression happens via -Xlint:-category + if (!emit) + return; + } + + // Proceed + reportReady(diag); + } + + /** + * Step 3: Handle a diagnostic to which the applicable Lint instance (if any) has been applied. + */ + protected abstract void reportReady(JCDiagnostic diag); + + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { + lintWaitersMap.computeIfAbsent(sourceFile, s -> new LinkedList<>()).add(diagnostic); + } + + /** + * Flush any lint waiters whose {@link Lint} configurations are now known. + */ + public void flushLintWaiters() { + lintWaitersMap.entrySet().removeIf(entry -> { + + // Is the source file no longer recognized? If so, discard warnings (e.g., this can happen with JShell) + JavaFileObject sourceFile = entry.getKey(); + if (!lintMapper.isKnown(sourceFile)) + return true; + + // Flush those diagnostics for which we now know the applicable Lint + List diagnosticList = entry.getValue(); + JavaFileObject prevSourceFile = useSource(sourceFile); + try { + diagnosticList.removeIf(diag -> { + Lint lint = lintFor(diag); + if (lint != null) { + reportWithLint(diag, lint); + return true; + } + return false; + }); + } finally { + useSource(prevSourceFile); + } + + // Discard list if empty + return diagnosticList.isEmpty(); + }); + } } /** @@ -124,7 +228,10 @@ public class Log extends AbstractLog { public class DiscardDiagnosticHandler extends DiagnosticHandler { @Override - public void report(JCDiagnostic diag) { } + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { } + + @Override + protected void reportReady(JCDiagnostic diag) { } } /** @@ -157,11 +264,20 @@ public class Log extends AbstractLog { } @Override - public void report(JCDiagnostic diag) { + protected void reportReady(JCDiagnostic diag) { if (deferrable(diag)) { deferred.add(diag); } else { - prev.report(diag); + prev.reportReady(diag); + } + } + + @Override + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diag) { + if (deferrable(diag)) { + super.addLintWaiter(sourceFile, diag); + } else { + prev.addLintWaiter(sourceFile, diag); } } @@ -182,6 +298,13 @@ public class Log extends AbstractLog { .filter(accepter) .forEach(prev::report); deferred = null; // prevent accidental ongoing use + + // Flush matching Lint waiters to the previous handler + lintWaitersMap.forEach( + (sourceFile, diagnostics) -> diagnostics.stream() + .filter(accepter) + .forEach(diagnostic -> prev.addLintWaiter(sourceFile, diagnostic))); + lintWaitersMap = null; // prevent accidental ongoing use } /** Report all deferred diagnostics in the specified order. */ @@ -247,6 +370,16 @@ public class Log extends AbstractLog { */ private final Context context; + /** + * The {@link Options} singleton. + */ + private final Options options; + + /** + * The lint positions table. + */ + private final LintMapper lintMapper; + /** * The root {@link Lint} singleton. */ @@ -350,6 +483,8 @@ public class Log extends AbstractLog { super(JCDiagnostic.Factory.instance(context)); context.put(logKey, this); this.context = context; + this.options = Options.instance(context); + this.lintMapper = LintMapper.instance(context); this.writers = writers; @SuppressWarnings("unchecked") // FIXME @@ -369,7 +504,6 @@ public class Log extends AbstractLog { this.diagFormatter = new BasicDiagnosticFormatter(messages); // Once Options is ready, complete the initialization - final Options options = Options.instance(context); options.whenReady(this::initOptions); } // where @@ -689,6 +823,21 @@ public class Log extends AbstractLog { diagnosticHandler.report(diagnostic); } +// Deferred Lint Calculation + + /** + * Report unreported lint warnings for which the applicable {@link Lint} configuration is now known. + */ + public void reportOutstandingWarnings() { + diagnosticHandler.flushLintWaiters(); + } + + // Get the Lint config for the given warning (if known) + private Lint lintFor(JCDiagnostic diag) { + Assert.check(diag.getLintCategory() != null); + return lintMapper.lintAt(diag.getSource(), diag.getDiagnosticPosition()).orElse(null); + } + // Obtain root Lint singleton lazily to avoid init loops private Lint rootLint() { if (rootLint == null) @@ -756,7 +905,7 @@ public class Log extends AbstractLog { private class DefaultDiagnosticHandler extends DiagnosticHandler { @Override - public void report(JCDiagnostic diagnostic) { + protected void reportReady(JCDiagnostic diagnostic) { if (expectDiagKeys != null) expectDiagKeys.remove(diagnostic.getCode()); @@ -783,13 +932,13 @@ public class Log extends AbstractLog { // Apply the appropriate mandatory warning aggregator, if needed if (diagnostic.isFlagSet(AGGREGATE)) { LintCategory category = diagnostic.getLintCategory(); - boolean verbose = rootLint().isEnabled(category); + boolean verbose = lintFor(diagnostic).isEnabled(category); if (!aggregatorFor(category).aggregate(diagnostic, verbose)) return; } // Strict warnings are always emitted - if (diagnostic.isFlagSet(DiagnosticFlag.STRICT)) { + if (diagnostic.isFlagSet(STRICT)) { writeDiagnostic(diagnostic); nwarnings++; return; diff --git a/test/langtools/tools/javac/6304921/TestLog.java b/test/langtools/tools/javac/6304921/TestLog.java index 41695554a88..8f00a14b9e2 100644 --- a/test/langtools/tools/javac/6304921/TestLog.java +++ b/test/langtools/tools/javac/6304921/TestLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -41,7 +41,7 @@ import javax.tools.SimpleJavaFileObject; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.parser.Parser; import com.sun.tools.javac.parser.ParserFactory; -import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; +import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeScanner; @@ -130,10 +130,11 @@ public class TestLog log.error(tree.pos(), Errors.NotStmt); log.error(nil, Errors.NotStmt); - log.warning(LintWarnings.DivZero); - log.warning(tree.pos, LintWarnings.DivZero); - log.warning(tree.pos(), LintWarnings.DivZero); - log.warning(nil, LintWarnings.DivZero); + // some warnings that will be emitted during parsing + log.warning(Warnings.ExtraneousSemicolon); + log.warning(tree.pos, Warnings.ExtraneousSemicolon); + log.warning(tree.pos(), Warnings.ExtraneousSemicolon); + log.warning(nil, Warnings.ExtraneousSemicolon); } private Log log; diff --git a/test/langtools/tools/javac/ImportModule.java b/test/langtools/tools/javac/ImportModule.java index f088dbef658..d26b73c0289 100644 --- a/test/langtools/tools/javac/ImportModule.java +++ b/test/langtools/tools/javac/ImportModule.java @@ -730,8 +730,8 @@ public class ImportModule extends TestRunner { .getOutputLines(Task.OutputKind.DIRECT); List expectedErrors = List.of( - "module-info.java:3:18: compiler.warn.module.not.found: M1", "module-info.java:6:9: compiler.err.cant.resolve: kindname.class, A, , ", + "module-info.java:3:18: compiler.warn.module.not.found: M1", "1 error", "1 warning" ); diff --git a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out index 2a12111fe40..23dcddc1e93 100644 --- a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out +++ b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out @@ -1,4 +1,4 @@ -T6400189a.java:14:35: compiler.warn.unchecked.call.mbr.of.raw.type: getAnnotation(java.lang.Class), java.lang.reflect.Constructor T6400189a.java:14:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.annotation.Annotation, java.lang.annotation.Documented) +T6400189a.java:14:35: compiler.warn.unchecked.call.mbr.of.raw.type: getAnnotation(java.lang.Class), java.lang.reflect.Constructor 1 error 1 warning diff --git a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out index 904cd3e677f..91e34b5beca 100644 --- a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out +++ b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out @@ -1,4 +1,4 @@ -T6400189b.java:24:24: compiler.warn.unchecked.call.mbr.of.raw.type: m(T6400189b), T6400189b.B T6400189b.java:24:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer) +T6400189b.java:24:24: compiler.warn.unchecked.call.mbr.of.raw.type: m(T6400189b), T6400189b.B 1 error 1 warning diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out index 78285aa45d9..b2dde3ff0ef 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out @@ -1,7 +1,7 @@ +DanglingDocCommentsClass.java:15:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass.java:19:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:10:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:14:8: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:14:69: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass.java:15:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass.java:19:5: compiler.warn.dangling.doc.comment 6 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out index 3ed89c5f5bc..e97bd630ad8 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out @@ -1,7 +1,7 @@ +DanglingDocCommentsClass_Line.java:21:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass_Line.java:26:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:11:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:15:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:17:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:19:9: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass_Line.java:21:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass_Line.java:26:5: compiler.warn.dangling.doc.comment 6 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out index 1eda729da19..13e15493579 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out @@ -1,4 +1,4 @@ -DanglingDocCommentsClass_Mixed.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Mixed.java:17:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Mixed.java:21:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass_Mixed.java:13:1: compiler.warn.dangling.doc.comment 3 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out index ddf1b2964de..33938e86078 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out @@ -1,8 +1,8 @@ +DanglingDocCommentsEnum.java:16:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsEnum.java:22:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsEnum.java:28:5: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:10:1: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:14:8: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:14:67: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:16:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:22:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:28:5: compiler.warn.dangling.doc.comment 7 warnings diff --git a/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out b/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out index efceb84c8c7..3c93e1711d6 100644 --- a/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out +++ b/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out @@ -1,11 +1,11 @@ T7188968.java:20:20: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) -T7188968.java:20:9: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:21:20: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) -T7188968.java:21:29: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:22:22: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) +T7188968.java:23:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) +T7188968.java:20:9: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo +T7188968.java:21:29: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:22:9: compiler.warn.unchecked.meth.invocation.applied: kindname.constructor, , java.util.List,java.lang.Object, java.util.List,unknown, kindname.class, T7188968.Foo T7188968.java:22:19: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.List, java.util.List -T7188968.java:23:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) T7188968.java:23:20: compiler.warn.unchecked.meth.invocation.applied: kindname.method, makeFoo, java.util.List,java.lang.Object, java.util.List,unknown, kindname.class, T7188968.Foo T7188968.java:23:21: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.List, java.util.List 4 errors diff --git a/test/langtools/tools/javac/lambda/TargetType22.out b/test/langtools/tools/javac/lambda/TargetType22.out index d94b2cc60b3..c19aef2411f 100644 --- a/test/langtools/tools/javac/lambda/TargetType22.out +++ b/test/langtools/tools/javac/lambda/TargetType22.out @@ -1,4 +1,4 @@ -TargetType22.java:29:21: compiler.warn.unchecked.varargs.non.reifiable.type: A TargetType22.java:40:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType22.Sam1), TargetType22, kindname.method, call(TargetType22.SamX), TargetType22 +TargetType22.java:29:21: compiler.warn.unchecked.varargs.non.reifiable.type: A 1 error 1 warning diff --git a/test/langtools/tools/javac/lint/LexicalLintNesting.java b/test/langtools/tools/javac/lint/LexicalLintNesting.java new file mode 100644 index 00000000000..f167921df81 --- /dev/null +++ b/test/langtools/tools/javac/lint/LexicalLintNesting.java @@ -0,0 +1,170 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8224228 + * @summary Verify lexical lint warnings handle nested declarations with SuppressWarnings correctly + * @compile/fail/ref=LexicalLintNesting.out -XDrawDiagnostics -Xlint:text-blocks -Werror LexicalLintNesting.java + */ + +//@SuppressWarnings("text-blocks") +public class LexicalLintNesting { + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s1 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s2 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested1 { + + @SuppressWarnings("text-blocks") + String s3 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s4 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested1A { + + //@SuppressWarnings("text-blocks") + String s5 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s6 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s7 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s8 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested1B { + + @SuppressWarnings("text-blocks") + String s9 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s10 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s11 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s12 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s13 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s14 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested2 { + + @SuppressWarnings("text-blocks") + String s15 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s16 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested2A { + + //@SuppressWarnings("text-blocks") + String s17 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s18 = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + + } + + @SuppressWarnings("text-blocks") + String s19 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s20 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested2B { + + @SuppressWarnings("text-blocks") + String s21 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s22 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s23 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s24 = """ + trailing space here:\u0020 + """; + + } + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s25 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s26 = """ + trailing space here:\u0020 + """; +} diff --git a/test/langtools/tools/javac/lint/LexicalLintNesting.out b/test/langtools/tools/javac/lint/LexicalLintNesting.out new file mode 100644 index 00000000000..b16db47cf52 --- /dev/null +++ b/test/langtools/tools/javac/lint/LexicalLintNesting.out @@ -0,0 +1,10 @@ +LexicalLintNesting.java:12:36: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:30:40: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:55:40: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:68:45: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:80:41: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:92:37: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:162:37: compiler.warn.trailing.white.space.will.be.removed +- compiler.err.warnings.and.werror +1 error +7 warnings diff --git a/test/langtools/tools/javac/lint/TextBlockSuppress.java b/test/langtools/tools/javac/lint/TextBlockSuppress.java new file mode 100644 index 00000000000..05019be6bc2 --- /dev/null +++ b/test/langtools/tools/javac/lint/TextBlockSuppress.java @@ -0,0 +1,61 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8224228 + * @summary Verify SuppressWarnings works for LintCategore.TEXT_BLOCKS + * @compile/fail/ref=TextBlockSuppress.out -XDrawDiagnostics -Xlint:text-blocks -Werror TextBlockSuppress.java + */ + +public class TextBlockSuppress { + + public static class Example1 { + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD get a warning here + } + } + + @SuppressWarnings("text-blocks") + public static class Example2 { + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example3 { + @SuppressWarnings("text-blocks") + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example4 { + { + String s = """ + trailing space here:\u0020 + """; // SHOULD get a warning here + } + } + + @SuppressWarnings("text-blocks") + public static class Example5 { + { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example6 { + public void method() { + @SuppressWarnings("text-blocks") + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } +} diff --git a/test/langtools/tools/javac/lint/TextBlockSuppress.out b/test/langtools/tools/javac/lint/TextBlockSuppress.out new file mode 100644 index 00000000000..d16f080a133 --- /dev/null +++ b/test/langtools/tools/javac/lint/TextBlockSuppress.out @@ -0,0 +1,5 @@ +TextBlockSuppress.java:12:24: compiler.warn.trailing.white.space.will.be.removed +TextBlockSuppress.java:38:24: compiler.warn.trailing.white.space.will.be.removed +- compiler.err.warnings.and.werror +1 error +2 warnings diff --git a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out index 6fe57b8979d..1656d8eeff5 100644 --- a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out +++ b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out @@ -1,4 +1,4 @@ -Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 P.java:10:18: compiler.warn.has.been.deprecated: foo(), Q +Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 Q.java:17:25: compiler.warn.has.been.deprecated: foo(), Q 3 warnings diff --git a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out index fe4a91e2584..5dee9c1414c 100644 --- a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out +++ b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out @@ -1,4 +1,4 @@ -Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 P.java:10:18: compiler.warn.has.been.deprecated: foo(), Q +Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 - compiler.note.deprecated.filename.additional: Q.java 2 warnings diff --git a/test/langtools/tools/javac/modules/AnnotationsOnModules.java b/test/langtools/tools/javac/modules/AnnotationsOnModules.java index 6d9bfaad406..f9858a105eb 100644 --- a/test/langtools/tools/javac/modules/AnnotationsOnModules.java +++ b/test/langtools/tools/javac/modules/AnnotationsOnModules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -599,8 +599,8 @@ public class AnnotationsOnModules extends ModuleTestBase { "1 warning"); } else if (suppress.equals(DEPRECATED_JAVADOC)) { expected = Arrays.asList( - "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", + "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", "2 warnings"); } else { expected = Arrays.asList(""); diff --git a/test/langtools/tools/javac/preview/PreviewErrors.java b/test/langtools/tools/javac/preview/PreviewErrors.java index eab5b2af9bf..db17aabbd42 100644 --- a/test/langtools/tools/javac/preview/PreviewErrors.java +++ b/test/langtools/tools/javac/preview/PreviewErrors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -324,7 +324,9 @@ public class PreviewErrors extends ComboInstance { ok = true; switch (elementType) { case LANGUAGE -> { - if (lint == Lint.ENABLE_PREVIEW) { + if (suppress == Suppress.YES) { + expected = Set.of(); + } else if (lint == Lint.ENABLE_PREVIEW) { expected = Set.of("5:41:compiler.warn.preview.feature.use"); } else { expected = Set.of("-1:-1:compiler.note.preview.filename", diff --git a/test/langtools/tools/javac/preview/PreviewTest.java b/test/langtools/tools/javac/preview/PreviewTest.java index 36f1e70acd0..e681f3f837e 100644 --- a/test/langtools/tools/javac/preview/PreviewTest.java +++ b/test/langtools/tools/javac/preview/PreviewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -587,12 +587,12 @@ public class PreviewTest extends TestRunner { "Test.java:19:11: compiler.err.is.preview: test()", "Test.java:20:11: compiler.err.is.preview: test()", "Test.java:21:11: compiler.err.is.preview: test()", - "Test.java:24:11: compiler.warn.is.preview.reflective: test()", "Test.java:29:16: compiler.err.is.preview: preview.api.Preview", "Test.java:32:21: compiler.err.is.preview: test()", "Test.java:36:21: compiler.err.is.preview: test()", "Test.java:40:13: compiler.err.is.preview: test()", "Test.java:41:21: compiler.err.is.preview: FIELD", + "Test.java:24:11: compiler.warn.is.preview.reflective: test()", "17 errors", "1 warning"); @@ -792,6 +792,99 @@ public class PreviewTest extends TestRunner { throw new Exception("expected output not found" + log); } + @Test //JDK-8224228: + public void testSuppressWarnings(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public class Preview { + public static int test() { + return 0; + } + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.Preview; + public class Test { + + public static class Example1 { + public void method() { + Preview.test(); // SHOULD get a warning here + } + } + + @SuppressWarnings("preview") + public static class Example2 { + public void method() { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example3 { + @SuppressWarnings("preview") + public void method() { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example4 { + { + Preview.test(); // SHOULD get a warning here + } + } + + @SuppressWarnings("preview") + public static class Example5 { + { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example6 { + @SuppressWarnings("preview") + int x = Preview.test(); // SHOULD NOT get a warning here + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "--enable-preview", + "-Xlint:preview", + "-source", String.valueOf(Runtime.version().feature()), + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:7:11: compiler.warn.is.preview: preview.api.Preview", + "Test.java:27:11: compiler.warn.is.preview: preview.api.Preview", + "2 warnings"); + + if (!log.equals(expected)) + throw new Exception("expected output not found: " + log); + } + @Test //JDK-8343540: public void nonPreviewImplementsPreview5(Path base) throws Exception { Path apiSrc = base.resolve("api-src"); diff --git a/test/langtools/tools/javac/varargs/7097436/T7097436.out b/test/langtools/tools/javac/varargs/7097436/T7097436.out index 5e35910d2fa..392869f3b9f 100644 --- a/test/langtools/tools/javac/varargs/7097436/T7097436.out +++ b/test/langtools/tools/javac/varargs/7097436/T7097436.out @@ -1,6 +1,6 @@ -T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls -T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls T7097436.java:15:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List[], java.lang.String) T7097436.java:16:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List[], java.lang.Integer[]) +T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls +T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls 2 errors 2 warnings diff --git a/test/langtools/tools/javac/warnings/6594914/T6594914a.out b/test/langtools/tools/javac/warnings/6594914/T6594914a.out index 62f99072a7a..d3d759ca044 100644 --- a/test/langtools/tools/javac/warnings/6594914/T6594914a.out +++ b/test/langtools/tools/javac/warnings/6594914/T6594914a.out @@ -1,7 +1,7 @@ T6594914a.java:11:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:16:16: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6594914a.java:16:52: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:16:33: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:17:20: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6594914a.java:16:52: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:24:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package 6 warnings diff --git a/test/langtools/tools/javac/warnings/7090499/T7090499.out b/test/langtools/tools/javac/warnings/7090499/T7090499.out index 1ff9e164e48..2241c0a04bb 100644 --- a/test/langtools/tools/javac/warnings/7090499/T7090499.out +++ b/test/langtools/tools/javac/warnings/7090499/T7090499.out @@ -1,14 +1,14 @@ +T7090499.java:26:10: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:27:10: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:28:17: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:28:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:18:5: compiler.warn.raw.class.use: T7090499, T7090499 T7090499.java:18:22: compiler.warn.raw.class.use: T7090499, T7090499 T7090499.java:20:10: compiler.warn.raw.class.use: T7090499.A.X, T7090499.A.X T7090499.java:21:10: compiler.warn.raw.class.use: T7090499.A.Z, T7090499.A.Z T7090499.java:24:17: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:26:10: compiler.err.improperly.formed.type.inner.raw.param -T7090499.java:27:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:28:18: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:28:17: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:28:11: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:28:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:30:32: compiler.warn.raw.class.use: T7090499.B, T7090499.B T7090499.java:33:13: compiler.warn.raw.class.use: T7090499.A, T7090499.A T7090499.java:33:24: compiler.warn.raw.class.use: T7090499.A, T7090499.A diff --git a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java index b5062a9b63f..a9101b47e42 100644 --- a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java +++ b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.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 @@ -116,8 +116,8 @@ public class UnneededStrictfpWarningToolBox extends TestRunner { var expected = List.of("UnneededStrictfpWarning1.java:1:17: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:10:10: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:12:29: compiler.warn.strictfp", - "UnneededStrictfpWarning1.java:16:28: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:18:21: compiler.warn.strictfp", + "UnneededStrictfpWarning1.java:16:28: compiler.warn.strictfp", "5 warnings"); checkLog(log, expected); } diff --git a/test/langtools/tools/javac/warnings/suppress/T6480588.out b/test/langtools/tools/javac/warnings/suppress/T6480588.out index 630ba55523d..267ef32c964 100644 --- a/test/langtools/tools/javac/warnings/suppress/T6480588.out +++ b/test/langtools/tools/javac/warnings/suppress/T6480588.out @@ -1,19 +1,19 @@ T6480588.java:16:24: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:16:51: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package T6480588.java:15:2: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package +T6480588.java:18:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:18:12: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:18:65: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:30:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:33:25: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:33:52: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package +T6480588.java:32:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:17:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package -T6480588.java:18:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:29:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:19:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:19:34: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:21:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:21:25: compiler.warn.deprecated.annotation.has.no.effect: kindname.variable T6480588.java:21:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:30:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:29:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:30:33: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:33:25: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:33:52: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package -T6480588.java:32:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package 18 warnings

  • NameDescripionDescriptionDefault valueFormatExample values
    {@code maxSize}Specifies the size, measured in bytes, at which data is kept in disk + * Specifies the size, measured in bytes, at which data is kept in the disk * repository. Only works if * {@code disk=true}, otherwise this parameter is ignored.{@code "0"} (no limit)See {@code Paths#getPath} for format.
    * If this method is invoked from another process, the data is written on the * machine where the target JVM is running. If destination is a relative path, it - * is relative to the working directory where the target JVM was started.}
    {@code "c:\recording\recotding.jfr"},
    + * is relative to the working directory where the target JVM was started.
    {@code "c:\recording\recording.jfr"},
    * {@code "/recordings/recording.jfr"}, {@code "recording.jfr"}
    {@code duration}Sets how long the recording should be runningSpecifies the duration of the recording.{@code "0"} (no limit, continuous){@code "0"} if no limit should be imposed, otherwise a string * representation of a positive {@code Long} followed by an empty space and one @@ -287,7 +288,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { *
    NameDescripionDescriptionDefault valueFormatExample values{@code "1.0"}A version number with a major and minor.
    *
    - * To be able to read from a running recording the value must be set
    {@code "1.0"} *
    arm-linux-gnueabihf
    ppc64-linux-gnuppc64le-linux-gnu
    ppc64le-linux-gnuriscv64-linux-gnu
    s390x-linux-gnu