diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 067548b9768e3..b16335b7091c2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -363,26 +363,23 @@ jobs: - test-windows-x64 steps: - # Hack to get hold of the api environment variables that are only defined for actions - - name: 'Get API configuration' - id: api - uses: actions/github-script@v7 - with: - script: 'return { url: process.env["ACTIONS_RUNTIME_URL"], token: process.env["ACTIONS_RUNTIME_TOKEN"] }' - - name: 'Remove bundle artifacts' run: | # Find and remove all bundle artifacts - ALL_ARTIFACT_URLS="$(curl -s \ - -H 'Accept: application/json;api-version=6.0-preview' \ - -H 'Authorization: Bearer ${{ fromJson(steps.api.outputs.result).token }}' \ - '${{ fromJson(steps.api.outputs.result).url }}_apis/pipelines/workflows/${{ github.run_id }}/artifacts?api-version=6.0-preview')" - BUNDLE_ARTIFACT_URLS="$(echo "$ALL_ARTIFACT_URLS" | jq -r -c '.value | map(select(.name|startswith("bundles-"))) | .[].url')" - for url in $BUNDLE_ARTIFACT_URLS; do - echo "Removing $url" - curl -s \ - -H 'Accept: application/json;api-version=6.0-preview' \ - -H 'Authorization: Bearer ${{ fromJson(steps.api.outputs.result).token }}' \ - -X DELETE "$url" \ + # See: https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28 + ALL_ARTIFACT_IDS="$(curl -sL \ + -H 'Accept: application/vnd.github+json' \ + -H 'Authorization: Bearer ${{ github.token }}' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + '${{ github.api_url }}/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts')" + BUNDLE_ARTIFACT_IDS="$(echo "$ALL_ARTIFACT_IDS" | jq -r -c '.artifacts | map(select(.name|startswith("bundles-"))) | .[].id')" + for id in $BUNDLE_ARTIFACT_IDS; do + echo "Removing $id" + curl -sL \ + -X DELETE \ + -H 'Accept: application/vnd.github+json' \ + -H 'Authorization: Bearer ${{ github.token }}' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "${{ github.api_url }}/repos/${{ github.repository }}/actions/artifacts/$id" \ || echo "Failed to remove bundle" done diff --git a/make/RunTests.gmk b/make/RunTests.gmk index b0291e4eff4e7..8cfee45892339 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -748,8 +748,6 @@ define SetupRunJtregTestBody # we may end up with a lot of JVM's $1_JTREG_MAX_RAM_PERCENTAGE := $$(shell $(AWK) 'BEGIN { print 25 / $$($1_JTREG_JOBS); }') - JTREG_TIMEOUT_FACTOR ?= 4 - JTREG_VERBOSE ?= fail,error,summary JTREG_RETAIN ?= fail,error JTREG_TEST_THREAD_FACTORY ?= @@ -837,6 +835,24 @@ define SetupRunJtregTestBody $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$($1_JTREG_PROBLEM_LIST)) endif + JTREG_ALL_OPTIONS := $$(JTREG_JAVA_OPTIONS) $$(JTREG_VM_OPTIONS) + + JTREG_AUTO_PROBLEM_LISTS := + JTREG_AUTO_TIMEOUT_FACTOR := 4 + + ifneq ($$(findstring -Xcomp, $$(JTREG_ALL_OPTIONS)), ) + JTREG_AUTO_PROBLEM_LISTS += ProblemList-Xcomp.txt + JTREG_AUTO_TIMEOUT_FACTOR := 10 + endif + + ifneq ($$(findstring -XX:+UseZGC, $$(JTREG_ALL_OPTIONS)), ) + ifneq ($$(findstring -XX:-ZGenerational, $$(JTREG_ALL_OPTIONS)), ) + JTREG_AUTO_PROBLEM_LISTS += ProblemList-zgc.txt + else + JTREG_AUTO_PROBLEM_LISTS += ProblemList-generational-zgc.txt + endif + endif + ifneq ($$(JTREG_EXTRA_PROBLEM_LISTS), ) # Accept both absolute paths as well as relative to the current test root. $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ @@ -868,6 +884,18 @@ define SetupRunJtregTestBody $$(eval $$(call SetupRunJtregTestCustom, $1)) + # SetupRunJtregTestCustom might also adjust JTREG_AUTO_ variables + # so set the final results after setting values from custom setup + ifneq ($$(JTREG_AUTO_PROBLEM_LISTS), ) + # Accept both absolute paths as well as relative to the current test root. + $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ + $$(JTREG_AUTO_PROBLEM_LISTS) \ + $$(addprefix $$($1_TEST_ROOT)/, $$(JTREG_AUTO_PROBLEM_LISTS)) \ + )) + endif + + JTREG_TIMEOUT_FACTOR ?= $$(JTREG_AUTO_TIMEOUT_FACTOR) + clean-outputdirs-$1: $$(RM) -r $$($1_TEST_SUPPORT_DIR) $$(RM) -r $$($1_TEST_RESULTS_DIR) diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 6afa36ac18d33..f7e9844a64301 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -313,9 +313,11 @@ AC_OUTPUT # After AC_OUTPUT, we need to do final work CUSTOM_CONFIG_OUTPUT_GENERATED_HOOK -BASIC_POST_CONFIG_OUTPUT # Finally output some useful information to the user HELP_PRINT_SUMMARY_AND_WARNINGS CUSTOM_SUMMARY_AND_WARNINGS_HOOK HELP_REPEAT_WARNINGS + +# All output is done. Do the post-config output management. +BASIC_POST_CONFIG_OUTPUT diff --git a/make/autoconf/lib-alsa.m4 b/make/autoconf/lib-alsa.m4 index 19a91f94809f0..8d0fb324cd092 100644 --- a/make/autoconf/lib-alsa.m4 +++ b/make/autoconf/lib-alsa.m4 @@ -70,6 +70,25 @@ AC_DEFUN_ONCE([LIB_SETUP_ALSA], PKG_CHECK_MODULES(ALSA, alsa, [ALSA_FOUND=yes], [ALSA_FOUND=no]) fi fi + if test "x$ALSA_FOUND" = xno; then + # If we have sysroot set, and no explicit library location is set, + # look at known locations in sysroot. + if test "x$SYSROOT" != "x" && test "x${with_alsa_lib}" == x; then + if test -f "$SYSROOT/usr/lib64/libasound.so" && test "x$OPENJDK_TARGET_CPU_BITS" = x64; then + ALSA_LIBS="-L$SYSROOT/usr/lib64 -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI -lasound" + ALSA_FOUND=yes + fi + fi + fi if test "x$ALSA_FOUND" = xno; then AC_CHECK_HEADERS([alsa/asoundlib.h], [ diff --git a/make/autoconf/lib-x11.m4 b/make/autoconf/lib-x11.m4 index b1902a432a1e0..6849b4a26c776 100644 --- a/make/autoconf/lib-x11.m4 +++ b/make/autoconf/lib-x11.m4 @@ -71,9 +71,9 @@ AC_DEFUN_ONCE([LIB_SETUP_X11], elif test -f "$SYSROOT/usr/lib/libX11.so"; then x_libraries="$SYSROOT/usr/lib" elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so"; then - x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so" + x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI" elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so"; then - x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so" + x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI" fi fi fi diff --git a/make/common/native/Link.gmk b/make/common/native/Link.gmk index 2090218ffbb34..ca03c6ee6b12f 100644 --- a/make/common/native/Link.gmk +++ b/make/common/native/Link.gmk @@ -109,6 +109,11 @@ define CreateStaticLibrary $(if $$($1_LINK_OBJS_RELATIVE), $$(CD) $$(OUTPUTDIR) ; ) \ $$($1_LD) $(LDFLAGS_CXX_PARTIAL_LINKING) $$($1_SYSROOT_LDFLAGS) \ -o $$($1_TARGET_RELOCATABLE) $$($1_LD_OBJ_ARG)) + # 'ld -r' might invalidate the .llvm_addrsig section, and this will cause subsequent + # calls to lld (with '-Wl,--icf=safe') to fail when linking with this library, so + # remove that section. + $$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_objcopy_remove_llvm_addrsig_section, \ + $$($1_OBJCOPY) --remove-section=.llvm_addrsig $$($1_TARGET_RELOCATABLE)) endif $$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_run_ar, \ $(if $$($1_LINK_OBJS_RELATIVE), $$(CD) $$(OUTPUTDIR) ; ) \ diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index deb5e36f60572..3cb56b47b50b8 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -29,17 +29,17 @@ GTEST_VERSION=1.14.0 JTREG_VERSION=7.4+1 LINUX_X64_BOOT_JDK_EXT=tar.gz -LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22/830ec9fcccef480bb3e73fb7ecafe059/36/GPL/openjdk-22_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_SHA256=4d65cc6ed28711768fd72c2043a7925f7c83f5f51bb64970bd9d52f7791fc6ac - -MACOS_X64_BOOT_JDK_EXT=tar.gz -MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22/830ec9fcccef480bb3e73fb7ecafe059/36/GPL/openjdk-22_macos-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_SHA256=ae31fe10916429e3fe284266095067a5ce9fecbdc03ff1a079d20459f731ca36 +LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_SHA256=41536f115668308ecf4eba92aaf6acaeb0936225828b741efd83b6173ba82963 MACOS_AARCH64_BOOT_JDK_EXT=tar.gz -MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22/830ec9fcccef480bb3e73fb7ecafe059/36/GPL/openjdk-22_macos-aarch64_bin.tar.gz -MACOS_AARCH64_BOOT_JDK_SHA256=d10f82429d01047968c52c7975c326388cb5d212791e14c1de21c987463a4b53 +MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_macos-aarch64_bin.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=3dab98730234e1a87aec14bcb8171d2cae101e96ff4eed1dab96abbb08e843fd + +MACOS_X64_BOOT_JDK_EXT=tar.gz +MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_macos-x64_bin.tar.gz +MACOS_X64_BOOT_JDK_SHA256=e8b3ec7a7077711223d31156e771f11723cd7af31c2017f1bd2eda20855940fb WINDOWS_X64_BOOT_JDK_EXT=zip -WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22/830ec9fcccef480bb3e73fb7ecafe059/36/GPL/openjdk-22_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_SHA256=8f5138fecb53c08c20abd4fa6812f9400051f3852582a2142ffda0dff73a5824 +WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_SHA256=f2a9b9ab944e71a64637fcdc6b13a1188cf02d4eb9ecf71dc927e98b3e45f5dc diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk index 11f92585bd919..be29805f35271 100644 --- a/make/modules/java.desktop/lib/AwtLibraries.gmk +++ b/make/modules/java.desktop/lib/AwtLibraries.gmk @@ -237,7 +237,6 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false) DISABLED_WARNINGS_gcc := int-to-pointer-cast, \ DISABLED_WARNINGS_gcc_awt_Taskbar.c := parentheses, \ DISABLED_WARNINGS_gcc_GLXSurfaceData.c := unused-function, \ - DISABLED_WARNINGS_gcc_gtk2_interface.c := parentheses type-limits, \ DISABLED_WARNINGS_gcc_gtk3_interface.c := parentheses type-limits \ unused-function, \ DISABLED_WARNINGS_gcc_OGLBufImgOps.c := format-nonliteral, \ @@ -252,7 +251,6 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false) DISABLED_WARNINGS_gcc_XToolkit.c := unused-result, \ DISABLED_WARNINGS_gcc_XWindow.c := unused-function, \ DISABLED_WARNINGS_clang_awt_Taskbar.c := parentheses, \ - DISABLED_WARNINGS_clang_gtk2_interface.c := parentheses, \ DISABLED_WARNINGS_clang_gtk3_interface.c := parentheses, \ DISABLED_WARNINGS_clang_OGLBufImgOps.c := format-nonliteral, \ DISABLED_WARNINGS_clang_OGLPaints.c := format-nonliteral, \ @@ -262,8 +260,6 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false) DISABLED_WARNINGS_clang_aix_awt_Taskbar.c := parentheses, \ DISABLED_WARNINGS_clang_aix_OGLPaints.c := format-nonliteral, \ DISABLED_WARNINGS_clang_aix_OGLBufImgOps.c := format-nonliteral, \ - DISABLED_WARNINGS_clang_aix_gtk2_interface.c := parentheses \ - logical-op-parentheses, \ DISABLED_WARNINGS_clang_aix_gtk3_interface.c := parentheses \ logical-op-parentheses, \ DISABLED_WARNINGS_clang_aix_sun_awt_X11_GtkFileDialogPeer.c := \ diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index ecc7e34c917e0..fee5fe86031ff 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -871,6 +871,7 @@ ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libCompleteExit.c libMonitorWithDeadObjectTest.c libTestPsig.c exeGetCreatedJavaVMs.c BUILD_HOTSPOT_JTREG_LIBRARIES_JDK_LIBS_libnativeStack := java.base:libjvm + BUILD_HOTSPOT_JTREG_LIBRARIES_JDK_LIBS_libVThreadEventTest := java.base:libjvm else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libsystemclssearch_agent += -lpthread @@ -1509,6 +1510,7 @@ else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libCompleteExit += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libMonitorWithDeadObjectTest += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread + BUILD_HOTSPOT_JTREG_LIBRARIES_JDK_LIBS_libVThreadEventTest := java.base:libjvm BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -lpthread BUILD_HOTSPOT_JTREG_EXECUTABLES_JDK_LIBS_exeGetCreatedJavaVMs := java.base:libjvm diff --git a/src/demo/share/jfc/SwingSet2/SwingSet2.java b/src/demo/share/jfc/SwingSet2/SwingSet2.java index 13b3d63a6932f..75eeb4e365785 100644 --- a/src/demo/share/jfc/SwingSet2/SwingSet2.java +++ b/src/demo/share/jfc/SwingSet2/SwingSet2.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -848,7 +848,14 @@ private void updateThisSwingSet() { if (frame == null) { SwingUtilities.updateComponentTreeUI(this); } else { + if (currentLookAndFeel.name.contains("GTK")) { + this.setPreferredSize(new Dimension(PREFERRED_WIDTH + 260, PREFERRED_HEIGHT + 80)); + } else { + this.setPreferredSize(new Dimension(PREFERRED_WIDTH, PREFERRED_HEIGHT)); + } + SwingUtilities.updateComponentTreeUI(frame); + frame.pack(); } SwingUtilities.updateComponentTreeUI(popupMenu); diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index d4b3d63443877..d8db75961d8af 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2745,10 +2745,7 @@ typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, } if (index == -1) { - /* If we get an out-of-range offset it is a bug in the compiler, - so we assert here. */ - assert(Address::offset_ok_for_immed(disp, exact_log2(size_in_memory)), "c2 compiler bug"); - /* Fix up any out-of-range offsets. */ + // Fix up any out-of-range offsets. assert_different_registers(rscratch1, base); Address addr = Address(base, disp); addr = __ legitimize_address(addr, size_in_memory, rscratch1); @@ -3348,7 +3345,11 @@ encode %{ int scale = $mem$$scale; int disp = $mem$$disp; if (index == -1) { - __ prfm(Address(base, disp), PSTL1KEEP); + // Fix up any out-of-range offsets. + assert_different_registers(rscratch1, base); + Address addr = Address(base, disp); + addr = __ legitimize_address(addr, 8, rscratch1); + __ prfm(addr, PSTL1KEEP); } else { Register index_reg = as_Register(index); if (disp == 0) { @@ -4229,9 +4230,9 @@ operand immIOffset16() interface(CONST_INTER); %} -operand immLoffset() +operand immLOffset() %{ - predicate(Address::offset_ok_for_immed(n->get_long(), 0)); + predicate(n->get_long() >= -256 && n->get_long() <= 65520); match(ConL); op_cost(0); @@ -5341,6 +5342,34 @@ operand indOffL16(iRegP reg, immLoffset16 off) %} %} +operand indirectX2P(iRegL reg) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(CastX2P reg); + op_cost(0); + format %{ "[$reg]\t# long -> ptr" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp(0x0); + %} +%} + +operand indOffX2P(iRegL reg, immLOffset off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP (CastX2P reg) off); + op_cost(0); + format %{ "[$reg, $off]\t# long -> ptr" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + operand indirectN(iRegN reg) %{ predicate(CompressedOops::shift() == 0); @@ -5431,7 +5460,7 @@ operand indOffIN(iRegN reg, immIOffset off) %} %} -operand indOffLN(iRegN reg, immLoffset off) +operand indOffLN(iRegN reg, immLOffset off) %{ predicate(CompressedOops::shift() == 0); constraint(ALLOC_IN_RC(ptr_reg)); @@ -5664,6 +5693,17 @@ operand iRegL2I(iRegL reg) %{ interface(REG_INTER) %} +operand iRegL2P(iRegL reg) %{ + + op_cost(0); + + match(CastX2P reg); + + format %{ "l2p($reg)" %} + + interface(REG_INTER) +%} + opclass vmem2(indirect, indIndex, indOffI2, indOffL2); opclass vmem4(indirect, indIndex, indOffI4, indOffL4); opclass vmem8(indirect, indIndex, indOffI8, indOffL8); @@ -5680,21 +5720,21 @@ opclass vmem16(indirect, indIndex, indOffI16, indOffL16); // instruction defs. we can turn a memory op into an Address opclass memory1(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI1, indOffL1, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); opclass memory2(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI2, indOffL2, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); opclass memory4(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI4, indOffL4, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); opclass memory8(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI8, indOffL8, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); // All of the memory operands. For the pipeline description. opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI1, indOffL1, indOffI2, indOffL2, indOffI4, indOffL4, indOffI8, indOffL8, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); // iRegIorL2I is used for src inputs in rules for 32 bit int (I) @@ -5711,6 +5751,7 @@ opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indInde // movw is actually redundant but its not too costly. opclass iRegIorL2I(iRegI, iRegL2I); +opclass iRegPorL2P(iRegP, iRegL2P); //----------PIPELINE----------------------------------------------------------- // Rules which define the behavior of the target architectures pipeline. @@ -9811,7 +9852,7 @@ instruct addI_reg_imm_i2l(iRegINoSp dst, iRegL src1, immIAddSub src2) %{ %} // Pointer Addition -instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{ +instruct addP_reg_reg(iRegPNoSp dst, iRegPorL2P src1, iRegL src2) %{ match(Set dst (AddP src1 src2)); ins_cost(INSN_COST); @@ -9826,7 +9867,7 @@ instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{ ins_pipe(ialu_reg_reg); %} -instruct addP_reg_reg_ext(iRegPNoSp dst, iRegP src1, iRegIorL2I src2) %{ +instruct addP_reg_reg_ext(iRegPNoSp dst, iRegPorL2P src1, iRegIorL2I src2) %{ match(Set dst (AddP src1 (ConvI2L src2))); ins_cost(1.9 * INSN_COST); @@ -9841,7 +9882,7 @@ instruct addP_reg_reg_ext(iRegPNoSp dst, iRegP src1, iRegIorL2I src2) %{ ins_pipe(ialu_reg_reg); %} -instruct addP_reg_reg_lsl(iRegPNoSp dst, iRegP src1, iRegL src2, immIScale scale) %{ +instruct addP_reg_reg_lsl(iRegPNoSp dst, iRegPorL2P src1, iRegL src2, immIScale scale) %{ match(Set dst (AddP src1 (LShiftL src2 scale))); ins_cost(1.9 * INSN_COST); @@ -9856,7 +9897,7 @@ instruct addP_reg_reg_lsl(iRegPNoSp dst, iRegP src1, iRegL src2, immIScale scale ins_pipe(ialu_reg_reg_shift); %} -instruct addP_reg_reg_ext_shift(iRegPNoSp dst, iRegP src1, iRegIorL2I src2, immIScale scale) %{ +instruct addP_reg_reg_ext_shift(iRegPNoSp dst, iRegPorL2P src1, iRegIorL2I src2, immIScale scale) %{ match(Set dst (AddP src1 (LShiftL (ConvI2L src2) scale))); ins_cost(1.9 * INSN_COST); @@ -9889,7 +9930,7 @@ instruct lshift_ext(iRegLNoSp dst, iRegIorL2I src, immI scale, rFlagsReg cr) %{ // Pointer Immediate Addition // n.b. this needs to be more expensive than using an indirect memory // operand -instruct addP_reg_imm(iRegPNoSp dst, iRegP src1, immLAddSub src2) %{ +instruct addP_reg_imm(iRegPNoSp dst, iRegPorL2P src1, immLAddSub src2) %{ match(Set dst (AddP src1 src2)); ins_cost(INSN_COST); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 615c8e19ac863..91430be5835b5 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -647,7 +647,7 @@ void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmi break; case T_OBJECT: case T_ARRAY: - assert(c->as_jobject() == 0, "should be"); + assert(c->as_jobject() == nullptr, "should be"); if (UseCompressedOops && !wide) { insn = &Assembler::strw; } else { diff --git a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp index 63a32e714e365..780055a611f3b 100644 --- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1029,4 +1029,4 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { #undef __ -const char *Runtime1::pd_name_for_address(address entry) { Unimplemented(); return 0; } +const char *Runtime1::pd_name_for_address(address entry) { Unimplemented(); } diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp index 0387f763bbdd7..c2b127f31bb6d 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp @@ -293,7 +293,7 @@ void frame::patch_pc(Thread* thread, address pc) { // Either the return address is the original one or we are going to // patch in the same address that's already there. - assert(_pc == pc_old || pc == pc_old || pc_old == 0, ""); + assert(_pc == pc_old || pc == pc_old || pc_old == nullptr, ""); DEBUG_ONLY(address old_pc = _pc;) *pc_addr = signed_pc; _pc = pc; // must be set before call to get_deopt_original_pc @@ -497,10 +497,10 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const { bool frame::is_interpreted_frame_valid(JavaThread* thread) const { assert(is_interpreted_frame(), "Not an interpreted frame"); // These are reasonable sanity checks - if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { + if (fp() == nullptr || (intptr_t(fp()) & (wordSize-1)) != 0) { return false; } - if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { + if (sp() == nullptr || (intptr_t(sp()) & (wordSize-1)) != 0) { return false; } if (fp() + interpreter_frame_initial_sp_offset < sp()) { diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index 073d992235517..0161843036bc5 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -375,7 +375,7 @@ void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) { __ load_method_holder_cld(rscratch1, rmethod); // Is it a strong CLD? - __ ldrw(rscratch2, Address(rscratch1, ClassLoaderData::keep_alive_offset())); + __ ldrw(rscratch2, Address(rscratch1, ClassLoaderData::keep_alive_ref_count_offset())); __ cbnz(rscratch2, method_live); // Is it a weak but alive CLD? diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp index c02f93313b3cc..261502d582363 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp @@ -41,8 +41,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler* masm) { Register tmp2 = _tmp2->as_register(); Register result = result_opr()->as_register(); - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), newval, rscratch2); - if (UseCompressedOops) { __ encode_heap_oop(tmp1, cmpval); cmpval = tmp1; @@ -102,10 +100,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - assert(type == T_INT || is_reference_type(type) LP64_ONLY( || type == T_LONG ), "unexpected type"); LIR_Opr tmp = gen->new_register(T_INT); __ xchg(access.resolved_addr(), value_opr, result, tmp); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 5db29729239d3..06f4382015603 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -47,7 +47,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec Register src, Register dst, Register count, RegSet saved_regs) { if (is_oop) { bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; - if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahIUBarrier || ShenandoahLoadRefBarrier) { + if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) { Label done; @@ -300,14 +300,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ leave(); } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) { - if (ShenandoahIUBarrier) { - __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm, noreg, dst, rthread, tmp, rscratch1, true, false); - __ pop_call_clobbered_registers(); - } -} - // // Arguments: // @@ -398,8 +390,7 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet if (val == noreg) { BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg); } else { - iu_barrier(masm, val, tmp1); - // G1 barrier needs uncompressed oop for region cross check. + // Barrier needs uncompressed oop for region cross check. Register new_val = val; if (UseCompressedOops) { new_val = rscratch2; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index 375893702e1de..ee11b2e73f73f 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -60,9 +60,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators); public: - - void iu_barrier(MacroAssembler* masm, Register dst, Register tmp); - virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad index c7c7165affb57..6e401724baa82 100644 --- a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -62,7 +62,13 @@ instruct xLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) format %{ "ldr $dst, $mem" %} ins_encode %{ - const Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + if (ref_addr.getMode() == Address::base_plus_offset) { + // Fix up any out-of-range offsets. + assert_different_registers(rscratch1, as_Register($mem$$base)); + assert_different_registers(rscratch1, $dst$$Register); + ref_addr = __ legitimize_address(ref_addr, 8, rscratch1); + } __ ldr($dst$$Register, ref_addr); x_load_barrier(masm, this, ref_addr, $dst$$Register, rscratch2 /* tmp */, barrier_data()); %} diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 92181e2b6b908..56d4538477920 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -111,7 +111,13 @@ instruct zLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) format %{ "ldr $dst, $mem" %} ins_encode %{ - const Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + if (ref_addr.getMode() == Address::base_plus_offset) { + // Fix up any out-of-range offsets. + assert_different_registers(rscratch2, as_Register($mem$$base)); + assert_different_registers(rscratch2, $dst$$Register); + ref_addr = __ legitimize_address(ref_addr, 8, rscratch2); + } __ ldr($dst$$Register, ref_addr); z_load_barrier(masm, this, ref_addr, $dst$$Register, rscratch1); %} diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index f0f032409d35d..34dca56eaf746 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -35,8 +35,6 @@ typedef ByteSize (*OffsetFunction)(uint); class InterpreterMacroAssembler: public MacroAssembler { - protected: - protected: // Interpreter specific version of call_VM_base using MacroAssembler::call_VM_leaf_base; @@ -112,8 +110,6 @@ class InterpreterMacroAssembler: public MacroAssembler { void get_dispatch(); - // Helpers for runtime call arguments/results - // Helpers for runtime call arguments/results void get_method(Register reg) { ldr(reg, Address(rfp, frame::interpreter_frame_method_offset * wordSize)); @@ -181,7 +177,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_ptr(int n, Register val); void store_ptr(int n, Register val); -// Load float value from 'address'. The value is loaded onto the FPU register v0. + // Load float value from 'address'. The value is loaded onto the FPU register v0. void load_float(Address src); void load_double(Address src); diff --git a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp index e79b93651b072..aea268ea94443 100644 --- a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -201,7 +201,7 @@ address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { { __ enter(); - __ lea(rscratch1, ExternalAddress(slow_case_addr)); + __ lea(rscratch1, RuntimeAddress(slow_case_addr)); __ blr(rscratch1); __ leave(); __ ret(lr); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index f90aefc8fd3e6..25dfaca6dca20 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -215,7 +215,7 @@ class RelocActions { break; } else { // nothing to do - assert(target == 0, "did not expect to relocate target for polling page load"); + assert(target == nullptr, "did not expect to relocate target for polling page load"); } break; } @@ -736,7 +736,7 @@ void MacroAssembler::reserved_stack_check() { br(Assembler::LO, no_reserved_zone_enabling); enter(); // LR and FP are live. - lea(rscratch1, CAST_FROM_FN_PTR(address, SharedRuntime::enable_stack_reserved_zone)); + lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::enable_stack_reserved_zone))); mov(c_rarg0, rthread); blr(rscratch1); leave(); @@ -1879,7 +1879,7 @@ void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file, movptr(rscratch1, (uintptr_t)(address)b); // call indirectly to solve generation ordering problem - lea(rscratch2, ExternalAddress(StubRoutines::verify_oop_subroutine_entry_address())); + lea(rscratch2, RuntimeAddress(StubRoutines::verify_oop_subroutine_entry_address())); ldr(rscratch2, Address(rscratch2)); blr(rscratch2); @@ -1918,7 +1918,7 @@ void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* f movptr(rscratch1, (uintptr_t)(address)b); // call indirectly to solve generation ordering problem - lea(rscratch2, ExternalAddress(StubRoutines::verify_oop_subroutine_entry_address())); + lea(rscratch2, RuntimeAddress(StubRoutines::verify_oop_subroutine_entry_address())); ldr(rscratch2, Address(rscratch2)); blr(rscratch2); @@ -6454,7 +6454,7 @@ void MacroAssembler::verify_cross_modify_fence_not_required() { Label fence_not_required; cbz(rscratch1, fence_not_required); // If it does then fail. - lea(rscratch1, CAST_FROM_FN_PTR(address, JavaThread::verify_cross_modify_fence_failure)); + lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::verify_cross_modify_fence_failure))); mov(c_rarg0, rthread); blr(rscratch1); bind(fence_not_required); diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index 5a3f9d228ca89..770ed2f6c3868 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -228,7 +228,7 @@ address NativeJump::jump_destination() const { // load // return -1 if jump to self or to 0 - if ((dest == (address)this) || dest == 0) { + if ((dest == (address)this) || dest == nullptr) { dest = (address) -1; } return dest; @@ -256,7 +256,7 @@ address NativeGeneralJump::jump_destination() const { // a general jump // return -1 if jump to self or to 0 - if ((dest == (address)this) || dest == 0) { + if ((dest == (address)this) || dest == nullptr) { dest = (address) -1; } return dest; diff --git a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index 61d27e3224248..09bb370f210f7 100644 --- a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp @@ -36,6 +36,349 @@ #include "runtime/vframeArray.hpp" #include "utilities/globalDefinitions.hpp" #include "vmreg_aarch64.inline.hpp" + +class SimpleRuntimeFrame { + + public: + + // Most of the runtime stubs have this simple frame layout. + // This class exists to make the layout shared in one place. + // Offsets are for compiler stack slots, which are jints. + enum layout { + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + // we don't expect any arg reg save area so aarch64 asserts that + // frame::arg_reg_save_area_bytes == 0 + rfp_off = 0, + rfp_off2, + return_off, return_off2, + framesize + }; +}; + +#define __ masm-> + +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + address start = __ pc(); + + // Push self-frame. We get here with a return address in LR + // and sp should be 16 byte aligned + // push rfp and retaddr by hand + __ protect_return_address(); + __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); + // we don't expect an arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // compiler left unloaded_class_index in j_rarg0 move to where the + // runtime expects it. + if (c_rarg1 != j_rarg0) { + __ movw(c_rarg1, j_rarg0); + } + + // we need to set the past SP to the stack pointer of the stub frame + // and the pc to the address where this runtime call will return + // although actually any pc in this code blob will do). + Label retaddr; + __ set_last_Java_frame(sp, noreg, retaddr, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + // + // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); + // + // n.b. 2 gp args, 0 fp args, integral return type + + __ mov(c_rarg0, rthread); + __ movw(c_rarg2, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ lea(rscratch1, + RuntimeAddress(CAST_FROM_FN_PTR(address, + Deoptimization::uncommon_trap))); + __ blr(rscratch1); + __ bind(retaddr); + + // Set an oopmap for the call site + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); + + // location of rfp is known implicitly by the frame sender code + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false); + + // move UnrollBlock* into r4 + __ mov(r4, r0); + +#ifdef ASSERT + { Label L; + __ ldrw(rscratch1, Address(r4, Deoptimization::UnrollBlock::unpack_kind_offset())); + __ cmpw(rscratch1, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ br(Assembler::EQ, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on r0 and sp. + __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! + + // Pop deoptimized frame (int) + __ ldrw(r2, Address(r4, + Deoptimization::UnrollBlock:: + size_of_deoptimized_frame_offset())); + __ sub(r2, r2, 2 * wordSize); + __ add(sp, sp, r2); + __ ldp(rfp, zr, __ post(sp, 2 * wordSize)); + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ ldrw(r1, Address(r4, + Deoptimization::UnrollBlock:: + total_frame_sizes_offset())); + __ bang_stack_size(r1, r2); +#endif + + // Load address of array of frame pcs into r2 (address*) + __ ldr(r2, Address(r4, + Deoptimization::UnrollBlock::frame_pcs_offset())); + + // Load address of array of frame sizes into r5 (intptr_t*) + __ ldr(r5, Address(r4, + Deoptimization::UnrollBlock:: + frame_sizes_offset())); + + // Counter + __ ldrw(r3, Address(r4, + Deoptimization::UnrollBlock:: + number_of_frames_offset())); // (int) + + // Now adjust the caller's stack to make up for the extra locals but + // record the original sp so that we can save it in the skeletal + // interpreter frame and the stack walking of interpreter_sender + // will get the unextended sp value and not the "real" sp value. + + const Register sender_sp = r8; + + __ mov(sender_sp, sp); + __ ldrw(r1, Address(r4, + Deoptimization::UnrollBlock:: + caller_adjustment_offset())); // (int) + __ sub(sp, sp, r1); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ ldr(r1, Address(r5, 0)); // Load frame size + __ sub(r1, r1, 2 * wordSize); // We'll push pc and rfp by hand + __ ldr(lr, Address(r2, 0)); // Save return address + __ enter(); // and old rfp & set new rfp + __ sub(sp, sp, r1); // Prolog + __ str(sender_sp, Address(rfp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable + // This value is corrected by layout_activation_impl + __ str(zr, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ mov(sender_sp, sp); // Pass sender_sp to next frame + __ add(r5, r5, wordSize); // Bump array pointer (sizes) + __ add(r2, r2, wordSize); // Bump array pointer (pcs) + __ subsw(r3, r3, 1); // Decrement counter + __ br(Assembler::GT, loop); + __ ldr(lr, Address(r2, 0)); // save final return address + // Re-push self-frame + __ enter(); // & old rfp & set new rfp + + // Use rfp because the frames look interpreted now + // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. + // Don't need the precise return PC here, just precise enough to point into this code blob. + address the_pc = __ pc(); + __ set_last_Java_frame(sp, rfp, the_pc, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // Thread is in rdi already. + // + // BasicType unpack_frames(JavaThread* thread, int exec_mode); + // + // n.b. 2 gp args, 0 fp args, integral return type + + // sp should already be aligned + __ mov(c_rarg0, rthread); + __ movw(c_rarg1, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + __ blr(rscratch1); + + // Set an oopmap for the call site + // Use the same PC we used for the last java frame + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + // Clear fp AND pc + __ reset_last_Java_frame(true); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(lr); + + // Make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); +} + +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in aarch64.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// r0: exception oop +// r3: exception pc +// +// Results: +// r0: exception oop +// r3: exception pc in caller or ??? +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Registers r0, r3, r2, r4, r5, r8-r11 are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + assert(!OptoRuntime::is_callee_saved_register(R3_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R0_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R2_num), ""); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("exception_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + // TODO check various assumptions made here + // + // make sure we do so before running this + + address start = __ pc(); + + // push rfp and retaddr by hand + // Exception pc is 'return address' for stack walker + __ protect_return_address(); + __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); + // there are no callee save registers and we don't expect an + // arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + __ str(r0, Address(rthread, JavaThread::exception_oop_offset())); + __ str(r3, Address(rthread, JavaThread::exception_pc_offset())); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + // address OptoRuntime::handle_exception_C(JavaThread* thread) + // + // n.b. 1 gp arg, 0 fp args, integral return type + + // the stack should always be aligned + address the_pc = __ pc(); + __ set_last_Java_frame(sp, noreg, the_pc, rscratch1); + __ mov(c_rarg0, rthread); + __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); + __ blr(rscratch1); + // handle_exception_C is a special VM call which does not require an explicit + // instruction sync afterwards. + + // May jump to SVE compiled code + __ reinitialize_ptrue(); + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet* oop_maps = new OopMapSet(); + + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(false); + + // Restore callee-saved registers + + // rfp is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers now that adapter frames are gone. + // and we dont' expect an arg reg save area + __ ldp(rfp, r3, Address(__ post(sp, 2 * wordSize))); + __ authenticate_return_address(r3); + + // r0: exception handler + + // We have a handler in r0 (could be deopt blob). + __ mov(r8, r0); + + // Get the exception oop + __ ldr(r0, Address(rthread, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ ldr(r4, Address(rthread, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ str(zr, Address(rthread, JavaThread::exception_handler_pc_offset())); + __ str(zr, Address(rthread, JavaThread::exception_pc_offset())); #endif + // Clear the exception oop so GC no longer processes it as a root. + __ str(zr, Address(rthread, JavaThread::exception_oop_offset())); + + // r0: exception oop + // r8: exception handler + // r4: exception pc + // Jump to handler + + __ br(r8); + + // Make sure all code is generated + masm->flush(); + + // Set exception blob + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index bb2554e65ce83..8ce4230baa2b4 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -68,26 +68,6 @@ const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; -class SimpleRuntimeFrame { - - public: - - // Most of the runtime stubs have this simple frame layout. - // This class exists to make the layout shared in one place. - // Offsets are for compiler stack slots, which are jints. - enum layout { - // The frame sender code expects that rbp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - // we don't expect any arg reg save area so aarch64 asserts that - // frame::arg_reg_save_area_bytes == 0 - rfp_off = 0, - rfp_off2, - return_off, return_off2, - framesize - }; -}; - // FIXME -- this is used by C1 class RegisterSaver { const bool _save_vectors; @@ -1903,16 +1883,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Check for safepoint operation in progress and/or pending suspend requests. { - // We need an acquire here to ensure that any subsequent load of the - // global SafepointSynchronize::_state flag is ordered after this load - // of the thread-local polling word. We don't want this poll to - // return false (i.e. not safepointing) and a later poll of the global - // SafepointSynchronize::_state spuriously to return true. - // - // This is to avoid a race when we're in a native->Java transition - // racing the code which wakes up from a safepoint. - - __ safepoint_poll(safepoint_in_progress, true /* at_return */, true /* acquire */, false /* in_nmethod */); + // No need for acquire as Java threads always disarm themselves. + __ safepoint_poll(safepoint_in_progress, true /* at_return */, false /* acquire */, false /* in_nmethod */); __ ldrw(rscratch1, Address(rthread, JavaThread::suspend_flags_offset())); __ cbnzw(rscratch1, safepoint_in_progress); __ bind(safepoint_in_progress_done); @@ -2581,197 +2553,6 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - address start = __ pc(); - - // Push self-frame. We get here with a return address in LR - // and sp should be 16 byte aligned - // push rfp and retaddr by hand - __ protect_return_address(); - __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); - // we don't expect an arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // compiler left unloaded_class_index in j_rarg0 move to where the - // runtime expects it. - if (c_rarg1 != j_rarg0) { - __ movw(c_rarg1, j_rarg0); - } - - // we need to set the past SP to the stack pointer of the stub frame - // and the pc to the address where this runtime call will return - // although actually any pc in this code blob will do). - Label retaddr; - __ set_last_Java_frame(sp, noreg, retaddr, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - // Thread is in rdi already. - // - // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); - // - // n.b. 2 gp args, 0 fp args, integral return type - - __ mov(c_rarg0, rthread); - __ movw(c_rarg2, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ lea(rscratch1, - RuntimeAddress(CAST_FROM_FN_PTR(address, - Deoptimization::uncommon_trap))); - __ blr(rscratch1); - __ bind(retaddr); - - // Set an oopmap for the call site - OopMapSet* oop_maps = new OopMapSet(); - OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); - - // location of rfp is known implicitly by the frame sender code - - oop_maps->add_gc_map(__ pc() - start, map); - - __ reset_last_Java_frame(false); - - // move UnrollBlock* into r4 - __ mov(r4, r0); - -#ifdef ASSERT - { Label L; - __ ldrw(rscratch1, Address(r4, Deoptimization::UnrollBlock::unpack_kind_offset())); - __ cmpw(rscratch1, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ br(Assembler::EQ, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - // Pop self-frame. We have no frame, and must rely only on r0 and sp. - __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! - - // Pop deoptimized frame (int) - __ ldrw(r2, Address(r4, - Deoptimization::UnrollBlock:: - size_of_deoptimized_frame_offset())); - __ sub(r2, r2, 2 * wordSize); - __ add(sp, sp, r2); - __ ldp(rfp, zr, __ post(sp, 2 * wordSize)); - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ ldrw(r1, Address(r4, - Deoptimization::UnrollBlock:: - total_frame_sizes_offset())); - __ bang_stack_size(r1, r2); -#endif - - // Load address of array of frame pcs into r2 (address*) - __ ldr(r2, Address(r4, - Deoptimization::UnrollBlock::frame_pcs_offset())); - - // Load address of array of frame sizes into r5 (intptr_t*) - __ ldr(r5, Address(r4, - Deoptimization::UnrollBlock:: - frame_sizes_offset())); - - // Counter - __ ldrw(r3, Address(r4, - Deoptimization::UnrollBlock:: - number_of_frames_offset())); // (int) - - // Now adjust the caller's stack to make up for the extra locals but - // record the original sp so that we can save it in the skeletal - // interpreter frame and the stack walking of interpreter_sender - // will get the unextended sp value and not the "real" sp value. - - const Register sender_sp = r8; - - __ mov(sender_sp, sp); - __ ldrw(r1, Address(r4, - Deoptimization::UnrollBlock:: - caller_adjustment_offset())); // (int) - __ sub(sp, sp, r1); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ ldr(r1, Address(r5, 0)); // Load frame size - __ sub(r1, r1, 2 * wordSize); // We'll push pc and rfp by hand - __ ldr(lr, Address(r2, 0)); // Save return address - __ enter(); // and old rfp & set new rfp - __ sub(sp, sp, r1); // Prolog - __ str(sender_sp, Address(rfp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable - // This value is corrected by layout_activation_impl - __ str(zr, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize)); - __ mov(sender_sp, sp); // Pass sender_sp to next frame - __ add(r5, r5, wordSize); // Bump array pointer (sizes) - __ add(r2, r2, wordSize); // Bump array pointer (pcs) - __ subsw(r3, r3, 1); // Decrement counter - __ br(Assembler::GT, loop); - __ ldr(lr, Address(r2, 0)); // save final return address - // Re-push self-frame - __ enter(); // & old rfp & set new rfp - - // Use rfp because the frames look interpreted now - // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. - // Don't need the precise return PC here, just precise enough to point into this code blob. - address the_pc = __ pc(); - __ set_last_Java_frame(sp, rfp, the_pc, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - // Thread is in rdi already. - // - // BasicType unpack_frames(JavaThread* thread, int exec_mode); - // - // n.b. 2 gp args, 0 fp args, integral return type - - // sp should already be aligned - __ mov(c_rarg0, rthread); - __ movw(c_rarg1, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); - __ blr(rscratch1); - - // Set an oopmap for the call site - // Use the same PC we used for the last java frame - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - // Clear fp AND pc - __ reset_last_Java_frame(true); - - // Pop self-frame. - __ leave(); // Epilog - - // Jump to interpreter - __ ret(lr); - - // Make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // @@ -2983,141 +2764,3 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // frame_size_words or bytes?? return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true); } - -#ifdef COMPILER2 -// This is here instead of runtime_aarch64_64.cpp because it uses SimpleRuntimeFrame -// -//------------------------------generate_exception_blob--------------------------- -// creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in x86_64.ad file) -// -// Given an exception pc at a call we call into the runtime for the -// handler in this method. This handler might merely restore state -// (i.e. callee save registers) unwind the frame and jump to the -// exception handler for the nmethod if there is no Java level handler -// for the nmethod. -// -// This code is entered with a jmp. -// -// Arguments: -// r0: exception oop -// r3: exception pc -// -// Results: -// r0: exception oop -// r3: exception pc in caller or ??? -// destination: exception handler of caller -// -// Note: the exception pc MUST be at a call (precise debug information) -// Registers r0, r3, r2, r4, r5, r8-r11 are not callee saved. -// - -void OptoRuntime::generate_exception_blob() { - assert(!OptoRuntime::is_callee_saved_register(R3_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R0_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R2_num), ""); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("exception_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - // TODO check various assumptions made here - // - // make sure we do so before running this - - address start = __ pc(); - - // push rfp and retaddr by hand - // Exception pc is 'return address' for stack walker - __ protect_return_address(); - __ stp(rfp, lr, Address(__ pre(sp, -2 * wordSize))); - // there are no callee save registers and we don't expect an - // arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // Store exception in Thread object. We cannot pass any arguments to the - // handle_exception call, since we do not want to make any assumption - // about the size of the frame where the exception happened in. - __ str(r0, Address(rthread, JavaThread::exception_oop_offset())); - __ str(r3, Address(rthread, JavaThread::exception_pc_offset())); - - // This call does all the hard work. It checks if an exception handler - // exists in the method. - // If so, it returns the handler address. - // If not, it prepares for stack-unwinding, restoring the callee-save - // registers of the frame being removed. - // - // address OptoRuntime::handle_exception_C(JavaThread* thread) - // - // n.b. 1 gp arg, 0 fp args, integral return type - - // the stack should always be aligned - address the_pc = __ pc(); - __ set_last_Java_frame(sp, noreg, the_pc, rscratch1); - __ mov(c_rarg0, rthread); - __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); - __ blr(rscratch1); - // handle_exception_C is a special VM call which does not require an explicit - // instruction sync afterwards. - - // May jump to SVE compiled code - __ reinitialize_ptrue(); - - // Set an oopmap for the call site. This oopmap will only be used if we - // are unwinding the stack. Hence, all locations will be dead. - // Callee-saved registers will be the same as the frame above (i.e., - // handle_exception_stub), since they were restored when we got the - // exception. - - OopMapSet* oop_maps = new OopMapSet(); - - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - __ reset_last_Java_frame(false); - - // Restore callee-saved registers - - // rfp is an implicitly saved callee saved register (i.e. the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers now that adapter frames are gone. - // and we dont' expect an arg reg save area - __ ldp(rfp, r3, Address(__ post(sp, 2 * wordSize))); - __ authenticate_return_address(r3); - - // r0: exception handler - - // We have a handler in r0 (could be deopt blob). - __ mov(r8, r0); - - // Get the exception oop - __ ldr(r0, Address(rthread, JavaThread::exception_oop_offset())); - // Get the exception pc in case we are deoptimized - __ ldr(r4, Address(rthread, JavaThread::exception_pc_offset())); -#ifdef ASSERT - __ str(zr, Address(rthread, JavaThread::exception_handler_pc_offset())); - __ str(zr, Address(rthread, JavaThread::exception_pc_offset())); -#endif - // Clear the exception oop so GC no longer processes it as a root. - __ str(zr, Address(rthread, JavaThread::exception_oop_offset())); - - // r0: exception oop - // r8: exception handler - // r4: exception pc - // Jump to handler - - __ br(r8); - - // Make sure all code is generated - masm->flush(); - - // Set exception blob - _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); -} - -#endif // COMPILER2 diff --git a/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp index db38b9cf5dfe3..dcba233c9dc75 100644 --- a/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ assert (r == rfp->as_VMReg() || r == rfp->as_VMReg()->next(), "Reg: %s", r->name()); }) @@ -48,17 +55,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - #ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) assert_is_rfp(r); - } - #endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert_is_rfp(reg); return (address)(sp - frame::sender_sp_offset); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 3f1a4423b5efe..5e2ef97e4a3ad 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -7045,7 +7045,7 @@ class StubGenerator: public StubCodeGenerator { Label thaw_success; // rscratch2 contains the size of the frames to thaw, 0 if overflow or no more frames __ cbnz(rscratch2, thaw_success); - __ lea(rscratch1, ExternalAddress(StubRoutines::throw_StackOverflowError_entry())); + __ lea(rscratch1, RuntimeAddress(StubRoutines::throw_StackOverflowError_entry())); __ br(rscratch1); __ bind(thaw_success); diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 89f5fbd281b79..ed2450a9110e5 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1337,8 +1337,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { { Label L; __ ldr(r10, Address(rmethod, Method::native_function_offset())); - address unsatisfied = (SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); - __ mov(rscratch2, unsatisfied); + ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); + __ lea(rscratch2, unsatisfied); __ ldr(rscratch2, rscratch2); __ cmp(r10, rscratch2); __ br(Assembler::NE, L); @@ -1413,15 +1413,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { { Label L, Continue; - // We need an acquire here to ensure that any subsequent load of the - // global SafepointSynchronize::_state flag is ordered after this load - // of the thread-local polling word. We don't want this poll to - // return false (i.e. not safepointing) and a later poll of the global - // SafepointSynchronize::_state spuriously to return true. - // - // This is to avoid a race when we're in a native->Java transition - // racing the code which wakes up from a safepoint. - __ safepoint_poll(L, true /* at_return */, true /* acquire */, false /* in_nmethod */); + // No need for acquire as Java threads always disarm themselves. + __ safepoint_poll(L, true /* at_return */, false /* acquire */, false /* in_nmethod */); __ ldrw(rscratch2, Address(rthread, JavaThread::suspend_flags_offset())); __ cbz(rscratch2, Continue); __ bind(L); @@ -1432,7 +1425,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // hand. // __ mov(c_rarg0, rthread); - __ mov(rscratch2, CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)); + __ lea(rscratch2, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); __ blr(rscratch2); __ get_method(rmethod); __ reinit_heapbase(); @@ -1482,7 +1475,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ push_call_clobbered_registers(); __ mov(c_rarg0, rthread); - __ mov(rscratch2, CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); + __ lea(rscratch2, RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); __ blr(rscratch2); __ pop_call_clobbered_registers(); @@ -2085,7 +2078,7 @@ void TemplateInterpreterGenerator::trace_bytecode(Template* t) { assert(Interpreter::trace_code(t->tos_in()) != nullptr, "entry must have been generated"); - __ bl(Interpreter::trace_code(t->tos_in())); + __ bl(RuntimeAddress(Interpreter::trace_code(t->tos_in()))); __ reinit_heapbase(); } diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index aa64f411dbfb9..c0f10adc85df9 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -606,12 +606,12 @@ static bool check_info_file(const char* fpath, return false; } while (fgets(line, sizeof(line), fp) != nullptr) { - if (strcasestr(line, virt1) != 0) { + if (strcasestr(line, virt1) != nullptr) { Abstract_VM_Version::_detected_virtualization = vt1; fclose(fp); return true; } - if (virt2 != nullptr && strcasestr(line, virt2) != 0) { + if (virt2 != nullptr && strcasestr(line, virt2) != nullptr) { Abstract_VM_Version::_detected_virtualization = vt2; fclose(fp); return true; diff --git a/src/hotspot/cpu/arm/runtime_arm.cpp b/src/hotspot/cpu/arm/runtime_arm.cpp index 94a9ef553c75e..6f6c0c17e000d 100644 --- a/src/hotspot/cpu/arm/runtime_arm.cpp +++ b/src/hotspot/cpu/arm/runtime_arm.cpp @@ -37,10 +37,146 @@ #include "runtime/vframeArray.hpp" #include "utilities/globalDefinitions.hpp" #include "vmreg_arm.inline.hpp" -#endif #define __ masm-> +//------------------------------generate_uncommon_trap_blob-------------------- +// Ought to generate an ideal graph & compile, but here's some ASM +// instead. +void OptoRuntime::generate_uncommon_trap_blob() { + // allocate space for the code + ResourceMark rm; + + // setup code generation tools +#ifdef _LP64 + CodeBuffer buffer("uncommon_trap_blob", 2700, 512); +#else + // Measured 8/7/03 at 660 in 32bit debug build + CodeBuffer buffer("uncommon_trap_blob", 2000, 512); +#endif + // bypassed when code generation useless + MacroAssembler* masm = new MacroAssembler(&buffer); + const Register Rublock = R6; + const Register Rsender = altFP_7_11; + assert_different_registers(Rublock, Rsender, Rexception_obj, R0, R1, R2, R3, R8, Rtemp); + + // + // This is the entry point for all traps the compiler takes when it thinks + // it cannot handle further execution of compilation code. The frame is + // deoptimized in these cases and converted into interpreter frames for + // execution + // The steps taken by this frame are as follows: + // - push a fake "unpack_frame" + // - call the C routine Deoptimization::uncommon_trap (this function + // packs the current compiled frame into vframe arrays and returns + // information about the number and size of interpreter frames which + // are equivalent to the frame which is being deoptimized) + // - deallocate the "unpack_frame" + // - deallocate the deoptimization frame + // - in a loop using the information returned in the previous step + // push interpreter frames; + // - create a dummy "unpack_frame" + // - call the C routine: Deoptimization::unpack_frames (this function + // lays out values on the interpreter frame which was just created) + // - deallocate the dummy unpack_frame + // - return to the interpreter entry point + // + // Refer to the following methods for more information: + // - Deoptimization::uncommon_trap + // - Deoptimization::unpack_frame + + // the unloaded class index is in R0 (first parameter to this blob) + + __ raw_push(FP, LR); + __ set_last_Java_frame(SP, FP, false, Rtemp); + __ mov(R2, Deoptimization::Unpack_uncommon_trap); + __ mov(R1, R0); + __ mov(R0, Rthread); + __ call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); + __ mov(Rublock, R0); + __ reset_last_Java_frame(Rtemp); + __ raw_pop(FP, LR); + +#ifdef ASSERT + { Label L; + __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::unpack_kind_offset())); + __ cmp_32(Rtemp, Deoptimization::Unpack_uncommon_trap); + __ b(L, eq); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + + // Set initial stack state before pushing interpreter frames + __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); + __ ldr(R2, Address(Rublock, Deoptimization::UnrollBlock::frame_pcs_offset())); + __ ldr(R3, Address(Rublock, Deoptimization::UnrollBlock::frame_sizes_offset())); + + __ add(SP, SP, Rtemp); + + // See if it is enough stack to push deoptimized frames. +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + // + // The compiled method that we are deoptimizing was popped from the stack. + // If the stack bang results in a stack overflow, we don't return to the + // method that is being deoptimized. The stack overflow exception is + // propagated to the caller of the deoptimized method. Need to get the pc + // from the caller in LR and restore FP. + __ ldr(LR, Address(R2, 0)); + __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); + __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::total_frame_sizes_offset())); + __ arm_stack_overflow_check(R8, Rtemp); +#endif + __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::number_of_frames_offset())); + __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::caller_adjustment_offset())); + __ mov(Rsender, SP); + __ sub(SP, SP, Rtemp); + // __ ldr(FP, Address(FP)); + __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ ldr(LR, Address(R2, wordSize, post_indexed)); // load frame pc + __ ldr(Rtemp, Address(R3, wordSize, post_indexed)); // load frame size + + __ raw_push(FP, LR); // create new frame + __ mov(FP, SP); + __ sub(Rtemp, Rtemp, 2*wordSize); + + __ sub(SP, SP, Rtemp); + + __ str(Rsender, Address(FP, frame::interpreter_frame_sender_sp_offset * wordSize)); + __ mov(LR, 0); + __ str(LR, Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); + __ subs(R8, R8, 1); // decrement counter + __ mov(Rsender, SP); + __ b(loop, ne); + + // Re-push self-frame + __ ldr(LR, Address(R2)); + __ raw_push(FP, LR); + __ mov(FP, SP); + + // Call unpack_frames with proper arguments + __ mov(R0, Rthread); + __ mov(R1, Deoptimization::Unpack_uncommon_trap); + __ set_last_Java_frame(SP, FP, true, Rtemp); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); + // oop_maps->add_gc_map(__ pc() - start, new OopMap(frame_size_in_words, 0)); + __ reset_last_Java_frame(Rtemp); + + __ mov(SP, FP); + __ pop(RegisterSet(FP) | RegisterSet(PC)); + + masm->flush(); + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, nullptr, 2 /* LR+FP */); +} + //------------------------------ generate_exception_blob --------------------------- // creates exception blob at the end // Using exception blob, this code is jumped from a compiled method. @@ -148,3 +284,6 @@ void OptoRuntime::generate_exception_blob() { _exception_blob = ExceptionBlob::create(&buffer, oop_maps, framesize_in_words); } + +#endif // COMPILER2 + diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index 3792fab082ba6..1305283aeaeba 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -1595,147 +1595,6 @@ void SharedRuntime::generate_deopt_blob() { _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); } -#ifdef COMPILER2 - -//------------------------------generate_uncommon_trap_blob-------------------- -// Ought to generate an ideal graph & compile, but here's some ASM -// instead. -void SharedRuntime::generate_uncommon_trap_blob() { - // allocate space for the code - ResourceMark rm; - - // setup code generation tools -#ifdef _LP64 - CodeBuffer buffer("uncommon_trap_blob", 2700, 512); -#else - // Measured 8/7/03 at 660 in 32bit debug build - CodeBuffer buffer("uncommon_trap_blob", 2000, 512); -#endif - // bypassed when code generation useless - MacroAssembler* masm = new MacroAssembler(&buffer); - const Register Rublock = R6; - const Register Rsender = altFP_7_11; - assert_different_registers(Rublock, Rsender, Rexception_obj, R0, R1, R2, R3, R8, Rtemp); - - // - // This is the entry point for all traps the compiler takes when it thinks - // it cannot handle further execution of compilation code. The frame is - // deoptimized in these cases and converted into interpreter frames for - // execution - // The steps taken by this frame are as follows: - // - push a fake "unpack_frame" - // - call the C routine Deoptimization::uncommon_trap (this function - // packs the current compiled frame into vframe arrays and returns - // information about the number and size of interpreter frames which - // are equivalent to the frame which is being deoptimized) - // - deallocate the "unpack_frame" - // - deallocate the deoptimization frame - // - in a loop using the information returned in the previous step - // push interpreter frames; - // - create a dummy "unpack_frame" - // - call the C routine: Deoptimization::unpack_frames (this function - // lays out values on the interpreter frame which was just created) - // - deallocate the dummy unpack_frame - // - return to the interpreter entry point - // - // Refer to the following methods for more information: - // - Deoptimization::uncommon_trap - // - Deoptimization::unpack_frame - - // the unloaded class index is in R0 (first parameter to this blob) - - __ raw_push(FP, LR); - __ set_last_Java_frame(SP, FP, false, Rtemp); - __ mov(R2, Deoptimization::Unpack_uncommon_trap); - __ mov(R1, R0); - __ mov(R0, Rthread); - __ call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); - __ mov(Rublock, R0); - __ reset_last_Java_frame(Rtemp); - __ raw_pop(FP, LR); - -#ifdef ASSERT - { Label L; - __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::unpack_kind_offset())); - __ cmp_32(Rtemp, Deoptimization::Unpack_uncommon_trap); - __ b(L, eq); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - - // Set initial stack state before pushing interpreter frames - __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); - __ ldr(R2, Address(Rublock, Deoptimization::UnrollBlock::frame_pcs_offset())); - __ ldr(R3, Address(Rublock, Deoptimization::UnrollBlock::frame_sizes_offset())); - - __ add(SP, SP, Rtemp); - - // See if it is enough stack to push deoptimized frames. -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - // - // The compiled method that we are deoptimizing was popped from the stack. - // If the stack bang results in a stack overflow, we don't return to the - // method that is being deoptimized. The stack overflow exception is - // propagated to the caller of the deoptimized method. Need to get the pc - // from the caller in LR and restore FP. - __ ldr(LR, Address(R2, 0)); - __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); - __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::total_frame_sizes_offset())); - __ arm_stack_overflow_check(R8, Rtemp); -#endif - __ ldr_s32(R8, Address(Rublock, Deoptimization::UnrollBlock::number_of_frames_offset())); - __ ldr_s32(Rtemp, Address(Rublock, Deoptimization::UnrollBlock::caller_adjustment_offset())); - __ mov(Rsender, SP); - __ sub(SP, SP, Rtemp); - // __ ldr(FP, Address(FP)); - __ ldr(FP, Address(Rublock, Deoptimization::UnrollBlock::initial_info_offset())); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ ldr(LR, Address(R2, wordSize, post_indexed)); // load frame pc - __ ldr(Rtemp, Address(R3, wordSize, post_indexed)); // load frame size - - __ raw_push(FP, LR); // create new frame - __ mov(FP, SP); - __ sub(Rtemp, Rtemp, 2*wordSize); - - __ sub(SP, SP, Rtemp); - - __ str(Rsender, Address(FP, frame::interpreter_frame_sender_sp_offset * wordSize)); - __ mov(LR, 0); - __ str(LR, Address(FP, frame::interpreter_frame_last_sp_offset * wordSize)); - __ subs(R8, R8, 1); // decrement counter - __ mov(Rsender, SP); - __ b(loop, ne); - - // Re-push self-frame - __ ldr(LR, Address(R2)); - __ raw_push(FP, LR); - __ mov(FP, SP); - - // Call unpack_frames with proper arguments - __ mov(R0, Rthread); - __ mov(R1, Deoptimization::Unpack_uncommon_trap); - __ set_last_Java_frame(SP, FP, true, Rtemp); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); - // oop_maps->add_gc_map(__ pc() - start, new OopMap(frame_size_in_words, 0)); - __ reset_last_Java_frame(Rtemp); - - __ mov(SP, FP); - __ pop(RegisterSet(FP) | RegisterSet(PC)); - - masm->flush(); - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, nullptr, 2 /* LR+FP */); -} - -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, diff --git a/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp b/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp index 4186eafd35f59..08adbbd89d892 100644 --- a/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp +++ b/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ Unimplemented(); }) @@ -46,12 +53,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - Unimplemented(); - } - inline address location(VMReg reg, intptr_t* sp) const { Unimplemented(); return nullptr; diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp index 16de0b6a7ddd5..956b082d194b4 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -238,7 +238,7 @@ void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler *masm, Register tmp1, __ ld(tmp1_class_loader_data, in_bytes(InstanceKlass::class_loader_data_offset()), tmp1); // Fast path: If class loader is strong, the holder cannot be unloaded. - __ lwz(tmp2, in_bytes(ClassLoaderData::keep_alive_offset()), tmp1_class_loader_data); + __ lwz(tmp2, in_bytes(ClassLoaderData::keep_alive_ref_count_offset()), tmp1_class_loader_data); __ cmpdi(CCR0, tmp2, 0); __ bne(CCR0, skip_barrier); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp index 6d9a1db1ed4b8..5f87281bdf967 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp @@ -43,11 +43,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler *masm) { Register tmp2 = _tmp2->as_register(); Register result = result_opr()->as_register(); - if (ShenandoahIUBarrier) { - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), new_val, tmp1, tmp2, - MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); - } - if (UseCompressedOops) { __ encode_heap_oop(cmp_val, cmp_val); __ encode_heap_oop(new_val, new_val); @@ -122,10 +117,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess &access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - assert(type == T_INT || is_reference_type(type) LP64_ONLY( || type == T_LONG ), "unexpected type"); LIR_Opr tmp_xchg = gen->new_register(T_INT); __ xchg(access.resolved_addr(), value_opr, result, tmp_xchg); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 535fe88a68075..62aa9a2bf5930 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -61,20 +61,6 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier(MacroAssembler *masm, } } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler *masm, - Register val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level, - DecoratorSet decorators) { - // IU barriers are also employed to avoid resurrection of weak references, - // even if Shenandoah does not operate in incremental update mode. - if (ShenandoahIUBarrier || ShenandoahSATBBarrier) { - __ block_comment("iu_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, decorators, noreg, noreg, val, tmp1, tmp2, preservation_level); - __ block_comment("} iu_barrier (shenandoahgc)"); - } -} - void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler *masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, @@ -110,7 +96,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, Dec // Fast path: No barrier required if for every barrier type, it is either disabled or would not store // any useful information. - if ((!ShenandoahSATBBarrier || dest_uninitialized) && !ShenandoahIUBarrier && !ShenandoahLoadRefBarrier) { + if ((!ShenandoahSATBBarrier || dest_uninitialized) && !ShenandoahLoadRefBarrier) { return; } @@ -582,7 +568,11 @@ void ShenandoahBarrierSetAssembler::load_at( /* ==== Apply keep-alive barrier, if required (e.g., to inhibit weak reference resurrection) ==== */ if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { - iu_barrier(masm, dst, tmp1, tmp2, preservation_level); + if (ShenandoahSATBBarrier) { + __ block_comment("keep_alive_barrier (shenandoahgc) {"); + satb_write_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); + __ block_comment("} keep_alive_barrier (shenandoahgc)"); + } } } @@ -597,10 +587,6 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler *masm, DecoratorSet if (ShenandoahSATBBarrier) { satb_write_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); } - - if (ShenandoahIUBarrier && val != noreg) { - iu_barrier(masm, val, tmp1, tmp2, preservation_level, decorators); - } } BarrierSetAssembler::store_at(masm, decorators, type, diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 4514f2540ac9b..2e56187c169b2 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -82,11 +82,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp1, Register tmp2, Register tmp3, MacroAssembler::PreservationLevel preservation_level); - void iu_barrier(MacroAssembler* masm, - Register val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level, DecoratorSet decorators = 0); - void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 9b5a86bc45bfd..505f67ab6f952 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -3078,7 +3078,7 @@ void SharedRuntime::generate_deopt_blob() { } #ifdef COMPILER2 -void SharedRuntime::generate_uncommon_trap_blob() { +void OptoRuntime::generate_uncommon_trap_blob() { // Allocate space for the code. ResourceMark rm; // Setup code generation tools. @@ -3144,7 +3144,7 @@ void SharedRuntime::generate_uncommon_trap_blob() { #ifdef ASSERT __ lwz(R22_tmp2, in_bytes(Deoptimization::UnrollBlock::unpack_kind_offset()), unroll_block_reg); __ cmpdi(CCR0, R22_tmp2, (unsigned)Deoptimization::Unpack_uncommon_trap); - __ asm_assert_eq("SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap"); + __ asm_assert_eq("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); #endif // Freezing continuation frames requires that the caller is trimmed to unextended sp if compiled. diff --git a/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp b/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp index 8c96f51fd88eb..a6246cd7e74fb 100644 --- a/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp @@ -30,9 +30,16 @@ // Java frames don't have callee saved registers, so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; -public: + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } + // as_RegisterMap is used when we didn't want to templatize and abstract over RegisterMap type to support SmallRegisterMap // Consider enhancing SmallRegisterMap to support those cases const RegisterMap* as_RegisterMap() const { return nullptr; } @@ -44,19 +51,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { -#ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) { - assert(false, "Reg: %s", r->name()); // Should not reach here - } - } -#endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert(false, "Reg: %s", reg->name()); return nullptr; diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index e4280ab34e1ce..79279be7acc1b 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -1828,6 +1828,19 @@ enum Nf { #undef INSN +#define INSN(NAME, op, width, umop, mop, mew, nf) \ + void NAME(VectorRegister Vd_or_Vs3, Register Rs1, VectorMask vm = unmasked) { \ + patch_VLdSt(op, Vd_or_Vs3, width, Rs1, umop, vm, mop, mew, nf); \ + } + + // Vector Unit-Stride Segment Load Instructions + INSN(vlseg3e8_v, 0b0000111, 0b000, 0b00000, 0b00, 0b0, g3); + + // Vector Unit-Stride Segment Store Instructions + INSN(vsseg4e8_v, 0b0100111, 0b000, 0b00000, 0b00, 0b0, g4); + +#undef INSN + #define INSN(NAME, op, width, mop, mew) \ void NAME(VectorRegister Vd, Register Rs1, VectorRegister Vs2, VectorMask vm = unmasked, Nf nf = g1) { \ patch_VLdSt(op, Vd, width, Rs1, Vs2->raw_encoding(), vm, mop, mew, nf); \ diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 798679185d3a2..3d146b87707aa 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -542,7 +542,7 @@ void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmi insn = &MacroAssembler::sw; break; case T_OBJECT: // fall through case T_ARRAY: - assert(c->as_jobject() == 0, "should be"); + assert(c->as_jobject() == nullptr, "should be"); if (UseCompressedOops && !wide) { insn = &MacroAssembler::sw; } else { diff --git a/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp b/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp index 9fa8939837a85..e999b703b3e7b 100644 --- a/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp @@ -1066,4 +1066,4 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { #undef __ -const char *Runtime1::pd_name_for_address(address entry) { Unimplemented(); return 0; } +const char *Runtime1::pd_name_for_address(address entry) { Unimplemented(); } diff --git a/src/hotspot/cpu/riscv/frame_riscv.cpp b/src/hotspot/cpu/riscv/frame_riscv.cpp index 32825a02c5eac..96dca4704ad95 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.cpp +++ b/src/hotspot/cpu/riscv/frame_riscv.cpp @@ -269,7 +269,7 @@ void frame::patch_pc(Thread* thread, address pc) { // Either the return address is the original one or we are going to // patch in the same address that's already there. - assert(_pc == pc_old || pc == pc_old || pc_old == 0, "must be"); + assert(_pc == pc_old || pc == pc_old || pc_old == nullptr, "must be"); DEBUG_ONLY(address old_pc = _pc;) *pc_addr = pc; _pc = pc; // must be set before call to get_deopt_original_pc diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index 0f3850809775c..efdf3765965f7 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -326,7 +326,7 @@ void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) { __ load_method_holder_cld(t0, xmethod); // Is it a strong CLD? - __ lwu(t1, Address(t0, ClassLoaderData::keep_alive_offset())); + __ lwu(t1, Address(t0, ClassLoaderData::keep_alive_ref_count_offset())); __ bnez(t1, method_live); // Is it a weak but alive CLD? diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp index cd568cc723fe9..f503cb762e792 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp @@ -41,8 +41,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler* masm) { Register tmp2 = _tmp2->as_register(); Register result = result_opr()->as_register(); - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), newval, t1); - if (UseCompressedOops) { __ encode_heap_oop(tmp1, cmpval); cmpval = tmp1; @@ -94,10 +92,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - assert(type == T_INT || is_reference_type(type) LP64_ONLY( || type == T_LONG ), "unexpected type"); LIR_Opr tmp = gen->new_register(T_INT); __ xchg(access.resolved_addr(), value_opr, result, tmp); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 9be8259e7e85d..9a79a92327723 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -48,7 +48,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec Register src, Register dst, Register count, RegSet saved_regs) { if (is_oop) { bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; - if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahIUBarrier || ShenandoahLoadRefBarrier) { + if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) { Label done; @@ -308,16 +308,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ leave(); } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) { - if (ShenandoahIUBarrier) { - __ push_call_clobbered_registers(); - - satb_write_barrier_pre(masm, noreg, dst, xthread, tmp, t0, true, false); - - __ pop_call_clobbered_registers(); - } -} - // // Arguments: // @@ -420,8 +410,7 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet if (val == noreg) { BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg); } else { - iu_barrier(masm, val, tmp1); - // G1 barrier needs uncompressed oop for region cross check. + // Barrier needs uncompressed oop for region cross check. Register new_val = val; if (UseCompressedOops) { new_val = t1; diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index 0ae352f325d16..bfdea7f607ea8 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -63,8 +63,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { public: - void iu_barrier(MacroAssembler* masm, Register dst, Register tmp); - virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/riscv/jniFastGetField_riscv.cpp b/src/hotspot/cpu/riscv/jniFastGetField_riscv.cpp index 8423ecad8a3da..f7d702c631069 100644 --- a/src/hotspot/cpu/riscv/jniFastGetField_riscv.cpp +++ b/src/hotspot/cpu/riscv/jniFastGetField_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -173,12 +173,7 @@ address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { { __ enter(); - ExternalAddress target(slow_case_addr); - __ relocate(target.rspec(), [&] { - int32_t offset; - __ la(t0, target.target(), offset); - __ jalr(t0, offset); - }); + __ rt_call(slow_case_addr); __ leave(); __ ret(); } diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index e349eab317760..e0b24b7fd66ca 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -547,7 +547,7 @@ void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file, } // call indirectly to solve generation ordering problem - ExternalAddress target(StubRoutines::verify_oop_subroutine_entry_address()); + RuntimeAddress target(StubRoutines::verify_oop_subroutine_entry_address()); relocate(target.rspec(), [&] { int32_t offset; la(t1, target.target(), offset); @@ -592,7 +592,7 @@ void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* f } // call indirectly to solve generation ordering problem - ExternalAddress target(StubRoutines::verify_oop_subroutine_entry_address()); + RuntimeAddress target(StubRoutines::verify_oop_subroutine_entry_address()); relocate(target.rspec(), [&] { int32_t offset; la(t1, target.target(), offset); @@ -4130,29 +4130,25 @@ void MacroAssembler::remove_frame(int framesize) { } void MacroAssembler::reserved_stack_check() { - // testing if reserved zone needs to be enabled - Label no_reserved_zone_enabling; + // testing if reserved zone needs to be enabled + Label no_reserved_zone_enabling; - ld(t0, Address(xthread, JavaThread::reserved_stack_activation_offset())); - bltu(sp, t0, no_reserved_zone_enabling); + ld(t0, Address(xthread, JavaThread::reserved_stack_activation_offset())); + bltu(sp, t0, no_reserved_zone_enabling); - enter(); // RA and FP are live. - mv(c_rarg0, xthread); - rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::enable_stack_reserved_zone)); - leave(); + enter(); // RA and FP are live. + mv(c_rarg0, xthread); + rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::enable_stack_reserved_zone)); + leave(); - // We have already removed our own frame. - // throw_delayed_StackOverflowError will think that it's been - // called by our caller. - RuntimeAddress target(StubRoutines::throw_delayed_StackOverflowError_entry()); - relocate(target.rspec(), [&] { - int32_t offset; - movptr(t0, target.target(), offset); - jr(t0, offset); - }); - should_not_reach_here(); + // We have already removed our own frame. + // throw_delayed_StackOverflowError will think that it's been + // called by our caller. + la(t0, RuntimeAddress(StubRoutines::throw_delayed_StackOverflowError_entry())); + jr(t0); + should_not_reach_here(); - bind(no_reserved_zone_enabling); + bind(no_reserved_zone_enabling); } // Move the address of the polling page into dest. diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp index f0357f1cd3029..6c9e0986869b6 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp @@ -685,7 +685,7 @@ address NativeJump::jump_destination() const { // load // return -1 if jump to self or to 0 - if ((dest == (address) this) || dest == 0) { + if ((dest == (address) this) || dest == nullptr) { dest = (address) -1; } @@ -714,7 +714,7 @@ address NativeGeneralJump::jump_destination() const { // a general jump // return -1 if jump to self or to 0 - if ((dest == (address) this) || dest == 0) { + if ((dest == (address) this) || dest == nullptr) { dest = (address) -1; } diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index b176c6e9cb7c2..1e26c2bf14202 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -84,8 +84,8 @@ reg_def R0 ( NS, NS, Op_RegI, 0, x0->as_VMReg() ); // zr reg_def R0_H ( NS, NS, Op_RegI, 0, x0->as_VMReg()->next() ); reg_def R1 ( NS, SOC, Op_RegI, 1, x1->as_VMReg() ); // ra reg_def R1_H ( NS, SOC, Op_RegI, 1, x1->as_VMReg()->next() ); -reg_def R2 ( NS, SOE, Op_RegI, 2, x2->as_VMReg() ); // sp -reg_def R2_H ( NS, SOE, Op_RegI, 2, x2->as_VMReg()->next() ); +reg_def R2 ( NS, NS, Op_RegI, 2, x2->as_VMReg() ); // sp +reg_def R2_H ( NS, NS, Op_RegI, 2, x2->as_VMReg()->next() ); reg_def R3 ( NS, NS, Op_RegI, 3, x3->as_VMReg() ); // gp reg_def R3_H ( NS, NS, Op_RegI, 3, x3->as_VMReg()->next() ); reg_def R4 ( NS, NS, Op_RegI, 4, x4->as_VMReg() ); // tp diff --git a/src/hotspot/cpu/riscv/runtime_riscv.cpp b/src/hotspot/cpu/riscv/runtime_riscv.cpp new file mode 100644 index 0000000000000..9e16278c3b547 --- /dev/null +++ b/src/hotspot/cpu/riscv/runtime_riscv.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * 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 "precompiled.hpp" +#ifdef COMPILER2 +#include "asm/macroAssembler.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "code/vmreg.hpp" +#include "interpreter/interpreter.hpp" +#include "opto/runtime.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/vframeArray.hpp" +#include "utilities/globalDefinitions.hpp" +#include "vmreg_riscv.inline.hpp" + +class SimpleRuntimeFrame { +public: + + // Most of the runtime stubs have this simple frame layout. + // This class exists to make the layout shared in one place. + // Offsets are for compiler stack slots, which are jints. + enum layout { + // The frame sender code expects that fp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + // we don't expect any arg reg save area so riscv asserts that + // frame::arg_reg_save_area_bytes == 0 + fp_off = 0, fp_off2, + return_off, return_off2, + framesize + }; +}; + +#define __ masm-> + +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + assert_cond(masm != nullptr); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + address start = __ pc(); + + // Push self-frame. We get here with a return address in RA + // and sp should be 16 byte aligned + // push fp and retaddr by hand + __ addi(sp, sp, -2 * wordSize); + __ sd(ra, Address(sp, wordSize)); + __ sd(fp, Address(sp, 0)); + // we don't expect an arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // compiler left unloaded_class_index in j_rarg0 move to where the + // runtime expects it. + __ sign_extend(c_rarg1, j_rarg0, 32); + + // we need to set the past SP to the stack pointer of the stub frame + // and the pc to the address where this runtime call will return + // although actually any pc in this code blob will do). + Label retaddr; + __ set_last_Java_frame(sp, noreg, retaddr, t0); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + // + // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index, jint exec_mode) + // + // n.b. 3 gp args, 0 fp args, integral return type + + __ mv(c_rarg0, xthread); + __ mv(c_rarg2, Deoptimization::Unpack_uncommon_trap); + __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); + __ bind(retaddr); + + // Set an oopmap for the call site + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); + assert_cond(oop_maps != nullptr && map != nullptr); + + // location of fp is known implicitly by the frame sender code + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false); + + // move UnrollBlock* into x14 + __ mv(x14, x10); + +#ifdef ASSERT + { Label L; + __ lwu(t0, Address(x14, Deoptimization::UnrollBlock::unpack_kind_offset())); + __ mv(t1, Deoptimization::Unpack_uncommon_trap); + __ beq(t0, t1, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! + + // Pop deoptimized frame (int) + __ lwu(x12, Address(x14, + Deoptimization::UnrollBlock:: + size_of_deoptimized_frame_offset())); + __ sub(x12, x12, 2 * wordSize); + __ add(sp, sp, x12); + __ ld(fp, Address(sp, 0)); + __ ld(ra, Address(sp, wordSize)); + __ addi(sp, sp, 2 * wordSize); + // RA should now be the return address to the caller (3) frame + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ lwu(x11, Address(x14, + Deoptimization::UnrollBlock:: + total_frame_sizes_offset())); + __ bang_stack_size(x11, x12); +#endif + + // Load address of array of frame pcs into x12 (address*) + __ ld(x12, Address(x14, + Deoptimization::UnrollBlock::frame_pcs_offset())); + + // Load address of array of frame sizes into x15 (intptr_t*) + __ ld(x15, Address(x14, + Deoptimization::UnrollBlock:: + frame_sizes_offset())); + + // Counter + __ lwu(x13, Address(x14, + Deoptimization::UnrollBlock:: + number_of_frames_offset())); // (int) + + // Now adjust the caller's stack to make up for the extra locals but + // record the original sp so that we can save it in the skeletal + // interpreter frame and the stack walking of interpreter_sender + // will get the unextended sp value and not the "real" sp value. + + const Register sender_sp = t1; // temporary register + + __ lwu(x11, Address(x14, + Deoptimization::UnrollBlock:: + caller_adjustment_offset())); // (int) + __ mv(sender_sp, sp); + __ sub(sp, sp, x11); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ ld(x11, Address(x15, 0)); // Load frame size + __ sub(x11, x11, 2 * wordSize); // We'll push pc and fp by hand + __ ld(ra, Address(x12, 0)); // Save return address + __ enter(); // and old fp & set new fp + __ sub(sp, sp, x11); // Prolog + __ sd(sender_sp, Address(fp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable + // This value is corrected by layout_activation_impl + __ sd(zr, Address(fp, frame::interpreter_frame_last_sp_offset * wordSize)); + __ mv(sender_sp, sp); // Pass sender_sp to next frame + __ add(x15, x15, wordSize); // Bump array pointer (sizes) + __ add(x12, x12, wordSize); // Bump array pointer (pcs) + __ subw(x13, x13, 1); // Decrement counter + __ bgtz(x13, loop); + __ ld(ra, Address(x12, 0)); // save final return address + // Re-push self-frame + __ enter(); // & old fp & set new fp + + // Use fp because the frames look interpreted now + // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. + // Don't need the precise return PC here, just precise enough to point into this code blob. + address the_pc = __ pc(); + __ set_last_Java_frame(sp, fp, the_pc, t0); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // + // BasicType unpack_frames(JavaThread* thread, int exec_mode) + // + + // n.b. 2 gp args, 0 fp args, integral return type + + // sp should already be aligned + __ mv(c_rarg0, xthread); + __ mv(c_rarg1, Deoptimization::Unpack_uncommon_trap); + __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); + + // Set an oopmap for the call site + // Use the same PC we used for the last java frame + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + // Clear fp AND pc + __ reset_last_Java_frame(true); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(); + + // Make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); +} + +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in riscv.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// x10: exception oop +// x13: exception pc +// +// Results: +// x10: exception oop +// x13: exception pc in caller +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Registers x10, x13, x12, x14, x15, t0 are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + assert(!OptoRuntime::is_callee_saved_register(R13_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R10_num), ""); + assert(!OptoRuntime::is_callee_saved_register(R12_num), ""); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("exception_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + assert_cond(masm != nullptr); + + // TODO check various assumptions made here + // + // make sure we do so before running this + + address start = __ pc(); + + // push fp and retaddr by hand + // Exception pc is 'return address' for stack walker + __ addi(sp, sp, -2 * wordSize); + __ sd(ra, Address(sp, wordSize)); + __ sd(fp, Address(sp)); + // there are no callee save registers and we don't expect an + // arg reg save area +#ifndef PRODUCT + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); +#endif + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + __ sd(x10, Address(xthread, JavaThread::exception_oop_offset())); + __ sd(x13, Address(xthread, JavaThread::exception_pc_offset())); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + // address OptoRuntime::handle_exception_C(JavaThread* thread) + // + // n.b. 1 gp arg, 0 fp args, integral return type + + // the stack should always be aligned + address the_pc = __ pc(); + __ set_last_Java_frame(sp, noreg, the_pc, t0); + __ mv(c_rarg0, xthread); + __ rt_call(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C)); + + // handle_exception_C is a special VM call which does not require an explicit + // instruction sync afterwards. + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet* oop_maps = new OopMapSet(); + assert_cond(oop_maps != nullptr); + + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(false); + + // Restore callee-saved registers + + // fp is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers now that adapter frames are gone. + // and we dont' expect an arg reg save area + __ ld(fp, Address(sp)); + __ ld(x13, Address(sp, wordSize)); + __ addi(sp, sp , 2 * wordSize); + + // x10: exception handler + + // We have a handler in x10 (could be deopt blob). + __ mv(t0, x10); + + // Get the exception oop + __ ld(x10, Address(xthread, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ ld(x14, Address(xthread, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ sd(zr, Address(xthread, JavaThread::exception_handler_pc_offset())); + __ sd(zr, Address(xthread, JavaThread::exception_pc_offset())); +#endif + // Clear the exception oop so GC no longer processes it as a root. + __ sd(zr, Address(xthread, JavaThread::exception_oop_offset())); + + // x10: exception oop + // t0: exception handler + // x14: exception pc + // Jump to handler + + __ jr(t0); + + // Make sure all code is generated + masm->flush(); + + // Set exception blob + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 + + diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 01ab3d5c27403..ad06f688d6a36 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -67,24 +67,6 @@ const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; -class SimpleRuntimeFrame { -public: - - // Most of the runtime stubs have this simple frame layout. - // This class exists to make the layout shared in one place. - // Offsets are for compiler stack slots, which are jints. - enum layout { - // The frame sender code expects that fp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - // we don't expect any arg reg save area so riscv asserts that - // frame::arg_reg_save_area_bytes == 0 - fp_off = 0, fp_off2, - return_off, return_off2, - framesize - }; -}; - class RegisterSaver { const bool _save_vectors; public: @@ -2441,195 +2423,6 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - assert_cond(masm != nullptr); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - address start = __ pc(); - - // Push self-frame. We get here with a return address in RA - // and sp should be 16 byte aligned - // push fp and retaddr by hand - __ addi(sp, sp, -2 * wordSize); - __ sd(ra, Address(sp, wordSize)); - __ sd(fp, Address(sp, 0)); - // we don't expect an arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // compiler left unloaded_class_index in j_rarg0 move to where the - // runtime expects it. - __ sign_extend(c_rarg1, j_rarg0, 32); - - // we need to set the past SP to the stack pointer of the stub frame - // and the pc to the address where this runtime call will return - // although actually any pc in this code blob will do). - Label retaddr; - __ set_last_Java_frame(sp, noreg, retaddr, t0); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - // - // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index, jint exec_mode) - // - // n.b. 3 gp args, 0 fp args, integral return type - - __ mv(c_rarg0, xthread); - __ mv(c_rarg2, Deoptimization::Unpack_uncommon_trap); - __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); - __ bind(retaddr); - - // Set an oopmap for the call site - OopMapSet* oop_maps = new OopMapSet(); - OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); - assert_cond(oop_maps != nullptr && map != nullptr); - - // location of fp is known implicitly by the frame sender code - - oop_maps->add_gc_map(__ pc() - start, map); - - __ reset_last_Java_frame(false); - - // move UnrollBlock* into x14 - __ mv(x14, x10); - -#ifdef ASSERT - { Label L; - __ lwu(t0, Address(x14, Deoptimization::UnrollBlock::unpack_kind_offset())); - __ mv(t1, Deoptimization::Unpack_uncommon_trap); - __ beq(t0, t1, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - __ add(sp, sp, (SimpleRuntimeFrame::framesize) << LogBytesPerInt); // Epilog! - - // Pop deoptimized frame (int) - __ lwu(x12, Address(x14, - Deoptimization::UnrollBlock:: - size_of_deoptimized_frame_offset())); - __ sub(x12, x12, 2 * wordSize); - __ add(sp, sp, x12); - __ ld(fp, Address(sp, 0)); - __ ld(ra, Address(sp, wordSize)); - __ addi(sp, sp, 2 * wordSize); - // RA should now be the return address to the caller (3) frame - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ lwu(x11, Address(x14, - Deoptimization::UnrollBlock:: - total_frame_sizes_offset())); - __ bang_stack_size(x11, x12); -#endif - - // Load address of array of frame pcs into x12 (address*) - __ ld(x12, Address(x14, - Deoptimization::UnrollBlock::frame_pcs_offset())); - - // Load address of array of frame sizes into x15 (intptr_t*) - __ ld(x15, Address(x14, - Deoptimization::UnrollBlock:: - frame_sizes_offset())); - - // Counter - __ lwu(x13, Address(x14, - Deoptimization::UnrollBlock:: - number_of_frames_offset())); // (int) - - // Now adjust the caller's stack to make up for the extra locals but - // record the original sp so that we can save it in the skeletal - // interpreter frame and the stack walking of interpreter_sender - // will get the unextended sp value and not the "real" sp value. - - const Register sender_sp = t1; // temporary register - - __ lwu(x11, Address(x14, - Deoptimization::UnrollBlock:: - caller_adjustment_offset())); // (int) - __ mv(sender_sp, sp); - __ sub(sp, sp, x11); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ ld(x11, Address(x15, 0)); // Load frame size - __ sub(x11, x11, 2 * wordSize); // We'll push pc and fp by hand - __ ld(ra, Address(x12, 0)); // Save return address - __ enter(); // and old fp & set new fp - __ sub(sp, sp, x11); // Prolog - __ sd(sender_sp, Address(fp, frame::interpreter_frame_sender_sp_offset * wordSize)); // Make it walkable - // This value is corrected by layout_activation_impl - __ sd(zr, Address(fp, frame::interpreter_frame_last_sp_offset * wordSize)); - __ mv(sender_sp, sp); // Pass sender_sp to next frame - __ add(x15, x15, wordSize); // Bump array pointer (sizes) - __ add(x12, x12, wordSize); // Bump array pointer (pcs) - __ subw(x13, x13, 1); // Decrement counter - __ bgtz(x13, loop); - __ ld(ra, Address(x12, 0)); // save final return address - // Re-push self-frame - __ enter(); // & old fp & set new fp - - // Use fp because the frames look interpreted now - // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. - // Don't need the precise return PC here, just precise enough to point into this code blob. - address the_pc = __ pc(); - __ set_last_Java_frame(sp, fp, the_pc, t0); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - // - // BasicType unpack_frames(JavaThread* thread, int exec_mode) - // - - // n.b. 2 gp args, 0 fp args, integral return type - - // sp should already be aligned - __ mv(c_rarg0, xthread); - __ mv(c_rarg1, Deoptimization::Unpack_uncommon_trap); - __ rt_call(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames)); - - // Set an oopmap for the call site - // Use the same PC we used for the last java frame - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - // Clear fp AND pc - __ reset_last_Java_frame(true); - - // Pop self-frame. - __ leave(); // Epilog - - // Jump to interpreter - __ ret(); - - // Make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, @@ -2835,139 +2628,3 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // return the blob return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true); } - -#ifdef COMPILER2 -//------------------------------generate_exception_blob--------------------------- -// creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in riscv.ad file) -// -// Given an exception pc at a call we call into the runtime for the -// handler in this method. This handler might merely restore state -// (i.e. callee save registers) unwind the frame and jump to the -// exception handler for the nmethod if there is no Java level handler -// for the nmethod. -// -// This code is entered with a jmp. -// -// Arguments: -// x10: exception oop -// x13: exception pc -// -// Results: -// x10: exception oop -// x13: exception pc in caller -// destination: exception handler of caller -// -// Note: the exception pc MUST be at a call (precise debug information) -// Registers x10, x13, x12, x14, x15, t0 are not callee saved. -// - -void OptoRuntime::generate_exception_blob() { - assert(!OptoRuntime::is_callee_saved_register(R13_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R10_num), ""); - assert(!OptoRuntime::is_callee_saved_register(R12_num), ""); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("exception_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - assert_cond(masm != nullptr); - - // TODO check various assumptions made here - // - // make sure we do so before running this - - address start = __ pc(); - - // push fp and retaddr by hand - // Exception pc is 'return address' for stack walker - __ addi(sp, sp, -2 * wordSize); - __ sd(ra, Address(sp, wordSize)); - __ sd(fp, Address(sp)); - // there are no callee save registers and we don't expect an - // arg reg save area -#ifndef PRODUCT - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); -#endif - // Store exception in Thread object. We cannot pass any arguments to the - // handle_exception call, since we do not want to make any assumption - // about the size of the frame where the exception happened in. - __ sd(x10, Address(xthread, JavaThread::exception_oop_offset())); - __ sd(x13, Address(xthread, JavaThread::exception_pc_offset())); - - // This call does all the hard work. It checks if an exception handler - // exists in the method. - // If so, it returns the handler address. - // If not, it prepares for stack-unwinding, restoring the callee-save - // registers of the frame being removed. - // - // address OptoRuntime::handle_exception_C(JavaThread* thread) - // - // n.b. 1 gp arg, 0 fp args, integral return type - - // the stack should always be aligned - address the_pc = __ pc(); - __ set_last_Java_frame(sp, noreg, the_pc, t0); - __ mv(c_rarg0, xthread); - __ rt_call(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C)); - - // handle_exception_C is a special VM call which does not require an explicit - // instruction sync afterwards. - - // Set an oopmap for the call site. This oopmap will only be used if we - // are unwinding the stack. Hence, all locations will be dead. - // Callee-saved registers will be the same as the frame above (i.e., - // handle_exception_stub), since they were restored when we got the - // exception. - - OopMapSet* oop_maps = new OopMapSet(); - assert_cond(oop_maps != nullptr); - - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - __ reset_last_Java_frame(false); - - // Restore callee-saved registers - - // fp is an implicitly saved callee saved register (i.e. the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers now that adapter frames are gone. - // and we dont' expect an arg reg save area - __ ld(fp, Address(sp)); - __ ld(x13, Address(sp, wordSize)); - __ addi(sp, sp , 2 * wordSize); - - // x10: exception handler - - // We have a handler in x10 (could be deopt blob). - __ mv(t0, x10); - - // Get the exception oop - __ ld(x10, Address(xthread, JavaThread::exception_oop_offset())); - // Get the exception pc in case we are deoptimized - __ ld(x14, Address(xthread, JavaThread::exception_pc_offset())); -#ifdef ASSERT - __ sd(zr, Address(xthread, JavaThread::exception_handler_pc_offset())); - __ sd(zr, Address(xthread, JavaThread::exception_pc_offset())); -#endif - // Clear the exception oop so GC no longer processes it as a root. - __ sd(zr, Address(xthread, JavaThread::exception_oop_offset())); - - // x10: exception oop - // t0: exception handler - // x14: exception pc - // Jump to handler - - __ jr(t0); - - // Make sure all code is generated - masm->flush(); - - // Set exception blob - _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 diff --git a/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp b/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp index 93adaadcef6b9..9fc4f1d7b0a70 100644 --- a/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for fp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_fp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ assert (r == fp->as_VMReg() || r == fp->as_VMReg()->next(), "Reg: %s", r->name()); }) @@ -48,17 +55,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - #ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) assert_is_fp(r); - } - #endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert_is_fp(reg); return (address)(sp - 2); diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 2a9faee7e2fdf..6a2c6c7d6c9bd 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -3774,7 +3774,7 @@ class StubGenerator: public StubCodeGenerator { Label thaw_success; // t1 contains the size of the frames to thaw, 0 if overflow or no more frames __ bnez(t1, thaw_success); - __ la(t0, ExternalAddress(StubRoutines::throw_StackOverflowError_entry())); + __ la(t0, RuntimeAddress(StubRoutines::throw_StackOverflowError_entry())); __ jr(t0); __ bind(thaw_success); @@ -5103,6 +5103,225 @@ class StubGenerator: public StubCodeGenerator { return (address) start; } + /** + * vector registers: + * input VectorRegister's: intputV1-V3, for m2 they could be v2, v4, v6, for m1 they could be v1, v2, v3 + * index VectorRegister's: idxV1-V4, for m2 they could be v8, v10, v12, v14, for m1 they could be v4, v5, v6, v7 + * output VectorRegister's: outputV1-V4, for m2 they could be v16, v18, v20, v22, for m1 they could be v8, v9, v10, v11 + * + * NOTE: each field will occupy a vector register group + */ + void base64_vector_encode_round(Register src, Register dst, Register codec, + Register size, Register stepSrc, Register stepDst, + VectorRegister inputV1, VectorRegister inputV2, VectorRegister inputV3, + VectorRegister idxV1, VectorRegister idxV2, VectorRegister idxV3, VectorRegister idxV4, + VectorRegister outputV1, VectorRegister outputV2, VectorRegister outputV3, VectorRegister outputV4, + Assembler::LMUL lmul) { + // set vector register type/len + __ vsetvli(x0, size, Assembler::e8, lmul); + + // segmented load src into v registers: mem(src) => vr(3) + __ vlseg3e8_v(inputV1, src); + + // src = src + register_group_len_bytes * 3 + __ add(src, src, stepSrc); + + // encoding + // 1. compute index into lookup table: vr(3) => vr(4) + __ vsrl_vi(idxV1, inputV1, 2); + + __ vsrl_vi(idxV2, inputV2, 2); + __ vsll_vi(inputV1, inputV1, 6); + __ vor_vv(idxV2, idxV2, inputV1); + __ vsrl_vi(idxV2, idxV2, 2); + + __ vsrl_vi(idxV3, inputV3, 4); + __ vsll_vi(inputV2, inputV2, 4); + __ vor_vv(idxV3, inputV2, idxV3); + __ vsrl_vi(idxV3, idxV3, 2); + + __ vsll_vi(idxV4, inputV3, 2); + __ vsrl_vi(idxV4, idxV4, 2); + + // 2. indexed load: vr(4) => vr(4) + __ vluxei8_v(outputV1, codec, idxV1); + __ vluxei8_v(outputV2, codec, idxV2); + __ vluxei8_v(outputV3, codec, idxV3); + __ vluxei8_v(outputV4, codec, idxV4); + + // segmented store encoded data in v registers back to dst: vr(4) => mem(dst) + __ vsseg4e8_v(outputV1, dst); + + // dst = dst + register_group_len_bytes * 4 + __ add(dst, dst, stepDst); + } + + /** + * void j.u.Base64.Encoder.encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) + * + * Input arguments: + * c_rarg0 - src, source array + * c_rarg1 - sp, src start offset + * c_rarg2 - sl, src end offset + * c_rarg3 - dst, dest array + * c_rarg4 - dp, dst start offset + * c_rarg5 - isURL, Base64 or URL character set + */ + address generate_base64_encodeBlock() { + alignas(64) static const char toBase64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + alignas(64) static const char toBase64URL[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "encodeBlock"); + address start = __ pc(); + __ enter(); + + Register src = c_rarg0; + Register soff = c_rarg1; + Register send = c_rarg2; + Register dst = c_rarg3; + Register doff = c_rarg4; + Register isURL = c_rarg5; + + Register codec = c_rarg6; + Register length = c_rarg7; // total length of src data in bytes + + Label ProcessData, Exit; + + // length should be multiple of 3 + __ sub(length, send, soff); + // real src/dst to process data + __ add(src, src, soff); + __ add(dst, dst, doff); + + // load the codec base address + __ la(codec, ExternalAddress((address) toBase64)); + __ beqz(isURL, ProcessData); + __ la(codec, ExternalAddress((address) toBase64URL)); + __ BIND(ProcessData); + + // vector version + if (UseRVV) { + Label ProcessM2, ProcessM1, ProcessScalar; + + Register size = soff; + Register stepSrcM1 = send; + Register stepSrcM2 = doff; + Register stepDst = isURL; + + __ mv(size, MaxVectorSize * 2); + __ mv(stepSrcM1, MaxVectorSize * 3); + __ slli(stepSrcM2, stepSrcM1, 1); + __ mv(stepDst, MaxVectorSize * 2 * 4); + + __ blt(length, stepSrcM2, ProcessM1); + + __ BIND(ProcessM2); + base64_vector_encode_round(src, dst, codec, + size, stepSrcM2, stepDst, + v2, v4, v6, // inputs + v8, v10, v12, v14, // indexes + v16, v18, v20, v22, // outputs + Assembler::m2); + + __ sub(length, length, stepSrcM2); + __ bge(length, stepSrcM2, ProcessM2); + + __ BIND(ProcessM1); + __ blt(length, stepSrcM1, ProcessScalar); + + __ srli(size, size, 1); + __ srli(stepDst, stepDst, 1); + base64_vector_encode_round(src, dst, codec, + size, stepSrcM1, stepDst, + v1, v2, v3, // inputs + v4, v5, v6, v7, // indexes + v8, v9, v10, v11, // outputs + Assembler::m1); + __ sub(length, length, stepSrcM1); + + __ BIND(ProcessScalar); + } + + // scalar version + { + Register byte1 = soff, byte0 = send, byte2 = doff; + Register combined24Bits = isURL; + + __ beqz(length, Exit); + + Label ScalarLoop; + __ BIND(ScalarLoop); + { + // plain: [byte0[7:0] : byte1[7:0] : byte2[7:0]] => + // encoded: [byte0[7:2] : byte0[1:0]+byte1[7:4] : byte1[3:0]+byte2[7:6] : byte2[5:0]] + + // load 3 bytes src data + __ lbu(byte0, Address(src, 0)); + __ lbu(byte1, Address(src, 1)); + __ lbu(byte2, Address(src, 2)); + __ addi(src, src, 3); + + // construct 24 bits from 3 bytes + __ slliw(byte0, byte0, 16); + __ slliw(byte1, byte1, 8); + __ orr(combined24Bits, byte0, byte1); + __ orr(combined24Bits, combined24Bits, byte2); + + // get codec index and encode(ie. load from codec by index) + __ slliw(byte0, combined24Bits, 8); + __ srliw(byte0, byte0, 26); + __ add(byte0, codec, byte0); + __ lbu(byte0, byte0); + + __ slliw(byte1, combined24Bits, 14); + __ srliw(byte1, byte1, 26); + __ add(byte1, codec, byte1); + __ lbu(byte1, byte1); + + __ slliw(byte2, combined24Bits, 20); + __ srliw(byte2, byte2, 26); + __ add(byte2, codec, byte2); + __ lbu(byte2, byte2); + + __ andi(combined24Bits, combined24Bits, 0x3f); + __ add(combined24Bits, codec, combined24Bits); + __ lbu(combined24Bits, combined24Bits); + + // store 4 bytes encoded data + __ sb(byte0, Address(dst, 0)); + __ sb(byte1, Address(dst, 1)); + __ sb(byte2, Address(dst, 2)); + __ sb(combined24Bits, Address(dst, 3)); + + __ sub(length, length, 3); + __ addi(dst, dst, 4); + // loop back + __ bnez(length, ScalarLoop); + } + } + + __ BIND(Exit); + + __ leave(); + __ ret(); + + return (address) start; + } + void adler32_process_bytes(Register buff, Register s1, Register s2, VectorRegister vtable, VectorRegister vzero, VectorRegister vbytes, VectorRegister vs1acc, VectorRegister vs2acc, Register temp0, Register temp1, Register temp2, Register temp3, @@ -5936,7 +6155,6 @@ static const int64_t right_3_bits = right_n_bits(3); } void generate_compiler_stubs() { -#if COMPILER2_OR_JVMCI #ifdef COMPILER2 if (UseMulAddIntrinsic) { StubRoutines::_mulAdd = generate_mulAdd(); @@ -5970,7 +6188,6 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_bigIntegerLeftShiftWorker = generate_bigIntegerLeftShift(); StubRoutines::_bigIntegerRightShiftWorker = generate_bigIntegerRightShift(); } -#endif // COMPILER2 if (UseSHA256Intrinsics) { Sha2Generator sha2(_masm, this); @@ -5984,10 +6201,6 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_sha512_implCompressMB = sha2.generate_sha512_implCompress(true); } - generate_compare_long_strings(); - - generate_string_indexof_stubs(); - if (UseMD5Intrinsics) { StubRoutines::_md5_implCompress = generate_md5_implCompress(false, "md5_implCompress"); StubRoutines::_md5_implCompressMB = generate_md5_implCompress(true, "md5_implCompressMB"); @@ -6002,11 +6215,19 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_sha1_implCompressMB = generate_sha1_implCompress(true, "sha1_implCompressMB"); } + if (UseBASE64Intrinsics) { + StubRoutines::_base64_encodeBlock = generate_base64_encodeBlock(); + } + if (UseAdler32Intrinsics) { StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); } -#endif // COMPILER2_OR_JVMCI + generate_compare_long_strings(); + + generate_string_indexof_stubs(); + +#endif // COMPILER2 } public: diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 769e4dc5ccc78..f01945bc6a3d1 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1111,8 +1111,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { { Label L; __ ld(x28, Address(xmethod, Method::native_function_offset())); - address unsatisfied = (SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); - __ mv(t, unsatisfied); + ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry()); + __ la(t, unsatisfied); __ load_long_misaligned(t1, Address(t, 0), t0, 2); // 2 bytes aligned, but not 4 or 8 __ bne(x28, t1, L); @@ -1815,7 +1815,7 @@ void TemplateInterpreterGenerator::trace_bytecode(Template* t) { // the tosca in-state for the given template. assert(Interpreter::trace_code(t->tos_in()) != nullptr, "entry must have been generated"); - __ call(Interpreter::trace_code(t->tos_in())); + __ rt_call(Interpreter::trace_code(t->tos_in())); __ reinit_heapbase(); } diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index c52c809f9350c..e9c6226f44639 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -58,6 +58,13 @@ void VM_Version::useRVA23U64Profile() { } void VM_Version::initialize() { + common_initialize(); +#ifdef COMPILER2 + c2_initialize(); +#endif // COMPILER2 +} + +void VM_Version::common_initialize() { _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; _supports_atomic_getset8 = true; @@ -152,10 +159,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); } - if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { - FLAG_SET_DEFAULT(UseMD5Intrinsics, true); - } - if (FLAG_IS_DEFAULT(UsePoly1305Intrinsics)) { FLAG_SET_DEFAULT(UsePoly1305Intrinsics, true); } @@ -230,15 +233,110 @@ void VM_Version::initialize() { _initial_vector_length = cpu_vector_length(); } } +} #ifdef COMPILER2 - c2_initialize(); -#endif // COMPILER2 +void VM_Version::c2_initialize() { + if (UseCMoveUnconditionally) { + FLAG_SET_DEFAULT(UseCMoveUnconditionally, false); + } + + if (ConditionalMoveLimit > 0) { + FLAG_SET_DEFAULT(ConditionalMoveLimit, 0); + } - // NOTE: Make sure codes dependent on UseRVV are put after c2_initialize(), + if (!UseRVV) { + FLAG_SET_DEFAULT(MaxVectorSize, 0); + FLAG_SET_DEFAULT(UseRVVForBigIntegerShiftIntrinsics, false); + } else { + if (!FLAG_IS_DEFAULT(MaxVectorSize) && MaxVectorSize != _initial_vector_length) { + warning("Current system does not support RVV vector length for MaxVectorSize %d. Set MaxVectorSize to %d", + (int)MaxVectorSize, _initial_vector_length); + } + MaxVectorSize = _initial_vector_length; + if (MaxVectorSize < 16) { + warning("RVV does not support vector length less than 16 bytes. Disabling RVV."); + UseRVV = false; + FLAG_SET_DEFAULT(MaxVectorSize, 0); + } + } + + // NOTE: Make sure codes dependent on UseRVV are put after MaxVectorSize initialize, // as there are extra checks inside it which could disable UseRVV // in some situations. + // Base64 + if (FLAG_IS_DEFAULT(UseBASE64Intrinsics)) { + FLAG_SET_DEFAULT(UseBASE64Intrinsics, true); + } + + if (FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { + FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); + } + + if (!UseZicbop) { + if (!FLAG_IS_DEFAULT(AllocatePrefetchStyle)) { + warning("Zicbop is not available on this CPU"); + } + FLAG_SET_DEFAULT(AllocatePrefetchStyle, 0); + } else { + // Limit AllocatePrefetchDistance so that it does not exceed the + // static constraint of 512 defined in runtime/globals.hpp. + if (FLAG_IS_DEFAULT(AllocatePrefetchDistance)) { + FLAG_SET_DEFAULT(AllocatePrefetchDistance, MIN2(512, 3 * (int)CacheLineSize)); + } + if (FLAG_IS_DEFAULT(AllocatePrefetchStepSize)) { + FLAG_SET_DEFAULT(AllocatePrefetchStepSize, (int)CacheLineSize); + } + if (FLAG_IS_DEFAULT(PrefetchScanIntervalInBytes)) { + FLAG_SET_DEFAULT(PrefetchScanIntervalInBytes, 3 * (int)CacheLineSize); + } + if (FLAG_IS_DEFAULT(PrefetchCopyIntervalInBytes)) { + FLAG_SET_DEFAULT(PrefetchCopyIntervalInBytes, 3 * (int)CacheLineSize); + } + + if (PrefetchCopyIntervalInBytes != -1 && + ((PrefetchCopyIntervalInBytes & 7) || (PrefetchCopyIntervalInBytes >= 32768))) { + warning("PrefetchCopyIntervalInBytes must be -1, or a multiple of 8 and < 32768"); + PrefetchCopyIntervalInBytes &= ~7; + if (PrefetchCopyIntervalInBytes >= 32768) { + PrefetchCopyIntervalInBytes = 32760; + } + } + if (AllocatePrefetchDistance !=-1 && (AllocatePrefetchDistance & 7)) { + warning("AllocatePrefetchDistance must be multiple of 8"); + AllocatePrefetchDistance &= ~7; + } + if (AllocatePrefetchStepSize & 7) { + warning("AllocatePrefetchStepSize must be multiple of 8"); + AllocatePrefetchStepSize &= ~7; + } + } + + if (FLAG_IS_DEFAULT(UseMulAddIntrinsic)) { + FLAG_SET_DEFAULT(UseMulAddIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { + FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) { + FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { + FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { + FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { + FLAG_SET_DEFAULT(UseMD5Intrinsics, true); + } + // Adler32 if (UseRVV) { if (FLAG_IS_DEFAULT(UseAdler32Intrinsics)) { @@ -252,7 +350,9 @@ void VM_Version::initialize() { } // ChaCha20 - if (UseRVV) { + if (UseRVV && MaxVectorSize >= 32) { + // performance tests on hardwares (MaxVectorSize == 16, 32) show that + // it brings regression when MaxVectorSize == 16. if (FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) { FLAG_SET_DEFAULT(UseChaCha20Intrinsics, true); } @@ -330,96 +430,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA, false); } } - -#ifdef COMPILER2 -void VM_Version::c2_initialize() { - if (UseCMoveUnconditionally) { - FLAG_SET_DEFAULT(UseCMoveUnconditionally, false); - } - - if (ConditionalMoveLimit > 0) { - FLAG_SET_DEFAULT(ConditionalMoveLimit, 0); - } - - if (!UseRVV) { - FLAG_SET_DEFAULT(MaxVectorSize, 0); - FLAG_SET_DEFAULT(UseRVVForBigIntegerShiftIntrinsics, false); - } else { - if (!FLAG_IS_DEFAULT(MaxVectorSize) && MaxVectorSize != _initial_vector_length) { - warning("Current system does not support RVV vector length for MaxVectorSize %d. Set MaxVectorSize to %d", - (int)MaxVectorSize, _initial_vector_length); - } - MaxVectorSize = _initial_vector_length; - if (MaxVectorSize < 16) { - warning("RVV does not support vector length less than 16 bytes. Disabling RVV."); - UseRVV = false; - FLAG_SET_DEFAULT(MaxVectorSize, 0); - } - } - - if (FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { - FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); - } - - if (!UseZicbop) { - if (!FLAG_IS_DEFAULT(AllocatePrefetchStyle)) { - warning("Zicbop is not available on this CPU"); - } - FLAG_SET_DEFAULT(AllocatePrefetchStyle, 0); - } else { - // Limit AllocatePrefetchDistance so that it does not exceed the - // static constraint of 512 defined in runtime/globals.hpp. - if (FLAG_IS_DEFAULT(AllocatePrefetchDistance)) { - FLAG_SET_DEFAULT(AllocatePrefetchDistance, MIN2(512, 3 * (int)CacheLineSize)); - } - if (FLAG_IS_DEFAULT(AllocatePrefetchStepSize)) { - FLAG_SET_DEFAULT(AllocatePrefetchStepSize, (int)CacheLineSize); - } - if (FLAG_IS_DEFAULT(PrefetchScanIntervalInBytes)) { - FLAG_SET_DEFAULT(PrefetchScanIntervalInBytes, 3 * (int)CacheLineSize); - } - if (FLAG_IS_DEFAULT(PrefetchCopyIntervalInBytes)) { - FLAG_SET_DEFAULT(PrefetchCopyIntervalInBytes, 3 * (int)CacheLineSize); - } - - if (PrefetchCopyIntervalInBytes != -1 && - ((PrefetchCopyIntervalInBytes & 7) || (PrefetchCopyIntervalInBytes >= 32768))) { - warning("PrefetchCopyIntervalInBytes must be -1, or a multiple of 8 and < 32768"); - PrefetchCopyIntervalInBytes &= ~7; - if (PrefetchCopyIntervalInBytes >= 32768) { - PrefetchCopyIntervalInBytes = 32760; - } - } - if (AllocatePrefetchDistance !=-1 && (AllocatePrefetchDistance & 7)) { - warning("AllocatePrefetchDistance must be multiple of 8"); - AllocatePrefetchDistance &= ~7; - } - if (AllocatePrefetchStepSize & 7) { - warning("AllocatePrefetchStepSize must be multiple of 8"); - AllocatePrefetchStepSize &= ~7; - } - } - - if (FLAG_IS_DEFAULT(UseMulAddIntrinsic)) { - FLAG_SET_DEFAULT(UseMulAddIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { - FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) { - FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { - FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { - FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true); - } -} #endif // COMPILER2 void VM_Version::initialize_cpu_information(void) { diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 9556e2dc9ad5d..bd4bfe86d9bf7 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -264,6 +264,8 @@ class VM_Version : public Abstract_VM_Version { static uint32_t cpu_vector_length(); static uint32_t _initial_vector_length; + static void common_initialize(); + #ifdef COMPILER2 static void c2_initialize(); #endif // COMPILER2 diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 66d6f25f0fd4a..72d10ee80aae1 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. + * Copyright 2024 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3152,6 +3153,301 @@ void MacroAssembler::check_klass_subtype(Register sub_klass, BLOCK_COMMENT("} check_klass_subtype"); } +// scans r_count pointer sized words at [r_addr] for occurrence of r_value, +// generic (r_count must be >0) +// iff found: CC eq, r_result == 0 +void MacroAssembler::repne_scan(Register r_addr, Register r_value, Register r_count, Register r_result) { + NearLabel L_loop, L_exit; + + BLOCK_COMMENT("repne_scan {"); +#ifdef ASSERT + z_chi(r_count, 0); + asm_assert(bcondHigh, "count must be positive", 11); +#endif + + clear_reg(r_result, true /* whole_reg */, false /* set_cc */); // sets r_result=0, let's hope that search will be successful + + bind(L_loop); + z_cg(r_value, Address(r_addr)); + z_bre(L_exit); // branch on success + z_la(r_addr, wordSize, r_addr); + z_brct(r_count, L_loop); + + // z_brct above doesn't change CC. + // If we reach here, then the value in r_value is not present. Set r_result to 1. + z_lghi(r_result, 1); + + bind(L_exit); + BLOCK_COMMENT("} repne_scan"); +} + +// Ensure that the inline code and the stub are using the same registers. +#define LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS \ +do { \ + assert(r_super_klass == Z_ARG1 && \ + r_array_base == Z_ARG5 && \ + r_array_length == Z_ARG4 && \ + (r_array_index == Z_ARG3 || r_array_index == noreg) && \ + (r_sub_klass == Z_ARG2 || r_sub_klass == noreg) && \ + (r_bitmap == Z_R10 || r_bitmap == noreg) && \ + (r_result == Z_R11 || r_result == noreg), "registers must match s390.ad"); \ +} while(0) + +// Note: this method also kills Z_R1_scratch register on machines older than z15 +void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_temp1, + Register r_temp2, + Register r_temp3, + Register r_temp4, + Register r_result, + u1 super_klass_slot) { + NearLabel L_done, L_failure; + + BLOCK_COMMENT("lookup_secondary_supers_table {"); + + const Register + r_array_base = r_temp1, + r_array_length = r_temp2, + r_array_index = r_temp3, + r_bitmap = r_temp4; + + LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + + z_lg(r_bitmap, Address(r_sub_klass, Klass::bitmap_offset())); + + // First check the bitmap to see if super_klass might be present. If + // the bit is zero, we are certain that super_klass is not one of + // the secondary supers. + u1 bit = super_klass_slot; + int shift_count = Klass::SECONDARY_SUPERS_TABLE_MASK - bit; + + z_sllg(r_array_index, r_bitmap, shift_count); // take the bit to 63rd location + + // Initialize r_result with 0 (indicating success). If searching fails, r_result will be loaded + // with 1 (failure) at the end of this method. + clear_reg(r_result, true /* whole_reg */, false /* set_cc */); // r_result = 0 + + // We test the MSB of r_array_index, i.e., its sign bit + testbit(r_array_index, 63); + z_bfalse(L_failure); // if not set, then jump!!! + + // We will consult the secondary-super array. + z_lg(r_array_base, Address(r_sub_klass, Klass::secondary_supers_offset())); + + // The value i in r_array_index is >= 1, so even though r_array_base + // points to the length, we don't need to adjust it to point to the + // data. + assert(Array::base_offset_in_bytes() == wordSize, "Adjust this code"); + + // Get the first array index that can contain super_klass. + if (bit != 0) { + pop_count_long(r_array_index, r_array_index, Z_R1_scratch); // kills Z_R1_scratch on machines older than z15 + + // NB! r_array_index is off by 1. It is compensated by keeping r_array_base off by 1 word. + z_sllg(r_array_index, r_array_index, LogBytesPerWord); // scale + } else { + // Actually use index 0, but r_array_base and r_array_index are off by 1 word + // such that the sum is precise. + z_lghi(r_array_index, BytesPerWord); // for slow path (scaled) + } + + z_cg(r_super_klass, Address(r_array_base, r_array_index)); + branch_optimized(bcondEqual, L_done); // found a match; success + + // Is there another entry to check? Consult the bitmap. + testbit(r_bitmap, (bit + 1) & Klass::SECONDARY_SUPERS_TABLE_MASK); + z_bfalse(L_failure); + + // Linear probe. Rotate the bitmap so that the next bit to test is + // in Bit 2 for the look-ahead check in the slow path. + if (bit != 0) { + z_rllg(r_bitmap, r_bitmap, 64-bit); // rotate right + } + + // Calls into the stub generated by lookup_secondary_supers_table_slow_path. + // Arguments: r_super_klass, r_array_base, r_array_index, r_bitmap. + // Kills: r_array_length. + // Returns: r_result + + call_stub(StubRoutines::lookup_secondary_supers_table_slow_path_stub()); + + z_bru(L_done); // pass whatever result we got from a slow path + + bind(L_failure); + // TODO: use load immediate on condition and z_bru above will not be required + z_lghi(r_result, 1); + + bind(L_done); + BLOCK_COMMENT("} lookup_secondary_supers_table"); + + if (VerifySecondarySupers) { + verify_secondary_supers_table(r_sub_klass, r_super_klass, r_result, + r_temp1, r_temp2, r_temp3); + } +} + +// Called by code generated by check_klass_subtype_slow_path +// above. This is called when there is a collision in the hashed +// lookup in the secondary supers array. +void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_klass, + Register r_array_base, + Register r_array_index, + Register r_bitmap, + Register r_result, + Register r_temp1) { + assert_different_registers(r_super_klass, r_array_base, r_array_index, r_bitmap, r_result, r_temp1); + + const Register + r_array_length = r_temp1, + r_sub_klass = noreg; + + LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + + BLOCK_COMMENT("lookup_secondary_supers_table_slow_path {"); + NearLabel L_done, L_failure; + + // Load the array length. + z_llgf(r_array_length, Address(r_array_base, Array::length_offset_in_bytes())); + + // And adjust the array base to point to the data. + // NB! + // Effectively increments the current slot index by 1. + assert(Array::base_offset_in_bytes() == wordSize, ""); + add2reg(r_array_base, Array::base_offset_in_bytes()); + + // Linear probe + NearLabel L_huge; + + // The bitmap is full to bursting. + z_cghi(r_bitmap, Klass::SECONDARY_SUPERS_BITMAP_FULL); + z_bre(L_huge); + + // NB! Our caller has checked bits 0 and 1 in the bitmap. The + // current slot (at secondary_supers[r_array_index]) has not yet + // been inspected, and r_array_index may be out of bounds if we + // wrapped around the end of the array. + + { // This is conventional linear probing, but instead of terminating + // when a null entry is found in the table, we maintain a bitmap + // in which a 0 indicates missing entries. + // The check above guarantees there are 0s in the bitmap, so the loop + // eventually terminates. + +#ifdef ASSERT + // r_result is set to 0 by lookup_secondary_supers_table. + // clear_reg(r_result, true /* whole_reg */, false /* set_cc */); + z_cghi(r_result, 0); + asm_assert(bcondEqual, "r_result required to be 0, used by z_locgr", 44); + + // We should only reach here after having found a bit in the bitmap. + z_ltgr(r_array_length, r_array_length); + asm_assert(bcondHigh, "array_length > 0, should hold", 22); +#endif // ASSERT + + // Compute limit in r_array_length + add2reg(r_array_length, -1); + z_sllg(r_array_length, r_array_length, LogBytesPerWord); + + NearLabel L_loop; + bind(L_loop); + + // Check for wraparound. + z_cgr(r_array_index, r_array_length); + z_locgr(r_array_index, r_result, bcondHigh); // r_result is containing 0 + + z_cg(r_super_klass, Address(r_array_base, r_array_index)); + z_bre(L_done); // success + + // look-ahead check: if Bit 2 is 0, we're done + testbit(r_bitmap, 2); + z_bfalse(L_failure); + + z_rllg(r_bitmap, r_bitmap, 64-1); // rotate right + add2reg(r_array_index, BytesPerWord); + + z_bru(L_loop); + } + + { // Degenerate case: more than 64 secondary supers. + // FIXME: We could do something smarter here, maybe a vectorized + // comparison or a binary search, but is that worth any added + // complexity? + + bind(L_huge); + repne_scan(r_array_base, r_super_klass, r_array_length, r_result); + + z_bru(L_done); // forward the result we got from repne_scan + } + + bind(L_failure); + z_lghi(r_result, 1); + + bind(L_done); + BLOCK_COMMENT("} lookup_secondary_supers_table_slow_path"); +} + +// Make sure that the hashed lookup and a linear scan agree. +void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_result /* expected */, + Register r_temp1, + Register r_temp2, + Register r_temp3) { + assert_different_registers(r_sub_klass, r_super_klass, r_result, r_temp1, r_temp2, r_temp3); + + const Register + r_array_base = r_temp1, + r_array_length = r_temp2, + r_array_index = r_temp3, + r_bitmap = noreg; // unused + + const Register r_one = Z_R0_scratch; + z_lghi(r_one, 1); // for locgr down there, to a load result for failure + + LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + + BLOCK_COMMENT("verify_secondary_supers_table {"); + + Label L_passed, L_failure; + + // We will consult the secondary-super array. + z_lg(r_array_base, Address(r_sub_klass, in_bytes(Klass::secondary_supers_offset()))); + + // Load the array length. + z_llgf(r_array_length, Address(r_array_base, Array::length_offset_in_bytes())); + + // And adjust the array base to point to the data. + z_aghi(r_array_base, Array::base_offset_in_bytes()); + + const Register r_linear_result = r_array_index; // reuse + z_chi(r_array_length, 0); + z_locgr(r_linear_result, r_one, bcondNotHigh); // load failure if array_length <= 0 + z_brc(bcondNotHigh, L_failure); + repne_scan(r_array_base, r_super_klass, r_array_length, r_linear_result); + bind(L_failure); + + z_cr(r_result, r_linear_result); + z_bre(L_passed); + + assert_different_registers(Z_ARG1, r_sub_klass, r_linear_result, r_result); + lgr_if_needed(Z_ARG1, r_super_klass); + assert_different_registers(Z_ARG2, r_linear_result, r_result); + lgr_if_needed(Z_ARG2, r_sub_klass); + assert_different_registers(Z_ARG3, r_result); + z_lgr(Z_ARG3, r_linear_result); + z_lgr(Z_ARG4, r_result); + const char* msg = "mismatch"; + load_const_optimized(Z_ARG5, (address)msg); + + call_VM_leaf(CAST_FROM_FN_PTR(address, Klass::on_secondary_supers_verification_failure)); + should_not_reach_here(); + + bind(L_passed); + + BLOCK_COMMENT("} verify_secondary_supers_table"); +} + void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fast_path, Label* L_slow_path) { assert(L_fast_path != nullptr || L_slow_path != nullptr, "at least one is required"); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 684741d79db2f..90210eb28c3ad 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -708,6 +708,31 @@ class MacroAssembler: public Assembler { Label* L_success, Label* L_failure); + void repne_scan(Register r_addr, Register r_value, Register r_count, Register r_scratch); + + void lookup_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_temp1, + Register r_temp2, + Register r_temp3, + Register r_temp4, + Register r_result, + u1 super_klass_slot); + + void lookup_secondary_supers_table_slow_path(Register r_super_klass, + Register r_array_base, + Register r_array_index, + Register r_bitmap, + Register r_result, + Register r_temp1); + + void verify_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_result /* expected */, + Register r_temp1, + Register r_temp2, + Register r_temp3); + // Simplified, combined version, good for typical uses. // Falls through on failure. void check_klass_subtype(Register sub_klass, diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 0fe3840b9b53c..e3dcfdb8759da 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -356,6 +356,8 @@ reg_class z_rarg3_ptr_reg(Z_R4_H,Z_R4); reg_class z_rarg4_ptr_reg(Z_R5_H,Z_R5); reg_class z_rarg5_ptr_reg(Z_R6_H,Z_R6); reg_class z_thread_ptr_reg(Z_R8_H,Z_R8); +reg_class z_r10_ptr_reg(Z_R10_H, Z_R10); +reg_class z_r11_ptr_reg(Z_R11_H, Z_R11); reg_class z_ptr_reg( /*Z_R0_H,Z_R0*/ // R0 @@ -2985,6 +2987,8 @@ operand iRegP() %{ match(rarg5RegP); match(revenRegP); match(roddRegP); + match(r10TempRegP); + match(r11TempRegP); format %{ %} interface(REG_INTER); %} @@ -2997,6 +3001,20 @@ operand threadRegP() %{ interface(REG_INTER); %} +operand r10TempRegP() %{ + constraint(ALLOC_IN_RC(z_r10_ptr_reg)); + match(iRegP); + format %{ %} + interface(REG_INTER); +%} + +operand r11TempRegP() %{ + constraint(ALLOC_IN_RC(z_r11_ptr_reg)); + match(iRegP); + format %{ %} + interface(REG_INTER); +%} + operand noArg_iRegP() %{ constraint(ALLOC_IN_RC(z_no_arg_ptr_reg)); match(iRegP); @@ -9560,6 +9578,32 @@ instruct partialSubtypeCheck(rarg1RegP index, rarg2RegP sub, rarg3RegP super, fl ins_pipe(pipe_class_dummy); %} +instruct partialSubtypeCheckConstSuper(rarg2RegP sub, rarg1RegP super, immP super_con, + r11TempRegP result, rarg5RegP temp1, rarg4RegP temp2, + rarg3RegP temp3, r10TempRegP temp4, flagsReg pcc) %{ + match(Set result (PartialSubtypeCheck sub (Binary super super_con))); + predicate(UseSecondarySupersTable); + effect(KILL pcc, TEMP temp1, TEMP temp2, TEMP temp3, TEMP temp4); + ins_cost(7 * DEFAULT_COST); // needs to be less than competing nodes + format %{ "partialSubtypeCheck $result, $sub, $super, $super_con" %} + + ins_encode %{ + u1 super_klass_slot = ((Klass*)$super_con$$constant)->hash_slot(); + if (InlineSecondarySupersTest) { + __ lookup_secondary_supers_table($sub$$Register, $super$$Register, + $temp1$$Register, $temp2$$Register, $temp3$$Register, + $temp4$$Register, $result$$Register, super_klass_slot); + } else { + AddressLiteral stub_address(StubRoutines::lookup_secondary_supers_table_stub(super_klass_slot)); + __ load_const_optimized(Z_ARG4, stub_address); + __ z_basr(Z_R14, Z_ARG4); + } + + %} + + ins_pipe(pipe_class_dummy); +%} + instruct partialSubtypeCheck_vs_zero(flagsReg pcc, rarg2RegP sub, rarg3RegP super, immP0 zero, rarg1RegP index, rarg4RegP scratch1, rarg5RegP scratch2) %{ match(Set pcc (CmpI (PartialSubtypeCheck sub super) zero)); diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 0ee88345282b7..641b3712a175b 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2706,7 +2706,7 @@ void SharedRuntime::generate_deopt_blob() { #ifdef COMPILER2 //------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { +void OptoRuntime::generate_uncommon_trap_blob() { // Allocate space for the code ResourceMark rm; // Setup code generation tools @@ -2769,7 +2769,7 @@ void SharedRuntime::generate_uncommon_trap_blob() { } else { __ z_cliy(unpack_kind_byte_offset, unroll_block_reg, Deoptimization::Unpack_uncommon_trap); } - __ asm_assert(Assembler::bcondEqual, "SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap", 0); + __ asm_assert(Assembler::bcondEqual, "OptoRuntime::generate_deopt_blob: expected Unpack_uncommon_trap", 0); #endif __ zap_from_to(Z_SP, Z_SP, Z_R0_scratch, Z_R1, 500, -1); diff --git a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp index 8c74eb7dd6dbf..625d17cf99233 100644 --- a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp +++ b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ Unimplemented(); }) @@ -46,12 +53,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - Unimplemented(); - } - inline address location(VMReg reg, intptr_t* sp) const { Unimplemented(); return nullptr; diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index dfd20730b8476..556c0d4703d54 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2023 SAP SE. All rights reserved. + * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -705,6 +705,50 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { + StubCodeMark mark(this, "StubRoutines", "lookup_secondary_supers_table"); + + const Register + r_super_klass = Z_ARG1, + r_sub_klass = Z_ARG2, + r_array_index = Z_ARG3, + r_array_length = Z_ARG4, + r_array_base = Z_ARG5, + r_bitmap = Z_R10, + r_result = Z_R11; + address start = __ pc(); + + __ lookup_secondary_supers_table(r_sub_klass, r_super_klass, + r_array_base, r_array_length, r_array_index, + r_bitmap, r_result, super_klass_index); + + __ z_br(Z_R14); + + return start; + } + + // Slow path implementation for UseSecondarySupersTable. + address generate_lookup_secondary_supers_table_slow_path_stub() { + StubCodeMark mark(this, "StubRoutines", "lookup_secondary_supers_table_slow_path"); + + address start = __ pc(); + + const Register + r_super_klass = Z_ARG1, + r_array_base = Z_ARG5, + r_temp1 = Z_ARG4, + r_array_index = Z_ARG3, + r_bitmap = Z_R10, + r_result = Z_R11; + + __ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, + r_array_index, r_bitmap, r_result, r_temp1); + + __ z_br(Z_R14); + + return start; + } + #if !defined(PRODUCT) // Wrapper which calls oopDesc::is_oop_or_null() // Only called by MacroAssembler::verify_oop @@ -3247,6 +3291,14 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_montgomerySquare = CAST_FROM_FN_PTR(address, SharedRuntime::montgomery_square); } + if (UseSecondarySupersTable) { + StubRoutines::_lookup_secondary_supers_table_slow_path_stub = generate_lookup_secondary_supers_table_slow_path_stub(); + if (!InlineSecondarySupersTest) { + for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = generate_lookup_secondary_supers_table_stub(slot); + } + } + } #endif #endif // COMPILER2_OR_JVMCI } diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index af0903884fb4f..4b17ff4594ccf 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2023 SAP SE. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -272,6 +272,13 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA, false); } + if (UseSecondarySupersTable && VM_Version::get_model_index() < 5 /* z196/z11 */) { + if (!FLAG_IS_DEFAULT(UseSecondarySupersTable)) { + warning("UseSecondarySupersTable requires z196 or later."); + } + FLAG_SET_DEFAULT(UseSecondarySupersTable, false); + } + #ifdef COMPILER2 if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true); diff --git a/src/hotspot/cpu/s390/vm_version_s390.hpp b/src/hotspot/cpu/s390/vm_version_s390.hpp index 4f963c4e4851a..31fdedc59f642 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.hpp +++ b/src/hotspot/cpu/s390/vm_version_s390.hpp @@ -413,6 +413,8 @@ class VM_Version: public Abstract_VM_Version { // s390 supports fast class initialization checks static bool supports_fast_class_init_checks() { return true; } + constexpr static bool supports_secondary_supers_table() { return true; } + constexpr static bool supports_recursive_lightweight_locking() { return true; } // CPU feature query functions diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 001ff472f40bb..207d3fbb61eba 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -1780,6 +1780,7 @@ void Assembler::call(Register dst) { void Assembler::call(Address adr) { + assert(!adr._rspec.reloc()->is_data(), "should not use ExternalAddress for call"); InstructionMark im(this); prefix(adr); emit_int8((unsigned char)0xFF); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 66a782ba9c622..faab09b7c17b4 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -1786,6 +1786,16 @@ void C2_MacroAssembler::reduce_operation_128(BasicType typ, int opcode, XMMRegis } } +void C2_MacroAssembler::unordered_reduce_operation_128(BasicType typ, int opcode, XMMRegister dst, XMMRegister src) { + switch (opcode) { + case Op_AddReductionVF: addps(dst, src); break; + case Op_AddReductionVD: addpd(dst, src); break; + case Op_MulReductionVF: mulps(dst, src); break; + case Op_MulReductionVD: mulpd(dst, src); break; + default: assert(false, "%s", NodeClassNames[opcode]); + } +} + void C2_MacroAssembler::reduce_operation_256(BasicType typ, int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2) { int vector_len = Assembler::AVX_256bit; @@ -1834,6 +1844,18 @@ void C2_MacroAssembler::reduce_operation_256(BasicType typ, int opcode, XMMRegis } } +void C2_MacroAssembler::unordered_reduce_operation_256(BasicType typ, int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2) { + int vector_len = Assembler::AVX_256bit; + + switch (opcode) { + case Op_AddReductionVF: vaddps(dst, src1, src2, vector_len); break; + case Op_AddReductionVD: vaddpd(dst, src1, src2, vector_len); break; + case Op_MulReductionVF: vmulps(dst, src1, src2, vector_len); break; + case Op_MulReductionVD: vmulpd(dst, src1, src2, vector_len); break; + default: assert(false, "%s", NodeClassNames[opcode]); + } +} + void C2_MacroAssembler::reduce_fp(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { @@ -1852,6 +1874,24 @@ void C2_MacroAssembler::reduce_fp(int opcode, int vlen, } } +void C2_MacroAssembler::unordered_reduce_fp(int opcode, int vlen, + XMMRegister dst, XMMRegister src, + XMMRegister vtmp1, XMMRegister vtmp2) { + switch (opcode) { + case Op_AddReductionVF: + case Op_MulReductionVF: + unorderedReduceF(opcode, vlen, dst, src, vtmp1, vtmp2); + break; + + case Op_AddReductionVD: + case Op_MulReductionVD: + unorderedReduceD(opcode, vlen, dst, src, vtmp1, vtmp2); + break; + + default: assert(false, "%s", NodeClassNames[opcode]); + } +} + void C2_MacroAssembler::reduceB(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { @@ -1954,6 +1994,45 @@ void C2_MacroAssembler::reduceD(int opcode, int vlen, XMMRegister dst, XMMRegist } } +void C2_MacroAssembler::unorderedReduceF(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { + switch (vlen) { + case 2: + assert(vtmp1 == xnoreg, ""); + assert(vtmp2 == xnoreg, ""); + unorderedReduce2F(opcode, dst, src); + break; + case 4: + assert(vtmp2 == xnoreg, ""); + unorderedReduce4F(opcode, dst, src, vtmp1); + break; + case 8: + unorderedReduce8F(opcode, dst, src, vtmp1, vtmp2); + break; + case 16: + unorderedReduce16F(opcode, dst, src, vtmp1, vtmp2); + break; + default: assert(false, "wrong vector length"); + } +} + +void C2_MacroAssembler::unorderedReduceD(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { + switch (vlen) { + case 2: + assert(vtmp1 == xnoreg, ""); + assert(vtmp2 == xnoreg, ""); + unorderedReduce2D(opcode, dst, src); + break; + case 4: + assert(vtmp2 == xnoreg, ""); + unorderedReduce4D(opcode, dst, src, vtmp1); + break; + case 8: + unorderedReduce8D(opcode, dst, src, vtmp1, vtmp2); + break; + default: assert(false, "wrong vector length"); + } +} + void C2_MacroAssembler::reduce2I(int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2) { if (opcode == Op_AddReductionVI) { if (vtmp1 != src2) { @@ -2181,6 +2260,29 @@ void C2_MacroAssembler::reduce16F(int opcode, XMMRegister dst, XMMRegister src, reduce8F(opcode, dst, vtmp1, vtmp1, vtmp2); } +void C2_MacroAssembler::unorderedReduce2F(int opcode, XMMRegister dst, XMMRegister src) { + pshufd(dst, src, 0x1); + reduce_operation_128(T_FLOAT, opcode, dst, src); +} + +void C2_MacroAssembler::unorderedReduce4F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp) { + pshufd(vtmp, src, 0xE); + unordered_reduce_operation_128(T_FLOAT, opcode, vtmp, src); + unorderedReduce2F(opcode, dst, vtmp); +} + +void C2_MacroAssembler::unorderedReduce8F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { + vextractf128_high(vtmp1, src); + unordered_reduce_operation_128(T_FLOAT, opcode, vtmp1, src); + unorderedReduce4F(opcode, dst, vtmp1, vtmp2); +} + +void C2_MacroAssembler::unorderedReduce16F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { + vextractf64x4_high(vtmp2, src); + unordered_reduce_operation_256(T_FLOAT, opcode, vtmp2, vtmp2, src); + unorderedReduce8F(opcode, dst, vtmp2, vtmp1, vtmp2); +} + void C2_MacroAssembler::reduce2D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp) { reduce_operation_128(T_DOUBLE, opcode, dst, src); pshufd(vtmp, src, 0xE); @@ -2199,6 +2301,23 @@ void C2_MacroAssembler::reduce8D(int opcode, XMMRegister dst, XMMRegister src, X reduce4D(opcode, dst, vtmp1, vtmp1, vtmp2); } +void C2_MacroAssembler::unorderedReduce2D(int opcode, XMMRegister dst, XMMRegister src) { + pshufd(dst, src, 0xE); + reduce_operation_128(T_DOUBLE, opcode, dst, src); +} + +void C2_MacroAssembler::unorderedReduce4D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp) { + vextractf128_high(vtmp, src); + unordered_reduce_operation_128(T_DOUBLE, opcode, vtmp, src); + unorderedReduce2D(opcode, dst, vtmp); +} + +void C2_MacroAssembler::unorderedReduce8D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2) { + vextractf64x4_high(vtmp2, src); + unordered_reduce_operation_256(T_DOUBLE, opcode, vtmp2, vtmp2, src); + unorderedReduce4D(opcode, dst, vtmp2, vtmp1); +} + void C2_MacroAssembler::evmovdqu(BasicType type, KRegister kmask, XMMRegister dst, Address src, bool merge, int vector_len) { MacroAssembler::evmovdqu(type, kmask, dst, src, merge, vector_len); } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index e268ed3dd7a46..af57546b3d143 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -149,6 +149,9 @@ void reduce_fp(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2 = xnoreg); + void unordered_reduce_fp(int opcode, int vlen, + XMMRegister dst, XMMRegister src, + XMMRegister vtmp1 = xnoreg, XMMRegister vtmp2 = xnoreg); void reduceB(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); void mulreduceB(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); void reduceS(int opcode, int vlen, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); @@ -161,6 +164,8 @@ private: void reduceF(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); void reduceD(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + void unorderedReduceF(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + void unorderedReduceD(int opcode, int vlen, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); // Int Reduction void reduce2I (int opcode, Register dst, Register src1, XMMRegister src2, XMMRegister vtmp1, XMMRegister vtmp2); @@ -197,14 +202,27 @@ void reduce8F (int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); void reduce16F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + // Unordered Float Reduction + void unorderedReduce2F(int opcode, XMMRegister dst, XMMRegister src); + void unorderedReduce4F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp); + void unorderedReduce8F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + void unorderedReduce16F(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + // Double Reduction void reduce2D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp); void reduce4D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); void reduce8D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + // Unordered Double Reduction + void unorderedReduce2D(int opcode, XMMRegister dst, XMMRegister src); + void unorderedReduce4D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp); + void unorderedReduce8D(int opcode, XMMRegister dst, XMMRegister src, XMMRegister vtmp1, XMMRegister vtmp2); + // Base reduction instruction void reduce_operation_128(BasicType typ, int opcode, XMMRegister dst, XMMRegister src); void reduce_operation_256(BasicType typ, int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2); + void unordered_reduce_operation_128(BasicType typ, int opcode, XMMRegister dst, XMMRegister src); + void unordered_reduce_operation_256(BasicType typ, int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2); public: #ifdef _LP64 diff --git a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp index 2366d31992d6e..aeb77763e7fff 100644 --- a/src/hotspot/cpu/x86/frame_x86.cpp +++ b/src/hotspot/cpu/x86/frame_x86.cpp @@ -279,7 +279,7 @@ void frame::patch_pc(Thread* thread, address pc) { assert(!Continuation::is_return_barrier_entry(*pc_addr), "return barrier"); - assert(_pc == *pc_addr || pc == *pc_addr || *pc_addr == 0, ""); + assert(_pc == *pc_addr || pc == *pc_addr || *pc_addr == nullptr, ""); DEBUG_ONLY(address old_pc = _pc;) *pc_addr = pc; _pc = pc; // must be set before call to get_deopt_original_pc @@ -483,10 +483,10 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const { bool frame::is_interpreted_frame_valid(JavaThread* thread) const { assert(is_interpreted_frame(), "Not an interpreted frame"); // These are reasonable sanity checks - if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { + if (fp() == nullptr || (intptr_t(fp()) & (wordSize-1)) != 0) { return false; } - if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { + if (sp() == nullptr || (intptr_t(sp()) & (wordSize-1)) != 0) { return false; } if (fp() + interpreter_frame_initial_sp_offset < sp()) { diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index 64ad743067476..cd0e43b68bf9e 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -423,7 +423,7 @@ void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) { __ load_method_holder_cld(tmp1, rbx); // Is it a strong CLD? - __ cmpl(Address(tmp1, ClassLoaderData::keep_alive_offset()), 0); + __ cmpl(Address(tmp1, ClassLoaderData::keep_alive_ref_count_offset()), 0); __ jcc(Assembler::greater, method_live); // Is it a weak but alive CLD? diff --git a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp index 9995a87f5cf5b..032b33f40e038 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp @@ -46,9 +46,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler* masm) { assert(cmpval != addr, "cmp and addr must be in different registers"); assert(newval != addr, "new value and addr must be in different registers"); - // Apply IU barrier to newval. - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), newval, tmp1); - #ifdef _LP64 if (UseCompressedOops) { __ encode_heap_oop(cmpval); @@ -101,10 +98,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - // Because we want a 2-arg form of xchg and xadd __ move(value_opr, result); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 25a2b931468cd..47078dff90738 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -121,7 +121,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec if (is_reference_type(type)) { - if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahIUBarrier || ShenandoahLoadRefBarrier) { + if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) { #ifdef _LP64 Register thread = r15_thread; #else @@ -472,40 +472,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, #endif } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) { - if (ShenandoahIUBarrier) { - iu_barrier_impl(masm, dst, tmp); - } -} - -void ShenandoahBarrierSetAssembler::iu_barrier_impl(MacroAssembler* masm, Register dst, Register tmp) { - assert(ShenandoahIUBarrier, "should be enabled"); - - if (dst == noreg) return; - - if (ShenandoahIUBarrier) { - save_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); - -#ifdef _LP64 - Register thread = r15_thread; -#else - Register thread = rcx; - if (thread == dst || thread == tmp) { - thread = rdi; - } - if (thread == dst || thread == tmp) { - thread = rbx; - } - __ get_thread(thread); -#endif - assert_different_registers(dst, tmp, thread); - - satb_write_barrier_pre(masm, noreg, dst, thread, tmp, true, false); - - restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); - } -} - // // Arguments: // @@ -626,12 +592,7 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet val != noreg /* tosca_live */, false /* expand_call */); } - if (val == noreg) { - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); - } else { - iu_barrier(masm, val, tmp3); - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); - } + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); NOT_LP64(imasm->restore_bcp()); } else { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index 47dfe14492802..c8140f4a76a47 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -56,10 +56,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { bool tosca_live, bool expand_call); - void iu_barrier_impl(MacroAssembler* masm, Register dst, Register tmp); - public: - void iu_barrier(MacroAssembler* masm, Register dst, Register tmp); #ifdef COMPILER1 void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); diff --git a/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp b/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp index 34be2a61b5a47..52e4388c5d249 100644 --- a/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp +++ b/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -138,7 +138,7 @@ address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { default: ShouldNotReachHere(); } // tail call - __ jump (ExternalAddress(slow_case_addr)); + __ jump (RuntimeAddress(slow_case_addr)); __ flush (); @@ -251,7 +251,7 @@ address JNI_FastGetField::generate_fast_get_long_field() { __ pop (rsi); address slow_case_addr = jni_GetLongField_addr();; // tail call - __ jump (ExternalAddress(slow_case_addr)); + __ jump (RuntimeAddress(slow_case_addr)); __ flush (); @@ -350,7 +350,7 @@ address JNI_FastGetField::generate_fast_get_float_field0(BasicType type) { default: ShouldNotReachHere(); } // tail call - __ jump (ExternalAddress(slow_case_addr)); + __ jump (RuntimeAddress(slow_case_addr)); __ flush (); diff --git a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp index 4506205c259fb..e94b7d12b0b3c 100644 --- a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp +++ b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,7 +118,7 @@ address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { default: break; } // tail call - __ jump (ExternalAddress(slow_case_addr), rscratch1); + __ jump (RuntimeAddress(slow_case_addr), rscratch1); __ flush (); @@ -206,7 +206,7 @@ address JNI_FastGetField::generate_fast_get_float_field0(BasicType type) { default: break; } // tail call - __ jump (ExternalAddress(slow_case_addr), rscratch1); + __ jump (RuntimeAddress(slow_case_addr), rscratch1); __ flush (); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index affd18f937ca8..c8234d6b9b88b 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -2329,7 +2329,7 @@ void MacroAssembler::incrementl(Address dst, int value) { void MacroAssembler::jump(AddressLiteral dst, Register rscratch) { assert(rscratch != noreg || always_reachable(dst), "missing"); - + assert(!dst.rspec().reloc()->is_data(), "should not use ExternalAddress for jump"); if (reachable(dst)) { jmp_literal(dst.target(), dst.rspec()); } else { @@ -2340,7 +2340,7 @@ void MacroAssembler::jump(AddressLiteral dst, Register rscratch) { void MacroAssembler::jump_cc(Condition cc, AddressLiteral dst, Register rscratch) { assert(rscratch != noreg || always_reachable(dst), "missing"); - + assert(!dst.rspec().reloc()->is_data(), "should not use ExternalAddress for jump_cc"); if (reachable(dst)) { InstructionMark im(this); relocate(dst.reloc()); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp index c4e9e836fd312..090de71425f17 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016, 2021, Intel Corporation. All rights reserved. +* Copyright (c) 2016, 2024, Intel Corporation. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -689,7 +689,7 @@ void MacroAssembler::sha256_AVX2(XMMRegister msg, XMMRegister state0, XMMRegiste address K256_W = StubRoutines::x86::k256_W_addr(); address pshuffle_byte_flip_mask = StubRoutines::x86::pshuffle_byte_flip_mask_addr(); - address pshuffle_byte_flip_mask_addr = 0; + address pshuffle_byte_flip_mask_addr = nullptr; const XMMRegister& SHUF_00BA = xmm10; // ymm10: shuffle xBxA -> 00BA const XMMRegister& SHUF_DC00 = xmm12; // ymm12: shuffle xDxC -> DC00 @@ -1247,7 +1247,7 @@ void MacroAssembler::sha512_AVX2(XMMRegister msg, XMMRegister state0, XMMRegiste address K512_W = StubRoutines::x86::k512_W_addr(); address pshuffle_byte_flip_mask_sha512 = StubRoutines::x86::pshuffle_byte_flip_mask_addr_sha512(); - address pshuffle_byte_flip_mask_addr = 0; + address pshuffle_byte_flip_mask_addr = nullptr; const XMMRegister& XFER = xmm0; // YTMP0 const XMMRegister& BYTE_FLIP_MASK = xmm9; // ymm9 diff --git a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp index de897d71facef..3a8c104876677 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.cpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -572,7 +572,7 @@ void trace_method_handle_stub(const char* adaptername, frame cur_frame = os::current_frame(); - if (cur_frame.fp() != 0) { // not walkable + if (cur_frame.fp() != nullptr) { // not walkable // Robust search of trace_calling_frame (independent of inlining). // Assumes saved_regs comes from a pusha in the trace_calling_frame. diff --git a/src/hotspot/cpu/x86/runtime_x86_32.cpp b/src/hotspot/cpu/x86/runtime_x86_32.cpp index d38fa3c60bdd1..2a21c42a5e606 100644 --- a/src/hotspot/cpu/x86/runtime_x86_32.cpp +++ b/src/hotspot/cpu/x86/runtime_x86_32.cpp @@ -41,6 +41,180 @@ #define __ masm-> +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // allocate space for the code + ResourceMark rm; + // setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 512, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + enum frame_layout { + arg0_off, // thread sp + 0 // Arg location for + arg1_off, // unloaded_class_index sp + 1 // calling C + arg2_off, // exec_mode sp + 2 + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off, // callee saved register sp + 3 + return_off, // slot for return address sp + 4 + framesize + }; + + address start = __ pc(); + + // Push self-frame. + __ subptr(rsp, return_off*wordSize); // Epilog! + + // rbp, is an implicitly saved callee saved register (i.e. the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers no that adapter frames are gone. + __ movptr(Address(rsp, rbp_off*wordSize), rbp); + + // Clear the floating point exception stack + __ empty_FPU_stack(); + + // set last_Java_sp + __ get_thread(rdx); + __ set_last_Java_frame(rdx, noreg, noreg, nullptr, noreg); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + __ movptr(Address(rsp, arg0_off*wordSize), rdx); + // argument already in ECX + __ movl(Address(rsp, arg1_off*wordSize),rcx); + __ movl(Address(rsp, arg2_off*wordSize), Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); + + // Set an oopmap for the call site + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = new OopMap( framesize, 0 ); + // No oopMap for rbp, it is known implicitly + + oop_maps->add_gc_map( __ pc()-start, map); + + __ get_thread(rcx); + + __ reset_last_Java_frame(rcx, false); + + // Load UnrollBlock into EDI + __ movptr(rdi, rax); + +#ifdef ASSERT + { Label L; + __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), + (int32_t)Deoptimization::Unpack_uncommon_trap); + __ jcc(Assembler::equal, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on EAX and ESP. + __ addptr(rsp,(framesize-1)*wordSize); // Epilog! + + // Pop deoptimized frame + __ movl2ptr(rcx, Address(rdi,Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); + __ addptr(rsp, rcx); + + // sp should be pointing at the return address to the caller (3) + + // Pick up the initial fp we should save + // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) + __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); + __ bang_stack_size(rbx, rcx); +#endif + + // Load array of frame pcs into ECX + __ movl(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset())); + + __ pop(rsi); // trash the pc + + // Load array of frame sizes into ESI + __ movptr(rsi,Address(rdi,Deoptimization::UnrollBlock::frame_sizes_offset())); + + Address counter(rdi, Deoptimization::UnrollBlock::counter_temp_offset()); + + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::number_of_frames_offset())); + __ movl(counter, rbx); + + // Now adjust the caller's stack to make up for the extra locals + // but record the original sp so that we can save it in the skeletal interpreter + // frame and the stack walking of interpreter_sender will get the unextended sp + // value and not the "real" sp value. + + Address sp_temp(rdi, Deoptimization::UnrollBlock::sender_sp_temp_offset()); + __ movptr(sp_temp, rsp); + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::caller_adjustment_offset())); + __ subptr(rsp, rbx); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movptr(rbx, Address(rsi, 0)); // Load frame size + __ subptr(rbx, 2*wordSize); // we'll push pc and rbp, by hand + __ pushptr(Address(rcx, 0)); // save return address + __ enter(); // save old & set new rbp, + __ subptr(rsp, rbx); // Prolog! + __ movptr(rbx, sp_temp); // sender's sp + // This value is corrected by layout_activation_impl + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD ); + __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), rbx); // Make it walkable + __ movptr(sp_temp, rsp); // pass to next frame + __ addptr(rsi, wordSize); // Bump array pointer (sizes) + __ addptr(rcx, wordSize); // Bump array pointer (pcs) + __ decrementl(counter); // decrement counter + __ jcc(Assembler::notZero, loop); + __ pushptr(Address(rcx, 0)); // save final return address + + // Re-push self-frame + __ enter(); // save old & set new rbp, + __ subptr(rsp, (framesize-2) * wordSize); // Prolog! + + + // set last_Java_sp, last_Java_fp + __ get_thread(rdi); + __ set_last_Java_frame(rdi, noreg, rbp, nullptr, noreg); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + __ movptr(Address(rsp,arg0_off*wordSize),rdi); + __ movl(Address(rsp,arg1_off*wordSize), Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + // Set an oopmap for the call site + oop_maps->add_gc_map( __ pc()-start, new OopMap( framesize, 0 ) ); + + __ get_thread(rdi); + __ reset_last_Java_frame(rdi, true); + + // Pop self-frame. + __ leave(); // Epilog! + + // Jump to interpreter + __ ret(0); + + // ------------- + // make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, framesize); +} + //------------------------------generate_exception_blob--------------------------- // creates exception blob at the end // Using exception blob, this code is jumped from a compiled method. diff --git a/src/hotspot/cpu/x86/runtime_x86_64.cpp b/src/hotspot/cpu/x86/runtime_x86_64.cpp index e08550715b495..eb3bab36b88cb 100644 --- a/src/hotspot/cpu/x86/runtime_x86_64.cpp +++ b/src/hotspot/cpu/x86/runtime_x86_64.cpp @@ -34,11 +34,328 @@ #include "runtime/vframeArray.hpp" #include "utilities/globalDefinitions.hpp" #include "vmreg_x86.inline.hpp" + +class SimpleRuntimeFrame { + + public: + + // Most of the runtime stubs have this simple frame layout. + // This class exists to make the layout shared in one place. + // Offsets are for compiler stack slots, which are jints. + enum layout { + // The frame sender code expects that rbp will be in the "natural" place and + // will override any oopMap setting for it. We must therefore force the layout + // so that it agrees with the frame sender code. + rbp_off = frame::arg_reg_save_area_bytes/BytesPerInt, + rbp_off2, + return_off, return_off2, + framesize + }; +}; + +#define __ masm-> + +//------------------------------generate_uncommon_trap_blob-------------------- +void OptoRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + address start = __ pc(); + + // Push self-frame. We get here with a return address on the + // stack, so rsp is 8-byte aligned until we allocate our frame. + __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog! + + // No callee saved registers. rbp is assumed implicitly saved + __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); + + // compiler left unloaded_class_index in j_rarg0 move to where the + // runtime expects it. + __ movl(c_rarg1, j_rarg0); + + __ set_last_Java_frame(noreg, noreg, nullptr, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // capture callee-saved registers as well as return values. + // Thread is in rdi already. + // + // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); + + __ mov(c_rarg0, r15_thread); + __ movl(c_rarg2, Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); + + // Set an oopmap for the call site + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); + + // location of rbp is known implicitly by the frame sender code + + oop_maps->add_gc_map(__ pc() - start, map); + + __ reset_last_Java_frame(false); + + // Load UnrollBlock* into rdi + __ mov(rdi, rax); + +#ifdef ASSERT + { Label L; + __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), + Deoptimization::Unpack_uncommon_trap); + __ jcc(Assembler::equal, L); + __ stop("OptoRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } #endif + // Pop all the frames we must move/replace. + // + // Frame picture (youngest to oldest) + // 1: self-frame (no frame link) + // 2: deopting frame (no frame link) + // 3: caller of deopting frame (could be compiled/interpreted). + + // Pop self-frame. We have no frame, and must rely only on rax and rsp. + __ addptr(rsp, (SimpleRuntimeFrame::framesize - 2) << LogBytesPerInt); // Epilog! + + // Pop deoptimized frame (int) + __ movl(rcx, Address(rdi, + Deoptimization::UnrollBlock:: + size_of_deoptimized_frame_offset())); + __ addptr(rsp, rcx); + + // rsp should be pointing at the return address to the caller (3) + + // Pick up the initial fp we should save + // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) + __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); + +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. + __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); + __ bang_stack_size(rbx, rcx); +#endif + + // Load address of array of frame pcs into rcx (address*) + __ movptr(rcx, Address(rdi, Deoptimization::UnrollBlock::frame_pcs_offset())); + + // Trash the return pc + __ addptr(rsp, wordSize); + + // Load address of array of frame sizes into rsi (intptr_t*) + __ movptr(rsi, Address(rdi, Deoptimization::UnrollBlock:: frame_sizes_offset())); + + // Counter + __ movl(rdx, Address(rdi, Deoptimization::UnrollBlock:: number_of_frames_offset())); // (int) + + // Now adjust the caller's stack to make up for the extra locals but + // record the original sp so that we can save it in the skeletal + // interpreter frame and the stack walking of interpreter_sender + // will get the unextended sp value and not the "real" sp value. + + const Register sender_sp = r8; + + __ mov(sender_sp, rsp); + __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock:: caller_adjustment_offset())); // (int) + __ subptr(rsp, rbx); + + // Push interpreter frames in a loop + Label loop; + __ bind(loop); + __ movptr(rbx, Address(rsi, 0)); // Load frame size + __ subptr(rbx, 2 * wordSize); // We'll push pc and rbp by hand + __ pushptr(Address(rcx, 0)); // Save return address + __ enter(); // Save old & set new rbp + __ subptr(rsp, rbx); // Prolog + __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), + sender_sp); // Make it walkable + // This value is corrected by layout_activation_impl + __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); + __ mov(sender_sp, rsp); // Pass sender_sp to next frame + __ addptr(rsi, wordSize); // Bump array pointer (sizes) + __ addptr(rcx, wordSize); // Bump array pointer (pcs) + __ decrementl(rdx); // Decrement counter + __ jcc(Assembler::notZero, loop); + __ pushptr(Address(rcx, 0)); // Save final return address + + // Re-push self-frame + __ enter(); // Save old & set new rbp + __ subptr(rsp, (SimpleRuntimeFrame::framesize - 4) << LogBytesPerInt); + // Prolog + + // Use rbp because the frames look interpreted now + // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. + // Don't need the precise return PC here, just precise enough to point into this code blob. + address the_pc = __ pc(); + __ set_last_Java_frame(noreg, rbp, the_pc, rscratch1); + + // Call C code. Need thread but NOT official VM entry + // crud. We cannot block on this call, no GC can happen. Call should + // restore return values to their stack-slots with the new SP. + // Thread is in rdi already. + // + // BasicType unpack_frames(JavaThread* thread, int exec_mode); + + __ andptr(rsp, -(StackAlignmentInBytes)); // Align SP as required by ABI + __ mov(c_rarg0, r15_thread); + __ movl(c_rarg1, Deoptimization::Unpack_uncommon_trap); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); + + // Set an oopmap for the call site + // Use the same PC we used for the last java frame + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + // Clear fp AND pc + __ reset_last_Java_frame(true); + + // Pop self-frame. + __ leave(); // Epilog + + // Jump to interpreter + __ ret(0); + + // Make sure all code is generated + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); +} + +//------------------------------generate_exception_blob--------------------------- +// creates exception blob at the end +// Using exception blob, this code is jumped from a compiled method. +// (see emit_exception_handler in x86_64.ad file) +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// rax: exception oop +// rdx: exception pc +// +// Results: +// rax: exception oop +// rdx: exception pc in caller or ??? +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// Registers rax, rdx, rcx, rsi, rdi, r8-r11 are not callee saved. +// + +void OptoRuntime::generate_exception_blob() { + assert(!OptoRuntime::is_callee_saved_register(RDX_num), ""); + assert(!OptoRuntime::is_callee_saved_register(RAX_num), ""); + assert(!OptoRuntime::is_callee_saved_register(RCX_num), ""); + + assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("exception_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + + address start = __ pc(); + + // Exception pc is 'return address' for stack walker + __ push(rdx); + __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Prolog + + // Save callee-saved registers. See x86_64.ad. + + // rbp is an implicitly saved callee saved register (i.e., the calling + // convention will save/restore it in the prolog/epilog). Other than that + // there are no callee save registers now that adapter frames are gone. + + __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); + + // Store exception in Thread object. We cannot pass any arguments to the + // handle_exception call, since we do not want to make any assumption + // about the size of the frame where the exception happened in. + // c_rarg0 is either rdi (Linux) or rcx (Windows). + __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()),rax); + __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), rdx); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + // + // address OptoRuntime::handle_exception_C(JavaThread* thread) + + // At a method handle call, the stack may not be properly aligned + // when returning with an exception. + address the_pc = __ pc(); + __ set_last_Java_frame(noreg, noreg, the_pc, rscratch1); + __ mov(c_rarg0, r15_thread); + __ andptr(rsp, -(StackAlignmentInBytes)); // Align stack + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + + OopMapSet* oop_maps = new OopMapSet(); + + oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); + + __ reset_last_Java_frame(false); + + // Restore callee-saved registers + + // rbp is an implicitly saved callee-saved register (i.e., the calling + // convention will save restore it in prolog/epilog) Other than that + // there are no callee save registers now that adapter frames are gone. + + __ movptr(rbp, Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt)); + + __ addptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog + __ pop(rdx); // No need for exception pc anymore + + // rax: exception handler + + // We have a handler in rax (could be deopt blob). + __ mov(r8, rax); + + // Get the exception oop + __ movptr(rax, Address(r15_thread, JavaThread::exception_oop_offset())); + // Get the exception pc in case we are deoptimized + __ movptr(rdx, Address(r15_thread, JavaThread::exception_pc_offset())); +#ifdef ASSERT + __ movptr(Address(r15_thread, JavaThread::exception_handler_pc_offset()), NULL_WORD); + __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), NULL_WORD); +#endif + // Clear the exception oop so GC no longer processes it as a root. + __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()), NULL_WORD); + + // rax: exception oop + // r8: exception handler + // rdx: exception pc + // Jump to handler + + __ jmp(r8); + + // Make sure all code is generated + masm->flush(); -// This file should really contain the code for generating the OptoRuntime -// exception_blob. However that code uses SimpleRuntimeFrame which only -// exists in sharedRuntime_x86_64.cpp. When there is a sharedRuntime_.hpp -// file and SimpleRuntimeFrame is able to move there then the exception_blob -// code will move here where it belongs. + // Set exception blob + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); +} +#endif // COMPILER2 diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp index 6303c279195ca..d313c1b216a40 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp @@ -704,10 +704,10 @@ static void range_check(MacroAssembler* masm, Register pc_reg, Register temp_reg address code_start, address code_end, Label& L_ok) { Label L_fail; - __ lea(temp_reg, ExternalAddress(code_start)); + __ lea(temp_reg, AddressLiteral(code_start, relocInfo::none)); __ cmpptr(pc_reg, temp_reg); __ jcc(Assembler::belowEqual, L_fail); - __ lea(temp_reg, ExternalAddress(code_end)); + __ lea(temp_reg, AddressLiteral(code_end, relocInfo::none)); __ cmpptr(pc_reg, temp_reg); __ jcc(Assembler::below, L_ok); __ bind(L_fail); @@ -2389,183 +2389,6 @@ void SharedRuntime::generate_deopt_blob() { _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); } - -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // allocate space for the code - ResourceMark rm; - // setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 512, 512); - MacroAssembler* masm = new MacroAssembler(&buffer); - - enum frame_layout { - arg0_off, // thread sp + 0 // Arg location for - arg1_off, // unloaded_class_index sp + 1 // calling C - arg2_off, // exec_mode sp + 2 - // The frame sender code expects that rbp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - rbp_off, // callee saved register sp + 3 - return_off, // slot for return address sp + 4 - framesize - }; - - address start = __ pc(); - - // Push self-frame. - __ subptr(rsp, return_off*wordSize); // Epilog! - - // rbp, is an implicitly saved callee saved register (i.e. the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers no that adapter frames are gone. - __ movptr(Address(rsp, rbp_off*wordSize), rbp); - - // Clear the floating point exception stack - __ empty_FPU_stack(); - - // set last_Java_sp - __ get_thread(rdx); - __ set_last_Java_frame(rdx, noreg, noreg, nullptr, noreg); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - __ movptr(Address(rsp, arg0_off*wordSize), rdx); - // argument already in ECX - __ movl(Address(rsp, arg1_off*wordSize),rcx); - __ movl(Address(rsp, arg2_off*wordSize), Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); - - // Set an oopmap for the call site - OopMapSet *oop_maps = new OopMapSet(); - OopMap* map = new OopMap( framesize, 0 ); - // No oopMap for rbp, it is known implicitly - - oop_maps->add_gc_map( __ pc()-start, map); - - __ get_thread(rcx); - - __ reset_last_Java_frame(rcx, false); - - // Load UnrollBlock into EDI - __ movptr(rdi, rax); - -#ifdef ASSERT - { Label L; - __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), - (int32_t)Deoptimization::Unpack_uncommon_trap); - __ jcc(Assembler::equal, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - // Pop self-frame. We have no frame, and must rely only on EAX and ESP. - __ addptr(rsp,(framesize-1)*wordSize); // Epilog! - - // Pop deoptimized frame - __ movl2ptr(rcx, Address(rdi,Deoptimization::UnrollBlock::size_of_deoptimized_frame_offset())); - __ addptr(rsp, rcx); - - // sp should be pointing at the return address to the caller (3) - - // Pick up the initial fp we should save - // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) - __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); - __ bang_stack_size(rbx, rcx); -#endif - - // Load array of frame pcs into ECX - __ movl(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset())); - - __ pop(rsi); // trash the pc - - // Load array of frame sizes into ESI - __ movptr(rsi,Address(rdi,Deoptimization::UnrollBlock::frame_sizes_offset())); - - Address counter(rdi, Deoptimization::UnrollBlock::counter_temp_offset()); - - __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::number_of_frames_offset())); - __ movl(counter, rbx); - - // Now adjust the caller's stack to make up for the extra locals - // but record the original sp so that we can save it in the skeletal interpreter - // frame and the stack walking of interpreter_sender will get the unextended sp - // value and not the "real" sp value. - - Address sp_temp(rdi, Deoptimization::UnrollBlock::sender_sp_temp_offset()); - __ movptr(sp_temp, rsp); - __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::caller_adjustment_offset())); - __ subptr(rsp, rbx); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ movptr(rbx, Address(rsi, 0)); // Load frame size - __ subptr(rbx, 2*wordSize); // we'll push pc and rbp, by hand - __ pushptr(Address(rcx, 0)); // save return address - __ enter(); // save old & set new rbp, - __ subptr(rsp, rbx); // Prolog! - __ movptr(rbx, sp_temp); // sender's sp - // This value is corrected by layout_activation_impl - __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD ); - __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), rbx); // Make it walkable - __ movptr(sp_temp, rsp); // pass to next frame - __ addptr(rsi, wordSize); // Bump array pointer (sizes) - __ addptr(rcx, wordSize); // Bump array pointer (pcs) - __ decrementl(counter); // decrement counter - __ jcc(Assembler::notZero, loop); - __ pushptr(Address(rcx, 0)); // save final return address - - // Re-push self-frame - __ enter(); // save old & set new rbp, - __ subptr(rsp, (framesize-2) * wordSize); // Prolog! - - - // set last_Java_sp, last_Java_fp - __ get_thread(rdi); - __ set_last_Java_frame(rdi, noreg, rbp, nullptr, noreg); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - __ movptr(Address(rsp,arg0_off*wordSize),rdi); - __ movl(Address(rsp,arg1_off*wordSize), Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); - // Set an oopmap for the call site - oop_maps->add_gc_map( __ pc()-start, new OopMap( framesize, 0 ) ); - - __ get_thread(rdi); - __ reset_last_Java_frame(rdi, true); - - // Pop self-frame. - __ leave(); // Epilog! - - // Jump to interpreter - __ ret(0); - - // ------------- - // make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, framesize); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index decaa9d1ee914..d27b1d141fc29 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -72,24 +72,6 @@ const int StackAlignmentInSlots = StackAlignmentInBytes / VMRegImpl::stack_slot_size; -class SimpleRuntimeFrame { - - public: - - // Most of the runtime stubs have this simple frame layout. - // This class exists to make the layout shared in one place. - // Offsets are for compiler stack slots, which are jints. - enum layout { - // The frame sender code expects that rbp will be in the "natural" place and - // will override any oopMap setting for it. We must therefore force the layout - // so that it agrees with the frame sender code. - rbp_off = frame::arg_reg_save_area_bytes/BytesPerInt, - rbp_off2, - return_off, return_off2, - framesize - }; -}; - class RegisterSaver { // Capture info about frame layout. Layout offsets are in jint // units because compiler frame slots are jints. @@ -842,10 +824,10 @@ static void range_check(MacroAssembler* masm, Register pc_reg, Register temp_reg address code_start, address code_end, Label& L_ok) { Label L_fail; - __ lea(temp_reg, ExternalAddress(code_start)); + __ lea(temp_reg, AddressLiteral(code_start, relocInfo::none)); __ cmpptr(pc_reg, temp_reg); __ jcc(Assembler::belowEqual, L_fail); - __ lea(temp_reg, ExternalAddress(code_end)); + __ lea(temp_reg, AddressLiteral(code_end, relocInfo::none)); __ cmpptr(pc_reg, temp_reg); __ jcc(Assembler::below, L_ok); __ bind(L_fail); @@ -2987,182 +2969,6 @@ void SharedRuntime::generate_deopt_blob() { #endif } -#ifdef COMPILER2 -//------------------------------generate_uncommon_trap_blob-------------------- -void SharedRuntime::generate_uncommon_trap_blob() { - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - address start = __ pc(); - - // Push self-frame. We get here with a return address on the - // stack, so rsp is 8-byte aligned until we allocate our frame. - __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog! - - // No callee saved registers. rbp is assumed implicitly saved - __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); - - // compiler left unloaded_class_index in j_rarg0 move to where the - // runtime expects it. - __ movl(c_rarg1, j_rarg0); - - __ set_last_Java_frame(noreg, noreg, nullptr, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // capture callee-saved registers as well as return values. - // Thread is in rdi already. - // - // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); - - __ mov(c_rarg0, r15_thread); - __ movl(c_rarg2, Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); - - // Set an oopmap for the call site - OopMapSet* oop_maps = new OopMapSet(); - OopMap* map = new OopMap(SimpleRuntimeFrame::framesize, 0); - - // location of rbp is known implicitly by the frame sender code - - oop_maps->add_gc_map(__ pc() - start, map); - - __ reset_last_Java_frame(false); - - // Load UnrollBlock* into rdi - __ mov(rdi, rax); - -#ifdef ASSERT - { Label L; - __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset()), - Deoptimization::Unpack_uncommon_trap); - __ jcc(Assembler::equal, L); - __ stop("SharedRuntime::generate_uncommon_trap_blob: expected Unpack_uncommon_trap"); - __ bind(L); - } -#endif - - // Pop all the frames we must move/replace. - // - // Frame picture (youngest to oldest) - // 1: self-frame (no frame link) - // 2: deopting frame (no frame link) - // 3: caller of deopting frame (could be compiled/interpreted). - - // Pop self-frame. We have no frame, and must rely only on rax and rsp. - __ addptr(rsp, (SimpleRuntimeFrame::framesize - 2) << LogBytesPerInt); // Epilog! - - // Pop deoptimized frame (int) - __ movl(rcx, Address(rdi, - Deoptimization::UnrollBlock:: - size_of_deoptimized_frame_offset())); - __ addptr(rsp, rcx); - - // rsp should be pointing at the return address to the caller (3) - - // Pick up the initial fp we should save - // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) - __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset())); - -#ifdef ASSERT - // Compilers generate code that bang the stack by as much as the - // interpreter would need. So this stack banging should never - // trigger a fault. Verify that it does not on non product builds. - __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset())); - __ bang_stack_size(rbx, rcx); -#endif - - // Load address of array of frame pcs into rcx (address*) - __ movptr(rcx, Address(rdi, Deoptimization::UnrollBlock::frame_pcs_offset())); - - // Trash the return pc - __ addptr(rsp, wordSize); - - // Load address of array of frame sizes into rsi (intptr_t*) - __ movptr(rsi, Address(rdi, Deoptimization::UnrollBlock:: frame_sizes_offset())); - - // Counter - __ movl(rdx, Address(rdi, Deoptimization::UnrollBlock:: number_of_frames_offset())); // (int) - - // Now adjust the caller's stack to make up for the extra locals but - // record the original sp so that we can save it in the skeletal - // interpreter frame and the stack walking of interpreter_sender - // will get the unextended sp value and not the "real" sp value. - - const Register sender_sp = r8; - - __ mov(sender_sp, rsp); - __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock:: caller_adjustment_offset())); // (int) - __ subptr(rsp, rbx); - - // Push interpreter frames in a loop - Label loop; - __ bind(loop); - __ movptr(rbx, Address(rsi, 0)); // Load frame size - __ subptr(rbx, 2 * wordSize); // We'll push pc and rbp by hand - __ pushptr(Address(rcx, 0)); // Save return address - __ enter(); // Save old & set new rbp - __ subptr(rsp, rbx); // Prolog - __ movptr(Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize), - sender_sp); // Make it walkable - // This value is corrected by layout_activation_impl - __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD); - __ mov(sender_sp, rsp); // Pass sender_sp to next frame - __ addptr(rsi, wordSize); // Bump array pointer (sizes) - __ addptr(rcx, wordSize); // Bump array pointer (pcs) - __ decrementl(rdx); // Decrement counter - __ jcc(Assembler::notZero, loop); - __ pushptr(Address(rcx, 0)); // Save final return address - - // Re-push self-frame - __ enter(); // Save old & set new rbp - __ subptr(rsp, (SimpleRuntimeFrame::framesize - 4) << LogBytesPerInt); - // Prolog - - // Use rbp because the frames look interpreted now - // Save "the_pc" since it cannot easily be retrieved using the last_java_SP after we aligned SP. - // Don't need the precise return PC here, just precise enough to point into this code blob. - address the_pc = __ pc(); - __ set_last_Java_frame(noreg, rbp, the_pc, rscratch1); - - // Call C code. Need thread but NOT official VM entry - // crud. We cannot block on this call, no GC can happen. Call should - // restore return values to their stack-slots with the new SP. - // Thread is in rdi already. - // - // BasicType unpack_frames(JavaThread* thread, int exec_mode); - - __ andptr(rsp, -(StackAlignmentInBytes)); // Align SP as required by ABI - __ mov(c_rarg0, r15_thread); - __ movl(c_rarg1, Deoptimization::Unpack_uncommon_trap); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames))); - - // Set an oopmap for the call site - // Use the same PC we used for the last java frame - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - // Clear fp AND pc - __ reset_last_Java_frame(true); - - // Pop self-frame. - __ leave(); // Epilog - - // Jump to interpreter - __ ret(0); - - // Make sure all code is generated - masm->flush(); - - _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 - //------------------------------generate_handler_blob------ // // Generate a special Compile2Runtime blob that saves all registers, @@ -3669,136 +3475,3 @@ void SharedRuntime::montgomery_square(jint *a_ints, jint *n_ints, reverse_words(m, (julong *)m_ints, longwords); } -#ifdef COMPILER2 -// This is here instead of runtime_x86_64.cpp because it uses SimpleRuntimeFrame -// -//------------------------------generate_exception_blob--------------------------- -// creates exception blob at the end -// Using exception blob, this code is jumped from a compiled method. -// (see emit_exception_handler in x86_64.ad file) -// -// Given an exception pc at a call we call into the runtime for the -// handler in this method. This handler might merely restore state -// (i.e. callee save registers) unwind the frame and jump to the -// exception handler for the nmethod if there is no Java level handler -// for the nmethod. -// -// This code is entered with a jmp. -// -// Arguments: -// rax: exception oop -// rdx: exception pc -// -// Results: -// rax: exception oop -// rdx: exception pc in caller or ??? -// destination: exception handler of caller -// -// Note: the exception pc MUST be at a call (precise debug information) -// Registers rax, rdx, rcx, rsi, rdi, r8-r11 are not callee saved. -// - -void OptoRuntime::generate_exception_blob() { - assert(!OptoRuntime::is_callee_saved_register(RDX_num), ""); - assert(!OptoRuntime::is_callee_saved_register(RAX_num), ""); - assert(!OptoRuntime::is_callee_saved_register(RCX_num), ""); - - assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); - - // Allocate space for the code - ResourceMark rm; - // Setup code generation tools - CodeBuffer buffer("exception_blob", 2048, 1024); - MacroAssembler* masm = new MacroAssembler(&buffer); - - - address start = __ pc(); - - // Exception pc is 'return address' for stack walker - __ push(rdx); - __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Prolog - - // Save callee-saved registers. See x86_64.ad. - - // rbp is an implicitly saved callee saved register (i.e., the calling - // convention will save/restore it in the prolog/epilog). Other than that - // there are no callee save registers now that adapter frames are gone. - - __ movptr(Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt), rbp); - - // Store exception in Thread object. We cannot pass any arguments to the - // handle_exception call, since we do not want to make any assumption - // about the size of the frame where the exception happened in. - // c_rarg0 is either rdi (Linux) or rcx (Windows). - __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()),rax); - __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), rdx); - - // This call does all the hard work. It checks if an exception handler - // exists in the method. - // If so, it returns the handler address. - // If not, it prepares for stack-unwinding, restoring the callee-save - // registers of the frame being removed. - // - // address OptoRuntime::handle_exception_C(JavaThread* thread) - - // At a method handle call, the stack may not be properly aligned - // when returning with an exception. - address the_pc = __ pc(); - __ set_last_Java_frame(noreg, noreg, the_pc, rscratch1); - __ mov(c_rarg0, r15_thread); - __ andptr(rsp, -(StackAlignmentInBytes)); // Align stack - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, OptoRuntime::handle_exception_C))); - - // Set an oopmap for the call site. This oopmap will only be used if we - // are unwinding the stack. Hence, all locations will be dead. - // Callee-saved registers will be the same as the frame above (i.e., - // handle_exception_stub), since they were restored when we got the - // exception. - - OopMapSet* oop_maps = new OopMapSet(); - - oop_maps->add_gc_map(the_pc - start, new OopMap(SimpleRuntimeFrame::framesize, 0)); - - __ reset_last_Java_frame(false); - - // Restore callee-saved registers - - // rbp is an implicitly saved callee-saved register (i.e., the calling - // convention will save restore it in prolog/epilog) Other than that - // there are no callee save registers now that adapter frames are gone. - - __ movptr(rbp, Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt)); - - __ addptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog - __ pop(rdx); // No need for exception pc anymore - - // rax: exception handler - - // We have a handler in rax (could be deopt blob). - __ mov(r8, rax); - - // Get the exception oop - __ movptr(rax, Address(r15_thread, JavaThread::exception_oop_offset())); - // Get the exception pc in case we are deoptimized - __ movptr(rdx, Address(r15_thread, JavaThread::exception_pc_offset())); -#ifdef ASSERT - __ movptr(Address(r15_thread, JavaThread::exception_handler_pc_offset()), NULL_WORD); - __ movptr(Address(r15_thread, JavaThread::exception_pc_offset()), NULL_WORD); -#endif - // Clear the exception oop so GC no longer processes it as a root. - __ movptr(Address(r15_thread, JavaThread::exception_oop_offset()), NULL_WORD); - - // rax: exception oop - // r8: exception handler - // rdx: exception pc - // Jump to handler - - __ jmp(r8); - - // Make sure all code is generated - masm->flush(); - - // Set exception blob - _exception_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); -} -#endif // COMPILER2 diff --git a/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp b/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp index 5f21939a3aa17..63212a686afc0 100644 --- a/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp +++ b/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp @@ -30,8 +30,16 @@ // Java frames don't have callee saved registers (except for rbp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } + private: static void assert_is_rbp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ assert(r == rbp->as_VMReg() || r == rbp->as_VMReg()->next(), "Reg: %s", r->name()); }) @@ -48,17 +56,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - #ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) assert_is_rbp(r); - } - #endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert_is_rbp(reg); return (address)(sp - frame::sender_sp_offset); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index c9c4b056eb59d..a7404b298f673 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -3702,7 +3702,7 @@ address StubGenerator::generate_cont_thaw(const char* label, Continuation::thaw_ Label L_thaw_success; __ testptr(rbx, rbx); __ jccb(Assembler::notZero, L_thaw_success); - __ jump(ExternalAddress(StubRoutines::throw_StackOverflowError_entry())); + __ jump(RuntimeAddress(StubRoutines::throw_StackOverflowError_entry())); __ bind(L_thaw_success); // Make room for the thawed frames and align the stack. diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index d8f7a6b272bc9..5b316881d0346 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -215,7 +215,7 @@ void StubGenerator::array_overlap_test(address no_overlap_target, Label* NOLp, A __ cmpptr(to, from); __ lea(end_from, Address(from, count, sf, 0)); if (NOLp == nullptr) { - ExternalAddress no_overlap(no_overlap_target); + RuntimeAddress no_overlap(no_overlap_target); __ jump_cc(Assembler::belowEqual, no_overlap); __ cmpptr(to, end_from); __ jump_cc(Assembler::aboveEqual, no_overlap); diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index fe2bf67afc96e..3b32d577d4470 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -178,7 +178,7 @@ address TemplateInterpreterGenerator::generate_exception_handler_common( rarg, rarg2); } // throw exception - __ jump(ExternalAddress(Interpreter::throw_exception_entry())); + __ jump(RuntimeAddress(Interpreter::throw_exception_entry())); return entry; } @@ -546,7 +546,7 @@ void TemplateInterpreterGenerator::generate_stack_overflow_check(void) { // Note: the restored frame is not necessarily interpreted. // Use the shared runtime version of the StackOverflowError. assert(StubRoutines::throw_StackOverflowError_entry() != nullptr, "stub not yet generated"); - __ jump(ExternalAddress(StubRoutines::throw_StackOverflowError_entry())); + __ jump(RuntimeAddress(StubRoutines::throw_StackOverflowError_entry())); // all done with frame size check __ bind(after_frame_check_pop); NOT_LP64(__ pop(rsi)); diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 6446ec6598791..fc6844aedd6b2 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -774,7 +774,7 @@ void TemplateTable::index_check_without_pop(Register array, Register index) { __ jccb(Assembler::below, skip); // Pass array to create more detailed exceptions. __ mov(NOT_LP64(rax) LP64_ONLY(c_rarg1), array); - __ jump(ExternalAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry)); + __ jump(RuntimeAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry)); __ bind(skip); } @@ -1152,7 +1152,7 @@ void TemplateTable::aastore() { // Come here on failure // object is at TOS - __ jump(ExternalAddress(Interpreter::_throw_ArrayStoreException_entry)); + __ jump(RuntimeAddress(Interpreter::_throw_ArrayStoreException_entry)); // Come here on success __ bind(ok_is_subtype); @@ -1432,7 +1432,7 @@ void TemplateTable::ldiv() { // generate explicit div0 check __ testq(rcx, rcx); __ jump_cc(Assembler::zero, - ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + RuntimeAddress(Interpreter::_throw_ArithmeticException_entry)); // Note: could xor rax and rcx and compare with (-1 ^ min_int). If // they are not equal, one could do a normal division (no correction // needed), which may speed up this implementation for the common case. @@ -1445,7 +1445,7 @@ void TemplateTable::ldiv() { // check if y = 0 __ orl(rax, rdx); __ jump_cc(Assembler::zero, - ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + RuntimeAddress(Interpreter::_throw_ArithmeticException_entry)); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::ldiv)); __ addptr(rsp, 4 * wordSize); // take off temporaries #endif @@ -1458,7 +1458,7 @@ void TemplateTable::lrem() { __ pop_l(rax); __ testq(rcx, rcx); __ jump_cc(Assembler::zero, - ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + RuntimeAddress(Interpreter::_throw_ArithmeticException_entry)); // Note: could xor rax and rcx and compare with (-1 ^ min_int). If // they are not equal, one could do a normal division (no correction // needed), which may speed up this implementation for the common case. @@ -1472,7 +1472,7 @@ void TemplateTable::lrem() { // check if y = 0 __ orl(rax, rdx); __ jump_cc(Assembler::zero, - ExternalAddress(Interpreter::_throw_ArithmeticException_entry)); + RuntimeAddress(Interpreter::_throw_ArithmeticException_entry)); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::lrem)); __ addptr(rsp, 4 * wordSize); #endif @@ -4222,7 +4222,7 @@ void TemplateTable::checkcast() { // Come here on failure __ push_ptr(rdx); // object is at TOS - __ jump(ExternalAddress(Interpreter::_throw_ClassCastException_entry)); + __ jump(RuntimeAddress(Interpreter::_throw_ClassCastException_entry)); // Come here on success __ bind(ok_is_subtype); @@ -4340,7 +4340,7 @@ void TemplateTable::_breakpoint() { void TemplateTable::athrow() { transition(atos, vtos); __ null_check(rax); - __ jump(ExternalAddress(Interpreter::throw_exception_entry())); + __ jump(RuntimeAddress(Interpreter::throw_exception_entry())); } //----------------------------------------------------------------------------- diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 02e147743cb1d..6216cf44b887a 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -53,13 +53,13 @@ const char* VM_Version::_features_names[] = { CPU_FEATURE_FLAGS(DECLARE_CPU_FEAT #undef DECLARE_CPU_FEATURE_FLAG // Address of instruction which causes SEGV -address VM_Version::_cpuinfo_segv_addr = 0; +address VM_Version::_cpuinfo_segv_addr = nullptr; // Address of instruction after the one which causes SEGV -address VM_Version::_cpuinfo_cont_addr = 0; +address VM_Version::_cpuinfo_cont_addr = nullptr; // Address of instruction which causes APX specific SEGV -address VM_Version::_cpuinfo_segv_addr_apx = 0; +address VM_Version::_cpuinfo_segv_addr_apx = nullptr; // Address of instruction after the one which causes APX specific SEGV -address VM_Version::_cpuinfo_cont_addr_apx = 0; +address VM_Version::_cpuinfo_cont_addr_apx = nullptr; static BufferBlob* stub_blob; static const int stub_size = 2000; diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index d90ba901ac0a8..2b29dd14e4b27 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -5109,7 +5109,7 @@ instruct reductionL_avx512dq(rRegL dst, rRegL src1, vec src2, vec vtmp1, vec vtm // =======================Float Reduction========================================== instruct reductionF128(regF dst, vec src, vec vtmp) %{ - predicate(Matcher::vector_length(n->in(2)) <= 4); // src + predicate(n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) <= 4); // src match(Set dst (AddReductionVF dst src)); match(Set dst (MulReductionVF dst src)); effect(TEMP dst, TEMP vtmp); @@ -5123,7 +5123,7 @@ instruct reductionF128(regF dst, vec src, vec vtmp) %{ %} instruct reduction8F(regF dst, vec src, vec vtmp1, vec vtmp2) %{ - predicate(Matcher::vector_length(n->in(2)) == 8); // src + predicate(n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 8); // src match(Set dst (AddReductionVF dst src)); match(Set dst (MulReductionVF dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -5137,7 +5137,7 @@ instruct reduction8F(regF dst, vec src, vec vtmp1, vec vtmp2) %{ %} instruct reduction16F(regF dst, legVec src, legVec vtmp1, legVec vtmp2) %{ - predicate(Matcher::vector_length(n->in(2)) == 16); // src + predicate(n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 16); // src match(Set dst (AddReductionVF dst src)); match(Set dst (MulReductionVF dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -5150,10 +5150,79 @@ instruct reduction16F(regF dst, legVec src, legVec vtmp1, legVec vtmp2) %{ ins_pipe( pipe_slow ); %} + +instruct unordered_reduction2F(regF dst, regF src1, vec src2) %{ + // Non-strictly ordered floating-point add/mul reduction for floats. This rule is + // intended for the VectorAPI (which allows for non-strictly ordered add/mul reduction). + // src1 contains reduction identity + predicate(!n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 2); // src2 + match(Set dst (AddReductionVF src1 src2)); + match(Set dst (MulReductionVF src1 src2)); + effect(TEMP dst); + format %{ "vector_reduction_float $dst,$src1,$src2 ;" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ unordered_reduce_fp(opcode, vlen, $dst$$XMMRegister, $src2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct unordered_reduction4F(regF dst, regF src1, vec src2, vec vtmp) %{ + // Non-strictly ordered floating-point add/mul reduction for floats. This rule is + // intended for the VectorAPI (which allows for non-strictly ordered add/mul reduction). + // src1 contains reduction identity + predicate(!n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 4); // src2 + match(Set dst (AddReductionVF src1 src2)); + match(Set dst (MulReductionVF src1 src2)); + effect(TEMP dst, TEMP vtmp); + format %{ "vector_reduction_float $dst,$src1,$src2 ; using $vtmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ unordered_reduce_fp(opcode, vlen, $dst$$XMMRegister, $src2$$XMMRegister, $vtmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct unordered_reduction8F(regF dst, regF src1, vec src2, vec vtmp1, vec vtmp2) %{ + // Non-strictly ordered floating-point add/mul reduction for floats. This rule is + // intended for the VectorAPI (which allows for non-strictly ordered add/mul reduction). + // src1 contains reduction identity + predicate(!n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 8); // src2 + match(Set dst (AddReductionVF src1 src2)); + match(Set dst (MulReductionVF src1 src2)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); + format %{ "vector_reduction_float $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ unordered_reduce_fp(opcode, vlen, $dst$$XMMRegister, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct unordered_reduction16F(regF dst, regF src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ + // Non-strictly ordered floating-point add/mul reduction for floats. This rule is + // intended for the VectorAPI (which allows for non-strictly ordered add/mul reduction). + // src1 contains reduction identity + predicate(!n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 16); // src2 + match(Set dst (AddReductionVF src1 src2)); + match(Set dst (MulReductionVF src1 src2)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); + format %{ "vector_reduction_float $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ unordered_reduce_fp(opcode, vlen, $dst$$XMMRegister, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + // =======================Double Reduction========================================== instruct reduction2D(regD dst, vec src, vec vtmp) %{ - predicate(Matcher::vector_length(n->in(2)) == 2); // src + predicate(n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 2); // src match(Set dst (AddReductionVD dst src)); match(Set dst (MulReductionVD dst src)); effect(TEMP dst, TEMP vtmp); @@ -5167,7 +5236,7 @@ instruct reduction2D(regD dst, vec src, vec vtmp) %{ %} instruct reduction4D(regD dst, vec src, vec vtmp1, vec vtmp2) %{ - predicate(Matcher::vector_length(n->in(2)) == 4); // src + predicate(n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 4); // src match(Set dst (AddReductionVD dst src)); match(Set dst (MulReductionVD dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -5181,7 +5250,7 @@ instruct reduction4D(regD dst, vec src, vec vtmp1, vec vtmp2) %{ %} instruct reduction8D(regD dst, legVec src, legVec vtmp1, legVec vtmp2) %{ - predicate(Matcher::vector_length(n->in(2)) == 8); // src + predicate(n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 8); // src match(Set dst (AddReductionVD dst src)); match(Set dst (MulReductionVD dst src)); effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); @@ -5194,6 +5263,57 @@ instruct reduction8D(regD dst, legVec src, legVec vtmp1, legVec vtmp2) %{ ins_pipe( pipe_slow ); %} +instruct unordered_reduction2D(regD dst, regD src1, vec src2) %{ + // Non-strictly ordered floating-point add/mul reduction for doubles. This rule is + // intended for the VectorAPI (which allows for non-strictly ordered add/mul reduction). + // src1 contains reduction identity + predicate(!n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 2); // src2 + match(Set dst (AddReductionVD src1 src2)); + match(Set dst (MulReductionVD src1 src2)); + effect(TEMP dst); + format %{ "vector_reduction_double $dst,$src1,$src2 ;" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ unordered_reduce_fp(opcode, vlen, $dst$$XMMRegister, $src2$$XMMRegister); +%} + ins_pipe( pipe_slow ); +%} + +instruct unordered_reduction4D(regD dst, regD src1, vec src2, vec vtmp) %{ + // Non-strictly ordered floating-point add/mul reduction for doubles. This rule is + // intended for the VectorAPI (which allows for non-strictly ordered add/mul reduction). + // src1 contains reduction identity + predicate(!n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 4); // src2 + match(Set dst (AddReductionVD src1 src2)); + match(Set dst (MulReductionVD src1 src2)); + effect(TEMP dst, TEMP vtmp); + format %{ "vector_reduction_double $dst,$src1,$src2 ; using $vtmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ unordered_reduce_fp(opcode, vlen, $dst$$XMMRegister, $src2$$XMMRegister, $vtmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct unordered_reduction8D(regD dst, regD src1, legVec src2, legVec vtmp1, legVec vtmp2) %{ + // Non-strictly ordered floating-point add/mul reduction for doubles. This rule is + // intended for the VectorAPI (which allows for non-strictly ordered add/mul reduction). + // src1 contains reduction identity + predicate(!n->as_Reduction()->requires_strict_order() && Matcher::vector_length(n->in(2)) == 8); // src2 + match(Set dst (AddReductionVD src1 src2)); + match(Set dst (MulReductionVD src1 src2)); + effect(TEMP dst, TEMP vtmp1, TEMP vtmp2); + format %{ "vector_reduction_double $dst,$src1,$src2 ; using $vtmp1, $vtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ unordered_reduce_fp(opcode, vlen, $dst$$XMMRegister, $src2$$XMMRegister, $vtmp1$$XMMRegister, $vtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + // =======================Byte Reduction========================================== #ifdef _LP64 diff --git a/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp b/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp index b85ead32fd22e..51fb114f59585 100644 --- a/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp +++ b/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ Unimplemented(); }) @@ -46,12 +53,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - Unimplemented(); - } - inline address location(VMReg reg, intptr_t* sp) const { Unimplemented(); return nullptr; diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 1c735f1b3c9d8..d4699567733b2 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -864,7 +864,7 @@ static void *thread_native_entry(Thread *thread) { log_info(os, thread)("Thread finished (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT ").", os::current_thread_id(), (uintx) pthread_self()); - return 0; + return nullptr; } // On Linux, glibc places static TLS blocks (for __thread variables) on @@ -4449,7 +4449,7 @@ void os::init(void) { check_pax(); // Check the availability of MADV_POPULATE_WRITE. - FLAG_SET_DEFAULT(UseMadvPopulateWrite, (::madvise(0, 0, MADV_POPULATE_WRITE) == 0)); + FLAG_SET_DEFAULT(UseMadvPopulateWrite, (::madvise(nullptr, 0, MADV_POPULATE_WRITE) == 0)); os::Posix::init(); } diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index 4339a21ae4e92..4eb46169878cd 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -499,11 +499,11 @@ static char* get_user_name_slow(int vmid, int nspid, TRAPS) { // short circuit the directory search if the process doesn't even exist. if (kill(vmid, 0) == OS_ERR) { if (errno == ESRCH) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), - "Process not found"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); } else /* EPERM */ { - THROW_MSG_0(vmSymbols::java_io_IOException(), os::strerror(errno)); + THROW_MSG_NULL(vmSymbols::java_io_IOException(), os::strerror(errno)); } } @@ -1329,7 +1329,7 @@ void PerfMemory::attach(int vmid, char** addrp, size_t* sizep, TRAPS) { // void PerfMemory::detach(char* addr, size_t bytes) { - assert(addr != 0, "address sanity check"); + assert(addr != nullptr, "address sanity check"); assert(bytes > 0, "capacity sanity check"); if (PerfMemory::contains(addr) || PerfMemory::contains(addr + bytes - 1)) { diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index a9d3bb9284c49..6a14d0a485643 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -1719,7 +1719,7 @@ static int SR_initialize() { struct sigaction act; char *s; // Get signal number to use for suspend/resume - if ((s = ::getenv("_JAVA_SR_SIGNUM")) != 0) { + if ((s = ::getenv("_JAVA_SR_SIGNUM")) != nullptr) { int sig; bool result = parse_integer(s, &sig); if (result && sig > MAX2(SIGSEGV, SIGBUS) && // See 4355769. @@ -1742,7 +1742,7 @@ static int SR_initialize() { pthread_sigmask(SIG_BLOCK, nullptr, &act.sa_mask); remove_error_signals_from_set(&(act.sa_mask)); - if (sigaction(PosixSignals::SR_signum, &act, 0) == -1) { + if (sigaction(PosixSignals::SR_signum, &act, nullptr) == -1) { return -1; } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 1cc7d9aa33adf..65ba13b0d9e81 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3463,6 +3463,7 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi } assert(aligned_base != nullptr, "Did not manage to re-map after %d attempts?", max_attempts); + assert(aligned_base != nullptr, "Did not manage to re-map after %d attempts (size %zu, alignment %zu, file descriptor %d)", max_attempts, size, alignment, file_desc); return aligned_base; } diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 78988dd4fd005..0d5d07fc8a804 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -224,7 +224,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, if (info != nullptr && uc != nullptr && thread != nullptr) { pc = (address) os::Posix::ucontext_get_pc(uc); - if (sig == SIGSEGV && info->si_addr == 0 && info->si_code == SI_KERNEL) { + if (sig == SIGSEGV && info->si_addr == nullptr && info->si_code == SI_KERNEL) { // An irrecoverable SI_KERNEL SIGSEGV has occurred. // It's likely caused by dereferencing an address larger than TASK_SIZE. return false; diff --git a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp index 7e0814c014bec..62022d780a20b 100644 --- a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp +++ b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -180,7 +180,7 @@ bool os::win32::register_code_area(char *low, char *high) { MacroAssembler* masm = new MacroAssembler(&cb); pDCD = (pDynamicCodeData) masm->pc(); - masm->jump(ExternalAddress((address)&HandleExceptionFromCodeCache), rscratch1); + masm->jump(RuntimeAddress((address)&HandleExceptionFromCodeCache), rscratch1); masm->flush(); // Create an Unwind Structure specifying no unwind info diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index ea47e435c0bb0..b25e07bc36d67 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -135,10 +135,11 @@ CodeBuffer::~CodeBuffer() { // Previous incarnations of this buffer are held live, so that internal // addresses constructed before expansions will not be confused. cb->free_blob(); + } + if (_overflow_arena != nullptr) { // free any overflow storage - delete cb->_overflow_arena; + delete _overflow_arena; } - if (_shared_trampoline_requests != nullptr) { delete _shared_trampoline_requests; } @@ -973,8 +974,6 @@ void CodeBuffer::take_over_code_from(CodeBuffer* cb) { CodeSection* this_sect = code_section(n); this_sect->take_over_code_from(cb_sect); } - _overflow_arena = cb->_overflow_arena; - cb->_overflow_arena = nullptr; // Make sure the old cb won't try to use it or free it. DEBUG_ONLY(cb->_blob = (BufferBlob*)badAddress); } diff --git a/src/hotspot/share/c1/c1_Instruction.hpp b/src/hotspot/share/c1/c1_Instruction.hpp index 32ff3d9f61cab..e577e7fd90bad 100644 --- a/src/hotspot/share/c1/c1_Instruction.hpp +++ b/src/hotspot/share/c1/c1_Instruction.hpp @@ -281,11 +281,11 @@ class Instruction: public CompilationResourceObj { #endif int _use_count; // the number of instructions referring to this value (w/o prev/next); only roots can have use count = 0 or > 1 int _pin_state; // set of PinReason describing the reason for pinning + unsigned int _flags; // Flag bits ValueType* _type; // the instruction value type Instruction* _next; // the next instruction if any (null for BlockEnd instructions) Instruction* _subst; // the substitution instruction if any LIR_Opr _operand; // LIR specific information - unsigned int _flags; // Flag bits ValueStack* _state_before; // Copy of state with input operands still on stack (or null) ValueStack* _exception_state; // Copy of state for exception handling @@ -403,11 +403,11 @@ class Instruction: public CompilationResourceObj { #endif _use_count(0) , _pin_state(0) + , _flags(0) , _type(type) , _next(nullptr) , _subst(nullptr) , _operand(LIR_OprFact::illegalOpr) - , _flags(0) , _state_before(state_before) , _exception_handlers(nullptr) , _block(nullptr) @@ -1518,9 +1518,9 @@ LEAF(MonitorExit, AccessMonitor) LEAF(Intrinsic, StateSplit) private: vmIntrinsics::ID _id; + ArgsNonNullState _nonnull_state; Values* _args; Value _recv; - ArgsNonNullState _nonnull_state; public: // preserves_state can be set to true for Intrinsics diff --git a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp index 3f53011024b01..f03ed38284c97 100644 --- a/src/hotspot/share/c1/c1_LIR.cpp +++ b/src/hotspot/share/c1/c1_LIR.cpp @@ -296,13 +296,13 @@ LIR_OpTypeCheck::LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, , _tmp1(tmp1) , _tmp2(tmp2) , _tmp3(tmp3) - , _fast_check(fast_check) , _info_for_patch(info_for_patch) , _info_for_exception(info_for_exception) , _stub(stub) , _profiled_method(nullptr) , _profiled_bci(-1) , _should_profile(false) + , _fast_check(fast_check) { if (code == lir_checkcast) { assert(info_for_exception != nullptr, "checkcast throws exceptions"); @@ -323,13 +323,13 @@ LIR_OpTypeCheck::LIR_OpTypeCheck(LIR_Code code, LIR_Opr object, LIR_Opr array, L , _tmp1(tmp1) , _tmp2(tmp2) , _tmp3(tmp3) - , _fast_check(false) , _info_for_patch(nullptr) , _info_for_exception(info_for_exception) , _stub(nullptr) , _profiled_method(nullptr) , _profiled_bci(-1) , _should_profile(false) + , _fast_check(false) { if (code == lir_store_check) { _stub = new ArrayStoreExceptionStub(object, info_for_exception); diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp index c69d29f8d619d..5d73ab5b88dba 100644 --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -528,44 +528,44 @@ class LIR_Address: public LIR_OprPtr { private: LIR_Opr _base; LIR_Opr _index; - Scale _scale; intx _disp; + Scale _scale; BasicType _type; public: LIR_Address(LIR_Opr base, LIR_Opr index, BasicType type): _base(base) , _index(index) - , _scale(times_1) , _disp(0) + , _scale(times_1) , _type(type) { verify(); } LIR_Address(LIR_Opr base, intx disp, BasicType type): _base(base) , _index(LIR_Opr::illegalOpr()) - , _scale(times_1) , _disp(disp) + , _scale(times_1) , _type(type) { verify(); } LIR_Address(LIR_Opr base, BasicType type): _base(base) , _index(LIR_Opr::illegalOpr()) - , _scale(times_1) , _disp(0) + , _scale(times_1) , _type(type) { verify(); } LIR_Address(LIR_Opr base, LIR_Opr index, intx disp, BasicType type): _base(base) , _index(index) - , _scale(times_1) , _disp(disp) + , _scale(times_1) , _type(type) { verify(); } LIR_Address(LIR_Opr base, LIR_Opr index, Scale scale, intx disp, BasicType type): _base(base) , _index(index) - , _scale(scale) , _disp(disp) + , _scale(scale) , _type(type) { verify(); } LIR_Opr base() const { return _base; } @@ -1544,13 +1544,13 @@ class LIR_OpTypeCheck: public LIR_Op { LIR_Opr _tmp1; LIR_Opr _tmp2; LIR_Opr _tmp3; - bool _fast_check; CodeEmitInfo* _info_for_patch; CodeEmitInfo* _info_for_exception; CodeStub* _stub; ciMethod* _profiled_method; int _profiled_bci; bool _should_profile; + bool _fast_check; public: LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass, @@ -1593,13 +1593,13 @@ class LIR_Op2: public LIR_Op { protected: LIR_Opr _opr1; LIR_Opr _opr2; - BasicType _type; LIR_Opr _tmp1; LIR_Opr _tmp2; LIR_Opr _tmp3; LIR_Opr _tmp4; LIR_Opr _tmp5; LIR_Condition _condition; + BasicType _type; void verify() const; @@ -1609,13 +1609,13 @@ class LIR_Op2: public LIR_Op { , _fpu_stack_size(0) , _opr1(opr1) , _opr2(opr2) - , _type(type) , _tmp1(LIR_OprFact::illegalOpr) , _tmp2(LIR_OprFact::illegalOpr) , _tmp3(LIR_OprFact::illegalOpr) , _tmp4(LIR_OprFact::illegalOpr) , _tmp5(LIR_OprFact::illegalOpr) - , _condition(condition) { + , _condition(condition) + , _type(type) { assert(code == lir_cmp || code == lir_branch || code == lir_cond_float_branch || code == lir_assert, "code check"); } @@ -1624,13 +1624,13 @@ class LIR_Op2: public LIR_Op { , _fpu_stack_size(0) , _opr1(opr1) , _opr2(opr2) - , _type(type) , _tmp1(LIR_OprFact::illegalOpr) , _tmp2(LIR_OprFact::illegalOpr) , _tmp3(LIR_OprFact::illegalOpr) , _tmp4(LIR_OprFact::illegalOpr) , _tmp5(LIR_OprFact::illegalOpr) - , _condition(condition) { + , _condition(condition) + , _type(type) { assert(code == lir_cmove, "code check"); assert(type != T_ILLEGAL, "cmove should have type"); } @@ -1641,13 +1641,13 @@ class LIR_Op2: public LIR_Op { , _fpu_stack_size(0) , _opr1(opr1) , _opr2(opr2) - , _type(type) , _tmp1(LIR_OprFact::illegalOpr) , _tmp2(LIR_OprFact::illegalOpr) , _tmp3(LIR_OprFact::illegalOpr) , _tmp4(LIR_OprFact::illegalOpr) , _tmp5(LIR_OprFact::illegalOpr) - , _condition(lir_cond_unknown) { + , _condition(lir_cond_unknown) + , _type(type) { assert(code != lir_cmp && code != lir_branch && code != lir_cond_float_branch && is_in_range(code, begin_op2, end_op2), "code check"); } @@ -1657,13 +1657,13 @@ class LIR_Op2: public LIR_Op { , _fpu_stack_size(0) , _opr1(opr1) , _opr2(opr2) - , _type(T_ILLEGAL) , _tmp1(tmp1) , _tmp2(tmp2) , _tmp3(tmp3) , _tmp4(tmp4) , _tmp5(tmp5) - , _condition(lir_cond_unknown) { + , _condition(lir_cond_unknown) + , _type(T_ILLEGAL) { assert(code != lir_cmp && code != lir_branch && code != lir_cond_float_branch && is_in_range(code, begin_op2, end_op2), "code check"); } @@ -1748,8 +1748,8 @@ class LIR_OpAllocArray : public LIR_Op { LIR_Opr _tmp2; LIR_Opr _tmp3; LIR_Opr _tmp4; - BasicType _type; CodeStub* _stub; + BasicType _type; bool _zero_array; public: @@ -1761,8 +1761,8 @@ class LIR_OpAllocArray : public LIR_Op { , _tmp2(t2) , _tmp3(t3) , _tmp4(t4) - , _type(type) , _stub(stub) + , _type(type) , _zero_array(zero_array) {} LIR_Opr klass() const { return _klass; } @@ -1811,13 +1811,13 @@ class LIR_Op4: public LIR_Op { LIR_Opr _opr2; LIR_Opr _opr3; LIR_Opr _opr4; - BasicType _type; LIR_Opr _tmp1; LIR_Opr _tmp2; LIR_Opr _tmp3; LIR_Opr _tmp4; LIR_Opr _tmp5; LIR_Condition _condition; + BasicType _type; public: LIR_Op4(LIR_Code code, LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr opr3, LIR_Opr opr4, @@ -1827,13 +1827,13 @@ class LIR_Op4: public LIR_Op { , _opr2(opr2) , _opr3(opr3) , _opr4(opr4) - , _type(type) , _tmp1(LIR_OprFact::illegalOpr) , _tmp2(LIR_OprFact::illegalOpr) , _tmp3(LIR_OprFact::illegalOpr) , _tmp4(LIR_OprFact::illegalOpr) , _tmp5(LIR_OprFact::illegalOpr) - , _condition(condition) { + , _condition(condition) + , _type(type) { assert(code == lir_cmove, "code check"); assert(type != T_ILLEGAL, "cmove should have type"); } diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index cd08413565808..7b519804bfecd 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -1300,7 +1300,7 @@ void LIRGenerator::do_isPrimitive(Intrinsic* x) { } __ move(new LIR_Address(rcvr.result(), java_lang_Class::klass_offset(), T_ADDRESS), temp, info); - __ cmp(lir_cond_notEqual, temp, LIR_OprFact::metadataConst(0)); + __ cmp(lir_cond_notEqual, temp, LIR_OprFact::metadataConst(nullptr)); __ cmove(lir_cond_notEqual, LIR_OprFact::intConst(0), LIR_OprFact::intConst(1), result, T_BOOLEAN); } @@ -1333,7 +1333,7 @@ void LIRGenerator::do_getModifiers(Intrinsic* x) { // Check if this is a Java mirror of primitive type, and select the appropriate klass. LIR_Opr klass = new_register(T_METADATA); - __ cmp(lir_cond_equal, recv_klass, LIR_OprFact::metadataConst(0)); + __ cmp(lir_cond_equal, recv_klass, LIR_OprFact::metadataConst(nullptr)); __ cmove(lir_cond_equal, prim_klass, recv_klass, klass, T_ADDRESS); // Get the answer. diff --git a/src/hotspot/share/ci/ciConstantPoolCache.cpp b/src/hotspot/share/ci/ciConstantPoolCache.cpp index 8dc9cfc75e14b..e831516b0f70b 100644 --- a/src/hotspot/share/ci/ciConstantPoolCache.cpp +++ b/src/hotspot/share/ci/ciConstantPoolCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ ciConstantPoolCache::ciConstantPoolCache(Arena* arena, int expected_size) { _elements = - new (arena) GrowableArray(arena, expected_size, 0, 0); + new (arena) GrowableArray(arena, expected_size, 0, nullptr); _keys = new (arena) GrowableArray(arena, expected_size, 0, 0); } diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index 3ed71806b078b..fd29e0cf85741 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -132,6 +132,7 @@ class CompileReplay : public StackObj { char* _bufptr; char* _buffer; int _buffer_length; + ReallocMark _nesting; // Safety checks for arena reallocation // "compile" data ciKlass* _iklass; @@ -601,6 +602,7 @@ class CompileReplay : public StackObj { int buffer_pos = 0; while(c != EOF) { if (buffer_pos + 1 >= _buffer_length) { + _nesting.check(); // Check if a reallocation in the resource arena is safe int new_length = _buffer_length * 2; // Next call will throw error in case of OOM. _buffer = REALLOC_RESOURCE_ARRAY(char, _buffer, _buffer_length, new_length); diff --git a/src/hotspot/share/ci/ciStreams.cpp b/src/hotspot/share/ci/ciStreams.cpp index 8220910d74c34..18d2a46a6862c 100644 --- a/src/hotspot/share/ci/ciStreams.cpp +++ b/src/hotspot/share/ci/ciStreams.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -135,7 +135,7 @@ Bytecodes::Code ciBytecodeStream::next_wide_or_table(Bytecodes::Code bc) { // ------------------------------------------------------------------ // ciBytecodeStream::reset_to_bci void ciBytecodeStream::reset_to_bci( int bci ) { - _bc_start=_was_wide=0; + _bc_start = _was_wide = nullptr; _pc = _start+bci; } diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 535b67887fca1..407078d64fc57 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -835,14 +835,12 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY, "Bad interface name in class file %s", CHECK); - // Call resolve_super so class circularity is checked - interf = SystemDictionary::resolve_super_or_fail( - _class_name, - unresolved_klass, - Handle(THREAD, _loader_data->class_loader()), - _protection_domain, - false, - CHECK); + // Call resolve on the interface class name with class circularity checking + interf = SystemDictionary::resolve_super_or_fail(_class_name, + unresolved_klass, + Handle(THREAD, _loader_data->class_loader()), + _protection_domain, + false, CHECK); } if (!interf->is_interface()) { @@ -1227,12 +1225,8 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, bool is_synthetic = false; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_annotations_exists = false; bool runtime_invisible_type_annotations_exists = false; const ConstantPool* const cp = _cp; @@ -1315,11 +1309,6 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, return; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null invisible annotations"); - } cfs->skip_u1(attribute_length, CHECK); } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { if (runtime_visible_type_annotations != nullptr) { @@ -1339,11 +1328,6 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, } else { runtime_invisible_type_annotations_exists = true; } - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null invisible type annotations"); - } cfs->skip_u1(attribute_length, CHECK); } else { cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes @@ -1356,16 +1340,12 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, *constantvalue_index_addr = constantvalue_index; *is_synthetic_addr = is_synthetic; *generic_signature_index_addr = generic_signature_index; - AnnotationArray* a = assemble_annotations(runtime_visible_annotations, + AnnotationArray* a = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK); parsed_annotations->set_field_annotations(a); - a = assemble_annotations(runtime_visible_type_annotations, + a = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK); parsed_annotations->set_field_type_annotations(a); return; @@ -2164,57 +2144,40 @@ void ClassFileParser::copy_localvariable_table(const ConstMethod* cm, void ClassFileParser::copy_method_annotations(ConstMethod* cm, const u1* runtime_visible_annotations, int runtime_visible_annotations_length, - const u1* runtime_invisible_annotations, - int runtime_invisible_annotations_length, const u1* runtime_visible_parameter_annotations, int runtime_visible_parameter_annotations_length, - const u1* runtime_invisible_parameter_annotations, - int runtime_invisible_parameter_annotations_length, const u1* runtime_visible_type_annotations, int runtime_visible_type_annotations_length, - const u1* runtime_invisible_type_annotations, - int runtime_invisible_type_annotations_length, const u1* annotation_default, int annotation_default_length, TRAPS) { AnnotationArray* a; - if (runtime_visible_annotations_length + - runtime_invisible_annotations_length > 0) { - a = assemble_annotations(runtime_visible_annotations, + if (runtime_visible_annotations_length > 0) { + a = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK); cm->set_method_annotations(a); } - if (runtime_visible_parameter_annotations_length + - runtime_invisible_parameter_annotations_length > 0) { - a = assemble_annotations(runtime_visible_parameter_annotations, + if (runtime_visible_parameter_annotations_length > 0) { + a = allocate_annotations(runtime_visible_parameter_annotations, runtime_visible_parameter_annotations_length, - runtime_invisible_parameter_annotations, - runtime_invisible_parameter_annotations_length, CHECK); cm->set_parameter_annotations(a); } if (annotation_default_length > 0) { - a = assemble_annotations(annotation_default, + a = allocate_annotations(annotation_default, annotation_default_length, - nullptr, - 0, CHECK); cm->set_default_annotations(a); } - if (runtime_visible_type_annotations_length + - runtime_invisible_type_annotations_length > 0) { - a = assemble_annotations(runtime_visible_type_annotations, + if (runtime_visible_type_annotations_length > 0) { + a = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK); cm->set_type_annotations(a); } @@ -2296,7 +2259,7 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, u2 max_stack = 0; u2 max_locals = 0; u4 code_length = 0; - const u1* code_start = 0; + const u1* code_start = nullptr; u2 exception_table_length = 0; const unsafe_u2* exception_table_start = nullptr; // (potentially unaligned) pointer to array of u2 elements Array* exception_handlers = Universe::the_empty_int_array(); @@ -2327,16 +2290,10 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, MethodAnnotationCollector parsed_annotations; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; const u1* runtime_visible_parameter_annotations = nullptr; int runtime_visible_parameter_annotations_length = 0; - const u1* runtime_invisible_parameter_annotations = nullptr; - int runtime_invisible_parameter_annotations_length = 0; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_annotations_exists = false; bool runtime_invisible_type_annotations_exists = false; bool runtime_invisible_parameter_annotations_exists = false; @@ -2607,11 +2564,6 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, return nullptr; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = method_attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null invisible annotations"); - } cfs->skip_u1(method_attribute_length, CHECK_NULL); } else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) { if (runtime_visible_parameter_annotations != nullptr) { @@ -2632,12 +2584,6 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, return nullptr; } runtime_invisible_parameter_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_parameter_annotations_length = method_attribute_length; - runtime_invisible_parameter_annotations = cfs->current(); - assert(runtime_invisible_parameter_annotations != nullptr, - "null invisible parameter annotations"); - } cfs->skip_u1(method_attribute_length, CHECK_NULL); } else if (method_attribute_name == vmSymbols::tag_annotation_default()) { if (annotation_default != nullptr) { @@ -2668,14 +2614,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, "Multiple RuntimeInvisibleTypeAnnotations attributes for method in class file %s", THREAD); return nullptr; - } else { - runtime_invisible_type_annotations_exists = true; - } - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = method_attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null invisible type annotations"); } + runtime_invisible_type_annotations_exists = true; cfs->skip_u1(method_attribute_length, CHECK_NULL); } else { // Skip unknown attributes @@ -2709,12 +2649,9 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, checked_exceptions_length, method_parameters_length, generic_signature_index, - runtime_visible_annotations_length + - runtime_invisible_annotations_length, - runtime_visible_parameter_annotations_length + - runtime_invisible_parameter_annotations_length, - runtime_visible_type_annotations_length + - runtime_invisible_type_annotations_length, + runtime_visible_annotations_length, + runtime_visible_parameter_annotations_length, + runtime_visible_type_annotations_length, annotation_default_length, 0); @@ -2806,16 +2743,10 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, copy_method_annotations(m->constMethod(), runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, runtime_visible_parameter_annotations, runtime_visible_parameter_annotations_length, - runtime_invisible_parameter_annotations, - runtime_invisible_parameter_annotations_length, runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, annotation_default, annotation_default_length, CHECK_NULL); @@ -3295,13 +3226,9 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons u2 generic_sig_index = 0; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; bool runtime_invisible_annotations_exists = false; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_type_annotations_exists = false; // Expected attributes for record components are Signature, Runtime(In)VisibleAnnotations, @@ -3352,11 +3279,6 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons return 0; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null record component invisible annotation"); - } cfs->skip_u1(attribute_length, CHECK_0); } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { @@ -3379,11 +3301,6 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons return 0; } runtime_invisible_type_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null record component invisible type annotation"); - } cfs->skip_u1(attribute_length, CHECK_0); } else { @@ -3393,15 +3310,11 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons calculate_attr_size += attribute_length; } // End of attributes For loop - AnnotationArray* annotations = assemble_annotations(runtime_visible_annotations, + AnnotationArray* annotations = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK_0); - AnnotationArray* type_annotations = assemble_annotations(runtime_visible_type_annotations, + AnnotationArray* type_annotations = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK_0); RecordComponent* record_component = @@ -3538,12 +3451,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf bool parsed_bootstrap_methods_attribute = false; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_type_annotations_exists = false; bool runtime_invisible_annotations_exists = false; bool parsed_source_debug_ext_annotations_exist = false; @@ -3656,11 +3565,6 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf return; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null invisible annotations"); - } cfs->skip_u1(attribute_length, CHECK); } else if (tag == vmSymbols::tag_enclosing_method()) { if (parsed_enclosingmethod_attribute) { @@ -3712,14 +3616,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf classfile_parse_error( "Multiple RuntimeInvisibleTypeAnnotations attributes in class file %s", THREAD); return; - } else { - runtime_invisible_type_annotations_exists = true; - } - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null invisible type annotations"); } + runtime_invisible_type_annotations_exists = true; cfs->skip_u1(attribute_length, CHECK); } else if (_major_version >= JAVA_11_VERSION) { if (tag == vmSymbols::tag_nest_members()) { @@ -3799,15 +3697,11 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf cfs->skip_u1(attribute_length, CHECK); } } - _class_annotations = assemble_annotations(runtime_visible_annotations, + _class_annotations = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK); - _class_type_annotations = assemble_annotations(runtime_visible_type_annotations, + _class_type_annotations = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK); if (parsed_innerclasses_attribute || parsed_enclosingmethod_attribute) { @@ -3943,28 +3837,16 @@ void ClassFileParser::apply_parsed_class_metadata( clear_class_metadata(); } -AnnotationArray* ClassFileParser::assemble_annotations(const u1* const runtime_visible_annotations, - int runtime_visible_annotations_length, - const u1* const runtime_invisible_annotations, - int runtime_invisible_annotations_length, +AnnotationArray* ClassFileParser::allocate_annotations(const u1* const anno, + int anno_length, TRAPS) { AnnotationArray* annotations = nullptr; - if (runtime_visible_annotations != nullptr || - runtime_invisible_annotations != nullptr) { + if (anno != nullptr) { annotations = MetadataFactory::new_array(_loader_data, - runtime_visible_annotations_length + - runtime_invisible_annotations_length, - CHECK_(annotations)); - if (runtime_visible_annotations != nullptr) { - for (int i = 0; i < runtime_visible_annotations_length; i++) { - annotations->at_put(i, runtime_visible_annotations[i]); - } - } - if (runtime_invisible_annotations != nullptr) { - for (int i = 0; i < runtime_invisible_annotations_length; i++) { - int append = runtime_visible_annotations_length+i; - annotations->at_put(append, runtime_invisible_annotations[i]); - } + anno_length, + CHECK_(annotations)); + for (int i = 0; i < anno_length; i++) { + annotations->at_put(i, anno[i]); } } return annotations; diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index f43303722a8ab..69d1f357ca55d 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -347,10 +347,8 @@ class ClassFileParser { TRAPS); // Annotations handling - AnnotationArray* assemble_annotations(const u1* const runtime_visible_annotations, - int runtime_visible_annotations_length, - const u1* const runtime_invisible_annotations, - int runtime_invisible_annotations_length, + AnnotationArray* allocate_annotations(const u1* const anno, + int anno_length, TRAPS); void set_precomputed_flags(InstanceKlass* k); @@ -514,16 +512,10 @@ class ClassFileParser { void copy_method_annotations(ConstMethod* cm, const u1* runtime_visible_annotations, int runtime_visible_annotations_length, - const u1* runtime_invisible_annotations, - int runtime_invisible_annotations_length, const u1* runtime_visible_parameter_annotations, int runtime_visible_parameter_annotations_length, - const u1* runtime_invisible_parameter_annotations, - int runtime_invisible_parameter_annotations_length, const u1* runtime_visible_type_annotations, int runtime_visible_type_annotations_length, - const u1* runtime_invisible_type_annotations, - int runtime_invisible_type_annotations_length, const u1* annotation_default, int annotation_default_length, TRAPS); diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp index de4490e73f326..fab012456d922 100644 --- a/src/hotspot/share/classfile/classLoaderData.cpp +++ b/src/hotspot/share/classfile/classLoaderData.cpp @@ -141,7 +141,7 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho // A non-strong hidden class loader data doesn't have anything to keep // it from being unloaded during parsing of the non-strong hidden class. // The null-class-loader should always be kept alive. - _keep_alive((has_class_mirror_holder || h_class_loader.is_null()) ? 1 : 0), + _keep_alive_ref_count((has_class_mirror_holder || h_class_loader.is_null()) ? 1 : 0), _claim(0), _handles(), _klasses(nullptr), _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr), _dictionary(nullptr), @@ -345,26 +345,26 @@ void ClassLoaderData::demote_strong_roots() { // while the class is being parsed, and if the class appears on the module fixup list. // Due to the uniqueness that no other class shares the hidden class' name or // ClassLoaderData, no other non-GC thread has knowledge of the hidden class while -// it is being defined, therefore _keep_alive is not volatile or atomic. -void ClassLoaderData::inc_keep_alive() { +// it is being defined, therefore _keep_alive_ref_count is not volatile or atomic. +void ClassLoaderData::inc_keep_alive_ref_count() { if (has_class_mirror_holder()) { - assert(_keep_alive > 0, "Invalid keep alive increment count"); - _keep_alive++; + assert(_keep_alive_ref_count > 0, "Invalid keep alive increment count"); + _keep_alive_ref_count++; } } -void ClassLoaderData::dec_keep_alive() { +void ClassLoaderData::dec_keep_alive_ref_count() { if (has_class_mirror_holder()) { - assert(_keep_alive > 0, "Invalid keep alive decrement count"); - if (_keep_alive == 1) { - // When the keep_alive counter is 1, the oop handle area is a strong root, + assert(_keep_alive_ref_count > 0, "Invalid keep alive decrement count"); + if (_keep_alive_ref_count == 1) { + // When the keep_alive_ref_count counter is 1, the oop handle area is a strong root, // acting as input to the GC tracing. Such strong roots are part of the // snapshot-at-the-beginning, and can not just be pulled out from the // system when concurrent GCs are running at the same time, without // invoking the right barriers. demote_strong_roots(); } - _keep_alive--; + _keep_alive_ref_count--; } } @@ -680,8 +680,8 @@ oop ClassLoaderData::holder_no_keepalive() const { // Unloading support bool ClassLoaderData::is_alive() const { - bool alive = keep_alive() // null class loader and incomplete non-strong hidden class. - || (_holder.peek() != nullptr); // and not cleaned by the GC weak handle processing. + bool alive = (_keep_alive_ref_count > 0) // null class loader and incomplete non-strong hidden class. + || (_holder.peek() != nullptr); // and not cleaned by the GC weak handle processing. return alive; } @@ -1009,7 +1009,7 @@ void ClassLoaderData::print_on(outputStream* out) const { out->print_cr(" - unloading %s", _unloading ? "true" : "false"); out->print_cr(" - class mirror holder %s", _has_class_mirror_holder ? "true" : "false"); out->print_cr(" - modified oops %s", _modified_oops ? "true" : "false"); - out->print_cr(" - keep alive %d", _keep_alive); + out->print_cr(" - _keep_alive_ref_count %d", _keep_alive_ref_count); out->print (" - claim "); switch(_claim) { case _claim_none: out->print_cr("none"); break; diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp index c9d025aded141..19dbb0b1b3650 100644 --- a/src/hotspot/share/classfile/classLoaderData.hpp +++ b/src/hotspot/share/classfile/classLoaderData.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -125,10 +125,10 @@ class ClassLoaderData : public CHeapObj { // Remembered sets support for the oops in the class loader data. bool _modified_oops; // Card Table Equivalent - int _keep_alive; // if this CLD is kept alive. - // Used for non-strong hidden classes and the - // boot class loader. _keep_alive does not need to be volatile or - // atomic since there is one unique CLD per non-strong hidden class. + int _keep_alive_ref_count; // if this CLD should not be considered eligible for unloading. + // Used for non-strong hidden classes and the + // boot class loader. _keep_alive_ref_count does not need to be volatile or + // atomic since there is one unique CLD per non-strong hidden class. volatile int _claim; // non-zero if claimed, for example during GC traces. // To avoid applying oop closure more than once. @@ -205,12 +205,14 @@ class ClassLoaderData : public CHeapObj { bool has_modified_oops() { return _modified_oops; } oop holder_no_keepalive() const; + // Resolving the holder keeps this CLD alive for the current GC cycle. oop holder() const; + void keep_alive() const { (void)holder(); } void classes_do(void f(Klass* const)); private: - bool keep_alive() const { return _keep_alive > 0; } + int keep_alive_ref_count() const { return _keep_alive_ref_count; } void loaded_classes_do(KlassClosure* klass_closure); void classes_do(void f(InstanceKlass*)); @@ -302,9 +304,10 @@ class ClassLoaderData : public CHeapObj { return _unloading; } - // Used to refcount a non-strong hidden class's s CLD in order to indicate their aliveness. - void inc_keep_alive(); - void dec_keep_alive(); + // Used to refcount a non-strong hidden class's CLD in order to force its aliveness during + // loading, when gc tracing may not find this CLD alive through the holder. + void inc_keep_alive_ref_count(); + void dec_keep_alive_ref_count(); void initialize_holder(Handle holder); @@ -335,8 +338,8 @@ class ClassLoaderData : public CHeapObj { bool modules_defined() { return (_modules != nullptr); } // Offsets - static ByteSize holder_offset() { return byte_offset_of(ClassLoaderData, _holder); } - static ByteSize keep_alive_offset() { return byte_offset_of(ClassLoaderData, _keep_alive); } + static ByteSize holder_offset() { return byte_offset_of(ClassLoaderData, _holder); } + static ByteSize keep_alive_ref_count_offset() { return byte_offset_of(ClassLoaderData, _keep_alive_ref_count); } // Loaded class dictionary Dictionary* dictionary() const { return _dictionary; } diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index adec6dbdeeeaa..c7051cd58e7d6 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -201,7 +201,7 @@ void ClassLoaderDataGraph::cld_do(CLDClosure* cl) { void ClassLoaderDataGraph::roots_cld_do(CLDClosure* strong, CLDClosure* weak) { assert_is_safepoint_or_gc(); for (ClassLoaderData* cld = Atomic::load_acquire(&_head); cld != nullptr; cld = cld->next()) { - CLDClosure* closure = cld->keep_alive() ? strong : weak; + CLDClosure* closure = (cld->keep_alive_ref_count() > 0) ? strong : weak; if (closure != nullptr) { closure->do_cld(cld); } @@ -309,8 +309,7 @@ void ClassLoaderDataGraph::modules_do_keepalive(void f(ModuleEntry*)) { assert_locked_or_safepoint(Module_lock); ClassLoaderDataGraphIterator iter; while (ClassLoaderData* cld = iter.get_next()) { - // Keep the holder alive. - (void)cld->holder(); + cld->keep_alive(); cld->modules_do(f); } } @@ -334,8 +333,7 @@ void ClassLoaderDataGraph::packages_do(void f(PackageEntry*)) { void ClassLoaderDataGraph::loaded_classes_do_keepalive(KlassClosure* klass_closure) { ClassLoaderDataGraphIterator iter; while (ClassLoaderData* cld = iter.get_next()) { - // Keep the holder alive. - (void)cld->holder(); + cld->keep_alive(); cld->loaded_classes_do(klass_closure); } } diff --git a/src/hotspot/share/classfile/defaultMethods.cpp b/src/hotspot/share/classfile/defaultMethods.cpp index dd221efb4098c..58d24c16ad3d8 100644 --- a/src/hotspot/share/classfile/defaultMethods.cpp +++ b/src/hotspot/share/classfile/defaultMethods.cpp @@ -870,7 +870,7 @@ static Method* new_method( Symbol* sig, AccessFlags flags, int max_stack, int params, ConstMethod::MethodType mt, TRAPS) { - address code_start = 0; + address code_start = nullptr; int code_length = 0; InlineTableSizes sizes; diff --git a/src/hotspot/share/classfile/javaAssertions.cpp b/src/hotspot/share/classfile/javaAssertions.cpp index 2666683046b14..cf51bfb21d676 100644 --- a/src/hotspot/share/classfile/javaAssertions.cpp +++ b/src/hotspot/share/classfile/javaAssertions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,12 +38,12 @@ bool JavaAssertions::_userDefault = false; bool JavaAssertions::_sysDefault = false; -JavaAssertions::OptionList* JavaAssertions::_classes = 0; -JavaAssertions::OptionList* JavaAssertions::_packages = 0; +JavaAssertions::OptionList* JavaAssertions::_classes = nullptr; +JavaAssertions::OptionList* JavaAssertions::_packages = nullptr; JavaAssertions::OptionList::OptionList(const char* name, bool enabled, OptionList* next) { - assert(name != 0, "need a name"); + assert(name != nullptr, "need a name"); _name = name; _enabled = enabled; _next = next; @@ -51,12 +51,12 @@ JavaAssertions::OptionList::OptionList(const char* name, bool enabled, int JavaAssertions::OptionList::count(OptionList* p) { int rc; - for (rc = 0; p != 0; p = p->next(), ++rc) /* empty */; + for (rc = 0; p != nullptr; p = p->next(), ++rc) /* empty */; return rc; } void JavaAssertions::addOption(const char* name, bool enable) { - assert(name != 0, "must have a name"); + assert(name != nullptr, "must have a name"); // Copy the name. The storage needs to exist for the lifetime of the vm; // it is never freed, so will be leaked (along with other option strings - @@ -135,7 +135,7 @@ void JavaAssertions::fillJavaArrays(const OptionList* p, int len, // matches the order on the command line (the list is in reverse order, since // it was created by prepending successive items from the command line). int index; - for (index = len - 1; p != 0; p = p->next(), --index) { + for (index = len - 1; p != nullptr; p = p->next(), --index) { assert(index >= 0, "length does not match list"); TempNewSymbol name = SymbolTable::new_symbol(p->name()); Handle s = java_lang_String::externalize_classname(name, CHECK); @@ -147,12 +147,12 @@ void JavaAssertions::fillJavaArrays(const OptionList* p, int len, inline JavaAssertions::OptionList* JavaAssertions::match_class(const char* classname) { - for (OptionList* p = _classes; p != 0; p = p->next()) { + for (OptionList* p = _classes; p != nullptr; p = p->next()) { if (strcmp(p->name(), classname) == 0) { return p; } } - return 0; + return nullptr; } JavaAssertions::OptionList* @@ -160,7 +160,7 @@ JavaAssertions::match_package(const char* classname) { // Search the package list for any items that apply to classname. Each // sub-package in classname is checked, from most-specific to least, until one // is found. - if (_packages == 0) return 0; + if (_packages == nullptr) return nullptr; // Find the length of the "most-specific" package in classname. If classname // does not include a package, length will be 0 which will match items for the @@ -170,7 +170,7 @@ JavaAssertions::match_package(const char* classname) { do { assert(len == 0 || classname[len] == JVM_SIGNATURE_SLASH, "not a package name"); - for (OptionList* p = _packages; p != 0; p = p->next()) { + for (OptionList* p = _packages; p != nullptr; p = p->next()) { if (strncmp(p->name(), classname, len) == 0 && p->name()[len] == '\0') { return p; } @@ -181,7 +181,7 @@ JavaAssertions::match_package(const char* classname) { while (len > 0 && classname[--len] != JVM_SIGNATURE_SLASH) /* empty */; } while (len > 0); - return 0; + return nullptr; } inline void JavaAssertions::trace(const char* name, @@ -193,7 +193,7 @@ const char* typefound, const char* namefound, bool enabled) { } bool JavaAssertions::enabled(const char* classname, bool systemClass) { - assert(classname != 0, "must have a classname"); + assert(classname != nullptr, "must have a classname"); // This will be slow if the number of assertion options on the command line is // large--it traverses two lists, one of them multiple times. Could use a diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index a0b5b73c6d449..ab9460e2b9b01 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -475,7 +475,7 @@ Handle java_lang_String::externalize_classname(Symbol* java_name, TRAPS) { jchar* java_lang_String::as_unicode_string(oop java_string, int& length, TRAPS) { jchar* result = as_unicode_string_or_null(java_string, length); if (result == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_OutOfMemoryError(), "could not allocate Unicode string"); + THROW_MSG_NULL(vmSymbols::java_lang_OutOfMemoryError(), "could not allocate Unicode string"); } return result; } @@ -963,7 +963,7 @@ void java_lang_Class::set_mirror_module_field(JavaThread* current, Klass* k, Han // Keep list of classes needing java.base module fixup if (!ModuleEntryTable::javabase_defined()) { assert(k->java_mirror() != nullptr, "Class's mirror is null"); - k->class_loader_data()->inc_keep_alive(); + k->class_loader_data()->inc_keep_alive_ref_count(); assert(fixup_module_field_list() != nullptr, "fixup_module_field_list not initialized"); fixup_module_field_list()->push(k); } else { diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 6c60d60d21192..af4332ca6912e 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -705,7 +705,7 @@ void ModuleEntryTable::patch_javabase_entries(JavaThread* current, Handle module { java_lang_Class::fixup_module_field(k, module_handle); } - k->class_loader_data()->dec_keep_alive(); + k->class_loader_data()->dec_keep_alive_ref_count(); } delete java_lang_Class::fixup_module_field_list(); diff --git a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp index a6a86473ea794..38d359efcc245 100644 --- a/src/hotspot/share/classfile/placeholders.cpp +++ b/src/hotspot/share/classfile/placeholders.cpp @@ -95,8 +95,8 @@ SeenThread* PlaceholderEntry::actionToQueue(PlaceholderTable::classloadAction ac case PlaceholderTable::LOAD_INSTANCE: queuehead = _loadInstanceThreadQ; break; - case PlaceholderTable::LOAD_SUPER: - queuehead = _superThreadQ; + case PlaceholderTable::DETECT_CIRCULARITY: + queuehead = _circularityThreadQ; break; case PlaceholderTable::DEFINE_CLASS: queuehead = _defineThreadQ; @@ -111,8 +111,8 @@ void PlaceholderEntry::set_threadQ(SeenThread* seenthread, PlaceholderTable::cla case PlaceholderTable::LOAD_INSTANCE: _loadInstanceThreadQ = seenthread; break; - case PlaceholderTable::LOAD_SUPER: - _superThreadQ = seenthread; + case PlaceholderTable::DETECT_CIRCULARITY: + _circularityThreadQ = seenthread; break; case PlaceholderTable::DEFINE_CLASS: _defineThreadQ = seenthread; @@ -188,10 +188,10 @@ bool PlaceholderEntry::remove_seen_thread(JavaThread* thread, PlaceholderTable:: } -void PlaceholderEntry::set_supername(Symbol* supername) { +void PlaceholderEntry::set_next_klass_name(Symbol* next_klass_name) { assert_locked_or_safepoint(SystemDictionary_lock); - assert(_supername == nullptr || _supername->refcount() > 1, "must be referenced also by the loader"); - _supername = supername; + assert(_next_klass_name == nullptr || _next_klass_name->refcount() > 1, "must be referenced also by the loader"); + _next_klass_name = next_klass_name; } // Placeholder objects represent classes currently being loaded. @@ -199,12 +199,12 @@ void PlaceholderEntry::set_supername(Symbol* supername) { // SystemDictionary_lock, so we don't need special precautions // on store ordering here. static PlaceholderEntry* add_entry(Symbol* class_name, ClassLoaderData* loader_data, - Symbol* supername){ + Symbol* next_klass_name){ assert_locked_or_safepoint(SystemDictionary_lock); assert(class_name != nullptr, "adding nullptr obj"); PlaceholderEntry entry; - entry.set_supername(supername); + entry.set_next_klass_name(next_klass_name); PlaceholderKey key(class_name, loader_data); bool created; PlaceholderEntry* table_copy = _placeholders->put_if_absent(key, entry, &created); @@ -230,7 +230,7 @@ PlaceholderEntry* PlaceholderTable::get_entry(Symbol* class_name, ClassLoaderDat static const char* action_to_string(PlaceholderTable::classloadAction action) { switch (action) { case PlaceholderTable::LOAD_INSTANCE: return "LOAD_INSTANCE"; - case PlaceholderTable::LOAD_SUPER: return "LOAD_SUPER"; + case PlaceholderTable::DETECT_CIRCULARITY: return "DETECT_CIRCULARITY"; case PlaceholderTable::DEFINE_CLASS: return "DEFINE_CLASS"; } return ""; @@ -250,20 +250,21 @@ inline void log(Symbol* name, PlaceholderEntry* entry, const char* function, Pla // If no entry exists, add a placeholder entry // If entry exists, reuse entry // For both, push SeenThread for classloadAction -// If LOAD_SUPER, this is used for circularity detection for instanceklass loading. +// If DETECT_CIRCULARITY, this is used for circularity detection for instanceklass loading. PlaceholderEntry* PlaceholderTable::find_and_add(Symbol* name, ClassLoaderData* loader_data, classloadAction action, - Symbol* supername, + Symbol* next_klass_name, JavaThread* thread) { - assert(action != LOAD_SUPER || supername != nullptr, "must have a super class name"); + assert(action != DETECT_CIRCULARITY || next_klass_name != nullptr, + "must have a class name for the next step in the class resolution recursion"); PlaceholderEntry* probe = get_entry(name, loader_data); if (probe == nullptr) { // Nothing found, add place holder - probe = add_entry(name, loader_data, supername); + probe = add_entry(name, loader_data, next_klass_name); } else { - if (action == LOAD_SUPER) { - probe->set_supername(supername); + if (action == DETECT_CIRCULARITY) { + probe->set_next_klass_name(next_klass_name); } } probe->add_seen_thread(thread, action); @@ -295,11 +296,11 @@ void PlaceholderTable::find_and_remove(Symbol* name, ClassLoaderData* loader_dat assert(probe != nullptr, "must find an entry"); log(name, probe, "find_and_remove", action); probe->remove_seen_thread(thread, action); - if (probe->superThreadQ() == nullptr) { - probe->set_supername(nullptr); + if (probe->circularityThreadQ() == nullptr) { + probe->set_next_klass_name(nullptr); } // If no other threads using this entry, and this thread is not using this entry for other states - if ((probe->superThreadQ() == nullptr) && (probe->loadInstanceThreadQ() == nullptr) + if ((probe->circularityThreadQ() == nullptr) && (probe->loadInstanceThreadQ() == nullptr) && (probe->defineThreadQ() == nullptr) && (probe->definer() == nullptr)) { remove_entry(name, loader_data); } @@ -312,9 +313,9 @@ void PlaceholderKey::print_on(outputStream* st) const { } void PlaceholderEntry::print_on(outputStream* st) const { - if (supername() != nullptr) { - st->print(", supername "); - supername()->print_value_on(st); + if (next_klass_name() != nullptr) { + st->print(", next_klass_name "); + next_klass_name()->print_value_on(st); } if (definer() != nullptr) { st->print(", definer "); @@ -328,8 +329,8 @@ void PlaceholderEntry::print_on(outputStream* st) const { st->print("loadInstanceThreadQ threads:"); SeenThread::print_action_queue(loadInstanceThreadQ(), st); st->cr(); - st->print("superThreadQ threads:"); - SeenThread::print_action_queue(superThreadQ(), st); + st->print("circularityThreadQ threads:"); + SeenThread::print_action_queue(circularityThreadQ(), st); st->cr(); st->print("defineThreadQ threads:"); SeenThread::print_action_queue(defineThreadQ(), st); diff --git a/src/hotspot/share/classfile/placeholders.hpp b/src/hotspot/share/classfile/placeholders.hpp index 7f83cf5d058b9..6348f76a14eea 100644 --- a/src/hotspot/share/classfile/placeholders.hpp +++ b/src/hotspot/share/classfile/placeholders.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,15 +41,15 @@ class PlaceholderTable : public AllStatic { // caller claims ownership of that action // For parallel classloading: // multiple LOAD_INSTANCE threads can proceed in parallel - // multiple LOAD_SUPER threads can proceed in parallel - // LOAD_SUPER needed to check for class circularity + // multiple DETECT_CIRCULARITY threads can proceed in parallel + // DETECT_CIRCULARITY needed to check for class circularity // DEFINE_CLASS: ultimately define class must be single threaded // on a class/classloader basis // so the head of that queue owns the token // and the rest of the threads return the result the first thread gets enum classloadAction { LOAD_INSTANCE = 1, // calling load_instance_class - LOAD_SUPER = 2, // loading superclass for this class + DETECT_CIRCULARITY = 2, // loading while detecting class circularity DEFINE_CLASS = 3 // find_or_define class }; static void initialize(); @@ -81,13 +81,13 @@ class SeenThread; class PlaceholderEntry { friend class PlaceholderTable; private: - SymbolHandle _supername; - JavaThread* _definer; // owner of define token - InstanceKlass* _instanceKlass; // InstanceKlass from successful define - SeenThread* _superThreadQ; // doubly-linked queue of Threads loading a superclass for this class - SeenThread* _loadInstanceThreadQ; // loadInstance thread - // This can't be multiple threads since class loading waits for - // this token to be removed. + SymbolHandle _next_klass_name; // next step in the recursive process of class loading + JavaThread* _definer; // owner of define token + InstanceKlass* _instanceKlass; // InstanceKlass from successful define + SeenThread* _circularityThreadQ; // doubly-linked queue of Threads loading with circularity detection + SeenThread* _loadInstanceThreadQ; // loadInstance thread + // This can't be multiple threads since class loading + // waits for this token to be removed. SeenThread* _defineThreadQ; // queue of Threads trying to define this class // including _definer @@ -99,8 +99,8 @@ class PlaceholderEntry { void add_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action); bool remove_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action); - SeenThread* superThreadQ() const { return _superThreadQ; } - void set_superThreadQ(SeenThread* SeenThread) { _superThreadQ = SeenThread; } + SeenThread* circularityThreadQ() const { return _circularityThreadQ; } + void set_circularityThreadQ(SeenThread* SeenThread) { _circularityThreadQ = SeenThread; } SeenThread* loadInstanceThreadQ() const { return _loadInstanceThreadQ; } void set_loadInstanceThreadQ(SeenThread* SeenThread) { _loadInstanceThreadQ = SeenThread; } @@ -110,10 +110,10 @@ class PlaceholderEntry { public: PlaceholderEntry() : _definer(nullptr), _instanceKlass(nullptr), - _superThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { } + _circularityThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { } - Symbol* supername() const { return _supername; } - void set_supername(Symbol* supername); + Symbol* next_klass_name() const { return _next_klass_name; } + void set_next_klass_name(Symbol* next_klass_name); JavaThread* definer() const {return _definer; } void set_definer(JavaThread* definer) { _definer = definer; } @@ -121,8 +121,8 @@ class PlaceholderEntry { InstanceKlass* instance_klass() const {return _instanceKlass; } void set_instance_klass(InstanceKlass* ik) { _instanceKlass = ik; } - bool super_load_in_progress() { - return (_superThreadQ != nullptr); + bool circularity_detection_in_progress() { + return (_circularityThreadQ != nullptr); } bool instance_load_in_progress() { diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index ddbf180a4a188..95094238946e3 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -193,7 +193,7 @@ class SymbolTableConfig : public AllStatic { // We cannot use arena because arena chunks are allocated by the OS. As a result, for example, // the archived symbol of "java/lang/Object" may sometimes be lower than "java/lang/String", and // sometimes be higher. This would cause non-deterministic contents in the archive. - DEBUG_ONLY(static void* last = 0); + DEBUG_ONLY(static void* last = nullptr); void* p = (void*)MetaspaceShared::symbol_space_alloc(alloc_size); assert(p > last, "must increase monotonically"); DEBUG_ONLY(last = p); diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 852416e30f5d2..7f9e6430cc651 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -259,7 +259,7 @@ Handle SystemDictionary::get_loader_lock_or_null(Handle class_loader) { Symbol* SystemDictionary::class_name_symbol(const char* name, Symbol* exception, TRAPS) { if (name == nullptr) { - THROW_MSG_0(exception, "No class name given"); + THROW_MSG_NULL(exception, "No class name given"); } if ((int)strlen(name) > Symbol::max_length()) { // It's impossible to create this class; the name cannot fit @@ -399,32 +399,32 @@ static inline void log_circularity_error(Symbol* name, PlaceholderEntry* probe) // superclass checks on its own thread to catch class circularity and // to avoid deadlock. // -// resolve_super_or_fail adds a LOAD_SUPER placeholder to the placeholder table before calling -// resolve_instance_class_or_null. ClassCircularityError is detected when a LOAD_SUPER or LOAD_INSTANCE +// resolve_with_circularity_detection adds a DETECT_CIRCULARITY placeholder to the placeholder table before calling +// resolve_instance_class_or_null. ClassCircularityError is detected when a DETECT_CIRCULARITY or LOAD_INSTANCE // placeholder for the same thread, class, classloader is found. // This can be seen with logging option: -Xlog:class+load+placeholders=debug. // -InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, - Symbol* super_name, - Handle class_loader, - Handle protection_domain, - bool is_superclass, - TRAPS) { - - assert(super_name != nullptr, "null superclass for resolving"); - assert(!Signature::is_array(super_name), "invalid superclass name"); +InstanceKlass* SystemDictionary::resolve_with_circularity_detection(Symbol* class_name, + Symbol* next_name, + Handle class_loader, + Handle protection_domain, + bool is_superclass, + TRAPS) { + + assert(next_name != nullptr, "null superclass for resolving"); + assert(!Signature::is_array(next_name), "invalid superclass name"); #if INCLUDE_CDS if (CDSConfig::is_dumping_static_archive()) { // Special processing for handling UNREGISTERED shared classes. InstanceKlass* k = SystemDictionaryShared::lookup_super_for_unregistered_class(class_name, - super_name, is_superclass); + next_name, is_superclass); if (k) { return k; } } #endif // INCLUDE_CDS - // If klass is already loaded, just return the superclass or superinterface. + // If class_name is already loaded, just return the superclass or superinterface. // Make sure there's a placeholder for the class_name before resolving. // This is used as a claim that this thread is currently loading superclass/classloader // and for ClassCircularity checks. @@ -439,28 +439,27 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, InstanceKlass* klassk = dictionary->find_class(THREAD, class_name); InstanceKlass* quicksuperk; // To support parallel loading: if class is done loading, just return the superclass - // if the super_name matches class->super()->name() and if the class loaders match. - // Otherwise, a LinkageError will be thrown later. + // if the next_name matches class->super()->name() and if the class loaders match. if (klassk != nullptr && is_superclass && - ((quicksuperk = klassk->java_super()) != nullptr) && - ((quicksuperk->name() == super_name) && - (quicksuperk->class_loader() == class_loader()))) { - return quicksuperk; + ((quicksuperk = klassk->java_super()) != nullptr) && + ((quicksuperk->name() == next_name) && + (quicksuperk->class_loader() == class_loader()))) { + return quicksuperk; } else { // Must check ClassCircularity before checking if superclass is already loaded. PlaceholderEntry* probe = PlaceholderTable::get_entry(class_name, loader_data); - if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER)) { + if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::DETECT_CIRCULARITY)) { log_circularity_error(class_name, probe); throw_circularity_error = true; } } if (!throw_circularity_error) { - // Be careful not to exit resolve_super without removing this placeholder. + // Be careful not to exit resolve_with_circularity_detection without removing this placeholder. PlaceholderEntry* newprobe = PlaceholderTable::find_and_add(class_name, loader_data, - PlaceholderTable::LOAD_SUPER, - super_name, THREAD); + PlaceholderTable::DETECT_CIRCULARITY, + next_name, THREAD); } } @@ -471,7 +470,7 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, // Resolve the superclass or superinterface, check results on return InstanceKlass* superk = - SystemDictionary::resolve_instance_class_or_null(super_name, + SystemDictionary::resolve_instance_class_or_null(next_name, class_loader, protection_domain, THREAD); @@ -479,13 +478,13 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, // Clean up placeholder entry. { MutexLocker mu(THREAD, SystemDictionary_lock); - PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::LOAD_SUPER, THREAD); + PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::DETECT_CIRCULARITY, THREAD); SystemDictionary_lock->notify_all(); } // Check for pending exception or null superk, and throw exception if (HAS_PENDING_EXCEPTION || superk == nullptr) { - handle_resolution_exception(super_name, true, CHECK_NULL); + handle_resolution_exception(next_name, true, CHECK_NULL); } return superk; @@ -502,13 +501,15 @@ static void handle_parallel_super_load(Symbol* name, Handle class_loader, Handle protection_domain, TRAPS) { - // superk is not used; resolve_super_or_fail is called for circularity check only. - Klass* superk = SystemDictionary::resolve_super_or_fail(name, - superclassname, - class_loader, - protection_domain, - true, - CHECK); + // The result superk is not used; resolve_with_circularity_detection is called for circularity check only. + // This passes true to is_superclass even though it might not be the super class in order to perform the + // optimization anyway. + Klass* superk = SystemDictionary::resolve_with_circularity_detection(name, + superclassname, + class_loader, + protection_domain, + true, + CHECK); } // Bootstrap and non-parallel capable class loaders use the LOAD_INSTANCE placeholder to @@ -536,7 +537,7 @@ static InstanceKlass* handle_parallel_loading(JavaThread* current, // Wait until the first thread has finished loading this class. Also wait until all the // threads trying to load its superclass have removed their placeholders. while (oldprobe != nullptr && - (oldprobe->instance_load_in_progress() || oldprobe->super_load_in_progress())) { + (oldprobe->instance_load_in_progress() || oldprobe->circularity_detection_in_progress())) { // LOAD_INSTANCE placeholders are used to implement parallel capable class loading // for the bootclass loader. @@ -575,8 +576,9 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle protection_domain, TRAPS) { // name must be in the form of "java/lang/Object" -- cannot be "Ljava/lang/Object;" + DEBUG_ONLY(ResourceMark rm(THREAD)); assert(name != nullptr && !Signature::is_array(name) && - !Signature::has_envelope(name), "invalid class name"); + !Signature::has_envelope(name), "invalid class name: %s", name == nullptr ? "nullptr" : name->as_C_string()); EventClassLoad class_load_start_event; @@ -607,7 +609,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle lockObject = get_loader_lock_or_null(class_loader); ObjectLocker ol(lockObject, THREAD); - bool super_load_in_progress = false; + bool circularity_detection_in_progress = false; InstanceKlass* loaded_class = nullptr; SymbolHandle superclassname; // Keep alive while loading in parallel thread. @@ -625,9 +627,9 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, loaded_class = check; } else { PlaceholderEntry* placeholder = PlaceholderTable::get_entry(name, loader_data); - if (placeholder != nullptr && placeholder->super_load_in_progress()) { - super_load_in_progress = true; - superclassname = placeholder->supername(); + if (placeholder != nullptr && placeholder->circularity_detection_in_progress()) { + circularity_detection_in_progress = true; + superclassname = placeholder->next_klass_name(); assert(superclassname != nullptr, "superclass has to have a name"); } } @@ -635,7 +637,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, // If the class is in the placeholder table with super_class set, // handle superclass loading in progress. - if (super_load_in_progress) { + if (circularity_detection_in_progress) { handle_parallel_super_load(name, superclassname, class_loader, protection_domain, @@ -1052,8 +1054,8 @@ bool SystemDictionary::check_shared_class_super_type(InstanceKlass* klass, Insta } } - Klass *found = resolve_super_or_fail(klass->name(), super_type->name(), - class_loader, protection_domain, is_superclass, CHECK_0); + Klass *found = resolve_with_circularity_detection(klass->name(), super_type->name(), + class_loader, protection_domain, is_superclass, CHECK_0); if (found == super_type) { return true; } else { diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index 6355de9c4ceb5..ee50aa38dd0cf 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,15 +102,23 @@ class SystemDictionary : AllStatic { return resolve_or_null(class_name, Handle(), Handle(), THREAD); } + static InstanceKlass* resolve_with_circularity_detection(Symbol* class_name, + Symbol* next_name, + Handle class_loader, + Handle protection_domain, + bool is_superclass, + TRAPS); + // Resolve a superclass or superinterface. Called from ClassFileParser, // parse_interfaces, resolve_instance_class_or_null, load_shared_class // "class_name" is the class whose super class or interface is being resolved. - static InstanceKlass* resolve_super_or_fail(Symbol* class_name, - Symbol* super_name, + static InstanceKlass* resolve_super_or_fail(Symbol* class_name, Symbol* super_name, Handle class_loader, - Handle protection_domain, - bool is_superclass, - TRAPS); + Handle protection_domain, bool is_superclass, TRAPS) { + return resolve_with_circularity_detection(class_name, super_name, class_loader, protection_domain, + is_superclass, THREAD); + } + private: // Parse the stream to create a hidden class. // Used by jvm_lookup_define_class. diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 36656515942c3..a491dd62f070e 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -165,8 +165,8 @@ class CodeBlob_sizes { // Iterate over all CodeBlobs (cb) on the given CodeHeap #define FOR_ALL_BLOBS(cb, heap) for (CodeBlob* cb = first_blob(heap); cb != nullptr; cb = next_blob(heap, cb)) -address CodeCache::_low_bound = 0; -address CodeCache::_high_bound = 0; +address CodeCache::_low_bound = nullptr; +address CodeCache::_high_bound = nullptr; volatile int CodeCache::_number_of_nmethods_with_dependencies = 0; ExceptionCache* volatile CodeCache::_exception_cache_purge_list = nullptr; @@ -1788,20 +1788,22 @@ void CodeCache::log_state(outputStream* st) { } #ifdef LINUX -void CodeCache::write_perf_map(const char* filename) { +void CodeCache::write_perf_map(const char* filename, outputStream* st) { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - - // Perf expects to find the map file at /tmp/perf-.map - // if the file name is not specified. - char fname[32]; + char fname[JVM_MAXPATHLEN]; if (filename == nullptr) { - jio_snprintf(fname, sizeof(fname), "/tmp/perf-%d.map", os::current_process_id()); + // Invocation outside of jcmd requires pid substitution. + if (!Arguments::copy_expand_pid(DEFAULT_PERFMAP_FILENAME, + strlen(DEFAULT_PERFMAP_FILENAME), + fname, JVM_MAXPATHLEN)) { + st->print_cr("Warning: Not writing perf map as pid substitution failed."); + return; + } filename = fname; } - fileStream fs(filename, "w"); if (!fs.is_open()) { - log_warning(codecache)("Failed to create %s for perf map", filename); + st->print_cr("Warning: Failed to create %s for perf map", filename); return; } diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 8fcbf32b64484..b724268b65000 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -80,6 +80,10 @@ class ShenandoahParallelCodeHeapIterator; class NativePostCallNop; class DeoptimizationScope; +#ifdef LINUX +#define DEFAULT_PERFMAP_FILENAME "/tmp/perf-%p.map" +#endif + class CodeCache : AllStatic { friend class VMStructs; friend class JVMCIVMStructs; @@ -223,7 +227,7 @@ class CodeCache : AllStatic { static void print_trace(const char* event, CodeBlob* cb, uint size = 0) PRODUCT_RETURN; static void print_summary(outputStream* st, bool detailed = true); // Prints a summary of the code cache usage static void log_state(outputStream* st); - LINUX_ONLY(static void write_perf_map(const char* filename = nullptr);) + LINUX_ONLY(static void write_perf_map(const char* filename, outputStream* st);) // Prints warnings and error messages to outputStream static const char* get_code_heap_name(CodeBlobType code_blob_type) { return (heap_available(code_blob_type) ? get_code_heap(code_blob_type)->name() : "Unused"); } static void report_codemem_full(CodeBlobType code_blob_type, bool print); diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index 7d3b744313f12..074846e2e7919 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -72,7 +72,7 @@ void Dependencies::initialize(ciEnv* env) { #endif DEBUG_ONLY(_deps[end_marker] = nullptr); for (int i = (int)FIRST_TYPE; i < (int)TYPE_LIMIT; i++) { - _deps[i] = new(arena) GrowableArray(arena, 10, 0, 0); + _deps[i] = new(arena) GrowableArray(arena, 10, 0, nullptr); } _content_bytes = nullptr; _size_in_bytes = (size_t)-1; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 54aeac6be0495..0bab731ac565e 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -3270,7 +3270,7 @@ void nmethod::print_recorded_oop(int log_n, int i) { if (value == Universe::non_oop_word()) { tty->print("non-oop word"); } else { - if (value == 0) { + if (value == nullptr) { tty->print("nullptr-oop"); } else { oop_at(i)->print_value_on(tty); diff --git a/src/hotspot/share/code/oopRecorder.cpp b/src/hotspot/share/code/oopRecorder.cpp index b8ecc1eccc0d1..1849493974ca4 100644 --- a/src/hotspot/share/code/oopRecorder.cpp +++ b/src/hotspot/share/code/oopRecorder.cpp @@ -32,6 +32,7 @@ #include "oops/oop.inline.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/mutexLocker.hpp" +#include "runtime/os.hpp" #include "utilities/copy.hpp" #ifdef ASSERT @@ -68,11 +69,11 @@ template void ValueRecorder::copy_values_to(nmethod* nm) { template void ValueRecorder::maybe_initialize() { if (_handles == nullptr) { if (_arena != nullptr) { - _handles = new(_arena) GrowableArray(_arena, 10, 0, 0); - _no_finds = new(_arena) GrowableArray( _arena, 10, 0, 0); + _handles = new(_arena) GrowableArray(_arena, 10, 0, T{}); + _no_finds = new(_arena) GrowableArray(_arena, 10, 0, 0); } else { - _handles = new GrowableArray(10, 0, 0); - _no_finds = new GrowableArray( 10, 0, 0); + _handles = new GrowableArray(10, 0, T{}); + _no_finds = new GrowableArray(10, 0, 0); } } } @@ -220,6 +221,11 @@ ExternalsRecorder* ExternalsRecorder::_recorder = nullptr; ExternalsRecorder::ExternalsRecorder(): _arena(mtCode), _externals(&_arena) {} +#ifndef PRODUCT +static int total_access_count = 0; +static GrowableArray* extern_hist = nullptr; +#endif + void ExternalsRecorder_init() { ExternalsRecorder::initialize(); } @@ -228,30 +234,106 @@ void ExternalsRecorder::initialize() { // After Mutex and before CodeCache are initialized assert(_recorder == nullptr, "should initialize only once"); _recorder = new ExternalsRecorder(); +#ifndef PRODUCT + if (PrintNMethodStatistics) { + Arena* arena = &_recorder->_arena; + extern_hist = new(arena) GrowableArray(arena, 512, 512, 0); + } +#endif } int ExternalsRecorder::find_index(address adr) { - MutexLocker ml(ExternalsRecorder_lock, Mutex::_no_safepoint_check_flag); assert(_recorder != nullptr, "sanity"); - return _recorder->_externals.find_index(adr); + MutexLocker ml(ExternalsRecorder_lock, Mutex::_no_safepoint_check_flag); + int index = _recorder->_externals.find_index(adr); +#ifndef PRODUCT + if (PrintNMethodStatistics) { + total_access_count++; + int n = extern_hist->at_grow(index, 0); + extern_hist->at_put(index, (n + 1)); + } +#endif + return index; } address ExternalsRecorder::at(int index) { + assert(_recorder != nullptr, "sanity"); // find_index() may resize array by reallocating it and freeing old, // we need loock here to make sure we not accessing to old freed array. MutexLocker ml(ExternalsRecorder_lock, Mutex::_no_safepoint_check_flag); - assert(_recorder != nullptr, "sanity"); return _recorder->_externals.at(index); } int ExternalsRecorder::count() { - MutexLocker ml(ExternalsRecorder_lock, Mutex::_no_safepoint_check_flag); assert(_recorder != nullptr, "sanity"); + MutexLocker ml(ExternalsRecorder_lock, Mutex::_no_safepoint_check_flag); return _recorder->_externals.count(); } #ifndef PRODUCT +extern "C" { + // Order from large to small values + static int count_cmp(const void *i, const void *j) { + int a = *(int*)i; + int b = *(int*)j; + return a < b ? 1 : a > b ? -1 : 0; + } +} + void ExternalsRecorder::print_statistics() { - tty->print_cr("External addresses table: %d entries", count()); + int cnt = count(); + tty->print_cr("External addresses table: %d entries, %d accesses", cnt, total_access_count); + { // Print most accessed entries in the table. + int* array = NEW_C_HEAP_ARRAY(int, (2 * cnt), mtCode); + for (int i = 0; i < cnt; i++) { + array[(2 * i) + 0] = extern_hist->at(i); + array[(2 * i) + 1] = i; + } + // Reverse sort to have "hottest" addresses first. + qsort(array, cnt, 2*sizeof(int), count_cmp); + // Print all entries with Verbose flag otherwise only top 5. + int limit = (Verbose || cnt <= 5) ? cnt : 5; + int j = 0; + for (int i = 0; i < limit; i++) { + int index = array[(2 * i) + 1]; + int n = extern_hist->at(index); + if (n > 0) { + address addr = at(index); + tty->print("%d: %8d " INTPTR_FORMAT " :", j++, n, p2i(addr)); + if (addr != nullptr) { + if (StubRoutines::contains(addr)) { + StubCodeDesc* desc = StubCodeDesc::desc_for(addr); + if (desc == nullptr) { + desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); + } + const char* stub_name = (desc != nullptr) ? desc->name() : ""; + tty->print(" stub: %s", stub_name); + } else { + ResourceMark rm; + const int buflen = 1024; + char* buf = NEW_RESOURCE_ARRAY(char, buflen); + int offset = 0; + if (os::dll_address_to_function_name(addr, buf, buflen, &offset)) { + tty->print(" extn: %s", buf); + if (offset != 0) { + tty->print("+%d", offset); + } + } else { + if (CodeCache::contains((void*)addr)) { + // Something in CodeCache + tty->print(" in CodeCache"); + } else { + // It could be string + memcpy(buf, (char*)addr, 80); + buf[80] = '\0'; + tty->print(" '%s'", buf); + } + } + } + } + tty->cr(); + } + } + } } #endif diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp index 07d8ec41598d9..4cc2043656efa 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -51,29 +51,36 @@ #include "utilities/quickSort.hpp" #include "utilities/resourceHash.hpp" -ArenaStatCounter::ArenaStatCounter() : - _current(0), _start(0), _peak(0), - _na(0), _ra(0), - _limit(0), _hit_limit(false), _limit_in_process(false), - _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0) -{} - -size_t ArenaStatCounter::peak_since_start() const { - return _peak > _start ? _peak - _start : 0; +ArenaStatCounter::ArenaStatCounter() { + reset(); +} + +void ArenaStatCounter::reset() { + _current = 0; + _peak = 0; + _current_by_tag.clear(); + _peak_by_tag.clear(); + _limit = 0; + _hit_limit = false; + _limit_in_process = false; + _live_nodes_at_peak = 0; + _active = false; } void ArenaStatCounter::start(size_t limit) { - _peak = _start = _current; + reset(); + _active = true; _limit = limit; - _hit_limit = false; } -void ArenaStatCounter::end(){ +void ArenaStatCounter::end() { _limit = 0; _hit_limit = false; + _active = false; } void ArenaStatCounter::update_c2_node_count() { + assert(_active, "compilaton has not yet started"); #ifdef COMPILER2 CompilerThread* const th = Thread::current()->as_Compiler_thread(); const CompileTask* const task = th->task(); @@ -90,33 +97,27 @@ void ArenaStatCounter::update_c2_node_count() { // Account an arena allocation or de-allocation. bool ArenaStatCounter::account(ssize_t delta, int tag) { + assert(_active, "compilaton has not yet started"); bool rc = false; #ifdef ASSERT // Note: if this fires, we free more arena memory under the scope of the // CompilationMemoryHistoryMark than we allocate. This cannot be since we // assume arena allocations in CompilerThread to be stack bound and symmetric. assert(delta >= 0 || ((ssize_t)_current + delta) >= 0, - "Negative overflow (d=%zd %zu %zu %zu)", delta, _current, _start, _peak); + "Negative overflow (d=%zd %zu %zu)", delta, _current, _peak); #endif // Update totals _current += delta; - // Update detail counter - switch ((Arena::Tag)tag) { - case Arena::Tag::tag_ra: _ra += delta; break; - case Arena::Tag::tag_node: _na += delta; break; - default: // ignore - break; - }; + _current_by_tag.add(tag, delta); // Did we reach a peak? if (_current > _peak) { _peak = _current; - assert(delta > 0, "Sanity (%zu %zu %zu)", _current, _start, _peak); - _na_at_peak = _na; - _ra_at_peak = _ra; + assert(delta > 0, "Sanity (%zu %zu)", _current, _peak); update_c2_node_count(); + _peak_by_tag = _current_by_tag; rc = true; // Did we hit the memory limit? - if (!_hit_limit && _limit > 0 && peak_since_start() > _limit) { + if (!_hit_limit && _limit > 0 && _peak > _limit) { _hit_limit = true; } } @@ -124,9 +125,15 @@ bool ArenaStatCounter::account(ssize_t delta, int tag) { } void ArenaStatCounter::print_on(outputStream* st) const { - st->print("%zu [na %zu ra %zu]", peak_since_start(), _na_at_peak, _ra_at_peak); + st->print("%zu [", _peak); + for (int tag = 0; tag < _peak_by_tag.element_count(); tag++) { + if (_peak_by_tag.counter(tag) > 0) { + st->print("%s %zu ", _peak_by_tag.tag_name(tag), _peak_by_tag.counter(tag)); + } + } + st->print("]"); #ifdef ASSERT - st->print(" (%zu->%zu->%zu)", _start, _peak, _current); + st->print(" (%zu->%zu)", _peak, _current); #endif } @@ -186,10 +193,8 @@ class MemStatEntry : public CHeapObj { // peak usage, bytes, over all arenas size_t _total; - // usage in node arena when total peaked - size_t _na_at_peak; - // usage in resource area when total peaked - size_t _ra_at_peak; + // usage per arena tag when total peaked + ArenaCountersByTag _peak_by_tag; // number of nodes (c2 only) when total peaked unsigned _live_nodes_at_peak; const char* _result; @@ -199,8 +204,9 @@ class MemStatEntry : public CHeapObj { MemStatEntry(FullMethodName method) : _method(method), _comptype(compiler_c1), _time(0), _num_recomp(0), _thread(nullptr), _limit(0), - _total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0), + _total(0), _live_nodes_at_peak(0), _result(nullptr) { + _peak_by_tag.clear(); } void set_comptype(CompilerType comptype) { _comptype = comptype; } @@ -210,8 +216,7 @@ class MemStatEntry : public CHeapObj { void inc_recompilation() { _num_recomp++; } void set_total(size_t n) { _total = n; } - void set_na_at_peak(size_t n) { _na_at_peak = n; } - void set_ra_at_peak(size_t n) { _ra_at_peak = n; } + void set_peak_by_tag(ArenaCountersByTag peak_by_tag) { _peak_by_tag = peak_by_tag; } void set_live_nodes_at_peak(unsigned n) { _live_nodes_at_peak = n; } void set_result(const char* s) { _result = s; } @@ -219,21 +224,34 @@ class MemStatEntry : public CHeapObj { size_t total() const { return _total; } static void print_legend(outputStream* st) { +#define LEGEND_KEY_FMT "%11s" st->print_cr("Legend:"); - st->print_cr(" total : memory allocated via arenas while compiling"); - st->print_cr(" NA : ...how much in node arenas (if c2)"); - st->print_cr(" RA : ...how much in resource areas"); - st->print_cr(" result : Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed"); - st->print_cr(" #nodes : ...how many nodes (c2 only)"); - st->print_cr(" limit : memory limit, if set"); - st->print_cr(" time : time of last compilation (sec)"); - st->print_cr(" type : compiler type"); - st->print_cr(" #rc : how often recompiled"); - st->print_cr(" thread : compiler thread"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "total", "memory allocated via arenas while compiling"); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + st->print_cr(" " LEGEND_KEY_FMT ": %s", Arena::tag_name[tag], Arena::tag_desc[tag]); + } + st->print_cr(" " LEGEND_KEY_FMT ": %s", "result", "Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "#nodes", "...how many nodes (c2 only)"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "limit", "memory limit, if set"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "time", "time taken for last compilation (sec)"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "type", "compiler type"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "#rc", "how often recompiled"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "thread", "compiler thread"); +#undef LEGEND_KEY_FMT } static void print_header(outputStream* st) { - st->print_cr("total NA RA result #nodes limit time type #rc thread method"); +#define SIZE_FMT "%-10s" + st->print(SIZE_FMT, "total"); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + st->print(SIZE_FMT, Arena::tag_name[tag]); + } +#define HDR_FMT1 "%-8s%-8s%-8s%-8s" +#define HDR_FMT2 "%-6s%-4s%-19s%s" + + st->print(HDR_FMT1, "result", "#nodes", "limit", "time"); + st->print(HDR_FMT2, "type", "#rc", "thread", "method"); + st->print_cr(""); } void print_on(outputStream* st, bool human_readable) const { @@ -247,21 +265,14 @@ class MemStatEntry : public CHeapObj { } col += 10; st->fill_to(col); - // NA - if (human_readable) { - st->print(PROPERFMT " ", PROPERFMTARGS(_na_at_peak)); - } else { - st->print("%zu ", _na_at_peak); - } - col += 10; st->fill_to(col); - - // RA - if (human_readable) { - st->print(PROPERFMT " ", PROPERFMTARGS(_ra_at_peak)); - } else { - st->print("%zu ", _ra_at_peak); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + if (human_readable) { + st->print(PROPERFMT " ", PROPERFMTARGS(_peak_by_tag.counter(tag))); + } else { + st->print("%zu ", _peak_by_tag.counter(tag)); + } + col += 10; st->fill_to(col); } - col += 10; st->fill_to(col); // result? st->print("%s ", _result ? _result : ""); @@ -296,7 +307,7 @@ class MemStatEntry : public CHeapObj { col += 4; st->fill_to(col); // Thread - st->print(PTR_FORMAT " ", p2i(_thread)); + st->print(PTR_FORMAT " ", p2i(_thread)); // MethodName char buf[1024]; @@ -341,7 +352,7 @@ class MemStatTable : public: void add(const FullMethodName& fmn, CompilerType comptype, - size_t total, size_t na_at_peak, size_t ra_at_peak, + size_t total, ArenaCountersByTag peak_by_tag, unsigned live_nodes_at_peak, size_t limit, const char* result) { assert_lock_strong(NMTCompilationCostHistory_lock); MemStatTableKey key(fmn, comptype); @@ -360,8 +371,7 @@ class MemStatTable : e->set_comptype(comptype); e->inc_recompilation(); e->set_total(total); - e->set_na_at_peak(na_at_peak); - e->set_ra_at_peak(ra_at_peak); + e->set_peak_by_tag(peak_by_tag); e->set_live_nodes_at_peak(live_nodes_at_peak); e->set_limit(limit); e->set_result(result); @@ -427,7 +437,7 @@ void CompilationMemoryStatistic::on_end_compilation() { const bool print = directive->should_print_memstat(); // Store memory used in task, for later processing by JFR - task->set_arena_bytes(arena_stat->peak_since_start()); + task->set_arena_bytes(arena_stat->peak()); // Store result // For this to work, we must call on_end_compilation() at a point where @@ -447,9 +457,8 @@ void CompilationMemoryStatistic::on_end_compilation() { assert(_the_table != nullptr, "not initialized"); _the_table->add(fmn, ct, - arena_stat->peak_since_start(), // total - arena_stat->na_at_peak(), - arena_stat->ra_at_peak(), + arena_stat->peak(), // total + arena_stat->peak_by_tag(), arena_stat->live_nodes_at_peak(), arena_stat->limit(), result); @@ -511,7 +520,7 @@ void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* aren bool hit_limit_before = arena_stat->hit_limit(); - if (arena_stat->account(diff, (int)arena->get_tag())) { // new peak? + if (arena_stat->is_active() && arena_stat->account(diff, (int)arena->get_tag())) { // new peak? // Limit handling if (arena_stat->hit_limit()) { @@ -545,7 +554,7 @@ void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* aren } ss.print("Hit MemLimit %s(limit: %zu now: %zu)", (hit_limit_before ? "again " : ""), - arena_stat->limit(), arena_stat->peak_since_start()); + arena_stat->limit(), arena_stat->peak()); } // log if needed diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp index d7cccad17fda8..d669cc1472295 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp @@ -29,48 +29,70 @@ #include "compiler/compilerDefinitions.hpp" #include "memory/allocation.hpp" #include "memory/allStatic.hpp" +#include "memory/arena.hpp" #include "utilities/globalDefinitions.hpp" class outputStream; class Symbol; class DirectiveSet; -// Counters for allocations from one arena +// Helper class to wrap the array of arena tags for easier processing +class ArenaCountersByTag { +private: + size_t _counter[Arena::tag_count()]; + +public: + int element_count() const { return Arena::tag_count(); } + const char* tag_name(int tag) const { return Arena::tag_name[tag]; } + + size_t counter(int tag) const { + assert(tag < element_count(), "invalid tag %d", tag); + return _counter[tag]; + } + + void add(int tag, size_t value) { + assert(tag < element_count(), "invalid tag %d", tag); + _counter[tag] += value; + } + + void clear() { + memset(_counter, 0, sizeof(size_t) * element_count()); + } +}; + +// Counters for allocations from arenas during compilation class ArenaStatCounter : public CHeapObj { // Current bytes, total size_t _current; - // bytes when compilation started - size_t _start; // bytes at last peak, total size_t _peak; - // Current bytes used for node arenas, total - size_t _na; - // Current bytes used for resource areas - size_t _ra; + // Current bytes used by arenas per tag + ArenaCountersByTag _current_by_tag; + // Peak composition: + ArenaCountersByTag _peak_by_tag; // MemLimit handling size_t _limit; bool _hit_limit; bool _limit_in_process; - // Peak composition: - // Size of node arena when total peaked (c2 only) - size_t _na_at_peak; - // Size of resource area when total peaked - size_t _ra_at_peak; + // When to start accounting + bool _active; + // Number of live nodes when total peaked (c2 only) unsigned _live_nodes_at_peak; void update_c2_node_count(); + void reset(); + public: ArenaStatCounter(); // Size of peak since last compilation - size_t peak_since_start() const; + size_t peak() const { return _peak; } // Peak details - size_t na_at_peak() const { return _na_at_peak; } - size_t ra_at_peak() const { return _ra_at_peak; } + ArenaCountersByTag peak_by_tag() const { return _peak_by_tag; } unsigned live_nodes_at_peak() const { return _live_nodes_at_peak; } // Mark the start and end of a compilation. @@ -89,7 +111,7 @@ class ArenaStatCounter : public CHeapObj { bool hit_limit() const { return _hit_limit; } bool limit_in_process() const { return _limit_in_process; } void set_limit_in_process(bool v) { _limit_in_process = v; } - + bool is_active() const { return _active; } }; class CompilationMemoryStatistic : public AllStatic { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index afd7b8948598e..aa99fbecbee54 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2336,16 +2336,6 @@ void G1CollectedHeap::start_new_collection_set() { _cm->verify_no_collection_set_oops(); } -G1HeapVerifier::G1VerifyType G1CollectedHeap::young_collection_verify_type() const { - if (collector_state()->in_concurrent_start_gc()) { - return G1HeapVerifier::G1VerifyConcurrentStart; - } else if (collector_state()->in_young_only_phase()) { - return G1HeapVerifier::G1VerifyYoungNormal; - } else { - return G1HeapVerifier::G1VerifyMixed; - } -} - void G1CollectedHeap::verify_before_young_collection(G1HeapVerifier::G1VerifyType type) { if (!VerifyBeforeGC) { return; @@ -2941,20 +2931,6 @@ void G1CollectedHeap::retire_gc_alloc_region(G1HeapRegion* alloc_region, G1HeapRegionPrinter::retire(alloc_region); } -G1HeapRegion* G1CollectedHeap::alloc_highest_free_region() { - bool expanded = false; - uint index = _hrm.find_highest_free(&expanded); - - if (index != G1_NO_HRM_INDEX) { - if (expanded) { - log_debug(gc, ergo, heap)("Attempt heap expansion (requested address range outside heap bounds). region size: " SIZE_FORMAT "B", - G1HeapRegion::GrainWords * HeapWordSize); - } - return _hrm.allocate_free_regions_starting_at(index, 1); - } - return nullptr; -} - void G1CollectedHeap::mark_evac_failure_object(uint worker_id, const oop obj, size_t obj_size) const { assert(!_cm->is_marked_in_bitmap(obj), "must be"); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index ec3cf8eafe3a0..ea087aae6621f 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -669,10 +669,6 @@ class G1CollectedHeap : public CollectedHeap { // Allocates a new heap region instance. G1HeapRegion* new_heap_region(uint hrs_index, MemRegion mr); - // Allocate the highest free region in the reserved heap. This will commit - // regions as necessary. - G1HeapRegion* alloc_highest_free_region(); - // Frees a region by resetting its metadata and adding it to the free list // passed as a parameter (this is usually a local list which will be appended // to the master free list later or null if free list management is handled @@ -756,7 +752,6 @@ class G1CollectedHeap : public CollectedHeap { // of the incremental collection pause, executed by the vm thread. void do_collection_pause_at_safepoint_helper(); - G1HeapVerifier::G1VerifyType young_collection_verify_type() const; void verify_before_young_collection(G1HeapVerifier::G1VerifyType type); void verify_after_young_collection(G1HeapVerifier::G1VerifyType type); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp index 1b6b1eed7b13b..f62d42069004f 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp @@ -87,14 +87,19 @@ class G1RebuildRSAndScrubTask : public WorkerTask { _processed_words += processed; } - // Yield if enough has been processed; returns if the concurrent marking cycle - // has been aborted for any reason. - bool yield_if_necessary() { + // Yield if enough has been processed. Return whether we should stop + // processing this region because either the concurrent marking cycle has been + // aborted or the region has been reclaimed. + bool yield_if_necessary(G1HeapRegion* hr) { if (_processed_words >= ProcessingYieldLimitInWords) { reset_processed_words(); - _cm->do_yield_check(); + // If a yield occurs (potential young-gc pause), must recheck for + // potential regions reclamation. + if (_cm->do_yield_check() && !should_rebuild_or_scrub(hr)) { + return true; + } } - return _cm->has_aborted(); + return _cm->has_aborted() || !should_rebuild_or_scrub(hr); } // Returns whether the top at rebuild start value for the given region indicates @@ -111,8 +116,7 @@ class G1RebuildRSAndScrubTask : public WorkerTask { // Helper used by both humongous objects and when chunking an object larger than the // G1RebuildRemSetChunkSize. The heap region is needed check whether the region has // been reclaimed during yielding. - // Returns true if marking has been aborted or false if completed. - bool scan_large_object(G1HeapRegion* hr, const oop obj, MemRegion scan_range) { + void scan_large_object(G1HeapRegion* hr, const oop obj, MemRegion scan_range) { HeapWord* start = scan_range.start(); HeapWord* limit = scan_range.end(); do { @@ -122,20 +126,14 @@ class G1RebuildRSAndScrubTask : public WorkerTask { // Update processed words and yield, for humongous objects we will yield // after each chunk. add_processed_words(mr.word_size()); - bool mark_aborted = yield_if_necessary(); - if (mark_aborted) { - return true; - } else if (!should_rebuild_or_scrub(hr)) { - // We need to check should_rebuild_or_scrub() again because the region might - // have been reclaimed during above yield/safepoint. - log_trace(gc, marking)("Rebuild aborted for reclaimed region: %u", hr->hrm_index()); - return false; + + if (yield_if_necessary(hr)) { + return; } // Step to next chunk of the large object. start = mr.end(); } while (start < limit); - return false; } // Scan for references into regions that need remembered set update for the given @@ -165,102 +163,66 @@ class G1RebuildRSAndScrubTask : public WorkerTask { return obj_size; } - // Scrub a range of dead objects starting at scrub_start. Will never scrub past limit. - HeapWord* scrub_to_next_live(G1HeapRegion* hr, HeapWord* scrub_start, HeapWord* limit) { - assert(!_bitmap->is_marked(scrub_start), "Should not scrub live object"); - - HeapWord* scrub_end = _bitmap->get_next_marked_addr(scrub_start, limit); - hr->fill_range_with_dead_objects(scrub_start, scrub_end); - - // Return the next object to handle. - return scrub_end; - } - - // Scan the given region from bottom to parsable_bottom. Returns whether marking has - // been aborted. - bool scan_and_scrub_to_pb(G1HeapRegion* hr, HeapWord* start, HeapWord* const limit) { - - while (start < limit) { - if (_bitmap->is_marked(start)) { - // Live object, need to scan to rebuild remembered sets for this object. - start += scan_object(hr, start); - } else { - // Found dead object (which klass has potentially been unloaded). Scrub to next - // marked object and continue. - start = scrub_to_next_live(hr, start, limit); - } - - bool mark_aborted = yield_if_necessary(); - if (mark_aborted) { - return true; - } else if (!should_rebuild_or_scrub(hr)) { - // We need to check should_rebuild_or_scrub() again because the region might - // have been reclaimed during above yield/safepoint. - log_trace(gc, marking)("Scan and scrub aborted for reclaimed region: %u", hr->hrm_index()); - return false; - } - } - return false; - } - - // Scan the given region from parsable_bottom to tars. Returns whether marking has - // been aborted. - bool scan_from_pb_to_tars(G1HeapRegion* hr, HeapWord* start, HeapWord* const limit) { - - while (start < limit) { - start += scan_object(hr, start); - // Avoid stalling safepoints and stop iteration if mark cycle has been aborted. - bool mark_aborted = yield_if_necessary(); - if (mark_aborted) { - return true; - } else if (!should_rebuild_or_scrub(hr)) { - // We need to check should_rebuild_or_scrub() again because the region might - // have been reclaimed during above yield/safepoint. - log_trace(gc, marking)("Scan aborted for reclaimed region: %u", hr->hrm_index()); - return false; - } + // Scan or scrub depending on if addr is marked. + HeapWord* scan_or_scrub(G1HeapRegion* hr, HeapWord* addr, HeapWord* limit) { + if (_bitmap->is_marked(addr)) { + // Live object, need to scan to rebuild remembered sets for this object. + return addr + scan_object(hr, addr); + } else { + // Found dead object (which klass has potentially been unloaded). Scrub to next marked object. + HeapWord* scrub_end = _bitmap->get_next_marked_addr(addr, limit); + hr->fill_range_with_dead_objects(addr, scrub_end); + // Return the next object to handle. + return scrub_end; } - return false; } - // Scan and scrub the given region to tars. Returns whether marking has - // been aborted. - bool scan_and_scrub_region(G1HeapRegion* hr, HeapWord* const pb) { + // Scan and scrub the given region to tars. + void scan_and_scrub_region(G1HeapRegion* hr, HeapWord* const pb) { assert(should_rebuild_or_scrub(hr), "must be"); log_trace(gc, marking)("Scrub and rebuild region: " HR_FORMAT " pb: " PTR_FORMAT " TARS: " PTR_FORMAT " TAMS: " PTR_FORMAT, HR_FORMAT_PARAMS(hr), p2i(pb), p2i(_cm->top_at_rebuild_start(hr)), p2i(_cm->top_at_mark_start(hr))); - if (scan_and_scrub_to_pb(hr, hr->bottom(), pb)) { - log_trace(gc, marking)("Scan and scrub aborted for region: %u", hr->hrm_index()); - return true; - } + { + // Step 1: Scan the given region from bottom to parsable_bottom. + HeapWord* start = hr->bottom(); + HeapWord* limit = pb; + while (start < limit) { + start = scan_or_scrub(hr, start, limit); - // Yielding during scrubbing and scanning might have reclaimed the region, so need to - // re-check after above. - if (!should_rebuild_or_scrub(hr)) { - return false; + if (yield_if_necessary(hr)) { + return; + } + } } + // Scrubbing completed for this region - notify that we are done with it, resetting // pb to bottom. hr->note_end_of_scrubbing(); - // Rebuild from TAMS (= parsable_bottom) to TARS. - if (scan_from_pb_to_tars(hr, pb, _cm->top_at_rebuild_start(hr))) { - log_trace(gc, marking)("Rebuild aborted for region: %u (%s)", hr->hrm_index(), hr->get_short_type_str()); - return true; + { + // Step 2: Rebuild from TAMS (= parsable_bottom) to TARS. + HeapWord* start = pb; + HeapWord* limit = _cm->top_at_rebuild_start(hr); + while (start < limit) { + start += scan_object(hr, start); + + if (yield_if_necessary(hr)) { + return; + } + } } - return false; } // Scan a humongous region for remembered set updates. Scans in chunks to avoid - // stalling safepoints. Returns whether the concurrent marking phase has been aborted. - bool scan_humongous_region(G1HeapRegion* hr, HeapWord* const pb) { + // stalling safepoints. + void scan_humongous_region(G1HeapRegion* hr, HeapWord* const pb) { assert(should_rebuild_or_scrub(hr), "must be"); if (!_should_rebuild_remset) { // When not rebuilding there is nothing to do for humongous objects. - return false; + return; } // At this point we should only have live humongous objects, that @@ -278,12 +240,7 @@ class G1RebuildRSAndScrubTask : public WorkerTask { HeapWord* humongous_end = hr->humongous_start_region()->bottom() + humongous->size(); MemRegion mr(hr->bottom(), MIN2(hr->top(), humongous_end)); - bool mark_aborted = scan_large_object(hr, humongous, mr); - if (mark_aborted) { - log_trace(gc, marking)("Rebuild aborted for region: %u (%s)", hr->hrm_index(), hr->get_short_type_str()); - return true; - } - return false; + scan_large_object(hr, humongous, mr); } public: @@ -312,17 +269,16 @@ class G1RebuildRSAndScrubTask : public WorkerTask { return false; } - bool mark_aborted; if (hr->needs_scrubbing()) { // This is a region with potentially unparsable (dead) objects. - mark_aborted = scan_and_scrub_region(hr, pb); + scan_and_scrub_region(hr, pb); } else { assert(hr->is_humongous(), "must be, but %u is %s", hr->hrm_index(), hr->get_short_type_str()); // No need to scrub humongous, but we should scan it to rebuild remsets. - mark_aborted = scan_humongous_region(hr, pb); + scan_humongous_region(hr, pb); } - return mark_aborted; + return _cm->has_aborted(); } }; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index b37e65f8b868a..2369a0f7812ea 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -529,28 +529,6 @@ void G1HeapRegionManager::iterate(G1HeapRegionIndexClosure* blk) const { } } -uint G1HeapRegionManager::find_highest_free(bool* expanded) { - // Loop downwards from the highest region index, looking for an - // entry which is either free or not yet committed. If not yet - // committed, expand at that index. - for (uint curr = reserved_length(); curr-- > 0;) { - G1HeapRegion* hr = _regions.get_by_index(curr); - if (hr == nullptr || !is_available(curr)) { - // Found uncommitted and free region, expand to make it available for use. - expand_exact(curr, 1, nullptr); - assert(at(curr)->is_free(), "Region (%u) must be available and free after expand", curr); - - *expanded = true; - return curr; - } - if (hr->is_free()) { - *expanded = false; - return curr; - } - } - return G1_NO_HRM_INDEX; -} - bool G1HeapRegionManager::allocate_containing_regions(MemRegion range, size_t* commit_count, WorkerThreads* pretouch_workers) { size_t commits = 0; uint start_index = (uint)_regions.get_index_by_address(range.start()); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp index d3f1843f07dbf..81bca4ce6381c 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp @@ -256,11 +256,6 @@ class G1HeapRegionManager: public CHeapObj { G1HeapRegion* next_region_in_heap(const G1HeapRegion* r) const; - // Find the highest free or uncommitted region in the reserved heap, - // and if uncommitted, commit it. If none are available, return G1_NO_HRM_INDEX. - // Set the 'expanded' boolean true if a new region was committed. - uint find_highest_free(bool* expanded); - // Allocate the regions that contain the address range specified, committing the // regions if necessary. Return false if any of the regions is already committed // and not free, and return the number of regions newly committed in commit_count. diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index af1ac1f70c64b..c8016ddc0ddf5 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -112,7 +112,7 @@ \ product(uint, G1ConfidencePercent, 50, \ "Confidence level for MMU/pause predictions") \ - range(0, 100) \ + range(1, 100) \ \ product(uintx, G1SummarizeRSetStatsPeriod, 0, DIAGNOSTIC, \ "The period (in number of GCs) at which we will generate " \ diff --git a/src/hotspot/share/gc/parallel/gcAdaptivePolicyCounters.hpp b/src/hotspot/share/gc/parallel/gcAdaptivePolicyCounters.hpp index e83813c78f48f..e21d568955a38 100644 --- a/src/hotspot/share/gc/parallel/gcAdaptivePolicyCounters.hpp +++ b/src/hotspot/share/gc/parallel/gcAdaptivePolicyCounters.hpp @@ -223,10 +223,6 @@ class GCAdaptivePolicyCounters : public GCPolicyCounters { } void set_size_policy(AdaptiveSizePolicy* v) { _size_policy = v; } - - virtual GCPolicyCounters::Name kind() const { - return GCPolicyCounters::GCAdaptivePolicyCountersKind; - } }; #endif // SHARE_GC_PARALLEL_GCADAPTIVEPOLICYCOUNTERS_HPP diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index cfdb7e9eb29dd..288a21fd35d05 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -205,8 +205,6 @@ void PSAdaptiveSizePolicy::compute_eden_space_size( bool is_full_gc) { // Update statistics - // Time statistics are updated as we go, update footprint stats here - _avg_base_footprint->sample(BaseFootPrintEstimate); avg_young_live()->sample(young_live); avg_eden_live()->sample(eden_live); @@ -363,8 +361,7 @@ void PSAdaptiveSizePolicy::compute_eden_space_size( log_debug(gc, ergo)("Live_space: " SIZE_FORMAT " free_space: " SIZE_FORMAT, live_space(), free_space()); - log_trace(gc, ergo)("Base_footprint: " SIZE_FORMAT " avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, - (size_t)_avg_base_footprint->average(), + log_trace(gc, ergo)("avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, (size_t)avg_young_live()->average(), (size_t)avg_old_live()->average()); @@ -535,8 +532,7 @@ void PSAdaptiveSizePolicy::compute_old_gen_free_space( log_debug(gc, ergo)("Live_space: " SIZE_FORMAT " free_space: " SIZE_FORMAT, live_space(), free_space()); - log_trace(gc, ergo)("Base_footprint: " SIZE_FORMAT " avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, - (size_t)_avg_base_footprint->average(), + log_trace(gc, ergo)("avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, (size_t)avg_young_live()->average(), (size_t)avg_old_live()->average()); diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp index 90e1e60db9495..4fab160dcb4fb 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -145,8 +145,7 @@ class PSAdaptiveSizePolicy : public AdaptiveSizePolicy { // Footprint accessors size_t live_space() const { - return (size_t)(avg_base_footprint()->average() + - avg_young_live()->average() + + return (size_t)(avg_young_live()->average() + avg_old_live()->average()); } size_t free_space() const { diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index b95c7c619af7d..d9f2749230e4c 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -54,7 +54,9 @@ Monitor* ParCompactionManager::_shadow_region_monitor = nullptr; PreservedMarksSet* ParCompactionManager::_preserved_marks_set = nullptr; -ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks) { +ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks, + ReferenceProcessor* ref_processor) + : _mark_and_push_closure(this, ref_processor) { ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); @@ -66,8 +68,9 @@ ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks) { } void ParCompactionManager::initialize(ParMarkBitMap* mbm) { - assert(ParallelScavengeHeap::heap() != nullptr, - "Needed for initialization"); + assert(ParallelScavengeHeap::heap() != nullptr, "Needed for initialization"); + assert(PSParallelCompact::ref_processor() != nullptr, "precondition"); + assert(ParallelScavengeHeap::heap()->workers().max_workers() != 0, "Not initialized?"); _mark_bitmap = mbm; @@ -85,15 +88,13 @@ void ParCompactionManager::initialize(ParMarkBitMap* mbm) { // Create and register the ParCompactionManager(s) for the worker threads. for(uint i=0; iget(i)); + _manager_array[i] = new ParCompactionManager(_preserved_marks_set->get(i), + PSParallelCompact::ref_processor()); oop_task_queues()->register_queue(i, _manager_array[i]->oop_stack()); _objarray_task_queues->register_queue(i, &_manager_array[i]->_objarray_stack); region_task_queues()->register_queue(i, _manager_array[i]->region_stack()); } - assert(ParallelScavengeHeap::heap()->workers().max_workers() != 0, - "Not initialized?"); - _shadow_region_array = new (mtGC) GrowableArray(10, mtGC); _shadow_region_monitor = new Monitor(Mutex::nosafepoint, "CompactionManager_lock"); diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.hpp index 0dd68d2e2f7c7..da1609a32d8eb 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_PARALLEL_PSCOMPACTIONMANAGER_HPP #define SHARE_GC_PARALLEL_PSCOMPACTIONMANAGER_HPP +#include "classfile/classLoaderData.hpp" #include "gc/parallel/psParallelCompact.hpp" #include "gc/shared/preservedMarks.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" @@ -40,6 +41,19 @@ class ObjectStartArray; class ParallelCompactData; class ParMarkBitMap; +class PCMarkAndPushClosure: public ClaimMetadataVisitingOopIterateClosure { + ParCompactionManager* _compaction_manager; + + template void do_oop_work(T* p); +public: + PCMarkAndPushClosure(ParCompactionManager* cm, ReferenceProcessor* rp) : + ClaimMetadataVisitingOopIterateClosure(ClassLoaderData::_claim_stw_fullgc_mark, rp), + _compaction_manager(cm) { } + + virtual void do_oop(oop* p) { do_oop_work(p); } + virtual void do_oop(narrowOop* p) { do_oop_work(p); } +}; + class ParCompactionManager : public CHeapObj { friend class MarkFromRootsTask; friend class ParallelCompactRefProcProxyTask; @@ -47,6 +61,7 @@ class ParCompactionManager : public CHeapObj { friend class ParMarkBitMap; friend class PSParallelCompact; friend class FillDensePrefixAndCompactionTask; + friend class PCAddThreadRootsMarkingTaskClosure; private: typedef OverflowTaskQueue OopTaskQueue; @@ -71,6 +86,7 @@ class ParCompactionManager : public CHeapObj { ObjArrayTaskQueue _objarray_stack; size_t _next_shadow_region; + PCMarkAndPushClosure _mark_and_push_closure; // Is there a way to reuse the _oop_stack for the // saving empty regions? For now just create a different // type of TaskQueue. @@ -104,7 +120,9 @@ class ParCompactionManager : public CHeapObj { // objArray stack, otherwise returns false and the task is invalid. bool publish_or_pop_objarray_tasks(ObjArrayTask& task); - ParCompactionManager(PreservedMarks* preserved_marks); + ParCompactionManager(PreservedMarks* preserved_marks, + ReferenceProcessor* ref_processor); + // Array of task queues. Needed by the task terminator. static RegionTaskQueueSet* region_task_queues() { return _region_task_queues; } OopTaskQueue* oop_stack() { return &_oop_stack; } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp index 22acc11beec3c..0b92862223826 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp @@ -41,29 +41,10 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -class PCMarkAndPushClosure: public OopClosure { -private: - ParCompactionManager* _compaction_manager; -public: - PCMarkAndPushClosure(ParCompactionManager* cm) : _compaction_manager(cm) { } - - template void do_oop_work(T* p) { _compaction_manager->mark_and_push(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } -}; - -class PCIterateMarkAndPushClosure: public ClaimMetadataVisitingOopIterateClosure { -private: - ParCompactionManager* _compaction_manager; -public: - PCIterateMarkAndPushClosure(ParCompactionManager* cm, ReferenceProcessor* rp) : - ClaimMetadataVisitingOopIterateClosure(ClassLoaderData::_claim_stw_fullgc_mark, rp), - _compaction_manager(cm) { } - - template void do_oop_work(T* p) { _compaction_manager->mark_and_push(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } -}; +template +inline void PCMarkAndPushClosure::do_oop_work(T* p) { + _compaction_manager->mark_and_push(p); +} inline bool ParCompactionManager::steal(int queue_num, oop& t) { return oop_task_queues()->steal(queue_num, t); @@ -161,13 +142,12 @@ inline void ParCompactionManager::follow_array(objArrayOop obj, int index) { inline void ParCompactionManager::follow_contents(oop obj) { assert(PSParallelCompact::mark_bitmap()->is_marked(obj), "should be marked"); - PCIterateMarkAndPushClosure cl(this, PSParallelCompact::ref_processor()); if (obj->is_objArray()) { - cl.do_klass(obj->klass()); + _mark_and_push_closure.do_klass(obj->klass()); follow_array(objArrayOop(obj), 0); } else { - obj->oop_iterate(&cl); + obj->oop_iterate(&_mark_and_push_closure); } } diff --git a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp index 20e14b73ac390..ed96c5c418157 100644 --- a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp +++ b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -107,10 +107,6 @@ PSGCAdaptivePolicyCounters::PSGCAdaptivePolicyCounters(const char* name_arg, _free_space = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, ps_size_policy()->free_space(), CHECK); - cname = PerfDataManager::counter_name(name_space(), "avgBaseFootprint"); - _avg_base_footprint = PerfDataManager::create_variable(SUN_GC, cname, - PerfData::U_Bytes, (jlong) ps_size_policy()->avg_base_footprint()->average(), CHECK); - cname = PerfDataManager::counter_name(name_space(), "liveAtLastFullGc"); _live_at_last_full_gc_counter = PerfDataManager::create_variable(SUN_GC, cname, @@ -157,7 +153,6 @@ void PSGCAdaptivePolicyCounters::update_counters_from_policy() { update_decrement_tenuring_threshold_for_survivor_limit(); update_live_space(); update_free_space(); - update_avg_base_footprint(); update_change_old_gen_for_maj_pauses(); update_change_young_gen_for_maj_pauses(); diff --git a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp index 620c761004ecd..217cd7b9c5c36 100644 --- a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp +++ b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,6 @@ class PSGCAdaptivePolicyCounters : public GCAdaptivePolicyCounters { PerfVariable* _avg_major_interval; PerfVariable* _live_space; PerfVariable* _free_space; - PerfVariable* _avg_base_footprint; PerfVariable* _live_at_last_full_gc_counter; PerfVariable* _old_capacity; @@ -142,11 +141,6 @@ class PSGCAdaptivePolicyCounters : public GCAdaptivePolicyCounters { _free_space->set_value(ps_size_policy()->free_space()); } - inline void update_avg_base_footprint() { - _avg_base_footprint->set_value( - (jlong)(ps_size_policy()->avg_base_footprint()->average()) - ); - } inline void update_avg_old_live() { _avg_old_live_counter->set_value( (jlong)(ps_size_policy()->avg_old_live()->average()) @@ -186,10 +180,6 @@ class PSGCAdaptivePolicyCounters : public GCAdaptivePolicyCounters { // counter or from globals. This is distinguished from counters // that are updated via input parameters. void update_counters(); - - virtual GCPolicyCounters::Name kind() const { - return GCPolicyCounters::PSGCAdaptivePolicyCountersKind; - } }; #endif // SHARE_GC_PARALLEL_PSGCADAPTIVEPOLICYCOUNTERS_HPP diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index f41108d1a597f..4d54f11805ac2 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -1188,10 +1188,11 @@ class PCAddThreadRootsMarkingTaskClosure : public ThreadClosure { ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(_worker_id); - PCMarkAndPushClosure mark_and_push_closure(cm); - MarkingNMethodClosure mark_and_push_in_blobs(&mark_and_push_closure, !NMethodToOopClosure::FixRelocations, true /* keepalive nmethods */); + MarkingNMethodClosure mark_and_push_in_blobs(&cm->_mark_and_push_closure, + !NMethodToOopClosure::FixRelocations, + true /* keepalive nmethods */); - thread->oops_do(&mark_and_push_closure, &mark_and_push_in_blobs); + thread->oops_do(&cm->_mark_and_push_closure, &mark_and_push_in_blobs); // Do the real work cm->follow_marking_stacks(); @@ -1232,22 +1233,22 @@ class MarkFromRootsTask : public WorkerTask { virtual void work(uint worker_id) { ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(worker_id); cm->create_marking_stats_cache(); - PCMarkAndPushClosure mark_and_push_closure(cm); - { - CLDToOopClosure cld_closure(&mark_and_push_closure, ClassLoaderData::_claim_stw_fullgc_mark); + CLDToOopClosure cld_closure(&cm->_mark_and_push_closure, ClassLoaderData::_claim_stw_fullgc_mark); ClassLoaderDataGraph::always_strong_cld_do(&cld_closure); // Do the real work cm->follow_marking_stacks(); } - PCAddThreadRootsMarkingTaskClosure closure(worker_id); - Threads::possibly_parallel_threads_do(true /* is_par */, &closure); + { + PCAddThreadRootsMarkingTaskClosure closure(worker_id); + Threads::possibly_parallel_threads_do(_active_workers > 1 /* is_par */, &closure); + } // Mark from OopStorages { - _oop_storage_set_par_state.oops_do(&mark_and_push_closure); + _oop_storage_set_par_state.oops_do(&cm->_mark_and_push_closure); // Do the real work cm->follow_marking_stacks(); } @@ -1269,10 +1270,9 @@ class ParallelCompactRefProcProxyTask : public RefProcProxyTask { void work(uint worker_id) override { assert(worker_id < _max_workers, "sanity"); ParCompactionManager* cm = (_tm == RefProcThreadModel::Single) ? ParCompactionManager::get_vmthread_cm() : ParCompactionManager::gc_thread_compaction_manager(worker_id); - PCMarkAndPushClosure keep_alive(cm); BarrierEnqueueDiscoveredFieldClosure enqueue; ParCompactionManager::FollowStackClosure complete_gc(cm, (_tm == RefProcThreadModel::Single) ? nullptr : &_terminator, worker_id); - _rp_task->rp_work(worker_id, PSParallelCompact::is_alive_closure(), &keep_alive, &enqueue, &complete_gc); + _rp_task->rp_work(worker_id, PSParallelCompact::is_alive_closure(), &cm->_mark_and_push_closure, &enqueue, &complete_gc); } void prepare_run_task_hook() override { diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index 715b82fd38d32..047171a5eb364 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -572,11 +572,6 @@ HeapWord* DefNewGeneration::block_start(const void* p) const { return block_start_const(to(), p); } -HeapWord* DefNewGeneration::expand_and_allocate(size_t size, bool is_tlab) { - // We don't attempt to expand the young generation (but perhaps we should.) - return allocate(size, is_tlab); -} - void DefNewGeneration::adjust_desired_tenuring_threshold() { // Set the desired survivor size to half the real survivor space size_t const survivor_capacity = to()->capacity() / HeapWordSize; @@ -833,7 +828,6 @@ void DefNewGeneration::gc_epilogue(bool full) { assert(!GCLocker::is_active(), "We should not be executing here"); // update the generation and space performance counters update_counters(); - SerialHeap::heap()->counters()->update_counters(); } void DefNewGeneration::update_counters() { @@ -866,7 +860,7 @@ const char* DefNewGeneration::name() const { return "def new generation"; } -HeapWord* DefNewGeneration::allocate(size_t word_size, bool is_tlab) { +HeapWord* DefNewGeneration::allocate(size_t word_size) { // This is the slow-path allocation for the DefNewGeneration. // Most allocations are fast-path in compiled code. // We try to allocate from the eden. If that works, we are happy. @@ -876,8 +870,7 @@ HeapWord* DefNewGeneration::allocate(size_t word_size, bool is_tlab) { return result; } -HeapWord* DefNewGeneration::par_allocate(size_t word_size, - bool is_tlab) { +HeapWord* DefNewGeneration::par_allocate(size_t word_size) { return eden()->par_allocate(word_size); } diff --git a/src/hotspot/share/gc/serial/defNewGeneration.hpp b/src/hotspot/share/gc/serial/defNewGeneration.hpp index 011b79fdabd6b..c5b7c095ac4e6 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.hpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.hpp @@ -209,9 +209,9 @@ class DefNewGeneration: public Generation { return result; } - HeapWord* allocate(size_t word_size, bool is_tlab); - - HeapWord* par_allocate(size_t word_size, bool is_tlab); + // Allocate requested size or return null; single-threaded and lock-free versions. + HeapWord* allocate(size_t word_size); + HeapWord* par_allocate(size_t word_size); void gc_epilogue(bool full); @@ -227,8 +227,6 @@ class DefNewGeneration: public Generation { bool collect(bool clear_all_soft_refs); - HeapWord* expand_and_allocate(size_t size, bool is_tlab); - oop copy_to_survivor_space(oop old); uint tenuring_threshold() { return _tenuring_threshold; } diff --git a/src/hotspot/share/gc/serial/generation.hpp b/src/hotspot/share/gc/serial/generation.hpp index a757c97c5cb15..c6a9f94a8703a 100644 --- a/src/hotspot/share/gc/serial/generation.hpp +++ b/src/hotspot/share/gc/serial/generation.hpp @@ -103,20 +103,6 @@ class Generation: public CHeapObj { return _reserved.contains(p); } - // Allocate and returns a block of the requested size, or returns "null". - // Assumes the caller has done any necessary locking. - virtual HeapWord* allocate(size_t word_size, bool is_tlab) = 0; - - // Like "allocate", but performs any necessary locking internally. - virtual HeapWord* par_allocate(size_t word_size, bool is_tlab) = 0; - - // Perform a heap collection, attempting to create (at least) enough - // space to support an allocation of the given "word_size". If - // successful, perform the allocation and return the resulting - // "oop" (initializing the allocated block). If the allocation is - // still unsuccessful, return "null". - virtual HeapWord* expand_and_allocate(size_t word_size, bool is_tlab) = 0; - // Printing virtual const char* name() const = 0; virtual const char* short_name() const = 0; @@ -128,8 +114,7 @@ class Generation: public CHeapObj { public: // Performance Counter support - virtual void update_counters() = 0; - virtual CollectorCounters* counters() { return _gc_counters; } + CollectorCounters* counters() { return _gc_counters; } GCMemoryManager* gc_manager() const { assert(_gc_manager != nullptr, "not initialized yet"); diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index c1fdc1eba1a32..3c48177554121 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -156,7 +156,7 @@ void SerialHeap::safepoint_synchronize_end() { HeapWord* SerialHeap::allocate_loaded_archive_space(size_t word_size) { MutexLocker ml(Heap_lock); - return old_gen()->allocate(word_size, false /* is_tlab */); + return old_gen()->allocate(word_size); } void SerialHeap::complete_loaded_archive_space(MemRegion archive_space) { @@ -292,11 +292,12 @@ bool SerialHeap::should_try_older_generation_allocation(size_t word_size) const HeapWord* SerialHeap::expand_heap_and_allocate(size_t size, bool is_tlab) { HeapWord* result = nullptr; if (_old_gen->should_allocate(size, is_tlab)) { - result = _old_gen->expand_and_allocate(size, is_tlab); + result = _old_gen->expand_and_allocate(size); } if (result == nullptr) { if (_young_gen->should_allocate(size, is_tlab)) { - result = _young_gen->expand_and_allocate(size, is_tlab); + // Young-gen is not expanded. + result = _young_gen->allocate(size); } } assert(result == nullptr || is_in_reserved(result), "result not in heap"); @@ -314,7 +315,7 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, // First allocation attempt is lock-free. DefNewGeneration *young = _young_gen; if (young->should_allocate(size, is_tlab)) { - result = young->par_allocate(size, is_tlab); + result = young->par_allocate(size); if (result != nullptr) { assert(is_in_reserved(result), "result not in heap"); return result; @@ -406,14 +407,14 @@ HeapWord* SerialHeap::attempt_allocation(size_t size, HeapWord* res = nullptr; if (_young_gen->should_allocate(size, is_tlab)) { - res = _young_gen->allocate(size, is_tlab); + res = _young_gen->allocate(size); if (res != nullptr || first_only) { return res; } } if (_old_gen->should_allocate(size, is_tlab)) { - res = _old_gen->allocate(size, is_tlab); + res = _old_gen->allocate(size); } return res; diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index b1b7507094771..febc4713d0372 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp @@ -397,20 +397,19 @@ oop TenuredGeneration::allocate_for_promotion(oop obj, size_t obj_size) { #endif // #ifndef PRODUCT // Allocate new object. - HeapWord* result = allocate(obj_size, false); + HeapWord* result = allocate(obj_size); if (result == nullptr) { // Promotion of obj into gen failed. Try to expand and allocate. - result = expand_and_allocate(obj_size, false); + result = expand_and_allocate(obj_size); } return cast_to_oop(result); } HeapWord* -TenuredGeneration::expand_and_allocate(size_t word_size, bool is_tlab) { - assert(!is_tlab, "TenuredGeneration does not support TLAB allocation"); +TenuredGeneration::expand_and_allocate(size_t word_size) { expand(word_size*HeapWordSize, _min_heap_delta_bytes); - return allocate(word_size, is_tlab); + return allocate(word_size); } void TenuredGeneration::assert_correct_size_change_locking() { diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.hpp index dcec912d4887f..fc0578d4e4fe4 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.hpp @@ -130,10 +130,12 @@ class TenuredGeneration: public Generation { void complete_loaded_archive_space(MemRegion archive_space); inline void update_for_block(HeapWord* start, HeapWord* end); - virtual inline HeapWord* allocate(size_t word_size, bool is_tlab); - virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab); + // 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); - HeapWord* expand_and_allocate(size_t size, bool is_tlab); + // Expand the old-gen then invoke allocate above. + HeapWord* expand_and_allocate(size_t size); void gc_prologue(); void gc_epilogue(); diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp index ba0dbcdc02d57..c6a6a0ae71634 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp @@ -48,9 +48,7 @@ inline void TenuredGeneration::update_for_block(HeapWord* start, HeapWord* end) _bts->update_for_block(start, end); } -HeapWord* TenuredGeneration::allocate(size_t word_size, - bool is_tlab) { - assert(!is_tlab, "TenuredGeneration does not support TLAB allocation"); +HeapWord* TenuredGeneration::allocate(size_t word_size) { HeapWord* res = _the_space->allocate(word_size); if (res != nullptr) { _bts->update_for_block(res, res + word_size); @@ -58,14 +56,4 @@ HeapWord* TenuredGeneration::allocate(size_t word_size, return res; } -HeapWord* TenuredGeneration::par_allocate(size_t word_size, - bool is_tlab) { - assert(!is_tlab, "TenuredGeneration does not support TLAB allocation"); - 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 diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp index d03f416ff689b..c1485c069c83c 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp @@ -102,10 +102,10 @@ class C2AccessValuePtr: public C2AccessValue { class C2Access: public StackObj { protected: DecoratorSet _decorators; - BasicType _type; Node* _base; C2AccessValuePtr& _addr; Node* _raw_access; + BasicType _type; uint8_t _barrier_data; void fixup_decorators(); @@ -114,10 +114,10 @@ class C2Access: public StackObj { C2Access(DecoratorSet decorators, BasicType type, Node* base, C2AccessValuePtr& addr) : _decorators(decorators), - _type(type), _base(base), _addr(addr), _raw_access(nullptr), + _type(type), _barrier_data(0) {} diff --git a/src/hotspot/share/gc/shared/freeListAllocator.cpp b/src/hotspot/share/gc/shared/freeListAllocator.cpp index f33c6fcb14b6e..ef7d12ab0242f 100644 --- a/src/hotspot/share/gc/shared/freeListAllocator.cpp +++ b/src/hotspot/share/gc/shared/freeListAllocator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,7 +88,7 @@ void FreeListAllocator::delete_list(FreeNode* list) { FreeListAllocator::~FreeListAllocator() { uint index = Atomic::load(&_active_pending_list); NodeList pending_list = _pending_lists[index].take_all(); - delete_list(Atomic::load(&pending_list._head)); + delete_list(pending_list._head); delete_list(_free_list.pop_all()); } @@ -106,7 +106,7 @@ size_t FreeListAllocator::free_count() const { size_t FreeListAllocator::pending_count() const { uint index = Atomic::load(&_active_pending_list); - return _pending_lists[index].count();; + return _pending_lists[index].count(); } // To solve the ABA problem, popping a node from the _free_list is performed within diff --git a/src/hotspot/share/gc/shared/gcCause.cpp b/src/hotspot/share/gc/shared/gcCause.cpp index 426154fdca3e7..2be7ef7be076a 100644 --- a/src/hotspot/share/gc/shared/gcCause.cpp +++ b/src/hotspot/share/gc/shared/gcCause.cpp @@ -78,9 +78,6 @@ const char* GCCause::to_string(GCCause::Cause cause) { case _metadata_GC_clear_soft_refs: return "Metadata GC Clear Soft References"; - case _adaptive_size_policy: - return "Ergonomics"; - case _g1_inc_collection_pause: return "G1 Evacuation Pause"; diff --git a/src/hotspot/share/gc/shared/gcCause.hpp b/src/hotspot/share/gc/shared/gcCause.hpp index 152ca787fc2f7..f244d17c1ab92 100644 --- a/src/hotspot/share/gc/shared/gcCause.hpp +++ b/src/hotspot/share/gc/shared/gcCause.hpp @@ -66,8 +66,6 @@ class GCCause : public AllStatic { _metadata_GC_threshold, _metadata_GC_clear_soft_refs, - _adaptive_size_policy, - _g1_inc_collection_pause, _g1_compaction_pause, _g1_humongous_allocation, @@ -110,20 +108,16 @@ class GCCause : public AllStatic { // Causes for collection of the tenured gernation inline static bool is_tenured_allocation_failure_gc(GCCause::Cause cause) { - // _adaptive_size_policy for a full collection after a young GC // _allocation_failure is the generic cause a collection which could result // in the collection of the tenured generation if there is not enough space // in the tenured generation to support a young GC. - return (cause == GCCause::_adaptive_size_policy || - cause == GCCause::_allocation_failure); + return cause == GCCause::_allocation_failure; } // Causes for collection of the young generation inline static bool is_allocation_failure_gc(GCCause::Cause cause) { // _allocation_failure is the generic cause a collection for allocation failure - // _adaptive_size_policy is for a collection done before a full GC return (cause == GCCause::_allocation_failure || - cause == GCCause::_adaptive_size_policy || cause == GCCause::_shenandoah_allocation_failure_evac); } diff --git a/src/hotspot/share/gc/shared/gcPolicyCounters.hpp b/src/hotspot/share/gc/shared/gcPolicyCounters.hpp index a631664fcab93..9c7e5788a7eec 100644 --- a/src/hotspot/share/gc/shared/gcPolicyCounters.hpp +++ b/src/hotspot/share/gc/shared/gcPolicyCounters.hpp @@ -46,13 +46,6 @@ class GCPolicyCounters: public CHeapObj { const char* _name_space; public: - enum Name { - NONE, - GCPolicyCountersKind, - GCAdaptivePolicyCountersKind, - PSGCAdaptivePolicyCountersKind - }; - GCPolicyCounters(const char* name, int collectors, int generations); inline PerfVariable* tenuring_threshold() const { @@ -68,12 +61,6 @@ class GCPolicyCounters: public CHeapObj { } const char* name_space() const { return _name_space; } - - virtual void update_counters() {} - - virtual GCPolicyCounters::Name kind() const { - return GCPolicyCounters::GCPolicyCountersKind; - } }; #endif // SHARE_GC_SHARED_GCPOLICYCOUNTERS_HPP diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 66496544b9627..34bc638c9baca 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -422,10 +422,6 @@ "Initial ratio of young generation/survivor space size") \ range(0, max_uintx) \ \ - product(size_t, BaseFootPrintEstimate, 256*M, \ - "Estimate of footprint other than Java Heap") \ - range(0, max_uintx) \ - \ product(bool, UseGCOverheadLimit, true, \ "Use policy to limit of proportion of time spent in GC " \ "before an OutOfMemory error is thrown") \ diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 30bf1f7a39507..6b104e7e19935 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -183,20 +183,11 @@ LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr ob return obj; } -LIR_Opr ShenandoahBarrierSetC1::iu_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators) { - if (ShenandoahIUBarrier) { - obj = ensure_in_register(gen, obj, T_OBJECT); - pre_barrier(gen, info, decorators, LIR_OprFact::illegalOpr, obj); - } - return obj; -} - void ShenandoahBarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { if (access.is_oop()) { if (ShenandoahSATBBarrier) { pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), access.resolved_addr(), LIR_OprFact::illegalOpr /* pre_val */); } - value = iu_barrier(access.gen(), value, access.access_emit_info(), access.decorators()); } BarrierSetC1::store_at_resolved(access, value); } diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index 71093300e8296..98b2aad887130 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -200,7 +200,6 @@ class ShenandoahBarrierSetC1 : public BarrierSetC1 { void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); - LIR_Opr iu_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators); LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 7d1f5cc16a845..d2820f3aa267f 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -48,25 +48,7 @@ ShenandoahBarrierSetC2* ShenandoahBarrierSetC2::bsc2() { } ShenandoahBarrierSetC2State::ShenandoahBarrierSetC2State(Arena* comp_arena) - : _iu_barriers(new (comp_arena) GrowableArray(comp_arena, 8, 0, nullptr)), - _load_reference_barriers(new (comp_arena) GrowableArray(comp_arena, 8, 0, nullptr)) { -} - -int ShenandoahBarrierSetC2State::iu_barriers_count() const { - return _iu_barriers->length(); -} - -ShenandoahIUBarrierNode* ShenandoahBarrierSetC2State::iu_barrier(int idx) const { - return _iu_barriers->at(idx); -} - -void ShenandoahBarrierSetC2State::add_iu_barrier(ShenandoahIUBarrierNode* n) { - assert(!_iu_barriers->contains(n), "duplicate entry in barrier list"); - _iu_barriers->append(n); -} - -void ShenandoahBarrierSetC2State::remove_iu_barrier(ShenandoahIUBarrierNode* n) { - _iu_barriers->remove_if_existing(n); + : _load_reference_barriers(new (comp_arena) GrowableArray(comp_arena, 8, 0, nullptr)) { } int ShenandoahBarrierSetC2State::load_reference_barriers_count() const { @@ -88,13 +70,6 @@ void ShenandoahBarrierSetC2State::remove_load_reference_barrier(ShenandoahLoadRe } } -Node* ShenandoahBarrierSetC2::shenandoah_iu_barrier(GraphKit* kit, Node* obj) const { - if (ShenandoahIUBarrier) { - return kit->gvn().transform(new ShenandoahIUBarrierNode(obj)); - } - return obj; -} - #define __ kit-> bool ShenandoahBarrierSetC2::satb_can_remove_pre_barrier(GraphKit* kit, PhaseValues* phase, Node* adr, @@ -508,21 +483,8 @@ Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& uint adr_idx = kit->C->get_alias_index(adr_type); assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); - Node* value = val.node(); - value = shenandoah_iu_barrier(kit, value); - val.set_node(value); shenandoah_write_barrier_pre(kit, true /* do_load */, /*kit->control(),*/ access.base(), adr, adr_idx, val.node(), static_cast(val.type()), nullptr /* pre_val */, access.type()); - } else { - assert(access.is_opt_access(), "only for optimization passes"); - assert(((decorators & C2_TIGHTLY_COUPLED_ALLOC) != 0 || !ShenandoahSATBBarrier) && (decorators & C2_ARRAY_COPY) != 0, "unexpected caller of this code"); - C2OptAccess& opt_access = static_cast(access); - PhaseGVN& gvn = opt_access.gvn(); - - if (ShenandoahIUBarrier) { - Node* enqueue = gvn.transform(new ShenandoahIUBarrierNode(val.node())); - val.set_node(enqueue); - } } return BarrierSetC2::store_at_resolved(access, val); } @@ -598,7 +560,6 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess Node* new_val, const Type* value_type) const { GraphKit* kit = access.kit(); if (access.is_oop()) { - new_val = shenandoah_iu_barrier(kit, new_val); shenandoah_write_barrier_pre(kit, false /* do_load */, nullptr, nullptr, max_juint, nullptr, nullptr, expected_val /* pre_val */, T_OBJECT); @@ -646,7 +607,6 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAcces Node* new_val, const Type* value_type) const { GraphKit* kit = access.kit(); if (access.is_oop()) { - new_val = shenandoah_iu_barrier(kit, new_val); shenandoah_write_barrier_pre(kit, false /* do_load */, nullptr, nullptr, max_juint, nullptr, nullptr, expected_val /* pre_val */, T_OBJECT); @@ -699,9 +659,6 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAcces Node* ShenandoahBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* val, const Type* value_type) const { GraphKit* kit = access.kit(); - if (access.is_oop()) { - val = shenandoah_iu_barrier(kit, val); - } Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, val, value_type); if (access.is_oop()) { result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(nullptr, result, access.decorators())); @@ -719,7 +676,7 @@ bool ShenandoahBarrierSetC2::is_gc_pre_barrier_node(Node* node) const { // Support for GC barriers emitted during parsing bool ShenandoahBarrierSetC2::is_gc_barrier_node(Node* node) const { - if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier || node->Opcode() == Op_ShenandoahIUBarrier) return true; + if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) return true; if (node->Opcode() != Op_CallLeaf && node->Opcode() != Op_CallLeafNoFP) { return false; } @@ -740,9 +697,6 @@ Node* ShenandoahBarrierSetC2::step_over_gc_barrier(Node* c) const { if (c->Opcode() == Op_ShenandoahLoadReferenceBarrier) { return c->in(ShenandoahLoadReferenceBarrierNode::ValueIn); } - if (c->Opcode() == Op_ShenandoahIUBarrier) { - c = c->in(1); - } return c; } @@ -775,9 +729,6 @@ bool ShenandoahBarrierSetC2::array_copy_requires_gc_barriers(bool tightly_couple } return !is_clone; } - if (phase == Optimization) { - return !ShenandoahIUBarrier; - } return true; } @@ -836,11 +787,7 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo debug_only(gc_state_adr_type = phase->C->get_adr_type(gc_state_idx)); Node* gc_state = phase->transform_later(new LoadBNode(ctrl, mem, gc_state_addr, gc_state_adr_type, TypeInt::BYTE, MemNode::unordered)); - int flags = ShenandoahHeap::HAS_FORWARDED; - if (ShenandoahIUBarrier) { - flags |= ShenandoahHeap::MARKING; - } - Node* stable_and = phase->transform_later(new AndINode(gc_state, phase->igvn().intcon(flags))); + Node* stable_and = phase->transform_later(new AndINode(gc_state, phase->igvn().intcon(ShenandoahHeap::HAS_FORWARDED))); Node* stable_cmp = phase->transform_later(new CmpINode(stable_and, phase->igvn().zerocon(T_INT))); Node* stable_test = phase->transform_later(new BoolNode(stable_cmp, BoolTest::ne)); @@ -889,18 +836,12 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo // Support for macro expanded GC barriers void ShenandoahBarrierSetC2::register_potential_barrier_node(Node* node) const { - if (node->Opcode() == Op_ShenandoahIUBarrier) { - state()->add_iu_barrier((ShenandoahIUBarrierNode*) node); - } if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) { state()->add_load_reference_barrier((ShenandoahLoadReferenceBarrierNode*) node); } } void ShenandoahBarrierSetC2::unregister_potential_barrier_node(Node* node) const { - if (node->Opcode() == Op_ShenandoahIUBarrier) { - state()->remove_iu_barrier((ShenandoahIUBarrierNode*) node); - } if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) { state()->remove_load_reference_barrier((ShenandoahLoadReferenceBarrierNode*) node); } @@ -948,12 +889,7 @@ void ShenandoahBarrierSetC2::eliminate_useless_gc_barriers(Unique_Node_List &use } } } - for (int i = state()->iu_barriers_count() - 1; i >= 0; i--) { - ShenandoahIUBarrierNode* n = state()->iu_barrier(i); - if (!useful.member(n)) { - state()->remove_iu_barrier(n); - } - } + for (int i = state()->load_reference_barriers_count() - 1; i >= 0; i--) { ShenandoahLoadReferenceBarrierNode* n = state()->load_reference_barrier(i); if (!useful.member(n)) { @@ -1190,9 +1126,6 @@ bool ShenandoahBarrierSetC2::escape_add_to_con_graph(ConnectionGraph* conn_graph } return false; } - case Op_ShenandoahIUBarrier: - conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(1), delayed_worklist); - break; case Op_ShenandoahLoadReferenceBarrier: conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(ShenandoahLoadReferenceBarrierNode::ValueIn), delayed_worklist); return true; @@ -1216,9 +1149,6 @@ bool ShenandoahBarrierSetC2::escape_add_final_edges(ConnectionGraph* conn_graph, case Op_ShenandoahWeakCompareAndSwapP: case Op_ShenandoahWeakCompareAndSwapN: return conn_graph->add_final_edges_unsafe_access(n, opcode); - case Op_ShenandoahIUBarrier: - conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(1), nullptr); - return true; case Op_ShenandoahLoadReferenceBarrier: conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(ShenandoahLoadReferenceBarrierNode::ValueIn), nullptr); return true; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index 9b8e30c98a1d3..4619b217e96c6 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -31,17 +31,11 @@ class ShenandoahBarrierSetC2State : public ArenaObj { private: - GrowableArray* _iu_barriers; GrowableArray* _load_reference_barriers; public: ShenandoahBarrierSetC2State(Arena* comp_arena); - int iu_barriers_count() const; - ShenandoahIUBarrierNode* iu_barrier(int idx) const; - void add_iu_barrier(ShenandoahIUBarrierNode* n); - void remove_iu_barrier(ShenandoahIUBarrierNode * n); - int load_reference_barriers_count() const; ShenandoahLoadReferenceBarrierNode* load_reference_barrier(int idx) const; void add_load_reference_barrier(ShenandoahLoadReferenceBarrierNode* n); @@ -73,8 +67,6 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { Node* pre_val, BasicType bt) const; - Node* shenandoah_iu_barrier(GraphKit* kit, Node* obj) const; - void insert_pre_barrier(GraphKit* kit, Node* base_oop, Node* offset, Node* pre_val, bool need_mem_bar) const; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 8cd76dd3d6bc4..0a51f74299545 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -46,8 +46,7 @@ bool ShenandoahBarrierC2Support::expand(Compile* C, PhaseIterGVN& igvn) { ShenandoahBarrierSetC2State* state = ShenandoahBarrierSetC2::bsc2()->state(); - if ((state->iu_barriers_count() + - state->load_reference_barriers_count()) > 0) { + if (state->load_reference_barriers_count() > 0) { assert(C->post_loop_opts_phase(), "no loop opts allowed"); C->reset_post_loop_opts_phase(); // ... but we know what we are doing C->clear_major_progress(); @@ -186,28 +185,10 @@ bool ShenandoahBarrierC2Support::verify_helper(Node* in, Node_Stack& phis, Vecto } } else if (in->Opcode() == Op_ShenandoahLoadReferenceBarrier) { if (t == ShenandoahOopStore) { - uint i = 0; - for (; i < phis.size(); i++) { - Node* n = phis.node_at(i); - if (n->Opcode() == Op_ShenandoahIUBarrier) { - break; - } - } - if (i == phis.size()) { - return false; - } + return false; } barriers_used.push(in); if (trace) {tty->print("Found barrier"); in->dump();} - } else if (in->Opcode() == Op_ShenandoahIUBarrier) { - if (t != ShenandoahOopStore) { - in = in->in(1); - continue; - } - if (trace) {tty->print("Found enqueue barrier"); in->dump();} - phis.push(in, in->req()); - in = in->in(1); - continue; } else if (in->is_Proj() && in->in(0)->is_Allocate()) { if (trace) { tty->print("Found alloc"); @@ -326,7 +307,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { } } - if (verify && !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahIUBarrier ? ShenandoahOopStore : ShenandoahValue, trace, barriers_used)) { + if (verify && !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahValue, trace, barriers_used)) { report_verify_failure("Shenandoah verification: Store should have barriers", n); } } @@ -368,7 +349,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { } } else if (n->is_LoadStore()) { if (n->in(MemNode::ValueIn)->bottom_type()->make_ptr() && - !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahIUBarrier ? ShenandoahOopStore : ShenandoahValue, trace, barriers_used)) { + !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahValue, trace, barriers_used)) { report_verify_failure("Shenandoah verification: LoadStore (value) should have barriers", n); } @@ -532,7 +513,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { } } } - } else if (n->Opcode() == Op_ShenandoahIUBarrier || n->Opcode() == Op_ShenandoahLoadReferenceBarrier) { + } else if (n->Opcode() == Op_ShenandoahLoadReferenceBarrier) { // skip } else if (n->is_AddP() || n->is_Phi() @@ -1113,20 +1094,6 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { ShenandoahBarrierSetC2State* state = ShenandoahBarrierSetC2::bsc2()->state(); Unique_Node_List uses; - for (int i = 0; i < state->iu_barriers_count(); i++) { - Node* barrier = state->iu_barrier(i); - Node* ctrl = phase->get_ctrl(barrier); - IdealLoopTree* loop = phase->get_loop(ctrl); - Node* head = loop->head(); - if (head->is_OuterStripMinedLoop()) { - // Expanding a barrier here will break loop strip mining - // verification. Transform the loop so the loop nest doesn't - // appear as strip mined. - OuterStripMinedLoopNode* outer = head->as_OuterStripMinedLoop(); - hide_strip_mined_loop(outer, outer->unique_ctrl_out()->as_CountedLoop(), phase); - } - } - Node_Stack stack(0); Node_List clones; for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) { @@ -1459,157 +1426,6 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { } // Done expanding load-reference-barriers. assert(ShenandoahBarrierSetC2::bsc2()->state()->load_reference_barriers_count() == 0, "all load reference barrier nodes should have been replaced"); - - for (int i = state->iu_barriers_count() - 1; i >= 0; i--) { - Node* barrier = state->iu_barrier(i); - Node* pre_val = barrier->in(1); - - if (phase->igvn().type(pre_val)->higher_equal(TypePtr::NULL_PTR)) { - ShouldNotReachHere(); - continue; - } - - Node* ctrl = phase->get_ctrl(barrier); - - if (ctrl->is_Proj() && ctrl->in(0)->is_CallJava()) { - assert(is_dominator(phase->get_ctrl(pre_val), ctrl->in(0)->in(0), pre_val, ctrl->in(0), phase), "can't move"); - ctrl = ctrl->in(0)->in(0); - phase->set_ctrl(barrier, ctrl); - } else if (ctrl->is_CallRuntime()) { - assert(is_dominator(phase->get_ctrl(pre_val), ctrl->in(0), pre_val, ctrl, phase), "can't move"); - ctrl = ctrl->in(0); - phase->set_ctrl(barrier, ctrl); - } - - Node* init_ctrl = ctrl; - IdealLoopTree* loop = phase->get_loop(ctrl); - Node* raw_mem = fixer.find_mem(ctrl, barrier); - Node* init_raw_mem = raw_mem; - Node* raw_mem_for_ctrl = fixer.find_mem(ctrl, nullptr); - Node* heap_stable_ctrl = nullptr; - Node* null_ctrl = nullptr; - uint last = phase->C->unique(); - - enum { _heap_stable = 1, _heap_unstable, PATH_LIMIT }; - Node* region = new RegionNode(PATH_LIMIT); - Node* phi = PhiNode::make(region, raw_mem, Type::MEMORY, TypeRawPtr::BOTTOM); - - enum { _fast_path = 1, _slow_path, _null_path, PATH_LIMIT2 }; - Node* region2 = new RegionNode(PATH_LIMIT2); - Node* phi2 = PhiNode::make(region2, raw_mem, Type::MEMORY, TypeRawPtr::BOTTOM); - - // Stable path. - test_gc_state(ctrl, raw_mem, heap_stable_ctrl, phase, ShenandoahHeap::MARKING); - region->init_req(_heap_stable, heap_stable_ctrl); - phi->init_req(_heap_stable, raw_mem); - - // Null path - Node* reg2_ctrl = nullptr; - test_null(ctrl, pre_val, null_ctrl, phase); - if (null_ctrl != nullptr) { - reg2_ctrl = null_ctrl->in(0); - region2->init_req(_null_path, null_ctrl); - phi2->init_req(_null_path, raw_mem); - } else { - region2->del_req(_null_path); - phi2->del_req(_null_path); - } - - const int index_offset = in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()); - const int buffer_offset = in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()); - Node* thread = new ThreadLocalNode(); - phase->register_new_node(thread, ctrl); - Node* buffer_adr = new AddPNode(phase->C->top(), thread, phase->igvn().MakeConX(buffer_offset)); - phase->register_new_node(buffer_adr, ctrl); - Node* index_adr = new AddPNode(phase->C->top(), thread, phase->igvn().MakeConX(index_offset)); - phase->register_new_node(index_adr, ctrl); - - BasicType index_bt = TypeX_X->basic_type(); - assert(sizeof(size_t) == type2aelembytes(index_bt), "Loading Shenandoah SATBMarkQueue::_index with wrong size."); - const TypePtr* adr_type = TypeRawPtr::BOTTOM; - Node* index = new LoadXNode(ctrl, raw_mem, index_adr, adr_type, TypeX_X, MemNode::unordered); - phase->register_new_node(index, ctrl); - Node* index_cmp = new CmpXNode(index, phase->igvn().MakeConX(0)); - phase->register_new_node(index_cmp, ctrl); - Node* index_test = new BoolNode(index_cmp, BoolTest::ne); - phase->register_new_node(index_test, ctrl); - IfNode* queue_full_iff = new IfNode(ctrl, index_test, PROB_LIKELY(0.999), COUNT_UNKNOWN); - if (reg2_ctrl == nullptr) reg2_ctrl = queue_full_iff; - phase->register_control(queue_full_iff, loop, ctrl); - Node* not_full = new IfTrueNode(queue_full_iff); - phase->register_control(not_full, loop, queue_full_iff); - Node* full = new IfFalseNode(queue_full_iff); - phase->register_control(full, loop, queue_full_iff); - - ctrl = not_full; - - Node* next_index = new SubXNode(index, phase->igvn().MakeConX(sizeof(intptr_t))); - phase->register_new_node(next_index, ctrl); - - Node* buffer = new LoadPNode(ctrl, raw_mem, buffer_adr, adr_type, TypeRawPtr::NOTNULL, MemNode::unordered); - phase->register_new_node(buffer, ctrl); - Node *log_addr = new AddPNode(phase->C->top(), buffer, next_index); - phase->register_new_node(log_addr, ctrl); - Node* log_store = new StorePNode(ctrl, raw_mem, log_addr, adr_type, pre_val, MemNode::unordered); - phase->register_new_node(log_store, ctrl); - // update the index - Node* index_update = new StoreXNode(ctrl, log_store, index_adr, adr_type, next_index, MemNode::unordered); - phase->register_new_node(index_update, ctrl); - - // Fast-path case - region2->init_req(_fast_path, ctrl); - phi2->init_req(_fast_path, index_update); - - ctrl = full; - - Node* base = find_bottom_mem(ctrl, phase); - - MergeMemNode* mm = MergeMemNode::make(base); - mm->set_memory_at(Compile::AliasIdxRaw, raw_mem); - phase->register_new_node(mm, ctrl); - - Node* call = new CallLeafNode(ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type(), CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), "shenandoah_wb_pre", TypeRawPtr::BOTTOM); - call->init_req(TypeFunc::Control, ctrl); - call->init_req(TypeFunc::I_O, phase->C->top()); - call->init_req(TypeFunc::Memory, mm); - call->init_req(TypeFunc::FramePtr, phase->C->top()); - call->init_req(TypeFunc::ReturnAdr, phase->C->top()); - call->init_req(TypeFunc::Parms, pre_val); - call->init_req(TypeFunc::Parms+1, thread); - phase->register_control(call, loop, ctrl); - - Node* ctrl_proj = new ProjNode(call, TypeFunc::Control); - phase->register_control(ctrl_proj, loop, call); - Node* mem_proj = new ProjNode(call, TypeFunc::Memory); - phase->register_new_node(mem_proj, call); - - // Slow-path case - region2->init_req(_slow_path, ctrl_proj); - phi2->init_req(_slow_path, mem_proj); - - phase->register_control(region2, loop, reg2_ctrl); - phase->register_new_node(phi2, region2); - - region->init_req(_heap_unstable, region2); - phi->init_req(_heap_unstable, phi2); - - phase->register_control(region, loop, heap_stable_ctrl->in(0)); - phase->register_new_node(phi, region); - - fix_ctrl(barrier, region, fixer, uses, uses_to_ignore, last, phase); - for(uint next = 0; next < uses.size(); next++ ) { - Node *n = uses.at(next); - assert(phase->get_ctrl(n) == init_ctrl, "bad control"); - assert(n != init_raw_mem, "should leave input raw mem above the barrier"); - phase->set_ctrl(n, region); - follow_barrier_uses(n, init_ctrl, uses, phase); - } - fixer.fix_mem(init_ctrl, region, init_raw_mem, raw_mem_for_ctrl, phi, uses); - - phase->igvn().replace_node(barrier, pre_val); - } - assert(state->iu_barriers_count() == 0, "all enqueue barrier nodes should have been replaced"); - } Node* ShenandoahBarrierC2Support::get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* in) { @@ -1662,8 +1478,6 @@ Node* ShenandoahBarrierC2Support::get_load_addr(PhaseIdealLoop* phase, VectorSet } case Op_ShenandoahLoadReferenceBarrier: return get_load_addr(phase, visited, in->in(ShenandoahLoadReferenceBarrierNode::ValueIn)); - case Op_ShenandoahIUBarrier: - return get_load_addr(phase, visited, in->in(1)); case Op_CallDynamicJava: case Op_CallLeaf: case Op_CallStaticJava: @@ -1911,126 +1725,6 @@ void ShenandoahBarrierC2Support::optimize_after_expansion(VectorSet &visited, No } } -ShenandoahIUBarrierNode::ShenandoahIUBarrierNode(Node* val) : Node(nullptr, val) { - ShenandoahBarrierSetC2::bsc2()->state()->add_iu_barrier(this); -} - -const Type* ShenandoahIUBarrierNode::bottom_type() const { - if (in(1) == nullptr || in(1)->is_top()) { - return Type::TOP; - } - const Type* t = in(1)->bottom_type(); - if (t == TypePtr::NULL_PTR) { - return t; - } - return t->is_oopptr(); -} - -const Type* ShenandoahIUBarrierNode::Value(PhaseGVN* phase) const { - if (in(1) == nullptr) { - return Type::TOP; - } - const Type* t = phase->type(in(1)); - if (t == Type::TOP) { - return Type::TOP; - } - if (t == TypePtr::NULL_PTR) { - return t; - } - return t->is_oopptr(); -} - -int ShenandoahIUBarrierNode::needed(Node* n) { - if (n == nullptr || - n->is_Allocate() || - n->Opcode() == Op_ShenandoahIUBarrier || - n->bottom_type() == TypePtr::NULL_PTR || - (n->bottom_type()->make_oopptr() != nullptr && n->bottom_type()->make_oopptr()->const_oop() != nullptr)) { - return NotNeeded; - } - if (n->is_Phi() || - n->is_CMove()) { - return MaybeNeeded; - } - return Needed; -} - -Node* ShenandoahIUBarrierNode::next(Node* n) { - for (;;) { - if (n == nullptr) { - return n; - } else if (n->bottom_type() == TypePtr::NULL_PTR) { - return n; - } else if (n->bottom_type()->make_oopptr() != nullptr && n->bottom_type()->make_oopptr()->const_oop() != nullptr) { - return n; - } else if (n->is_ConstraintCast() || - n->Opcode() == Op_DecodeN || - n->Opcode() == Op_EncodeP) { - n = n->in(1); - } else if (n->is_Proj()) { - n = n->in(0); - } else { - return n; - } - } - ShouldNotReachHere(); - return nullptr; -} - -Node* ShenandoahIUBarrierNode::Identity(PhaseGVN* phase) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - - Node* n = next(in(1)); - - int cont = needed(n); - - if (cont == NotNeeded) { - return in(1); - } else if (cont == MaybeNeeded) { - if (igvn == nullptr) { - phase->record_for_igvn(this); - return this; - } else { - ResourceMark rm; - Unique_Node_List wq; - uint wq_i = 0; - - for (;;) { - if (n->is_Phi()) { - for (uint i = 1; i < n->req(); i++) { - Node* m = n->in(i); - if (m != nullptr) { - wq.push(m); - } - } - } else { - assert(n->is_CMove(), "nothing else here"); - Node* m = n->in(CMoveNode::IfFalse); - wq.push(m); - m = n->in(CMoveNode::IfTrue); - wq.push(m); - } - Node* orig_n = nullptr; - do { - if (wq_i >= wq.size()) { - return in(1); - } - n = wq.at(wq_i); - wq_i++; - orig_n = n; - n = next(n); - cont = needed(n); - if (cont == Needed) { - return this; - } - } while (cont != MaybeNeeded || (orig_n != n && wq.member(n))); - } - } - } - - return this; -} - #ifdef ASSERT static bool has_never_branch(Node* root) { for (uint i = 1; i < root->req(); i++) { @@ -3014,8 +2708,6 @@ bool ShenandoahLoadReferenceBarrierNode::needs_barrier_impl(PhaseGVN* phase, Nod case Op_CMoveP: return needs_barrier_impl(phase, n->in(2), visited) || needs_barrier_impl(phase, n->in(3), visited); - case Op_ShenandoahIUBarrier: - return needs_barrier_impl(phase, n->in(1), visited); case Op_CreateEx: return false; default: diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp index 7a6ed74f56393..164502e6358f9 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp @@ -87,23 +87,6 @@ class ShenandoahBarrierC2Support : public AllStatic { #endif }; -class ShenandoahIUBarrierNode : public Node { -public: - ShenandoahIUBarrierNode(Node* val); - - const Type *bottom_type() const; - const Type* Value(PhaseGVN* phase) const; - Node* Identity(PhaseGVN* phase); - - int Opcode() const; - -private: - enum { Needed, NotNeeded, MaybeNeeded }; - - static int needed(Node* n); - static Node* next(Node* n); -}; - class MemoryGraphFixer : public ResourceObj { private: Node_List _memory_nodes; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 331e98b1e85bf..0bcb236ccbd80 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -242,7 +242,7 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() { double avg_cycle_time = _gc_time_history->davg() + (_margin_of_error_sd * _gc_time_history->dsd()); double avg_alloc_rate = _allocation_rate.upper_bound(_margin_of_error_sd); - if (avg_cycle_time > allocation_headroom / avg_alloc_rate) { + if (avg_cycle_time * avg_alloc_rate > allocation_headroom) { log_info(gc)("Trigger: Average GC time (%.2f ms) is above the time for average allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (margin of error = %.2f)", avg_cycle_time * 1000, byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate), diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp deleted file mode 100644 index 7a1cf1bbbdf9a..0000000000000 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, 2022, Red Hat, Inc. All rights reserved. - * 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 "precompiled.hpp" -#include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahIUMode.hpp" -#include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "logging/log.hpp" -#include "logging/logTag.hpp" -#include "runtime/globals_extension.hpp" -#include "runtime/java.hpp" - -void ShenandoahIUMode::initialize_flags() const { - if (FLAG_IS_CMDLINE(ClassUnloadingWithConcurrentMark) && ClassUnloading) { - log_warning(gc)("Shenandoah I-U mode sets -XX:-ClassUnloadingWithConcurrentMark; see JDK-8261341 for details"); - } - FLAG_SET_DEFAULT(ClassUnloadingWithConcurrentMark, false); - - if (ClassUnloading) { - FLAG_SET_DEFAULT(VerifyBeforeExit, false); - } - - if (FLAG_IS_DEFAULT(ShenandoahIUBarrier)) { - FLAG_SET_DEFAULT(ShenandoahIUBarrier, true); - } - if (FLAG_IS_DEFAULT(ShenandoahSATBBarrier)) { - FLAG_SET_DEFAULT(ShenandoahSATBBarrier, false); - } - - SHENANDOAH_ERGO_ENABLE_FLAG(ExplicitGCInvokesConcurrent); - SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahImplicitGCInvokesConcurrent); - - // Final configuration checks - SHENANDOAH_CHECK_FLAG_SET(ShenandoahLoadRefBarrier); - SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahSATBBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahIUBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahStackWatermarkBarrier); -} - -ShenandoahHeuristics* ShenandoahIUMode::initialize_heuristics() const { - if (ShenandoahGCHeuristics == nullptr) { - vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option (null)"); - } - ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (strcmp(ShenandoahGCHeuristics, "aggressive") == 0) { - return new ShenandoahAggressiveHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "static") == 0) { - return new ShenandoahStaticHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "adaptive") == 0) { - return new ShenandoahAdaptiveHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "compact") == 0) { - return new ShenandoahCompactHeuristics(heap); - } - vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option"); - return nullptr; -} diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp index 39eb475a75def..9b83df5424dae 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp @@ -47,7 +47,6 @@ void ShenandoahPassiveMode::initialize_flags() const { // Disable known barriers by default. SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahLoadRefBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahSATBBarrier); - SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahIUBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCASBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCloneBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahStackWatermarkBarrier); diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp index 2a4560126568c..c3ea11cf71e2a 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp @@ -44,7 +44,6 @@ void ShenandoahSATBMode::initialize_flags() const { // Final configuration checks SHENANDOAH_CHECK_FLAG_SET(ShenandoahLoadRefBarrier); - SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahIUBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahSATBBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index d9b28c1bb4d3c..bcc370eeb314d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -48,7 +48,6 @@ void ShenandoahArguments::initialize() { FLAG_SET_DEFAULT(ShenandoahSATBBarrier, false); FLAG_SET_DEFAULT(ShenandoahLoadRefBarrier, false); - FLAG_SET_DEFAULT(ShenandoahIUBarrier, false); FLAG_SET_DEFAULT(ShenandoahCASBarrier, false); FLAG_SET_DEFAULT(ShenandoahCloneBarrier, false); @@ -142,7 +141,6 @@ void ShenandoahArguments::initialize() { if (ShenandoahVerifyOptoBarriers && (!FLAG_IS_DEFAULT(ShenandoahSATBBarrier) || !FLAG_IS_DEFAULT(ShenandoahLoadRefBarrier) || - !FLAG_IS_DEFAULT(ShenandoahIUBarrier) || !FLAG_IS_DEFAULT(ShenandoahCASBarrier) || !FLAG_IS_DEFAULT(ShenandoahCloneBarrier) )) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index beb4a1d289239..5215aa749aea1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -250,6 +250,25 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* file, line); } } + + // Do additional checks for special objects: their fields can hold metadata as well. + // We want to check class loading/unloading did not corrupt them. + + if (java_lang_Class::is_instance(obj)) { + Metadata* klass = obj->metadata_field(java_lang_Class::klass_offset()); + if (klass != nullptr && !Metaspace::contains(klass)) { + print_failure(_safe_all, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Instance class mirror should point to Metaspace", + file, line); + } + + Metadata* array_klass = obj->metadata_field(java_lang_Class::array_klass_offset()); + if (array_klass != nullptr && !Metaspace::contains(array_klass)) { + print_failure(_safe_all, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Array class mirror should point to Metaspace", + file, line); + } + } } void ShenandoahAsserts::assert_in_correct_region(void* interior_loc, oop obj, const char* file, int line) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index d2857daccf6f3..40b6d70ce454f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -138,7 +138,7 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { } void ShenandoahBarrierSet::clone_barrier_runtime(oop src) { - if (_heap->has_forwarded_objects() || (ShenandoahIUBarrier && _heap->is_concurrent_mark_in_progress())) { + if (_heap->has_forwarded_objects()) { clone_barrier(src); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index beb6ae492b807..e116e0225db18 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -89,7 +89,6 @@ class ShenandoahBarrierSet: public BarrierSet { template inline void satb_barrier(T* field); inline void satb_enqueue(oop value); - inline void iu_barrier(oop obj); inline void keep_alive_if_weak(DecoratorSet decorators, oop value); @@ -120,7 +119,6 @@ class ShenandoahBarrierSet: public BarrierSet { template inline void arraycopy_update(T* src, size_t count); - inline void clone_marking(oop src); inline void clone_evacuation(oop src); inline void clone_update(oop src); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index bfb69c38c332c..80735f453c63d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -164,12 +164,6 @@ inline void ShenandoahBarrierSet::satb_enqueue(oop value) { } } -inline void ShenandoahBarrierSet::iu_barrier(oop obj) { - if (ShenandoahIUBarrier && obj != nullptr && _heap->is_concurrent_mark_in_progress()) { - enqueue(obj); - } -} - inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oop value) { assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Reference strength must be known"); const bool on_strong_oop_ref = (decorators & ON_STRONG_OOP_REF) != 0; @@ -189,7 +183,6 @@ inline oop ShenandoahBarrierSet::oop_load(DecoratorSet decorators, T* addr) { template inline oop ShenandoahBarrierSet::oop_cmpxchg(DecoratorSet decorators, T* addr, oop compare_value, oop new_value) { - iu_barrier(new_value); oop res; oop expected = compare_value; do { @@ -207,7 +200,6 @@ inline oop ShenandoahBarrierSet::oop_cmpxchg(DecoratorSet decorators, T* addr, o template inline oop ShenandoahBarrierSet::oop_xchg(DecoratorSet decorators, T* addr, oop new_value) { - iu_barrier(new_value); oop previous = RawAccess<>::oop_atomic_xchg(addr, new_value); // Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway, // because it must be the previous value. @@ -245,7 +237,6 @@ inline void ShenandoahBarrierSet::AccessBarrier::oop_st shenandoah_assert_marked_if(nullptr, value, !CompressedOops::is_null(value) && ShenandoahHeap::heap()->is_evacuation_in_progress()); shenandoah_assert_not_in_cset_if(addr, value, value != nullptr && !ShenandoahHeap::heap()->cancelled_gc()); ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set(); - bs->iu_barrier(value); bs->satb_barrier(addr); Raw::oop_store(addr, value); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp index 13371f5e194d9..3b7bf9864deb0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp @@ -74,15 +74,6 @@ class ShenandoahUpdateRefsForOopClosure: public BasicOopIterateClosure { virtual void do_oop(narrowOop* p) { do_oop_work(p); } }; -void ShenandoahBarrierSet::clone_marking(oop obj) { - assert(_heap->is_concurrent_mark_in_progress(), "only during marking"); - assert(ShenandoahIUBarrier, "only with incremental-update"); - if (!_heap->marking_context()->allocated_after_mark_start(obj)) { - ShenandoahUpdateRefsForOopClosure cl; - obj->oop_iterate(&cl); - } -} - void ShenandoahBarrierSet::clone_evacuation(oop obj) { assert(_heap->is_evacuation_in_progress(), "only during evacuation"); if (need_bulk_update(cast_from_oop(obj))) { @@ -105,9 +96,7 @@ void ShenandoahBarrierSet::clone_barrier(oop obj) { shenandoah_assert_correct(nullptr, obj); int gc_state = _heap->gc_state(); - if ((gc_state & ShenandoahHeap::MARKING) != 0) { - clone_marking(obj); - } else if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { + if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { clone_evacuation(obj); } else { clone_update(obj); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index e61dca2bdbf6d..657cd681f4f64 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -103,11 +103,6 @@ class ShenandoahDisarmNMethodsTask : public WorkerTask { WorkerTask("Shenandoah Disarm NMethods"), _iterator(ShenandoahCodeRoots::table()) { assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint"); - _iterator.nmethods_do_begin(); - } - - ~ShenandoahDisarmNMethodsTask() { - _iterator.nmethods_do_end(); } virtual void work(uint worker_id) { @@ -175,13 +170,7 @@ class ShenandoahUnlinkTask : public WorkerTask { ShenandoahUnlinkTask(bool unloading_occurred) : WorkerTask("Shenandoah Unlink NMethods"), _cl(unloading_occurred), - _iterator(ShenandoahCodeRoots::table()) { - _iterator.nmethods_do_begin(); - } - - ~ShenandoahUnlinkTask() { - _iterator.nmethods_do_end(); - } + _iterator(ShenandoahCodeRoots::table()) {} virtual void work(uint worker_id) { _iterator.nmethods_do(&_cl); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index aea0af2457500..ac2e732052a28 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -767,16 +767,9 @@ class ShenandoahConcurrentWeakRootsEvacUpdateTask : public WorkerTask { _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/), _nmethod_itr(ShenandoahCodeRoots::table()), - _phase(phase) { - if (ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_begin(); - } - } + _phase(phase) {} ~ShenandoahConcurrentWeakRootsEvacUpdateTask() { - if (ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_end(); - } // Notify runtime data structures of potentially dead oops _vm_roots.report_num_dead(); } @@ -878,17 +871,7 @@ class ShenandoahConcurrentRootsEvacUpdateTask : public WorkerTask { _phase(phase), _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/), - _nmethod_itr(ShenandoahCodeRoots::table()) { - if (!ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_begin(); - } - } - - ~ShenandoahConcurrentRootsEvacUpdateTask() { - if (!ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_end(); - } - } + _nmethod_itr(ShenandoahCodeRoots::table()) {} void work(uint worker_id) { ShenandoahConcurrentWorkerSession worker_session(worker_id); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index fa37fcbcfd300..31157616e0ef8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -71,22 +71,14 @@ class ShenandoahConcurrentMarkingTask : public WorkerTask { class ShenandoahSATBAndRemarkThreadsClosure : public ThreadClosure { private: SATBMarkQueueSet& _satb_qset; - OopClosure* const _cl; public: - ShenandoahSATBAndRemarkThreadsClosure(SATBMarkQueueSet& satb_qset, OopClosure* cl) : - _satb_qset(satb_qset), - _cl(cl) {} + explicit ShenandoahSATBAndRemarkThreadsClosure(SATBMarkQueueSet& satb_qset) : + _satb_qset(satb_qset) {} - void do_thread(Thread* thread) { + void do_thread(Thread* thread) override { // Transfer any partial buffer to the qset for completed buffer processing. _satb_qset.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(thread)); - if (thread->is_Java_thread()) { - if (_cl != nullptr) { - ResourceMark rm; - thread->oops_do(_cl, nullptr); - } - } } }; @@ -118,9 +110,7 @@ class ShenandoahFinalMarkingTask : public WorkerTask { while (satb_mq_set.apply_closure_to_completed_buffer(&cl)) {} assert(!heap->has_forwarded_objects(), "Not expected"); - ShenandoahMarkRefsClosure mark_cl(q, rp); - ShenandoahSATBAndRemarkThreadsClosure tc(satb_mq_set, - ShenandoahIUBarrier ? &mark_cl : nullptr); + ShenandoahSATBAndRemarkThreadsClosure tc(satb_mq_set); Threads::possibly_parallel_threads_do(true /* is_par */, &tc); } _cm->mark_loop(worker_id, _terminator, rp, GENERATION, false /*not cancellable*/, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp index 2808f30b44459..6c28ff4e969c7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp @@ -22,8 +22,8 @@ * */ -#ifndef LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP -#define LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP #include "gc/shared/gcCause.hpp" #include "gc/shared/concurrentGCThread.hpp" @@ -102,4 +102,4 @@ class ShenandoahController: public ConcurrentGCThread { size_t get_gc_id(); void update_gc_id(); }; -#endif //LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 725cfc85da645..b47bb109031c8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -48,19 +48,19 @@ static const char* partition_name(ShenandoahFreeSetPartitionId t) { #ifndef PRODUCT void ShenandoahRegionPartitions::dump_bitmap() const { - log_info(gc)("Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "], Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", - _leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)], - _rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)], - _leftmosts[int(ShenandoahFreeSetPartitionId::Collector)], - _rightmosts[int(ShenandoahFreeSetPartitionId::Collector)]); - log_info(gc)("Empty Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT - "], Empty Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", - _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], - _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], - _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], - _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)]); - - log_info(gc)("%6s: %18s %18s %18s", "index", "Mutator Bits", "Collector Bits", "NotFree Bits"); + log_debug(gc)("Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "], Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)], + _rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)], + _leftmosts[int(ShenandoahFreeSetPartitionId::Collector)], + _rightmosts[int(ShenandoahFreeSetPartitionId::Collector)]); + log_debug(gc)("Empty Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT + "], Empty Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)]); + + log_debug(gc)("%6s: %18s %18s %18s", "index", "Mutator Bits", "Collector Bits", "NotFree Bits"); dump_bitmap_range(0, _max-1); } @@ -83,8 +83,8 @@ void ShenandoahRegionPartitions::dump_bitmap_row(idx_t region_idx) const { uintx collector_bits = _membership[int(ShenandoahFreeSetPartitionId::Collector)].bits_at(aligned_idx); uintx free_bits = mutator_bits | collector_bits; uintx notfree_bits = ~free_bits; - log_info(gc)(SSIZE_FORMAT_W(6) ": " SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0, - aligned_idx, mutator_bits, collector_bits, notfree_bits); + log_debug(gc)(SSIZE_FORMAT_W(6) ": " SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0, + aligned_idx, mutator_bits, collector_bits, notfree_bits); } #endif @@ -1060,8 +1060,8 @@ void ShenandoahFreeSet::move_regions_from_collector_to_mutator(size_t max_xfer_r } size_t collector_xfer = collector_empty_xfer + collector_not_empty_xfer; - log_info(gc)("At start of update refs, moving " SIZE_FORMAT "%s to Mutator free partition from Collector Reserve", - byte_size_in_proper_unit(collector_xfer), proper_unit_for_byte_size(collector_xfer)); + log_info(gc, ergo)("At start of update refs, moving " SIZE_FORMAT "%s to Mutator free partition from Collector Reserve", + byte_size_in_proper_unit(collector_xfer), proper_unit_for_byte_size(collector_xfer)); } void ShenandoahFreeSet::prepare_to_rebuild(size_t &cset_regions) { @@ -1351,7 +1351,6 @@ void ShenandoahFreeSet::print_on(outputStream* out) const { double ShenandoahFreeSet::internal_fragmentation() { double squared = 0; double linear = 0; - int count = 0; idx_t rightmost = _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator); for (idx_t index = _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator); index <= rightmost; ) { @@ -1361,11 +1360,10 @@ double ShenandoahFreeSet::internal_fragmentation() { size_t used = r->used(); squared += used * used; linear += used; - count++; index = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Mutator, index + 1); } - if (count > 0) { + if (linear > 0) { double s = squared / (ShenandoahHeapRegion::region_size_bytes() * linear); return 1 - s; } else { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 5c5b6f7ebe58d..eb6de9719dc62 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -66,7 +66,6 @@ #include "gc/shenandoah/shenandoahVMOperations.hpp" #include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" -#include "gc/shenandoah/mode/shenandoahIUMode.hpp" #include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" #include "gc/shenandoah/mode/shenandoahSATBMode.hpp" #if INCLUDE_JFR @@ -446,8 +445,6 @@ void ShenandoahHeap::initialize_mode() { if (ShenandoahGCMode != nullptr) { if (strcmp(ShenandoahGCMode, "satb") == 0) { _gc_mode = new ShenandoahSATBMode(); - } else if (strcmp(ShenandoahGCMode, "iu") == 0) { - _gc_mode = new ShenandoahIUMode(); } else if (strcmp(ShenandoahGCMode, "passive") == 0) { _gc_mode = new ShenandoahPassiveMode(); } else { @@ -1178,11 +1175,10 @@ oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { // Try to install the new forwarding pointer. oop copy_val = cast_to_oop(copy); - ContinuationGCSupport::relativize_stack_chunk(copy_val); - oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val); if (result == copy_val) { // Successfully evacuated. Our copy is now the public one! + ContinuationGCSupport::relativize_stack_chunk(copy_val); shenandoah_assert_correct(nullptr, copy_val); return copy_val; } else { @@ -1694,19 +1690,20 @@ class ShenandoahParallelHeapRegionTask : public WorkerTask { private: ShenandoahHeap* const _heap; ShenandoahHeapRegionClosure* const _blk; + size_t const _stride; shenandoah_padding(0); volatile size_t _index; shenandoah_padding(1); public: - ShenandoahParallelHeapRegionTask(ShenandoahHeapRegionClosure* blk) : + ShenandoahParallelHeapRegionTask(ShenandoahHeapRegionClosure* blk, size_t stride) : WorkerTask("Shenandoah Parallel Region Operation"), - _heap(ShenandoahHeap::heap()), _blk(blk), _index(0) {} + _heap(ShenandoahHeap::heap()), _blk(blk), _stride(stride), _index(0) {} void work(uint worker_id) { ShenandoahParallelWorkerSession worker_session(worker_id); - size_t stride = ShenandoahParallelRegionStride; + size_t stride = _stride; size_t max = _heap->num_regions(); while (Atomic::load(&_index) < max) { @@ -1725,8 +1722,20 @@ class ShenandoahParallelHeapRegionTask : public WorkerTask { void ShenandoahHeap::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* blk) const { assert(blk->is_thread_safe(), "Only thread-safe closures here"); - if (num_regions() > ShenandoahParallelRegionStride) { - ShenandoahParallelHeapRegionTask task(blk); + const uint active_workers = workers()->active_workers(); + const size_t n_regions = num_regions(); + size_t stride = ShenandoahParallelRegionStride; + if (stride == 0 && active_workers > 1) { + // Automatically derive the stride to balance the work between threads + // evenly. Do not try to split work if below the reasonable threshold. + constexpr size_t threshold = 4096; + stride = n_regions <= threshold ? + threshold : + (n_regions + active_workers - 1) / active_workers; + } + + if (n_regions > stride && active_workers > 1) { + ShenandoahParallelHeapRegionTask task(blk, stride); workers()->run_task(&task); } else { heap_region_iterate(blk); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp index 40f48bae6f55a..06ea6a8e232b8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP #include "memory/memRegion.hpp" #include "runtime/atomic.hpp" @@ -176,4 +176,4 @@ class ShenandoahMarkBitMap { }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp index 62c0eaea139a2..bf9261f47ac5c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP #include "gc/shenandoah/shenandoahMarkBitMap.hpp" @@ -205,4 +205,4 @@ inline void ShenandoahMarkBitMap::clear_range_of_words(idx_t beg, idx_t end) { } -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 54efd658eafdf..5eb0b277b5ef8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -31,6 +31,7 @@ #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/continuation.hpp" +#include "runtime/safepointVerifiers.hpp" ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray& oops, bool non_immediate_oops) : _nm(nm), _oops(nullptr), _oops_count(0), _unregistered(false), _lock(), _ic_lock() { @@ -475,21 +476,40 @@ void ShenandoahNMethodTableSnapshot::concurrent_nmethods_do(NMethodClosure* cl) } ShenandoahConcurrentNMethodIterator::ShenandoahConcurrentNMethodIterator(ShenandoahNMethodTable* table) : - _table(table), _table_snapshot(nullptr) { -} - -void ShenandoahConcurrentNMethodIterator::nmethods_do_begin() { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _table_snapshot = _table->snapshot_for_iteration(); -} + _table(table), + _table_snapshot(nullptr), + _started_workers(0), + _finished_workers(0) {} void ShenandoahConcurrentNMethodIterator::nmethods_do(NMethodClosure* cl) { - assert(_table_snapshot != nullptr, "Must first call nmethod_do_begin()"); - _table_snapshot->concurrent_nmethods_do(cl); -} + // Cannot safepoint when iteration is running, because this can cause deadlocks + // with other threads waiting on iteration to be over. + NoSafepointVerifier nsv; -void ShenandoahConcurrentNMethodIterator::nmethods_do_end() { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _table->finish_iteration(_table_snapshot); - CodeCache_lock->notify_all(); + MutexLocker ml(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + if (_finished_workers > 0) { + // Some threads have already finished. We are now in rampdown: we are now + // waiting for all currently recorded workers to finish. No new workers + // should start. + return; + } + + // Record a new worker and initialize the snapshot if it is a first visitor. + if (_started_workers++ == 0) { + _table_snapshot = _table->snapshot_for_iteration(); + } + + // All set, relinquish the lock and go concurrent. + { + MutexUnlocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + _table_snapshot->concurrent_nmethods_do(cl); + } + + // Record completion. Last worker shuts down the iterator and notifies any waiters. + uint count = ++_finished_workers; + if (count == _started_workers) { + _table->finish_iteration(_table_snapshot); + CodeCache_lock->notify_all(); + } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index 4e05d254db30a..c15953965001f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -181,13 +181,13 @@ class ShenandoahConcurrentNMethodIterator { private: ShenandoahNMethodTable* const _table; ShenandoahNMethodTableSnapshot* _table_snapshot; + uint _started_workers; + uint _finished_workers; public: ShenandoahConcurrentNMethodIterator(ShenandoahNMethodTable* table); - void nmethods_do_begin(); void nmethods_do(NMethodClosure* cl); - void nmethods_do_end(); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHNMETHOD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp index c4ecf8d359a17..fa5235517907e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP #include "gc/shared/referenceDiscoverer.hpp" #include "gc/shared/referencePolicy.hpp" @@ -188,4 +188,4 @@ class ShenandoahReferenceProcessor : public ReferenceDiscoverer { void abandon_partial_discovery(); }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index f67cafdb8fe7c..694736cea426c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -212,6 +212,21 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { fwd_reg = obj_reg; } + // Do additional checks for special objects: their fields can hold metadata as well. + // We want to check class loading/unloading did not corrupt them. + + if (java_lang_Class::is_instance(obj)) { + Metadata* klass = obj->metadata_field(java_lang_Class::klass_offset()); + check(ShenandoahAsserts::_safe_oop, obj, + klass == nullptr || Metaspace::contains(klass), + "Instance class mirror should point to Metaspace"); + + Metadata* array_klass = obj->metadata_field(java_lang_Class::array_klass_offset()); + check(ShenandoahAsserts::_safe_oop, obj, + array_klass == nullptr || Metaspace::contains(array_klass), + "Array class mirror should point to Metaspace"); + } + // ------------ obj and fwd are safe at this point -------------- switch (_options._verify_marked) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index b8baf6b3ebf43..87702afe98e14 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -318,9 +318,10 @@ "checking for cancellation, yielding, etc. Larger values improve "\ "marking performance at expense of responsiveness.") \ \ - product(uintx, ShenandoahParallelRegionStride, 1024, EXPERIMENTAL, \ + product(uintx, ShenandoahParallelRegionStride, 0, EXPERIMENTAL, \ "How many regions to process at once during parallel region " \ - "iteration. Affects heaps with lots of regions.") \ + "iteration. Affects heaps with lots of regions. " \ + "Set to 0 to let Shenandoah to decide the best value.") \ \ product(size_t, ShenandoahSATBBufferSize, 1 * K, EXPERIMENTAL, \ "Number of entries in an SATB log buffer.") \ @@ -333,9 +334,6 @@ product(bool, ShenandoahSATBBarrier, true, DIAGNOSTIC, \ "Turn on/off SATB barriers in Shenandoah") \ \ - product(bool, ShenandoahIUBarrier, false, DIAGNOSTIC, \ - "Turn on/off I-U barriers barriers in Shenandoah") \ - \ product(bool, ShenandoahCASBarrier, true, DIAGNOSTIC, \ "Turn on/off CAS barriers in Shenandoah") \ \ diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index dbec45fd96ea9..f72e84eaf5935 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -327,7 +327,7 @@ int ZBarrierSetC2::estimate_stub_size() const { int size = 0; for (int i = 0; i < stubs->length(); i++) { - CodeBuffer cb(blob->content_begin(), (address)C->output()->scratch_locs_memory() - blob->content_begin()); + CodeBuffer cb(blob->content_begin(), checked_cast((address)C->output()->scratch_locs_memory() - blob->content_begin())); MacroAssembler masm(&cb); stubs->at(i)->emit_code(masm); size += cb.insts_size(); diff --git a/src/hotspot/share/gc/z/zAddress.cpp b/src/hotspot/share/gc/z/zAddress.cpp index d1c199fad070c..1cd33e44a05d1 100644 --- a/src/hotspot/share/gc/z/zAddress.cpp +++ b/src/hotspot/share/gc/z/zAddress.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,9 +93,7 @@ static void initialize_check_oop_function() { #ifdef CHECK_UNHANDLED_OOPS if (ZVerifyOops) { // Enable extra verification of usages of oops in oopsHierarchy.hpp - check_oop_function = [](oopDesc* obj) { - (void)to_zaddress(obj); - }; + check_oop_function = &check_is_valid_zaddress; } #endif } diff --git a/src/hotspot/share/gc/z/zAddress.inline.hpp b/src/hotspot/share/gc/z/zAddress.inline.hpp index 4adbf50bc86c6..bbc92a7e2aaf3 100644 --- a/src/hotspot/share/gc/z/zAddress.inline.hpp +++ b/src/hotspot/share/gc/z/zAddress.inline.hpp @@ -333,10 +333,22 @@ inline void dereferenceable_test(zaddress addr) { } #endif -inline zaddress to_zaddress(uintptr_t value) { - const zaddress addr = zaddress(value); +inline void check_is_valid_zaddress(zaddress addr) { assert_is_valid(addr); DEBUG_ONLY(dereferenceable_test(addr)); +} + +inline void check_is_valid_zaddress(uintptr_t value) { + check_is_valid_zaddress(zaddress(value)); +} + +inline void check_is_valid_zaddress(oopDesc* o) { + check_is_valid_zaddress(uintptr_t(o)); +} + +inline zaddress to_zaddress(uintptr_t value) { + const zaddress addr = zaddress(value); + check_is_valid_zaddress(addr); return addr; } @@ -344,7 +356,7 @@ inline zaddress to_zaddress(oopDesc* o) { return to_zaddress(uintptr_t(o)); } -inline oop to_oop(zaddress addr) { +inline void assert_is_oop_or_null(zaddress addr) { const oop obj = cast_to_oop(addr); assert(!ZVerifyOops || oopDesc::is_oop_or_null(obj), "Broken oop: " PTR_FORMAT " [" PTR_FORMAT " " PTR_FORMAT " " PTR_FORMAT " " PTR_FORMAT "]", p2i(obj), @@ -352,7 +364,16 @@ inline oop to_oop(zaddress addr) { *(uintptr_t*)(untype(addr) + 0x08), *(uintptr_t*)(untype(addr) + 0x10), *(uintptr_t*)(untype(addr) + 0x18)); - return obj; +} + +inline void assert_is_oop(zaddress addr) { + assert(!is_null(addr), "Should not be null"); + assert_is_oop_or_null(addr); +} + +inline oop to_oop(zaddress addr) { + assert_is_oop_or_null(addr); + return cast_to_oop(addr); } inline zaddress operator+(zaddress addr, size_t size) { @@ -378,7 +399,6 @@ inline void assert_is_valid(zaddress_unsafe addr) { DEBUG_ONLY(is_valid(addr, true /* assert_on_failure */);) } - inline uintptr_t untype(zaddress_unsafe addr) { return static_cast(addr); } diff --git a/src/hotspot/share/gc/z/zArguments.cpp b/src/hotspot/share/gc/z/zArguments.cpp index c71e59944f095..f3ff568c64d14 100644 --- a/src/hotspot/share/gc/z/zArguments.cpp +++ b/src/hotspot/share/gc/z/zArguments.cpp @@ -146,7 +146,7 @@ void ZArguments::initialize() { ZHeuristics::set_medium_page_size(); if (!FLAG_IS_DEFAULT(ZTenuringThreshold) && ZTenuringThreshold != -1) { - FLAG_SET_ERGO_IF_DEFAULT(MaxTenuringThreshold, ZTenuringThreshold); + FLAG_SET_ERGO_IF_DEFAULT(MaxTenuringThreshold, (uint)ZTenuringThreshold); if (MaxTenuringThreshold == 0) { FLAG_SET_ERGO_IF_DEFAULT(AlwaysTenure, true); } diff --git a/src/hotspot/share/gc/z/zArray.inline.hpp b/src/hotspot/share/gc/z/zArray.inline.hpp index 2ec87a761564e..ec7feda8d6398 100644 --- a/src/hotspot/share/gc/z/zArray.inline.hpp +++ b/src/hotspot/share/gc/z/zArray.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ inline ZArrayIteratorImpl::ZArrayIteratorImpl(const T* array, size_ template inline ZArrayIteratorImpl::ZArrayIteratorImpl(const ZArray* array) - : ZArrayIteratorImpl(array->is_empty() ? nullptr : array->adr_at(0), array->length()) {} + : ZArrayIteratorImpl(array->is_empty() ? nullptr : array->adr_at(0), (size_t)array->length()) {} template inline bool ZArrayIteratorImpl::next(T* elem) { diff --git a/src/hotspot/share/gc/z/zBarrier.cpp b/src/hotspot/share/gc/z/zBarrier.cpp index c4b6e0f8239e1..7d7f1284bdfdc 100644 --- a/src/hotspot/share/gc/z/zBarrier.cpp +++ b/src/hotspot/share/gc/z/zBarrier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -291,7 +291,7 @@ zaddress ZBarrier::keep_alive_slow_path(zaddress addr) { // ON_WEAK barriers should only ever be applied to j.l.r.Reference.referents. void ZBarrier::verify_on_weak(volatile zpointer* referent_addr) { if (referent_addr != nullptr) { - const uintptr_t base = (uintptr_t)referent_addr - java_lang_ref_Reference::referent_offset(); + const uintptr_t base = (uintptr_t)referent_addr - (size_t)java_lang_ref_Reference::referent_offset(); const oop obj = cast_to_oop(base); assert(oopDesc::is_oop(obj), "Verification failed for: ref " PTR_FORMAT " obj: " PTR_FORMAT, (uintptr_t)referent_addr, base); assert(java_lang_ref_Reference::is_referent_field(obj, java_lang_ref_Reference::referent_offset()), "Sanity"); diff --git a/src/hotspot/share/gc/z/zBarrier.inline.hpp b/src/hotspot/share/gc/z/zBarrier.inline.hpp index b3191e9ae3f7a..a3eb7a9ca67d6 100644 --- a/src/hotspot/share/gc/z/zBarrier.inline.hpp +++ b/src/hotspot/share/gc/z/zBarrier.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -745,7 +745,7 @@ inline void ZBarrier::mark_and_remember(volatile zpointer* p, zaddress addr) { template inline void ZBarrier::mark(zaddress addr) { - assert(!ZVerifyOops || oopDesc::is_oop(to_oop(addr), false), "must be oop"); + assert_is_oop(addr); if (ZHeap::heap()->is_old(addr)) { ZGeneration::old()->mark_object_if_active(addr); @@ -757,7 +757,7 @@ inline void ZBarrier::mark(zaddress addr) { template inline void ZBarrier::mark_young(zaddress addr) { assert(ZGeneration::young()->is_phase_mark(), "Should only be called during marking"); - assert(!ZVerifyOops || oopDesc::is_oop(to_oop(addr), false), "must be oop"); + assert_is_oop(addr); assert(ZHeap::heap()->is_young(addr), "Must be young"); ZGeneration::young()->mark_object(addr); diff --git a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp index d53b69345dd98..174cdfd9e909d 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -430,7 +430,7 @@ class ZLoadBarrierOopClosure : public BasicOopIterateClosure { template inline void ZBarrierSet::AccessBarrier::clone_in_heap(oop src, oop dst, size_t size) { - assert_is_valid(to_zaddress(src)); + check_is_valid_zaddress(src); if (dst->is_objArray()) { // Cloning an object array is similar to performing array copy. diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index b8ecc3eddd3cd..33894f166a3e9 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,7 +79,7 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { ZNMethod::nmethod_oops_do_inner(nm, &cl); const uintptr_t prev_color = ZNMethod::color(nm); - const uintptr_t new_color = *(int*)ZPointerStoreGoodMaskLowOrderBitsAddr; + const uintptr_t new_color = *ZPointerStoreGoodMaskLowOrderBitsAddr; log_develop_trace(gc, nmethod)("nmethod: " PTR_FORMAT " visited by entry (complete) [" PTR_FORMAT " -> " PTR_FORMAT "]", p2i(nm), prev_color, new_color); // CodeCache unloading support diff --git a/src/hotspot/share/gc/z/zCPU.inline.hpp b/src/hotspot/share/gc/z/zCPU.inline.hpp index 67d26f4c2e17c..b3712dc5ac98b 100644 --- a/src/hotspot/share/gc/z/zCPU.inline.hpp +++ b/src/hotspot/share/gc/z/zCPU.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ #include "utilities/debug.hpp" inline uint32_t ZCPU::count() { - return os::processor_count(); + return (uint32_t)os::processor_count(); } inline uint32_t ZCPU::id() { diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index 162e05f2c0763..3ba8698bd8e22 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -102,7 +102,7 @@ static double estimated_gc_workers(double serial_gc_time, double parallelizable_ } static uint discrete_young_gc_workers(double gc_workers) { - return clamp(ceil(gc_workers), 1, ZYoungGCThreads); + return clamp((uint)ceil(gc_workers), 1, ZYoungGCThreads); } static double select_young_gc_workers(const ZDirectorStats& stats, double serial_gc_time, double parallelizable_gc_time, double alloc_rate_sd_percent, double time_until_oom) { @@ -426,7 +426,7 @@ static bool rule_major_warmup(const ZDirectorStats& stats) { const size_t soft_max_capacity = stats._heap._soft_max_heap_size; const size_t used = stats._heap._used; const double used_threshold_percent = (stats._old_stats._cycle._nwarmup_cycles + 1) * 0.1; - const size_t used_threshold = soft_max_capacity * used_threshold_percent; + const size_t used_threshold = (size_t)(soft_max_capacity * used_threshold_percent); log_debug(gc, director)("Rule Major: Warmup %.0f%%, Used: " SIZE_FORMAT "MB, UsedThreshold: " SIZE_FORMAT "MB", used_threshold_percent * 100, used / M, used_threshold / M); @@ -497,13 +497,13 @@ static bool rule_major_allocation_rate(const ZDirectorStats& stats) { // Doing an old collection makes subsequent young collections more efficient. // Calculate the number of young collections ahead that we will try to amortize // the cost of doing an old collection for. - const int lookahead = stats._heap._total_collections - stats._old_stats._general._total_collections_at_start; + const uint lookahead = stats._heap._total_collections - stats._old_stats._general._total_collections_at_start; // Calculate extra young collection overhead predicted for a number of future // young collections, due to not freeing up memory in the old generation. const double extra_young_gc_time_for_lookahead = extra_young_gc_time * lookahead; - log_debug(gc, director)("Rule Major: Allocation Rate, ExtraYoungGCTime: %.3fs, OldGCTime: %.3fs, Lookahead: %d, ExtraYoungGCTimeForLookahead: %.3fs", + log_debug(gc, director)("Rule Major: Allocation Rate, ExtraYoungGCTime: %.3fs, OldGCTime: %.3fs, Lookahead: %u, ExtraYoungGCTimeForLookahead: %.3fs", extra_young_gc_time, old_gc_time, lookahead, extra_young_gc_time_for_lookahead); // If we continue doing as many minor collections as we already did since the @@ -565,7 +565,7 @@ static bool rule_major_proactive(const ZDirectorStats& stats) { // passed since the previous GC. This helps avoid superfluous GCs when running // applications with very low allocation rate. const size_t used_after_last_gc = stats._old_stats._stat_heap._used_at_relocate_end; - const size_t used_increase_threshold = stats._heap._soft_max_heap_size * 0.10; // 10% + const size_t used_increase_threshold = (size_t)(stats._heap._soft_max_heap_size * 0.10); // 10% const size_t used_threshold = used_after_last_gc + used_increase_threshold; const size_t used = stats._heap._used; const double time_since_last_gc = stats._old_stats._cycle._time_since_last; diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp b/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp index 41d9106843450..42d006a5b3734 100644 --- a/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp +++ b/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ #include "utilities/debug.hpp" inline size_t ZForwardingAllocator::size() const { - return _end - _start; + return (size_t)(_end - _start); } inline bool ZForwardingAllocator::is_full() const { diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index be86550d32171..8a7d38e299166 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -299,7 +299,7 @@ void ZGeneration::reset_statistics() { _page_allocator->reset_statistics(_id); } -ssize_t ZGeneration::freed() const { +size_t ZGeneration::freed() const { return _freed; } @@ -448,7 +448,7 @@ class VM_ZOperation : public VM_Operation { _success = do_operation(); // Update statistics - ZStatSample(ZSamplerJavaThreads, Threads::number_of_threads()); + ZStatSample(ZSamplerJavaThreads, (uint64_t)Threads::number_of_threads()); } virtual void doit_epilogue() { diff --git a/src/hotspot/share/gc/z/zGeneration.hpp b/src/hotspot/share/gc/z/zGeneration.hpp index 32762a50b6278..aec48a0a07236 100644 --- a/src/hotspot/share/gc/z/zGeneration.hpp +++ b/src/hotspot/share/gc/z/zGeneration.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -122,7 +122,7 @@ class ZGeneration { // Statistics void reset_statistics(); virtual bool should_record_stats() = 0; - ssize_t freed() const; + size_t freed() const; void increase_freed(size_t size); size_t promoted() const; void increase_promoted(size_t size); diff --git a/src/hotspot/share/gc/z/zHeapIterator.cpp b/src/hotspot/share/gc/z/zHeapIterator.cpp index cbcac39a11bbc..e149a976add92 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.cpp +++ b/src/hotspot/share/gc/z/zHeapIterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -146,7 +146,7 @@ class ZHeapIteratorUncoloredRootOopClosure : public OopClosure { oop load_oop(oop* p) { const oop o = Atomic::load(p); - assert_is_valid(to_zaddress(o)); + check_is_valid_zaddress(o); return RawAccess<>::oop_load(p); } @@ -204,7 +204,7 @@ class ZHeapIteratorOopClosure : public OopIterateClosure { assert(ZCollectedHeap::heap()->is_in(p), "Should be in heap"); if (VisitReferents) { - return HeapAccess::oop_load_at(_base, _base->field_offset(p)); + return HeapAccess::oop_load_at(_base, (ptrdiff_t)_base->field_offset(p)); } return HeapAccess::oop_load(p); @@ -447,7 +447,7 @@ void ZHeapIterator::follow_array_chunk(const ZHeapIteratorContext& context, cons const objArrayOop obj = objArrayOop(array.obj()); const int length = obj->length(); const int start = array.index(); - const int stride = MIN2(length - start, ObjArrayMarkingStride); + const int stride = MIN2(length - start, (int)ObjArrayMarkingStride); const int end = start + stride; // Push remaining array chunk first diff --git a/src/hotspot/share/gc/z/zHeuristics.cpp b/src/hotspot/share/gc/z/zHeuristics.cpp index c764227061e88..ebf979af79518 100644 --- a/src/hotspot/share/gc/z/zHeuristics.cpp +++ b/src/hotspot/share/gc/z/zHeuristics.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,14 +39,14 @@ void ZHeuristics::set_medium_page_size() { // becomes larger than ZPageSizeSmall. const size_t min = ZGranuleSize; const size_t max = ZGranuleSize * 16; - const size_t unclamped = MaxHeapSize * 0.03125; + const size_t unclamped = (size_t)(MaxHeapSize * 0.03125); const size_t clamped = clamp(unclamped, min, max); const size_t size = round_down_power_of_2(clamped); if (size > ZPageSizeSmall) { // Enable medium pages ZPageSizeMedium = size; - ZPageSizeMediumShift = log2i_exact(ZPageSizeMedium); + ZPageSizeMediumShift = (size_t)log2i_exact(ZPageSizeMedium); ZObjectSizeLimitMedium = ZPageSizeMedium / 8; ZObjectAlignmentMediumShift = (int)ZPageSizeMediumShift - 13; ZObjectAlignmentMedium = 1 << ZObjectAlignmentMediumShift; @@ -68,11 +68,11 @@ bool ZHeuristics::use_per_cpu_shared_small_pages() { } static uint nworkers_based_on_ncpus(double cpu_share_in_percent) { - return ceil(os::initial_active_processor_count() * cpu_share_in_percent / 100.0); + return (uint)ceil(os::initial_active_processor_count() * cpu_share_in_percent / 100.0); } static uint nworkers_based_on_heap_size(double heap_share_in_percent) { - return (MaxHeapSize * (heap_share_in_percent / 100.0)) / ZPageSizeSmall; + return (uint)(MaxHeapSize * (heap_share_in_percent / 100.0) / ZPageSizeSmall); } static uint nworkers(double cpu_share_in_percent) { @@ -101,9 +101,9 @@ uint ZHeuristics::nconcurrent_workers() { } size_t ZHeuristics::significant_heap_overhead() { - return MaxHeapSize * (ZFragmentationLimit / 100); + return (size_t)(MaxHeapSize * (ZFragmentationLimit / 100)); } size_t ZHeuristics::significant_young_overhead() { - return MaxHeapSize * (ZYoungCompactionLimit / 100); + return (size_t)(MaxHeapSize * (ZYoungCompactionLimit / 100)); } diff --git a/src/hotspot/share/gc/z/zIndexDistributor.inline.hpp b/src/hotspot/share/gc/z/zIndexDistributor.inline.hpp index 172ba2505759d..26afdef9d052d 100644 --- a/src/hotspot/share/gc/z/zIndexDistributor.inline.hpp +++ b/src/hotspot/share/gc/z/zIndexDistributor.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ class ZIndexDistributorStriped : public CHeapObj { } volatile int* claim_addr(int index) { - return (volatile int*)(align_up(_mem, ZCacheLineSize) + index * ZCacheLineSize); + return (volatile int*)(align_up(_mem, ZCacheLineSize) + (size_t)index * ZCacheLineSize); } public: @@ -136,7 +136,7 @@ class ZIndexDistributorClaimTree : public CHeapObj { // Total size used to hold all claim variables static size_t claim_variables_size() { - return sizeof(int) * claim_level_end_index(ClaimLevels); + return sizeof(int) * (size_t)claim_level_end_index(ClaimLevels); } // Returns the index of the start of the current segment of the current level diff --git a/src/hotspot/share/gc/z/zLiveMap.cpp b/src/hotspot/share/gc/z/zLiveMap.cpp index 2e4a8edd356cc..4123620f8b7e0 100644 --- a/src/hotspot/share/gc/z/zLiveMap.cpp +++ b/src/hotspot/share/gc/z/zLiveMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ ZLiveMap::ZLiveMap(uint32_t size) _segment_live_bits(0), _segment_claim_bits(0), _bitmap(bitmap_size(size, nsegments)), - _segment_shift(exact_log2(segment_size())) {} + _segment_shift(log2i_exact(segment_size())) {} void ZLiveMap::reset(ZGenerationId id) { ZGeneration* const generation = ZGeneration::generation(id); @@ -130,6 +130,6 @@ void ZLiveMap::resize(uint32_t size) { const size_t new_bitmap_size = bitmap_size(size, nsegments); if (_bitmap.size() != new_bitmap_size) { _bitmap.reinitialize(new_bitmap_size, false /* clear */); - _segment_shift = exact_log2(segment_size()); + _segment_shift = log2i_exact(segment_size()); } } diff --git a/src/hotspot/share/gc/z/zLiveMap.hpp b/src/hotspot/share/gc/z/zLiveMap.hpp index e3bcd2e267ddb..f8b16d06dc52c 100644 --- a/src/hotspot/share/gc/z/zLiveMap.hpp +++ b/src/hotspot/share/gc/z/zLiveMap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ class ZLiveMap { BitMap::bm_word_t _segment_live_bits; BitMap::bm_word_t _segment_claim_bits; ZBitMap _bitmap; - size_t _segment_shift; + int _segment_shift; const BitMapView segment_live_bits() const; const BitMapView segment_claim_bits() const; diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index eb342495f5799..d33b86c83e57c 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -365,7 +365,7 @@ void ZMark::follow_array_object(objArrayOop obj, bool finalizable) { } // Should be convertible to colorless oop - assert_is_valid(to_zaddress(obj)); + check_is_valid_zaddress(obj); zpointer* const addr = (zpointer*)obj->base(); const size_t length = (size_t)obj->length(); diff --git a/src/hotspot/share/gc/z/zMark.inline.hpp b/src/hotspot/share/gc/z/zMark.inline.hpp index b530259361027..9edc57a60002e 100644 --- a/src/hotspot/share/gc/z/zMark.inline.hpp +++ b/src/hotspot/share/gc/z/zMark.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ template inline void ZMark::mark_object(zaddress addr) { - assert(!ZVerifyOops || oopDesc::is_oop(to_oop(addr)), "Should be oop"); + assert_is_oop(addr); ZPage* const page = _page_table->get(addr); if (page->is_allocating()) { diff --git a/src/hotspot/share/gc/z/zMarkCache.cpp b/src/hotspot/share/gc/z/zMarkCache.cpp index 7fbb5a8be4e61..0eecfbfaa6773 100644 --- a/src/hotspot/share/gc/z/zMarkCache.cpp +++ b/src/hotspot/share/gc/z/zMarkCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ #include "utilities/powerOfTwo.hpp" static size_t shift_for_stripes(size_t nstripes) { - return ZMarkStripeShift + exact_log2(nstripes); + return ZMarkStripeShift + (size_t)log2i_exact(nstripes); } ZMarkCacheEntry::ZMarkCacheEntry() diff --git a/src/hotspot/share/gc/z/zMarkStack.cpp b/src/hotspot/share/gc/z/zMarkStack.cpp index 1e10c41eb41dd..c4938af0a5f6d 100644 --- a/src/hotspot/share/gc/z/zMarkStack.cpp +++ b/src/hotspot/share/gc/z/zMarkStack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,7 @@ ZMarkStripe* ZMarkStripeSet::stripe_for_worker(uint nworkers, uint worker_id) { const size_t spillover_nworkers = nworkers - spillover_limit; const size_t spillover_worker_id = worker_id - spillover_limit; const double spillover_chunk = (double)nstripes / (double)spillover_nworkers; - index = spillover_worker_id * spillover_chunk; + index = (size_t)(spillover_worker_id * spillover_chunk); } assert(index < nstripes, "Invalid index"); diff --git a/src/hotspot/share/gc/z/zMetronome.cpp b/src/hotspot/share/gc/z/zMetronome.cpp index 0cc209cd7c843..876b1f6922799 100644 --- a/src/hotspot/share/gc/z/zMetronome.cpp +++ b/src/hotspot/share/gc/z/zMetronome.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ bool ZMetronome::wait_for_tick() { if (_nticks++ == 0) { // First tick, set start time const Ticks now = Ticks::now(); - _start_ms = TimeHelper::counter_to_millis(now.value()); + _start_ms = (uint64_t)TimeHelper::counter_to_millis(now.value()); } MonitorLocker ml(&_monitor, Monitor::_no_safepoint_check_flag); @@ -47,9 +47,9 @@ bool ZMetronome::wait_for_tick() { // We might wake up spuriously from wait, so always recalculate // the timeout after a wakeup to see if we need to wait again. const Ticks now = Ticks::now(); - const uint64_t now_ms = TimeHelper::counter_to_millis(now.value()); + const uint64_t now_ms = (uint64_t)TimeHelper::counter_to_millis(now.value()); const uint64_t next_ms = _start_ms + (_interval_ms * _nticks); - const int64_t timeout_ms = next_ms - now_ms; + const int64_t timeout_ms = (int64_t)(next_ms - now_ms); if (timeout_ms > 0) { // Wait @@ -57,7 +57,7 @@ bool ZMetronome::wait_for_tick() { } else { // Tick if (timeout_ms < 0) { - const uint64_t overslept = -timeout_ms; + const uint64_t overslept = (uint64_t)-timeout_ms; if (overslept > _interval_ms) { // Missed one or more ticks. Bump _nticks accordingly to // avoid firing a string of immediate ticks to make up diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index 7ae7b204d08c9..7c5b1e06edbb0 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -302,8 +302,8 @@ void ZNMethod::nmethods_do(bool secondary, NMethodClosure* cl) { uintptr_t ZNMethod::color(nmethod* nm) { BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); - // color is stored at low order bits of int; implicit conversion to uintptr_t is fine - return bs_nm->guard_value(nm); + // color is stored at low order bits of int; conversion to uintptr_t is fine + return (uintptr_t)bs_nm->guard_value(nm); } oop ZNMethod::load_oop(oop* p, DecoratorSet decorators) { diff --git a/src/hotspot/share/gc/z/zNMethodTable.cpp b/src/hotspot/share/gc/z/zNMethodTable.cpp index a4b56292c522b..9714bee4bd88f 100644 --- a/src/hotspot/share/gc/z/zNMethodTable.cpp +++ b/src/hotspot/share/gc/z/zNMethodTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -144,9 +144,9 @@ void ZNMethodTable::rebuild_if_needed() { // grows/shrinks by doubling/halving its size. Pruning of unregistered // entries is done by rebuilding the table with or without resizing it. const size_t min_size = 1024; - const size_t shrink_threshold = _size * 0.30; - const size_t prune_threshold = _size * 0.65; - const size_t grow_threshold = _size * 0.70; + const size_t shrink_threshold = (size_t)(_size * 0.30); + const size_t prune_threshold = (size_t)(_size * 0.65); + const size_t grow_threshold = (size_t)(_size * 0.70); if (_size == 0) { // Initialize table diff --git a/src/hotspot/share/gc/z/zObjArrayAllocator.cpp b/src/hotspot/share/gc/z/zObjArrayAllocator.cpp index ad19f273dcf0f..ada8351a9f65f 100644 --- a/src/hotspot/share/gc/z/zObjArrayAllocator.cpp +++ b/src/hotspot/share/gc/z/zObjArrayAllocator.cpp @@ -76,8 +76,8 @@ oop ZObjArrayAllocator::initialize(HeapWord* mem) const { ZThreadLocalData::set_invisible_root(_thread, (zaddress_unsafe*)&mem); const BasicType element_type = ArrayKlass::cast(_klass)->element_type(); - const size_t base_offset_in_bytes = arrayOopDesc::base_offset_in_bytes(element_type); - const size_t process_start_offset_in_bytes = align_up(base_offset_in_bytes, BytesPerWord); + const size_t base_offset_in_bytes = (size_t)arrayOopDesc::base_offset_in_bytes(element_type); + const size_t process_start_offset_in_bytes = align_up(base_offset_in_bytes, (size_t)BytesPerWord); if (process_start_offset_in_bytes != base_offset_in_bytes) { // initialize_memory can only fill word aligned memory, diff --git a/src/hotspot/share/gc/z/zPage.inline.hpp b/src/hotspot/share/gc/z/zPage.inline.hpp index 0b4ee28ba7d0a..d8ecad571907c 100644 --- a/src/hotspot/share/gc/z/zPage.inline.hpp +++ b/src/hotspot/share/gc/z/zPage.inline.hpp @@ -83,13 +83,13 @@ inline uint32_t ZPage::object_max_count() const { inline size_t ZPage::object_alignment_shift() const { switch (type()) { case ZPageType::small: - return ZObjectAlignmentSmallShift; + return (size_t)ZObjectAlignmentSmallShift; case ZPageType::medium: - return ZObjectAlignmentMediumShift; + return (size_t)ZObjectAlignmentMediumShift; case ZPageType::large: - return ZObjectAlignmentLargeShift; + return (size_t)ZObjectAlignmentLargeShift; default: fatal("Unexpected page type"); @@ -100,13 +100,13 @@ inline size_t ZPage::object_alignment_shift() const { inline size_t ZPage::object_alignment() const { switch (type()) { case ZPageType::small: - return ZObjectAlignmentSmall; + return (size_t)ZObjectAlignmentSmall; case ZPageType::medium: - return ZObjectAlignmentMedium; + return (size_t)ZObjectAlignmentMedium; case ZPageType::large: - return ZObjectAlignmentLarge; + return (size_t)ZObjectAlignmentLarge; default: fatal("Unexpected page type"); @@ -311,7 +311,7 @@ inline bool ZPage::mark_object(zaddress addr, bool finalizable, bool& inc_live) assert(is_in(addr), "Invalid address"); // Verify oop - (void)to_oop(addr); + assert_is_oop(addr); // Set mark bit const BitMap::idx_t index = bit_index(addr); diff --git a/src/hotspot/share/gc/z/zPageCache.cpp b/src/hotspot/share/gc/z/zPageCache.cpp index 96fd2eafe0f66..163bb395560de 100644 --- a/src/hotspot/share/gc/z/zPageCache.cpp +++ b/src/hotspot/share/gc/z/zPageCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -308,7 +308,7 @@ class ZPageCacheFlushForUncommitClosure : public ZPageCacheFlushClosure { }; size_t ZPageCache::flush_for_uncommit(size_t requested, ZList* to, uint64_t* timeout) { - const uint64_t now = os::elapsedTime(); + const uint64_t now = (uint64_t)os::elapsedTime(); const uint64_t expires = _last_commit + ZUncommitDelay; if (expires > now) { // Delay uncommit, set next timeout @@ -329,5 +329,5 @@ size_t ZPageCache::flush_for_uncommit(size_t requested, ZList* to, uint64 } void ZPageCache::set_last_commit() { - _last_commit = ceil(os::elapsedTime()); + _last_commit = (uint64_t)ceil(os::elapsedTime()); } diff --git a/src/hotspot/share/gc/z/zPageTable.inline.hpp b/src/hotspot/share/gc/z/zPageTable.inline.hpp index 3b4cbe9220cfb..79a2d297df889 100644 --- a/src/hotspot/share/gc/z/zPageTable.inline.hpp +++ b/src/hotspot/share/gc/z/zPageTable.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ inline ZPageTableParallelIterator::ZPageTableParallelIterator(const ZPageTable* template inline void ZPageTableParallelIterator::do_pages(Function function) { _index_distributor.do_indices([&](int index) { - ZPage* const page = _table->at(index); + ZPage* const page = _table->at(size_t(index)); if (page != nullptr) { const size_t start_index = untype(page->start()) >> ZGranuleSizeShift; if (size_t(index) == start_index) { diff --git a/src/hotspot/share/gc/z/zReferenceProcessor.cpp b/src/hotspot/share/gc/z/zReferenceProcessor.cpp index df8cb2b0e959f..1252de2ac2723 100644 --- a/src/hotspot/share/gc/z/zReferenceProcessor.cpp +++ b/src/hotspot/share/gc/z/zReferenceProcessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -143,7 +143,7 @@ bool ZReferenceProcessor::is_inactive(zaddress reference, oop referent, Referenc return !is_null(reference_next(reference)); } else { // Verification - (void)to_zaddress(referent); + check_is_valid_zaddress(referent); // A non-FinalReference is inactive if the referent is null. The referent can only // be null if the application called Reference.enqueue() or Reference.clear(). diff --git a/src/hotspot/share/gc/z/zRelocationSet.cpp b/src/hotspot/share/gc/z/zRelocationSet.cpp index 92f245777b4e9..5c82f55bbbfe7 100644 --- a/src/hotspot/share/gc/z/zRelocationSet.cpp +++ b/src/hotspot/share/gc/z/zRelocationSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,7 +84,7 @@ class ZRelocationSetInstallTask : public ZTask { : ZTask("ZRelocationSetInstallTask"), _allocator(allocator), _forwardings(nullptr), - _nforwardings(selector->selected_small()->length() + selector->selected_medium()->length()), + _nforwardings((size_t)selector->selected_small()->length() + (size_t)selector->selected_medium()->length()), _small(selector->selected_small()), _medium(selector->selected_medium()), _small_iter(selector->selected_small()), @@ -113,7 +113,7 @@ class ZRelocationSetInstallTask : public ZTask { for (size_t page_index; _small_iter.next_index(&page_index);) { ZPage* page = _small->at(int(page_index)); ZForwarding* const forwarding = ZForwarding::alloc(_allocator, page, to_age(page)); - install_small(forwarding, _medium->length() + page_index); + install_small(forwarding, (size_t)_medium->length() + page_index); SuspendibleThreadSet::yield(); } diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp index ab1df68fe5110..ec904b914fb0b 100644 --- a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp +++ b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ ZRelocationSetSelectorGroup::ZRelocationSetSelectorGroup(const char* name, _page_size(page_size), _object_size_limit(object_size_limit), _fragmentation_limit(fragmentation_limit), - _page_fragmentation_limit(page_size * (fragmentation_limit / 100)), + _page_fragmentation_limit((size_t)(page_size * (fragmentation_limit / 100))), _live_pages(), _not_selected_pages(), _forwarding_entries(0), @@ -72,7 +72,7 @@ void ZRelocationSetSelectorGroup::semi_sort() { const size_t npartitions_shift = 11; const size_t npartitions = (size_t)1 << npartitions_shift; const size_t partition_size = _page_size >> npartitions_shift; - const size_t partition_size_shift = exact_log2(partition_size); + const int partition_size_shift = log2i_exact(partition_size); // Partition slots/fingers int partitions[npartitions] = { /* zero initialize */ }; @@ -135,7 +135,7 @@ void ZRelocationSetSelectorGroup::select_inner() { // By subtracting the object size limit from the pages size we get the maximum // number of pages that the relocation set is guaranteed to fit in, regardless // of in which order the objects are relocated. - const int to = ceil((double)(from_live_bytes) / (double)(_page_size - _object_size_limit)); + const int to = (int)ceil(from_live_bytes / (double)(_page_size - _object_size_limit)); // Calculate the relative difference in reclaimable space compared to our // currently selected final relocation set. If this number is larger than the diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp index d838cb0b8138a..613f7b2740b43 100644 --- a/src/hotspot/share/gc/z/zStat.cpp +++ b/src/hotspot/share/gc/z/zStat.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -250,14 +250,14 @@ void ZStatUnitTime(LogTargetHandle log, const ZStatSampler& sampler, const ZStat "%9.3f / %-9.3f ms", sampler.group(), sampler.name(), - TimeHelper::counter_to_millis(history.avg_10_seconds()), - TimeHelper::counter_to_millis(history.max_10_seconds()), - TimeHelper::counter_to_millis(history.avg_10_minutes()), - TimeHelper::counter_to_millis(history.max_10_minutes()), - TimeHelper::counter_to_millis(history.avg_10_hours()), - TimeHelper::counter_to_millis(history.max_10_hours()), - TimeHelper::counter_to_millis(history.avg_total()), - TimeHelper::counter_to_millis(history.max_total())); + TimeHelper::counter_to_millis((jlong)history.avg_10_seconds()), + TimeHelper::counter_to_millis((jlong)history.max_10_seconds()), + TimeHelper::counter_to_millis((jlong)history.avg_10_minutes()), + TimeHelper::counter_to_millis((jlong)history.max_10_minutes()), + TimeHelper::counter_to_millis((jlong)history.avg_10_hours()), + TimeHelper::counter_to_millis((jlong)history.max_10_hours()), + TimeHelper::counter_to_millis((jlong)history.avg_total()), + TimeHelper::counter_to_millis((jlong)history.max_total())); } void ZStatUnitBytes(LogTargetHandle log, const ZStatSampler& sampler, const ZStatSamplerHistory& history) { @@ -677,7 +677,7 @@ void ZStatPhaseCollection::register_end(ConcurrentGCTimer* timer, const Ticks& s ZCollectedHeap::heap()->trace_heap_after_gc(jfr_tracer()); const Tickspan duration = end - start; - ZStatSample(_sampler, duration.value()); + ZStatDurationSample(_sampler, duration); const size_t used_at_end = ZHeap::heap()->used(); @@ -718,7 +718,7 @@ void ZStatPhaseGeneration::register_end(ConcurrentGCTimer* timer, const Ticks& s ZCollectedHeap::heap()->print_heap_after_gc(); const Tickspan duration = end - start; - ZStatSample(_sampler, duration.value()); + ZStatDurationSample(_sampler, duration); ZGeneration* const generation = ZGeneration::generation(_id); @@ -766,7 +766,7 @@ void ZStatPhasePause::register_end(ConcurrentGCTimer* timer, const Ticks& start, timer->register_gc_pause_end(end); const Tickspan duration = end - start; - ZStatSample(_sampler, duration.value()); + ZStatDurationSample(_sampler, duration); // Track max pause time if (_max < duration) { @@ -798,7 +798,7 @@ void ZStatPhaseConcurrent::register_end(ConcurrentGCTimer* timer, const Ticks& s timer->register_gc_concurrent_end(end); const Tickspan duration = end - start; - ZStatSample(_sampler, duration.value()); + ZStatDurationSample(_sampler, duration); LogTarget(Info, gc, phases) log; log_end(log, duration); @@ -835,7 +835,7 @@ void ZStatSubPhase::register_end(ConcurrentGCTimer* timer, const Ticks& start, c ZTracer::report_thread_phase(name(), start, end); const Tickspan duration = end - start; - ZStatSample(_sampler, duration.value()); + ZStatDurationSample(_sampler, duration); if (Thread::current()->is_Worker_thread()) { LogTarget(Trace, gc, phases) log; @@ -862,7 +862,7 @@ void ZStatCriticalPhase::register_end(ConcurrentGCTimer* timer, const Ticks& sta ZTracer::report_thread_phase(name(), start, end); const Tickspan duration = end - start; - ZStatSample(_sampler, duration.value()); + ZStatDurationSample(_sampler, duration); ZStatInc(_counter); if (_verbose) { @@ -914,6 +914,10 @@ void ZStatSample(const ZStatSampler& sampler, uint64_t value) { ZTracer::report_stat_sampler(sampler, value); } +void ZStatDurationSample(const ZStatSampler& sampler, const Tickspan& duration) { + ZStatSample(sampler, (uint64_t)duration.value()); +} + void ZStatInc(const ZStatCounter& counter, uint64_t increment) { ZStatCounterData* const cpu_data = counter.get(); const uint64_t value = Atomic::add(&cpu_data->_counter, increment); @@ -1036,7 +1040,7 @@ void ZStat::sample_and_collect(ZStatSamplerHistory* history) const { bool ZStat::should_print(LogTargetHandle log) const { static uint64_t print_at = ZStatisticsInterval; - const uint64_t now = os::elapsedTime(); + const uint64_t now = (uint64_t)os::elapsedTime(); if (now < print_at) { return false; @@ -1846,7 +1850,7 @@ void ZStatHeap::at_relocate_end(const ZPageAllocatorStats& stats, bool record_st } size_t ZStatHeap::reclaimed_avg() { - return _reclaimed_bytes.davg(); + return (size_t)_reclaimed_bytes.davg(); } size_t ZStatHeap::max_capacity() { diff --git a/src/hotspot/share/gc/z/zStat.hpp b/src/hotspot/share/gc/z/zStat.hpp index 346773c3b7e2a..d7482dbe6aaaf 100644 --- a/src/hotspot/share/gc/z/zStat.hpp +++ b/src/hotspot/share/gc/z/zStat.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -345,6 +345,7 @@ class ZStatTimerWorker : public ZStatTimer { // Stat sample/increment // void ZStatSample(const ZStatSampler& sampler, uint64_t value); +void ZStatDurationSample(const ZStatSampler& sampler, const Tickspan& duration); void ZStatInc(const ZStatCounter& counter, uint64_t increment = 1); void ZStatInc(const ZStatUnsampledCounter& counter, uint64_t increment = 1); diff --git a/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp b/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp index 537609e723bbb..c94551dc62d20 100644 --- a/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp +++ b/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,7 +79,7 @@ void ZStoreBarrierBuffer::install_base_pointers_inner() { (ZPointer::remap_bits(_last_processed_color) & ZPointerRemappedOldMask) == 0, "Should not have double bit errors"); - for (int i = current(); i < (int)_buffer_length; ++i) { + for (size_t i = current(); i < _buffer_length; ++i) { const ZStoreBarrierEntry& entry = _buffer[i]; volatile zpointer* const p = entry._p; const zaddress_unsafe p_unsafe = to_zaddress_unsafe((uintptr_t)p); @@ -141,7 +141,7 @@ static volatile zpointer* make_load_good(volatile zpointer* p, zaddress_unsafe p return (volatile zpointer*)p_remapped; } -void ZStoreBarrierBuffer::on_new_phase_relocate(int i) { +void ZStoreBarrierBuffer::on_new_phase_relocate(size_t i) { const uintptr_t last_remap_bits = ZPointer::remap_bits(_last_processed_color); if (last_remap_bits == ZPointerRemapped) { // All pointers are already remapped @@ -160,7 +160,7 @@ void ZStoreBarrierBuffer::on_new_phase_relocate(int i) { entry._p = make_load_good(entry._p, p_base, _last_processed_color); } -void ZStoreBarrierBuffer::on_new_phase_remember(int i) { +void ZStoreBarrierBuffer::on_new_phase_remember(size_t i) { volatile zpointer* const p = _buffer[i]._p; if (ZHeap::heap()->is_young(p)) { @@ -197,7 +197,7 @@ bool ZStoreBarrierBuffer::stored_during_old_mark() const { return last_mark_old_bits == ZPointerMarkedOld; } -void ZStoreBarrierBuffer::on_new_phase_mark(int i) { +void ZStoreBarrierBuffer::on_new_phase_mark(size_t i) { const ZStoreBarrierEntry& entry = _buffer[i]; const zpointer prev = entry._prev; @@ -229,7 +229,7 @@ void ZStoreBarrierBuffer::on_new_phase() { // Install all base pointers for relocation install_base_pointers(); - for (int i = current(); i < (int)_buffer_length; ++i) { + for (size_t i = current(); i < _buffer_length; ++i) { on_new_phase_relocate(i); on_new_phase_remember(i); on_new_phase_mark(i); @@ -259,8 +259,8 @@ void ZStoreBarrierBuffer::on_error(outputStream* st) { st->print_cr(" _last_processed_color: " PTR_FORMAT, _last_processed_color); st->print_cr(" _last_installed_color: " PTR_FORMAT, _last_installed_color); - for (int i = current(); i < (int)_buffer_length; ++i) { - st->print_cr(" [%2d]: base: " PTR_FORMAT " p: " PTR_FORMAT " prev: " PTR_FORMAT, + for (size_t i = current(); i < _buffer_length; ++i) { + st->print_cr(" [%2zu]: base: " PTR_FORMAT " p: " PTR_FORMAT " prev: " PTR_FORMAT, i, untype(_base_pointers[i]), p2i(_buffer[i]._p), @@ -276,7 +276,7 @@ void ZStoreBarrierBuffer::flush() { OnError on_error(this); VMErrorCallbackMark mark(&on_error); - for (int i = current(); i < (int)_buffer_length; ++i) { + for (size_t i = current(); i < _buffer_length; ++i) { const ZStoreBarrierEntry& entry = _buffer[i]; const zaddress addr = ZBarrier::make_load_good(entry._prev); ZBarrier::mark_and_remember(entry._p, addr); @@ -296,7 +296,7 @@ bool ZStoreBarrierBuffer::is_in(volatile zpointer* p) { const uintptr_t last_remap_bits = ZPointer::remap_bits(buffer->_last_processed_color) & ZPointerRemappedMask; const bool needs_remap = last_remap_bits != ZPointerRemapped; - for (int i = buffer->current(); i < (int)_buffer_length; ++i) { + for (size_t i = buffer->current(); i < _buffer_length; ++i) { const ZStoreBarrierEntry& entry = buffer->_buffer[i]; volatile zpointer* entry_p = entry._p; diff --git a/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp b/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp index f917a6c3e7b58..5903edb6ad409 100644 --- a/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp +++ b/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,16 +59,16 @@ class ZStoreBarrierBuffer : public CHeapObj { // sizeof(ZStoreBarrierEntry) scaled index growing downwards size_t _current; - void on_new_phase_relocate(int i); - void on_new_phase_remember(int i); - void on_new_phase_mark(int i); + void on_new_phase_relocate(size_t i); + void on_new_phase_remember(size_t i); + void on_new_phase_mark(size_t i); void clear(); bool is_old_mark() const; bool stored_during_old_mark() const; bool is_empty() const; - intptr_t current() const; + size_t current() const; void install_base_pointers_inner(); diff --git a/src/hotspot/share/gc/z/zStoreBarrierBuffer.inline.hpp b/src/hotspot/share/gc/z/zStoreBarrierBuffer.inline.hpp index 762aac3ccd5bf..72327fe83466b 100644 --- a/src/hotspot/share/gc/z/zStoreBarrierBuffer.inline.hpp +++ b/src/hotspot/share/gc/z/zStoreBarrierBuffer.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ #include "gc/z/zThreadLocalData.hpp" #include "runtime/thread.hpp" -inline intptr_t ZStoreBarrierBuffer::current() const { +inline size_t ZStoreBarrierBuffer::current() const { return _current / sizeof(ZStoreBarrierEntry); } diff --git a/src/hotspot/share/gc/z/zUnmapper.cpp b/src/hotspot/share/gc/z/zUnmapper.cpp index 3b2bac7eb00a7..b6ef40b6b059f 100644 --- a/src/hotspot/share/gc/z/zUnmapper.cpp +++ b/src/hotspot/share/gc/z/zUnmapper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,7 +85,7 @@ bool ZUnmapper::try_enqueue(ZPage* page) { } size_t ZUnmapper::queue_capacity() const { - return align_up(_page_allocator->max_capacity() * ZAsyncUnmappingLimit / 100.0, ZGranuleSize); + return align_up((size_t)(_page_allocator->max_capacity() * ZAsyncUnmappingLimit / 100.0), ZGranuleSize); } bool ZUnmapper::is_saturated() const { diff --git a/src/hotspot/share/gc/z/zVerify.cpp b/src/hotspot/share/gc/z/zVerify.cpp index fba8adfb3c16f..b735965e9d49b 100644 --- a/src/hotspot/share/gc/z/zVerify.cpp +++ b/src/hotspot/share/gc/z/zVerify.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -111,6 +111,16 @@ static bool z_is_null_relaxed(zpointer o) { return (untype(o) & ~color_mask) == 0; } +static void z_verify_oop_object(zaddress addr, zpointer o, void* p) { + const oop obj = cast_to_oop(addr); + guarantee(oopDesc::is_oop(obj), BAD_OOP_ARG(o, p)); +} + +static void z_verify_root_oop_object(zaddress addr, void* p) { + const oop obj = cast_to_oop(addr); + guarantee(oopDesc::is_oop(obj), BAD_OOP_ARG(addr, p)); +} + static void z_verify_old_oop(zpointer* p) { const zpointer o = *p; assert(o != zpointer::null, "Old should not contain raw null"); @@ -121,7 +131,7 @@ static void z_verify_old_oop(zpointer* p) { // safepoint after reference processing, where we hold the driver lock and // know there is no concurrent remembered set processing in the young generation. const zaddress addr = ZPointer::uncolor(o); - guarantee(oopDesc::is_oop(to_oop(addr)), BAD_OOP_ARG(o, p)); + z_verify_oop_object(addr, o, p); } else { const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o); // Old to young pointers might not be mark good if the young @@ -143,15 +153,11 @@ static void z_verify_young_oop(zpointer* p) { guarantee(ZPointer::is_marked_young(o), BAD_OOP_ARG(o, p)); if (ZPointer::is_load_good(o)) { - guarantee(oopDesc::is_oop(to_oop(ZPointer::uncolor(o))), BAD_OOP_ARG(o, p)); + z_verify_oop_object(ZPointer::uncolor(o), o, p); } } } -static void z_verify_root_oop_object(zaddress o, void* p) { - guarantee(oopDesc::is_oop(to_oop(o)), BAD_OOP_ARG(o, p)); -} - static void z_verify_uncolored_root_oop(zaddress* p) { assert(!ZHeap::heap()->is_in((uintptr_t)p), "Roots shouldn't be in heap"); const zaddress o = *p; @@ -168,7 +174,7 @@ static void z_verify_possibly_weak_oop(zpointer* p) { const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o); guarantee(ZHeap::heap()->is_old(addr) || ZPointer::is_marked_young(o), BAD_OOP_ARG(o, p)); guarantee(ZHeap::heap()->is_young(addr) || ZHeap::heap()->is_object_live(addr), BAD_OOP_ARG(o, p)); - guarantee(oopDesc::is_oop(to_oop(addr)), BAD_OOP_ARG(o, p)); + z_verify_oop_object(addr, o, p); // Verify no missing remset entries. We are holding the driver lock here and that // allows us to more precisely verify the remembered set, as there is no concurrent @@ -211,14 +217,14 @@ class ZVerifyColoredRootClosure : public OopClosure { // Minor collections could have relocated the object; // use load barrier to find correct object. const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o); - z_verify_root_oop_object(addr, p); + z_verify_oop_object(addr, o, p); } else { // Don't know the state of the oop if (is_valid(o)) { // it looks like a valid colored oop; // use load barrier to find correct object. const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o); - z_verify_root_oop_object(addr, p); + z_verify_oop_object(addr, o, p); } } } @@ -583,7 +589,7 @@ void ZVerify::on_color_flip() { for (JavaThreadIteratorWithHandle jtiwh; JavaThread* const jt = jtiwh.next(); ) { const ZStoreBarrierBuffer* const buffer = ZThreadLocalData::store_barrier_buffer(jt); - for (int i = buffer->current(); i < (int)ZStoreBarrierBuffer::_buffer_length; ++i) { + for (size_t i = buffer->current(); i < ZStoreBarrierBuffer::_buffer_length; ++i) { volatile zpointer* const p = buffer->_buffer[i]._p; bool created = false; z_verify_store_barrier_buffer_table->put_if_absent(p, true, &created); diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index a18e9bffc85df..6082af55487e2 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -295,9 +295,6 @@ JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj); JNIEXPORT jobject JNICALL JVM_GetStackTrace(JNIEnv *env, jobject thread); -JNIEXPORT void JNICALL -JVM_DumpAllStacks(JNIEnv *env, jclass unused); - JNIEXPORT jobjectArray JNICALL JVM_GetAllThreads(JNIEnv *env, jclass dummy); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 203ec3a3ec494..279b871d8181a 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -883,6 +883,15 @@ static void do_clds(CldWriter& cldw) { ModuleCldWriter mcw(&cldw); KlassAndModuleCldWriter kmcw(&kcw, &mcw); _artifacts->iterate_klasses(kmcw); + if (is_initial_typeset_for_chunk()) { + CldPtr bootloader = get_cld(Universe::boolArrayKlass()); + assert(bootloader != nullptr, "invariant"); + if (IS_NOT_SERIALIZED(bootloader)) { + write__cld(_writer, bootloader); + assert(IS_SERIALIZED(bootloader), "invariant"); + cldw.add(1); + } + } _artifacts->tally(cldw); } diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index e0bbd8a6ddc28..0759d02cb7c23 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -497,7 +497,7 @@ static bool prepare_for_emergency_dump(Thread* thread) { Service_lock->unlock(); } - if (UseNotificationThread && Notification_lock->owned_by_self()) { + if (Notification_lock->owned_by_self()) { Notification_lock->unlock(); } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index fc2043a4d921d..0395b711c6521 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -639,11 +639,7 @@ static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) { size_t JfrRecorderService::flush() { size_t total_elements = flush_metadata(_chunkwriter); - const size_t storage_elements = flush_storage(_storage, _chunkwriter); - if (0 == storage_elements) { - return total_elements; - } - total_elements += storage_elements; + total_elements = flush_storage(_storage, _chunkwriter); if (_string_pool.is_modified()) { total_elements += flush_stringpool(_string_pool, _chunkwriter); } diff --git a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp index cd47630228931..0c2fb0206ecee 100644 --- a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp +++ b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp @@ -136,7 +136,9 @@ jobject JdkJfrEvent::get_all_klasses(TRAPS) { transform_klasses_to_local_jni_handles(event_subklasses, THREAD); Handle h_array_list(THREAD, new_java_util_arraylist(THREAD)); - assert(h_array_list.not_null(), "invariant"); + if (h_array_list.is_null()) { + return empty_java_util_arraylist; + } static const char add_method_name[] = "add"; static const char add_method_signature[] = "(Ljava/lang/Object;)Z"; diff --git a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp index c62257bd23b1c..6bca2aa644e55 100644 --- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp +++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -664,7 +664,7 @@ JVMCI::CodeInstallResult CodeInstaller::install_runtime_stub(CodeBlob*& cb, GrowableArray *stubs_to_free = nullptr; #ifdef ASSERT const char* val = Arguments::PropertyList_get_value(Arguments::system_properties(), "test.jvmci.forceRuntimeStubAllocFail"); - if (val != nullptr && strstr(name , val) != 0) { + if (val != nullptr && strstr(name , val) != nullptr) { stubs_to_free = new GrowableArray(); JVMCI_event_1("forcing allocation of %s in code cache to fail", name); } @@ -722,6 +722,7 @@ JVMCI::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler, jint entry_bci = -1; JVMCICompileState* compile_state = nullptr; bool has_unsafe_access = false; + bool has_scoped_access = false; jint id = -1; if (is_nmethod) { @@ -729,6 +730,7 @@ JVMCI::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler, entry_bci = is_nmethod ? stream->read_s4("entryBCI") : -1; compile_state = (JVMCICompileState*) stream->read_u8("compileState"); has_unsafe_access = stream->read_bool("hasUnsafeAccess"); + has_scoped_access = stream->read_bool("hasScopedAccess"); id = stream->read_s4("id"); } stream->set_code_desc(name, method); @@ -795,6 +797,7 @@ JVMCI::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler, id, _has_monitors, has_unsafe_access, + has_scoped_access, _has_wide_vector, compiled_code, mirror, diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 5583ad8a6f5be..5a9b18fc4d3db 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -94,6 +94,12 @@ static void requireInHotSpot(const char* caller, JVMCI_TRAPS) { } } +static void requireNotInHotSpot(const char* caller, JVMCI_TRAPS) { + if (JVMCIENV->is_hotspot()) { + JVMCI_THROW_MSG(IllegalStateException, err_msg("Cannot call %s from HotSpot", caller)); + } +} + class JVMCITraceMark : public StackObj { const char* _msg; public: @@ -418,7 +424,7 @@ C2V_VMENTRY_NULL(jobject, getResolvedJavaMethod, (JNIEnv* env, jobject, jobject C2V_VMENTRY_NULL(jobject, getConstantPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass_or_method), jboolean is_klass)) ConstantPool* cp = nullptr; - if (UNPACK_PAIR(address, klass_or_method) == 0) { + if (UNPACK_PAIR(address, klass_or_method) == nullptr) { JVMCI_THROW_NULL(NullPointerException); } if (!is_klass) { @@ -702,6 +708,17 @@ C2V_VMENTRY_NULL(jobject, lookupJClass, (JNIEnv* env, jobject, jlong jclass_valu return JVMCIENV->get_jobject(result); C2V_END +C2V_VMENTRY_0(jlong, getJObjectValue, (JNIEnv* env, jobject, jobject constant_jobject)) + requireNotInHotSpot("getJObjectValue", JVMCI_CHECK_0); + if (!THREAD->has_last_Java_frame()) { + JVMCI_THROW_MSG_0(IllegalStateException, err_msg("Cannot call getJObjectValue without Java frame anchor")); + } + JVMCIObject constant = JVMCIENV->wrap(constant_jobject); + Handle constant_value = JVMCIENV->asConstant(constant, JVMCI_CHECK_0); + jobject jni_handle = JNIHandles::make_local(THREAD, constant_value()); + return reinterpret_cast(jni_handle); +C2V_END + C2V_VMENTRY_NULL(jobject, getUncachedStringInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index)) constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp)); constantTag tag = cp->tag_at(index); @@ -1081,7 +1098,7 @@ C2V_END C2V_VMENTRY_0(jlong, getMaxCallTargetOffset, (JNIEnv* env, jobject, jlong addr)) address target_addr = (address) addr; - if (target_addr != 0x0) { + if (target_addr != nullptr) { int64_t off_low = (int64_t)target_addr - ((int64_t)CodeCache::low_bound() + sizeof(int)); int64_t off_high = (int64_t)target_addr - ((int64_t)CodeCache::high_bound() + sizeof(int)); return MAX2(ABS(off_low), ABS(off_high)); @@ -3254,6 +3271,7 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "shouldInlineMethod", CC "(" HS_METHOD2 ")Z", FN_PTR(shouldInlineMethod)}, {CC "lookupType", CC "(" STRING HS_KLASS2 "IZ)" HS_RESOLVED_TYPE, FN_PTR(lookupType)}, {CC "lookupJClass", CC "(J)" HS_RESOLVED_TYPE, FN_PTR(lookupJClass)}, + {CC "getJObjectValue", CC "(" OBJECTCONSTANT ")J", FN_PTR(getJObjectValue)}, {CC "getArrayType", CC "(C" HS_KLASS2 ")" HS_KLASS, FN_PTR(getArrayType)}, {CC "lookupClass", CC "(" CLASS ")" HS_RESOLVED_TYPE, FN_PTR(lookupClass)}, {CC "lookupNameInPool", CC "(" HS_CONSTANT_POOL2 "II)" STRING, FN_PTR(lookupNameInPool)}, diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index f1028b4b2bb7e..2116133e56e97 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -240,7 +240,7 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { cardtable_shift = CardTable::card_shift(); } else { // No card mark barriers - cardtable_start_address = 0; + cardtable_start_address = nullptr; cardtable_shift = 0; } diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index 371a6540898b3..8241fc2498c39 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -628,7 +628,7 @@ JRT_LEAF(oopDesc*, JVMCIRuntime::load_and_clear_exception(JavaThread* thread)) oop exception = thread->exception_oop(); assert(exception != nullptr, "npe"); thread->set_exception_oop(nullptr); - thread->set_exception_pc(0); + thread->set_exception_pc(nullptr); return exception; JRT_END @@ -2078,6 +2078,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV, int compile_id, bool has_monitors, bool has_unsafe_access, + bool has_scoped_access, bool has_wide_vector, JVMCIObject compiled_code, JVMCIObject nmethod_mirror, @@ -2183,7 +2184,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV, nm->set_has_unsafe_access(has_unsafe_access); nm->set_has_wide_vectors(has_wide_vector); nm->set_has_monitors(has_monitors); - nm->set_has_scoped_access(true); // conservative + nm->set_has_scoped_access(has_scoped_access); JVMCINMethodData* data = nm->jvmci_nmethod_data(); assert(data != nullptr, "must be"); diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp index bc5bee4edebee..99738393b5b0d 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.hpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -452,6 +452,7 @@ class JVMCIRuntime: public CHeapObj { int compile_id, bool has_monitors, bool has_unsafe_access, + bool has_scoped_access, bool has_wide_vector, JVMCIObject compiled_code, JVMCIObject nmethod_mirror, diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 1e2ca0e990edb..ac895cc93f2f7 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -655,6 +655,7 @@ declare_constant(ConstMethodFlags::_misc_intrinsic_candidate) \ declare_constant(ConstMethodFlags::_misc_reserved_stack_access) \ declare_constant(ConstMethodFlags::_misc_changes_current_thread) \ + declare_constant(ConstMethodFlags::_misc_is_scoped) \ \ declare_constant(CounterData::count_off) \ \ diff --git a/src/hotspot/share/libadt/vectset.cpp b/src/hotspot/share/libadt/vectset.cpp index 9c711a7021b5a..b0d5d100400e7 100644 --- a/src/hotspot/share/libadt/vectset.cpp +++ b/src/hotspot/share/libadt/vectset.cpp @@ -48,6 +48,10 @@ void VectorSet::init(Arena* arena) { // Expand the existing set to a bigger size void VectorSet::grow(uint new_word_capacity) { + _nesting.check(_set_arena); // Check if a potential reallocation in the arena is safe + if (new_word_capacity < _size) { + return; // No need to grow + } assert(new_word_capacity < (1U << 30), ""); uint x = next_power_of_2(new_word_capacity); if (x > _data_size) { diff --git a/src/hotspot/share/libadt/vectset.hpp b/src/hotspot/share/libadt/vectset.hpp index d55837c3b2b88..eafa60db1fee2 100644 --- a/src/hotspot/share/libadt/vectset.hpp +++ b/src/hotspot/share/libadt/vectset.hpp @@ -42,10 +42,11 @@ class VectorSet : public AnyObj { // Used 32-bit words uint _size; - uint32_t* _data; // Allocated words uint _data_size; + uint32_t* _data; Arena* _set_arena; + ReallocMark _nesting; // Safety checks for arena reallocation void init(Arena* arena); // Grow vector to required word capacity @@ -77,10 +78,7 @@ class VectorSet : public AnyObj { // bool test_set(uint elem) { uint32_t word = elem >> word_bits; - if (word >= _size) { - // Then grow - grow(word); - } + grow(word); uint32_t mask = 1U << (elem & bit_mask); uint32_t data = _data[word]; _data[word] = data | mask; @@ -109,9 +107,7 @@ class VectorSet : public AnyObj { // Fast inlined set void set(uint elem) { uint32_t word = elem >> word_bits; - if (word >= _size) { - grow(word); - } + grow(word); uint32_t mask = 1U << (elem & bit_mask); _data[word] |= mask; } diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index 3a54d1f4e6369..87b195f4fbfdd 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -370,7 +370,7 @@ bool LogConfiguration::parse_command_line_arguments(const char* opts) { // Split the option string to its colon separated components. char* str = copy; - char* substrings[4] = {0}; + char* substrings[4] = {}; for (int i = 0 ; i < 4; i++) { substrings[i] = str; diff --git a/src/hotspot/share/memory/allocation.cpp b/src/hotspot/share/memory/allocation.cpp index 0f05bdda461f4..0f2ff7840b8d2 100644 --- a/src/hotspot/share/memory/allocation.cpp +++ b/src/hotspot/share/memory/allocation.cpp @@ -237,9 +237,10 @@ ReallocMark::ReallocMark() { #endif } -void ReallocMark::check() { +void ReallocMark::check(Arena* arena) { #ifdef ASSERT - if (_nesting != Thread::current()->resource_area()->nesting()) { + if ((arena == nullptr || arena == Thread::current()->resource_area()) && + _nesting != Thread::current()->resource_area()->nesting()) { fatal("allocation bug: array could grow within nested ResourceMark"); } #endif diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 495ca66d86785..da5f7b1983002 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -556,8 +556,8 @@ class ReallocMark: public StackObj { NOT_PRODUCT(int _nesting;) public: - ReallocMark() PRODUCT_RETURN; - void check() PRODUCT_RETURN; + ReallocMark() PRODUCT_RETURN; + void check(Arena* arena = nullptr) PRODUCT_RETURN; }; // Uses mmapped memory for all allocations. All allocations are initially diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index 0399c6922e38d..435a8cfdd584f 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -44,6 +44,19 @@ STATIC_ASSERT(is_aligned((int)Chunk::init_size, ARENA_AMALLOC_ALIGNMENT)); STATIC_ASSERT(is_aligned((int)Chunk::medium_size, ARENA_AMALLOC_ALIGNMENT)); STATIC_ASSERT(is_aligned((int)Chunk::size, ARENA_AMALLOC_ALIGNMENT)); + +const char* Arena::tag_name[] = { +#define ARENA_TAG_STRING(name, str, desc) XSTR(name), + DO_ARENA_TAG(ARENA_TAG_STRING) +#undef ARENA_TAG_STRING +}; + +const char* Arena::tag_desc[] = { +#define ARENA_TAG_DESC(name, str, desc) XSTR(desc), + DO_ARENA_TAG(ARENA_TAG_DESC) +#undef ARENA_TAG_DESC +}; + // MT-safe pool of same-sized chunks to reduce malloc/free thrashing // NB: not using Mutex because pools are used before Threads are initialized class ChunkPool { diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index 1ca8a78782541..1f3c5eb4e8bac 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -83,17 +83,29 @@ class Chunk { bool contains(char* p) const { return bottom() <= p && p <= top(); } }; +#define DO_ARENA_TAG(FN) \ + FN(other, Others, Other arenas) \ + FN(ra, RA, Resource areas) \ + FN(ha, HA, Handle area) \ + FN(node, NA, Node arena) \ + // Fast allocation of memory class Arena : public CHeapObjBase { public: - - enum class Tag : uint8_t { - tag_other = 0, - tag_ra, // resource area - tag_ha, // handle area - tag_node // C2 Node arena + enum class Tag: uint8_t { +#define ARENA_TAG_ENUM(name, str, desc) tag_##name, + DO_ARENA_TAG(ARENA_TAG_ENUM) +#undef ARENA_TAG_ENUM + tag_count }; + constexpr static int tag_count() { + return static_cast(Tag::tag_count); + } + + static const char* tag_name[static_cast(Arena::Tag::tag_count)]; + static const char* tag_desc[static_cast(Arena::Tag::tag_count)]; + private: const MEMFLAGS _flags; // Memory tracking flags const Tag _tag; diff --git a/src/hotspot/share/memory/metaspace/blockTree.cpp b/src/hotspot/share/memory/metaspace/blockTree.cpp index 934f25d84ccb3..1f1e54f4a4612 100644 --- a/src/hotspot/share/memory/metaspace/blockTree.cpp +++ b/src/hotspot/share/memory/metaspace/blockTree.cpp @@ -178,8 +178,6 @@ void BlockTree::verify() const { // (which also verifies that we visited every node, or at least // as many nodes as are in this tree) _counter.check(counter); - - #undef assrt0n } void BlockTree::zap_range(MetaWord* p, size_t word_size) { diff --git a/src/hotspot/share/memory/metaspace/metachunk.cpp b/src/hotspot/share/memory/metaspace/metachunk.cpp index 3d67aac80160e..0bf7e98f13022 100644 --- a/src/hotspot/share/memory/metaspace/metachunk.cpp +++ b/src/hotspot/share/memory/metaspace/metachunk.cpp @@ -253,7 +253,7 @@ void Metachunk::verify_neighborhood() const { } } -volatile MetaWord dummy = 0; +volatile MetaWord dummy = nullptr; void Metachunk::verify() const { // Note. This should be called under CLD lock protection. diff --git a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp index ee3af35a5b8b4..24377ec25ad12 100644 --- a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp +++ b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -59,7 +59,7 @@ RootChunkArea::~RootChunkArea() { // root chunk header. It will be partly initialized. // Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode. Metachunk* RootChunkArea::alloc_root_chunk_header(VirtualSpaceNode* node) { - assert(_first_chunk == 0, "already have a root"); + assert(_first_chunk == nullptr, "already have a root"); Metachunk* c = ChunkHeaderPool::pool()->allocate_chunk_header(); c->initialize(node, const_cast(_base), chunklevel::ROOT_CHUNK_LEVEL); _first_chunk = c; diff --git a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp index 7df35bbeec88e..c27e607353af1 100644 --- a/src/hotspot/share/memory/virtualspace.cpp +++ b/src/hotspot/share/memory/virtualspace.cpp @@ -159,7 +159,7 @@ static char* reserve_memory(char* requested_address, const size_t size, // If the memory was requested at a particular address, use // os::attempt_reserve_memory_at() to avoid mapping over something // important. If the reservation fails, return null. - if (requested_address != 0) { + if (requested_address != nullptr) { assert(is_aligned(requested_address, alignment), "Requested address " PTR_FORMAT " must be aligned to " SIZE_FORMAT, p2i(requested_address), alignment); diff --git a/src/hotspot/share/oops/arrayOop.hpp b/src/hotspot/share/oops/arrayOop.hpp index 0aa26500bd8d2..1ca8a9530a48c 100644 --- a/src/hotspot/share/oops/arrayOop.hpp +++ b/src/hotspot/share/oops/arrayOop.hpp @@ -68,10 +68,10 @@ class arrayOopDesc : public oopDesc { // The header is considered the oop part of this type plus the length. // This is not equivalent to sizeof(arrayOopDesc) which should not appear in the code. static int header_size_in_bytes() { - size_t hs = length_offset_in_bytes() + sizeof(int); + int hs = length_offset_in_bytes() + (int)sizeof(int); #ifdef ASSERT // make sure it isn't called before UseCompressedOops is initialized. - static size_t arrayoopdesc_hs = 0; + static int arrayoopdesc_hs = 0; if (arrayoopdesc_hs == 0) arrayoopdesc_hs = hs; assert(arrayoopdesc_hs == hs, "header size can't change"); #endif // ASSERT @@ -83,13 +83,13 @@ class arrayOopDesc : public oopDesc { // it occupies the second half of the _klass field in oopDesc. static int length_offset_in_bytes() { return UseCompressedClassPointers ? klass_gap_offset_in_bytes() : - sizeof(arrayOopDesc); + (int)sizeof(arrayOopDesc); } // Returns the offset of the first element. static int base_offset_in_bytes(BasicType type) { - size_t hs = header_size_in_bytes(); - return (int)(element_type_should_be_aligned(type) ? align_up(hs, BytesPerLong) : hs); + int hs = header_size_in_bytes(); + return element_type_should_be_aligned(type) ? align_up(hs, BytesPerLong) : hs; } // Returns the address of the first element. The elements in the array will not @@ -134,14 +134,14 @@ class arrayOopDesc : public oopDesc { assert(type < T_CONFLICT, "wrong type"); assert(type2aelembytes(type) != 0, "wrong type"); - size_t hdr_size_in_bytes = base_offset_in_bytes(type); + int hdr_size_in_bytes = base_offset_in_bytes(type); // This is rounded-up and may overlap with the first array elements. - size_t hdr_size_in_words = align_up(hdr_size_in_bytes, HeapWordSize) / HeapWordSize; + int hdr_size_in_words = align_up(hdr_size_in_bytes, HeapWordSize) / HeapWordSize; const size_t max_element_words_per_size_t = - align_down((SIZE_MAX/HeapWordSize - hdr_size_in_words), MinObjAlignment); + align_down((SIZE_MAX/HeapWordSize - (size_t)hdr_size_in_words), MinObjAlignment); const size_t max_elements_per_size_t = - HeapWordSize * max_element_words_per_size_t / type2aelembytes(type); + HeapWordSize * max_element_words_per_size_t / (size_t)type2aelembytes(type); if ((size_t)max_jint < max_elements_per_size_t) { // It should be ok to return max_jint here, but parts of the code // (CollectedHeap, Klass::oop_oop_iterate(), and more) uses an int for diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 35a1d95b6e647..1c295e10b21db 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -2065,7 +2065,7 @@ static void print_cpool_bytes(jint cnt, u1 *bytes) { size += ent_size; } printf("Cpool size: %d\n", size); - fflush(0); + fflush(nullptr); return; } /* end print_cpool_bytes */ diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 930f8cf177116..650279db1bee3 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2779,9 +2779,9 @@ void InstanceKlass::release_C_heap_structures(bool release_sub_metadata) { #if INCLUDE_JVMTI // Deallocate breakpoint records - if (breakpoints() != 0x0) { + if (breakpoints() != nullptr) { methods_do(clear_all_breakpoints); - assert(breakpoints() == 0x0, "should have cleared breakpoints"); + assert(breakpoints() == nullptr, "should have cleared breakpoints"); } // deallocate the cached class file diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index e5ba0d610314b..f4dcd4f149302 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -322,7 +322,7 @@ void Method::mask_for(const methodHandle& this_mh, int bci, InterpreterOopMap* m } int Method::bci_from(address bcp) const { - if (is_native() && bcp == 0) { + if (is_native() && bcp == nullptr) { return 0; } // Do not have a ResourceMark here because AsyncGetCallTrace stack walking code @@ -345,7 +345,7 @@ int Method::validate_bci(int bci) const { int Method::validate_bci_from_bcp(address bcp) const { // keep bci as -1 if not a valid bci int bci = -1; - if (bcp == 0 || bcp == code_base()) { + if (bcp == nullptr || bcp == code_base()) { // code_size() may return 0 and we allow 0 here // the method may be native bci = 0; diff --git a/src/hotspot/share/oops/objArrayKlass.cpp b/src/hotspot/share/oops/objArrayKlass.cpp index 94d3c6b6b358e..bee010b6d7244 100644 --- a/src/hotspot/share/oops/objArrayKlass.cpp +++ b/src/hotspot/share/oops/objArrayKlass.cpp @@ -173,7 +173,7 @@ oop ObjArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) { for (int i = 0; i < rank - 1; ++i) { sizes += 1; if (*sizes < 0) { - THROW_MSG_0(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", *sizes)); + THROW_MSG_NULL(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", *sizes)); } } } diff --git a/src/hotspot/share/oops/stackChunkOop.cpp b/src/hotspot/share/oops/stackChunkOop.cpp index 8d230b334c88f..344fef1530837 100644 --- a/src/hotspot/share/oops/stackChunkOop.cpp +++ b/src/hotspot/share/oops/stackChunkOop.cpp @@ -125,7 +125,7 @@ static int num_java_frames(const StackChunkFrameStream& f) { int stackChunkOopDesc::num_java_frames() const { int n = 0; for (StackChunkFrameStream f(const_cast(this)); !f.is_done(); - f.next(SmallRegisterMap::instance)) { + f.next(SmallRegisterMap::instance())) { if (!f.is_stub()) { n += ::num_java_frames(f); } diff --git a/src/hotspot/share/oops/stackChunkOop.inline.hpp b/src/hotspot/share/oops/stackChunkOop.inline.hpp index 54f1423fd1cd9..a54b8159e7ef5 100644 --- a/src/hotspot/share/oops/stackChunkOop.inline.hpp +++ b/src/hotspot/share/oops/stackChunkOop.inline.hpp @@ -201,7 +201,7 @@ inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure template inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure) { - const SmallRegisterMap* map = SmallRegisterMap::instance; + const SmallRegisterMap* map = SmallRegisterMap::instance(); assert(!map->in_cont(), ""); StackChunkFrameStream f(this); diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 78774ff2fd1c3..1af085cd1282d 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -39,7 +39,10 @@ #include "utilities/powerOfTwo.hpp" void Block_Array::grow( uint i ) { - assert(i >= Max(), "must be an overflow"); + _nesting.check(_arena); // Check if a potential reallocation in the arena is safe + if (i < Max()) { + return; // No need to grow + } debug_only(_limit = i+1); if( i < _size ) return; if( !_size ) { @@ -374,6 +377,7 @@ void Block::dump(const PhaseCFG* cfg) const { PhaseCFG::PhaseCFG(Arena* arena, RootNode* root, Matcher& matcher) : Phase(CFG) , _root(root) +, _blocks(arena) , _block_arena(arena) , _regalloc(nullptr) , _scheduling_for_pressure(false) @@ -1417,7 +1421,7 @@ UnionFind::UnionFind( uint max ) : _cnt(max), _max(max), _indices(NEW_RESOURCE_A } void UnionFind::extend( uint from_idx, uint to_idx ) { - _nesting.check(); + _nesting.check(); // Check if a potential reallocation in the resource arena is safe if( from_idx >= _max ) { uint size = 16; while( size <= from_idx ) size <<=1; diff --git a/src/hotspot/share/opto/block.hpp b/src/hotspot/share/opto/block.hpp index e48b50b06ae8a..231c09949037d 100644 --- a/src/hotspot/share/opto/block.hpp +++ b/src/hotspot/share/opto/block.hpp @@ -51,6 +51,7 @@ class Block_Array : public ArenaObj { uint _size; // allocated size, as opposed to formal limit debug_only(uint _limit;) // limit to formal domain Arena *_arena; // Arena to allocate in + ReallocMark _nesting; // Safety checks for arena reallocation protected: Block **_blocks; void grow( uint i ); // Grow array node to fit @@ -68,7 +69,7 @@ class Block_Array : public ArenaObj { Block *operator[] ( uint i ) const // Lookup, or assert for not mapped { assert( i < Max(), "oob" ); return _blocks[i]; } // Extend the mapping: index i maps to Block *n. - void map( uint i, Block *n ) { if( i>=Max() ) grow(i); _blocks[i] = n; } + void map( uint i, Block *n ) { grow(i); _blocks[i] = n; } uint Max() const { debug_only(return _limit); return _size; } }; @@ -77,7 +78,9 @@ class Block_List : public Block_Array { friend class VMStructs; public: uint _cnt; - Block_List() : Block_Array(Thread::current()->resource_area()), _cnt(0) {} + Block_List() : Block_List(Thread::current()->resource_area()) { } + Block_List(Arena* a) : Block_Array(a), _cnt(0) { } + void push( Block *b ) { map(_cnt++,b); } Block *pop() { return _blocks[--_cnt]; } Block *rpop() { Block *b = _blocks[0]; _blocks[0]=_blocks[--_cnt]; return b;} @@ -655,7 +658,7 @@ class PhaseCFG : public Phase { class UnionFind : public ResourceObj { uint _cnt, _max; uint* _indices; - ReallocMark _nesting; // assertion check for reallocations + ReallocMark _nesting; // Safety checks for arena reallocation public: UnionFind( uint max ); void reset( uint max ); // Reset to identity map for [0..max] diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 818640a6f6575..120bb112ec56b 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -754,18 +754,18 @@ class CallJavaNode : public CallNode { virtual bool cmp( const Node &n ) const; virtual uint size_of() const; // Size is bigger + ciMethod* _method; // Method being direct called bool _optimized_virtual; bool _method_handle_invoke; bool _override_symbolic_info; // Override symbolic call site info from bytecode - ciMethod* _method; // Method being direct called bool _arg_escape; // ArgEscape in parameter list public: CallJavaNode(const TypeFunc* tf , address addr, ciMethod* method) : CallNode(tf, addr, TypePtr::BOTTOM), + _method(method), _optimized_virtual(false), _method_handle_invoke(false), _override_symbolic_info(false), - _method(method), _arg_escape(false) { init_class_id(Class_CallJava); diff --git a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp index 3e20a92e848ed..87989235c6701 100644 --- a/src/hotspot/share/opto/castnode.hpp +++ b/src/hotspot/share/opto/castnode.hpp @@ -101,12 +101,9 @@ class CastIINode: public ConstraintCastNode { virtual uint size_of() const; public: - CastIINode(Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false, const TypeTuple* types = nullptr) - : ConstraintCastNode(nullptr, n, t, dependency, types), _range_check_dependency(range_check_dependency) { - init_class_id(Class_CastII); - } CastIINode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types), _range_check_dependency(range_check_dependency) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastII); } virtual int Opcode() const; @@ -134,6 +131,7 @@ class CastLLNode: public ConstraintCastNode { public: CastLLNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastLL); } @@ -147,6 +145,7 @@ class CastFFNode: public ConstraintCastNode { public: CastFFNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastFF); } virtual int Opcode() const; @@ -157,6 +156,7 @@ class CastDDNode: public ConstraintCastNode { public: CastDDNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastDD); } virtual int Opcode() const; @@ -167,6 +167,7 @@ class CastVVNode: public ConstraintCastNode { public: CastVVNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastVV); } virtual int Opcode() const; @@ -192,6 +193,7 @@ class CheckCastPPNode: public ConstraintCastNode { public: CheckCastPPNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CheckCastPP); } diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 18d7577d9e621..5edd31fa71695 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -449,6 +449,10 @@ class IfNode : public MultiBranchNode { static const TypeInt* filtered_int_type(PhaseGVN* phase, Node* val, Node* if_proj); #ifndef PRODUCT + AssertionPredicateType assertion_predicate_type() const { + return _assertion_predicate_type; + } + virtual void dump_spec(outputStream *st) const; #endif diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 7908f21de10ba..dc61377687302 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -324,7 +324,6 @@ shmacro(ShenandoahCompareAndSwapN) shmacro(ShenandoahCompareAndSwapP) shmacro(ShenandoahWeakCompareAndSwapN) shmacro(ShenandoahWeakCompareAndSwapP) -shmacro(ShenandoahIUBarrier) shmacro(ShenandoahLoadReferenceBarrier) macro(SCMemProj) macro(CopySignD) diff --git a/src/hotspot/share/opto/domgraph.cpp b/src/hotspot/share/opto/domgraph.cpp index 46cf3316af51e..363005d1181c2 100644 --- a/src/hotspot/share/opto/domgraph.cpp +++ b/src/hotspot/share/opto/domgraph.cpp @@ -61,9 +61,6 @@ struct Tarjan { // Compute the dominator tree of the CFG. The CFG must already have been // constructed. This is the Lengauer & Tarjan O(E-alpha(E,V)) algorithm. void PhaseCFG::build_dominator_tree() { - // Pre-grow the blocks array, prior to the ResourceMark kicking in - _blocks.map(number_of_blocks(), nullptr); - ResourceMark rm; // Setup mappings from my Graph to Tarjan's stuff and back // Note: Tarjan uses 1-based arrays diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 2ca722148b61e..eb6887f11baf6 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -3879,7 +3879,7 @@ PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, Gro // Return a new version of Memory Phi "orig_phi" with the inputs having the // specified alias index. // -PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist) { +PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, uint rec_depth) { assert(alias_idx != Compile::AliasIdxBot, "can't split out bottom memory"); Compile *C = _compile; PhaseGVN* igvn = _igvn; @@ -3895,7 +3895,7 @@ PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, Gro bool finished = false; while(!finished) { while (idx < phi->req()) { - Node *mem = find_inst_mem(phi->in(idx), alias_idx, orig_phi_worklist); + Node *mem = find_inst_mem(phi->in(idx), alias_idx, orig_phi_worklist, rec_depth + 1); if (mem != nullptr && mem->is_Phi()) { PhiNode *newphi = create_split_phi(mem->as_Phi(), alias_idx, orig_phi_worklist, new_phi_created); if (new_phi_created) { @@ -4037,7 +4037,12 @@ void ConnectionGraph::move_inst_mem(Node* n, GrowableArray &orig_phi // Search memory chain of "mem" to find a MemNode whose address // is the specified alias index. // -Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArray &orig_phis) { +#define FIND_INST_MEM_RECURSION_DEPTH_LIMIT 1000 +Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArray &orig_phis, uint rec_depth) { + if (rec_depth > FIND_INST_MEM_RECURSION_DEPTH_LIMIT) { + _compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis()); + return nullptr; + } if (orig_mem == nullptr) { return orig_mem; } @@ -4111,7 +4116,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra if (result == mmem->base_memory()) { // Didn't find instance memory, search through general slice recursively. result = mmem->memory_at(C->get_general_index(alias_idx)); - result = find_inst_mem(result, alias_idx, orig_phis); + result = find_inst_mem(result, alias_idx, orig_phis, rec_depth + 1); if (C->failing()) { return nullptr; } @@ -4179,7 +4184,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra orig_phis.append_if_missing(mphi); } else if (C->get_alias_index(t) != alias_idx) { // Create a new Phi with the specified alias index type. - result = split_memory_phi(mphi, alias_idx, orig_phis); + result = split_memory_phi(mphi, alias_idx, orig_phis, rec_depth + 1); } } // the result is either MemNode, PhiNode, InitializeNode. diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index 658c8f6e8feca..32e70be219ab0 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -549,10 +549,10 @@ class ConnectionGraph: public ArenaObj { bool split_AddP(Node *addp, Node *base); PhiNode *create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, bool &new_created); - PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist); + PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, uint rec_depth); void move_inst_mem(Node* n, GrowableArray &orig_phis); - Node* find_inst_mem(Node* mem, int alias_idx,GrowableArray &orig_phi_worklist); + Node* find_inst_mem(Node* mem, int alias_idx,GrowableArray &orig_phi_worklist, uint rec_depth = 0); Node* step_through_mergemem(MergeMemNode *mmem, int alias_idx, const TypeOopPtr *toop); Node_Array _node_map; // used for bookkeeping during type splitting diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 569d6bc35cd16..3bc5b9a8b2a7d 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2151,7 +2151,7 @@ Node* GraphKit::uncommon_trap(int trap_request, kill_dead_locals(); // Now insert the uncommon trap subroutine call - address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); + address call_addr = OptoRuntime::uncommon_trap_blob()->entry_point(); const TypePtr* no_memory_effects = nullptr; // Pass the index of the class to be loaded Node* call = make_runtime_call(RC_NO_LEAF | RC_UNCOMMON | diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index cedbc66bbb444..8e5cd2702137a 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -1843,10 +1843,10 @@ void IfProjNode::pin_array_access_nodes(PhaseIterGVN* igvn) { #ifndef PRODUCT void IfNode::dump_spec(outputStream* st) const { switch (_assertion_predicate_type) { - case AssertionPredicateType::Init_value: + case AssertionPredicateType::InitValue: st->print("#Init Value Assertion Predicate "); break; - case AssertionPredicateType::Last_value: + case AssertionPredicateType::LastValue: st->print("#Last Value Assertion Predicate "); break; case AssertionPredicateType::None: diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 549290397e3b7..9db94748ca27c 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -122,7 +122,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo for (uint i1 = 0; i1 < null_block->number_of_nodes(); i1++) { Node* nn = null_block->get_node(i1); if (nn->is_MachCall() && - nn->as_MachCall()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point()) { + nn->as_MachCall()->entry_point() == OptoRuntime::uncommon_trap_blob()->entry_point()) { const Type* trtype = nn->in(TypeFunc::Parms)->bottom_type(); if (trtype->isa_int() && trtype->is_int()->is_con()) { jint tr_con = trtype->is_int()->get_con(); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index f1c3e592fff53..9660413dd190f 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -5949,7 +5949,7 @@ CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* no for (DUIterator_Fast jmax, j = other_proj->fast_outs(jmax); j < jmax; j++) { Node* obs = other_proj->fast_out(j); if (obs->in(0) == other_proj && obs->is_CallStaticJava() && - (obs->as_CallStaticJava()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point())) { + (obs->as_CallStaticJava()->entry_point() == OptoRuntime::uncommon_trap_blob()->entry_point())) { return obs->as_CallStaticJava(); } } diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index f8d3fa0b6d91d..5e585a406f20c 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -374,10 +374,10 @@ IfProjNode* PhaseIdealLoop::clone_assertion_predicate_for_unswitched_loops(IfNod IfProjNode* predicate, Deoptimization::DeoptReason reason, ParsePredicateSuccessProj* parse_predicate_proj) { - TemplateAssertionPredicateExpression template_assertion_predicate_expression( - template_assertion_predicate->in(1)->as_Opaque4()); - Opaque4Node* cloned_opaque4_node = template_assertion_predicate_expression.clone(parse_predicate_proj->in(0)->in(0), this); - IfProjNode* if_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, template_assertion_predicate->Opcode(), false); + TemplateAssertionExpression template_assertion_expression(template_assertion_predicate->in(1)->as_Opaque4()); + Opaque4Node* cloned_opaque4_node = template_assertion_expression.clone(parse_predicate_proj->in(0)->in(0), this); + IfProjNode* if_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, + template_assertion_predicate->Opcode(), false); _igvn.replace_input_of(if_proj->in(0), 1, cloned_opaque4_node); _igvn.replace_input_of(parse_predicate_proj->in(0), 0, if_proj); set_idom(parse_predicate_proj->in(0), if_proj, dom_depth(if_proj)); @@ -1324,7 +1324,7 @@ IfTrueNode* PhaseIdealLoop::add_template_assertion_predicate(IfNode* iff, IdealL C->add_template_assertion_predicate_opaq(opaque_bol); register_new_node(opaque_bol, upper_bound_proj); IfTrueNode* new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode(), - false NOT_PRODUCT(COMMA AssertionPredicateType::Init_value)); + false NOT_PRODUCT(COMMA AssertionPredicateType::InitValue)); _igvn.replace_input_of(new_proj->in(0), 1, opaque_bol); assert(opaque_init->outcnt() > 0, "should be used"); @@ -1341,8 +1341,8 @@ IfTrueNode* PhaseIdealLoop::add_template_assertion_predicate(IfNode* iff, IdealL // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv Phi const Type* type_iv = loop->_head->as_CountedLoop()->phi()->bottom_type(); assert(!type_iv->is_int()->is_con(), "constant indicates one loop iteration for which we bailed out earlier"); - max_value = new CastIINode(max_value, type_iv); - register_new_node(max_value, parse_predicate_proj); + max_value = new CastIINode(new_proj, max_value, type_iv); + register_new_node(max_value, new_proj); bol = rc_predicate(new_proj, scale, offset, max_value, limit, stride, rng, (stride > 0) != (scale > 0), overflow); @@ -1350,7 +1350,7 @@ IfTrueNode* PhaseIdealLoop::add_template_assertion_predicate(IfNode* iff, IdealL C->add_template_assertion_predicate_opaq(opaque_bol); register_new_node(opaque_bol, new_proj); new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode(), - false NOT_PRODUCT(COMMA AssertionPredicateType::Last_value)); + false NOT_PRODUCT(COMMA AssertionPredicateType::LastValue)); _igvn.replace_input_of(new_proj->in(0), 1, opaque_bol); assert(max_value->outcnt() > 0, "should be used"); assert(assertion_predicate_has_loop_opaque_node(new_proj->in(0)->as_If()), "unexpected"); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 1b29d31ba68f7..99742a598e858 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1373,17 +1373,12 @@ void PhaseIdealLoop::copy_assertion_predicates_to_main_loop_helper(const Predica Node* bol = iff->in(1); assert(!bol->is_OpaqueInitializedAssertionPredicate(), "should not find an Initialized Assertion Predicate"); if (bol->is_Opaque4()) { - assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes"); // Clone the Assertion Predicate twice and initialize one with the initial // value of the loop induction variable. Leave the other predicate // to be initialized when increasing the stride during loop unrolling. - prev_proj = clone_assertion_predicate_and_initialize(iff, opaque_init, nullptr, predicate_proj, uncommon_proj, - current_proj, outer_loop, prev_proj); - assert(assertion_predicate_has_loop_opaque_node(prev_proj->in(0)->as_If()), ""); - - prev_proj = clone_assertion_predicate_and_initialize(iff, init, stride, predicate_proj, uncommon_proj, - current_proj, outer_loop, prev_proj); - assert(!assertion_predicate_has_loop_opaque_node(prev_proj->in(0)->as_If()), ""); + prev_proj = clone_template_assertion_predicate(iff, opaque_init, predicate_proj, uncommon_proj, + current_proj, outer_loop, prev_proj); + prev_proj = create_initialized_assertion_predicate(iff, init, stride, prev_proj); // Rewire any control inputs from the cloned Assertion Predicates down to the main and post loop for data nodes // that are part of the main loop (and were cloned to the pre and post loop). @@ -1460,7 +1455,7 @@ void PhaseIdealLoop::count_opaque_loop_nodes(Node* n, uint& init, uint& stride) wq.push(n); for (uint i = 0; i < wq.size(); i++) { Node* n = wq.at(i); - if (TemplateAssertionPredicateExpressionNode::is_maybe_in_expression(n)) { + if (TemplateAssertionExpressionNode::is_maybe_in_expression(n)) { if (n->is_OpaqueLoopInit()) { init++; } else if (n->is_OpaqueLoopStride()) { @@ -1477,27 +1472,28 @@ void PhaseIdealLoop::count_opaque_loop_nodes(Node* n, uint& init, uint& stride) } } -// Clone an Assertion Predicate for the main loop. new_init and new_stride are set as new inputs. Since the predicates -// cannot fail at runtime, Halt nodes are inserted instead of uncommon traps. -Node* PhaseIdealLoop::clone_assertion_predicate_and_initialize(Node* iff, Node* new_init, Node* new_stride, Node* predicate, - Node* uncommon_proj, Node* control, IdealLoopTree* outer_loop, - Node* input_proj) { - TemplateAssertionPredicateExpression template_assertion_predicate_expression(iff->in(1)->as_Opaque4()); - Node* new_opaque_node; - if (new_stride == nullptr) { - // Clone the Template Assertion Predicate and set a new OpaqueLoopInitNode to create a new Template Assertion Predicate. - // This is done when creating a new Template Assertion Predicate for the main loop which requires a new init node. - // We keep the Opaque4 node since it's still a template. - assert(new_init->is_OpaqueLoopInit(), "only for creating new Template Assertion Predicates"); - new_opaque_node = template_assertion_predicate_expression.clone_and_replace_init(new_init, control, this); - } else { - // Create an Initialized Assertion Predicate from the Template Assertion Predicate. - new_opaque_node = template_assertion_predicate_expression.clone_and_replace_init_and_stride(new_init, new_stride, - control, this); - // Since this is an Initialized Assertion Predicate, we use the dedicated opaque node. - new_opaque_node = new OpaqueInitializedAssertionPredicateNode(new_opaque_node->in(1)->as_Bool(), C); - register_new_node(new_opaque_node, control); - } +// Create an Initialized Assertion Predicate from the template_assertion_predicate +IfTrueNode* PhaseIdealLoop::create_initialized_assertion_predicate(IfNode* template_assertion_predicate, Node* new_init, + Node* new_stride, Node* control) { + assert(assertion_predicate_has_loop_opaque_node(template_assertion_predicate), + "must find OpaqueLoop* nodes for Template Assertion Predicate"); + InitializedAssertionPredicate initialized_assertion_predicate(template_assertion_predicate, new_init, new_stride, this); + IfTrueNode* success_proj = initialized_assertion_predicate.create(control); + assert(!assertion_predicate_has_loop_opaque_node(success_proj->in(0)->as_If()), + "Initialized Assertion Predicates do not have OpaqueLoop* nodes in the bool expression anymore"); + return success_proj; +} + +// Clone the Template Assertion Predicate and set a new OpaqueLoopInitNode to create a new Template Assertion Predicate. +// This is done when creating a new Template Assertion Predicate for the main loop which requires a new init node. +// We keep the Opaque4 node since it's still a template. Since the templates are eventually removed after loop opts, +// these are never executed. We therefore insert a Halt node instead of an uncommon trap. +Node* PhaseIdealLoop::clone_template_assertion_predicate(IfNode* iff, Node* new_init, Node* predicate, Node* uncommon_proj, + Node* control, IdealLoopTree* outer_loop, Node* input_proj) { + assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes for Template Assertion Predicate"); + TemplateAssertionExpression template_assertion_expression(iff->in(1)->as_Opaque4()); + assert(new_init->is_OpaqueLoopInit(), "only for creating new Template Assertion Predicates"); + Opaque4Node* new_opaque_node = template_assertion_expression.clone_and_replace_init(new_init, control, this); Node* proj = predicate->clone(); Node* other_proj = uncommon_proj->clone(); Node* new_iff = iff->clone(); @@ -1506,8 +1502,7 @@ Node* PhaseIdealLoop::clone_assertion_predicate_and_initialize(Node* iff, Node* other_proj->set_req(0, new_iff); Node* frame = new ParmNode(C->start(), TypeFunc::FramePtr); register_new_node(frame, C->start()); - // It's impossible for the predicate to fail at runtime. Use a Halt node. - Node* halt = new HaltNode(other_proj, frame, "duplicated predicate failed which is impossible"); + Node* halt = new HaltNode(other_proj, frame, "Template Assertion Predicates are always removed before code generation"); _igvn.add_input_to(C->root(), halt); new_iff->set_req(0, input_proj); @@ -1515,6 +1510,8 @@ Node* PhaseIdealLoop::clone_assertion_predicate_and_initialize(Node* iff, Node* register_control(proj, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, new_iff); register_control(other_proj, _ltree_root, new_iff); register_control(halt, _ltree_root, other_proj); + assert(assertion_predicate_has_loop_opaque_node(proj->in(0)->as_If()), + "Template Assertion Predicates must have OpaqueLoop* nodes in the bool expression"); return proj; } @@ -1967,9 +1964,7 @@ void PhaseIdealLoop::update_main_loop_assertion_predicates(Node* ctrl, CountedLo // Create an Initialized Assertion Predicates for it accordingly: // - For the initial access a[init] (same as before) // - For the last access a[init+new_stride-orig_stride] (with the new unroll stride) - prev_proj = clone_assertion_predicate_and_initialize(iff, init, max_value, entry, proj, ctrl, outer_loop, - prev_proj); - assert(!assertion_predicate_has_loop_opaque_node(prev_proj->in(0)->as_If()), "unexpected"); + prev_proj = create_initialized_assertion_predicate(iff, init, max_value, prev_proj); } else { // Ignore Opaque4 from a non-null-check for an intrinsic or unsafe access. This could happen when we maximally // unroll a non-main loop with such an If with an Opaque4 node directly above the loop entry. @@ -2008,10 +2003,7 @@ void PhaseIdealLoop::copy_assertion_predicates_to_post_loop(LoopNode* main_loop_ } if (iff->in(1)->is_Opaque4()) { // Initialize from Template Assertion Predicate. - assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes"); - prev_proj = clone_assertion_predicate_and_initialize(iff, init, stride, ctrl, proj, post_loop_entry, - post_loop, prev_proj); - assert(!assertion_predicate_has_loop_opaque_node(prev_proj->in(0)->as_If()), "must not find OpaqueLoop* nodes"); + prev_proj = create_initialized_assertion_predicate(iff, init, stride, prev_proj); } ctrl = ctrl->in(0)->in(0); } @@ -2030,9 +2022,7 @@ void PhaseIdealLoop::initialize_assertion_predicates_for_peeled_loop(const Predi if (!predicate_block->has_parse_predicate()) { return; } - Node* control = outer_loop_head->in(LoopNode::EntryControl); - Node* input_proj = control; - + Node* input_proj = outer_loop_head->in(LoopNode::EntryControl); const Node* parse_predicate_uncommon_trap = predicate_block->parse_predicate()->uncommon_trap(); Node* next_regular_predicate_proj = predicate_block->skip_parse_predicate(); while (next_regular_predicate_proj->is_IfProj()) { @@ -2046,9 +2036,7 @@ void PhaseIdealLoop::initialize_assertion_predicates_for_peeled_loop(const Predi assert(!bol->is_OpaqueInitializedAssertionPredicate(), "should not find an Initialized Assertion Predicate"); if (bol->is_Opaque4()) { // Initialize from Template Assertion Predicate. - assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes"); - input_proj = clone_assertion_predicate_and_initialize(iff, init, stride, next_regular_predicate_proj, uncommon_proj, control, - outer_loop, input_proj); + input_proj = create_initialized_assertion_predicate(iff, init, stride, input_proj); // Rewire any control inputs from the old Assertion Predicates above the peeled iteration down to the initialized // Assertion Predicates above the peeled loop. @@ -2871,6 +2859,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // Must know if its a count-up or count-down loop int stride_con = cl->stride_con(); + bool abs_stride_is_one = stride_con == 1 || stride_con == -1; Node* zero = _igvn.longcon(0); Node* one = _igvn.longcon(1); // Use symmetrical int range [-max_jint,max_jint] @@ -2882,6 +2871,15 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { Node* loop_entry = cl->skip_strip_mined()->in(LoopNode::EntryControl); assert(loop_entry->is_Proj() && loop_entry->in(0)->is_If(), "if projection only"); + // if abs(stride) == 1, an Assertion Predicate for the final iv value is added. We don't know the final iv value until + // we're done with range check elimination so use a place holder. + Node* final_iv_placeholder = nullptr; + if (abs_stride_is_one) { + final_iv_placeholder = new Node(1); + _igvn.set_type(final_iv_placeholder, TypeInt::INT); + final_iv_placeholder->init_req(0, loop_entry); + } + // Check loop body for tests of trip-counter plus loop-invariant vs loop-variant. for (uint i = 0; i < loop->_body.size(); i++) { Node *iff = loop->_body[i]; @@ -2985,6 +2983,20 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { Node* opaque_init = new OpaqueLoopInitNode(C, init); register_new_node(opaque_init, loop_entry); + if (abs_stride_is_one) { + // If the main loop becomes empty and the array access for this range check is sunk out of the loop, the index + // for the array access will be set to the index value of the final iteration which could be out of loop. + // Add an Assertion Predicate for that corner case. The final iv is computed from LoopLimit which is the + // LoopNode::limit() only if abs(stride) == 1 otherwise the computation depends on LoopNode::init_trip() as + // well. When LoopLimit only depends on LoopNode::limit(), there are cases where the zero trip guard for the + // main loop doesn't constant fold after range check elimination but, the array access for the final + // iteration of the main loop is out of bound and the index for that access is out of range for the range + // check CastII. + loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset, + int_limit, stride_con, final_iv_placeholder, false); + assert(!assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected"); + } + // Initialized Assertion Predicate for the value of the initial main-loop. loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset, int_limit, stride_con, init, false); @@ -2994,7 +3006,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // unrolling or splitting this main-loop further. loop_entry = add_range_check_elimination_assertion_predicate( loop, loop_entry, scale_con, int_offset, int_limit, stride_con, opaque_init, true - NOT_PRODUCT(COMMA AssertionPredicateType::Init_value)); + NOT_PRODUCT(COMMA AssertionPredicateType::InitValue)); assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected"); Node* opaque_stride = new OpaqueLoopStrideNode(C, cl->stride()); @@ -3004,11 +3016,11 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { max_value = new AddINode(opaque_init, max_value); register_new_node(max_value, loop_entry); // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv Phi - max_value = new CastIINode(max_value, loop->_head->as_CountedLoop()->phi()->bottom_type()); + max_value = new CastIINode(loop_entry, max_value, loop->_head->as_CountedLoop()->phi()->bottom_type()); register_new_node(max_value, loop_entry); loop_entry = add_range_check_elimination_assertion_predicate( loop, loop_entry, scale_con, int_offset, int_limit, stride_con, max_value, true - NOT_PRODUCT(COMMA AssertionPredicateType::Last_value)); + NOT_PRODUCT(COMMA AssertionPredicateType::LastValue)); assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected"); } else { @@ -3116,11 +3128,14 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { register_new_node(main_limit, pre_ctrl); // Hack the now-private loop bounds _igvn.replace_input_of(main_cmp, 2, main_limit); + if (abs_stride_is_one) { + Node* final_iv = new SubINode(main_limit, cl->stride()); + register_new_node(final_iv, loop_entry); + _igvn.replace_node(final_iv_placeholder, final_iv); + } // The OpaqueNode is unshared by design assert(opqzm->outcnt() == 1, "cannot hack shared node"); _igvn.replace_input_of(opqzm, 1, main_limit); - - return; } bool IdealLoopTree::compute_has_range_checks() const { diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 85e6f03d4280d..b4c134570e63f 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -567,7 +567,7 @@ void PhaseIdealLoop::add_parse_predicate(Deoptimization::DeoptReason reason, Nod register_control(if_true, loop, parse_predicate); int trap_request = Deoptimization::make_trap_request(reason, Deoptimization::Action_maybe_recompile); - address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); + address call_addr = OptoRuntime::uncommon_trap_blob()->entry_point(); const TypePtr* no_memory_effects = nullptr; JVMState* jvms = sfpt->jvms(); CallNode* unc = new CallStaticJavaNode(OptoRuntime::uncommon_trap_Type(), call_addr, "uncommon_trap", @@ -5248,6 +5248,7 @@ bool IdealLoopTree::verify_tree(IdealLoopTree* loop_verify) const { //------------------------------set_idom--------------------------------------- void PhaseIdealLoop::set_idom(Node* d, Node* n, uint dom_depth) { + _nesting.check(); // Check if a potential reallocation in the resource arena is safe uint idx = d->_idx; if (idx >= _idom_size) { uint newsize = next_power_of_2(idx); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 7d78bf5021c22..94632be268d58 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -611,6 +611,7 @@ class IdealLoopTree : public ResourceObj { _head(head), _tail(tail), _phase(phase), _local_loop_unroll_limit(0), _local_loop_unroll_factor(0), + _body(Compile::current()->comp_arena()), _nest(0), _irreducible(0), _has_call(0), _has_sfpt(0), _rce_candidate(0), _has_range_checks(0), _has_range_checks_computed(0), _safepts(nullptr), @@ -840,6 +841,8 @@ class PhaseIdealLoop : public PhaseTransform { uint *_preorders; uint _max_preorder; + ReallocMark _nesting; // Safety checks for arena reallocation + const PhaseIdealLoop* _verify_me; bool _verify_only; @@ -852,6 +855,7 @@ class PhaseIdealLoop : public PhaseTransform { // Allocate _preorders[] array void reallocate_preorders() { + _nesting.check(); // Check if a potential re-allocation in the resource arena is safe if ( _max_preorder < C->unique() ) { _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, C->unique()); _max_preorder = C->unique(); @@ -862,6 +866,7 @@ class PhaseIdealLoop : public PhaseTransform { // Check to grow _preorders[] array for the case when build_loop_tree_impl() // adds new nodes. void check_grow_preorders( ) { + _nesting.check(); // Check if a potential re-allocation in the resource arena is safe if ( _max_preorder < C->unique() ) { uint newsize = _max_preorder<<1; // double size of array _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, newsize); @@ -947,9 +952,10 @@ class PhaseIdealLoop : public PhaseTransform { LoopNode* outer_main_head, uint dd_main_head, uint idx_before_pre_post, uint idx_after_post_before_pre, Node* zero_trip_guard_proj_main, Node* zero_trip_guard_proj_post, const Node_List& old_new); - Node* clone_assertion_predicate_and_initialize(Node* iff, Node* new_init, Node* new_stride, Node* predicate, - Node* uncommon_proj, Node* control, IdealLoopTree* outer_loop, - Node* input_proj); + Node* clone_template_assertion_predicate(IfNode* iff, Node* new_init, Node* predicate, Node* uncommon_proj, Node* control, + IdealLoopTree* outer_loop, Node* input_proj); + IfTrueNode* create_initialized_assertion_predicate(IfNode* template_assertion_predicate, Node* new_init, + Node* new_stride, Node* control); static void count_opaque_loop_nodes(Node* n, uint& init, uint& stride); static bool assertion_predicate_has_loop_opaque_node(IfNode* iff); static void get_assertion_predicates(Node* predicate, Unique_Node_List& list, bool get_opaque = false); @@ -1769,7 +1775,7 @@ class PhaseIdealLoop : public PhaseTransform { bool clone_cmp_loadklass_down(Node* n, const Node* blk1, const Node* blk2); void clone_loadklass_nodes_at_cmp_index(const Node* n, Node* cmp, int i); bool clone_cmp_down(Node* n, const Node* blk1, const Node* blk2); - void clone_template_assertion_predicate_expression_down(Node* node); + void clone_template_assertion_expression_down(Node* node); Node* similar_subtype_check(const Node* x, Node* r_in); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index ba0ce344122af..361e03f06a219 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4670,6 +4670,9 @@ void DataNodeGraph::clone(Node* node, Node* new_ctrl) { _phase->igvn().register_new_node_with_optimizer(clone); _orig_to_new.put(node, clone); _phase->set_ctrl(clone, new_ctrl); + if (node->is_CastII()) { + clone->set_req(0, new_ctrl); + } } // Rewire the data inputs of all (unprocessed) cloned nodes, whose inputs are still pointing to the same inputs as their diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index 57361313f8533..39b804f7e5c96 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -395,7 +395,14 @@ const class TypePtr *MachNode::adr_type() const { // 32-bit unscaled narrow oop can be the base of any address expression t = t->make_ptr(); } - if (t->isa_intptr_t() && offset != 0 && offset != Type::OffsetBot) { + + if (t->isa_intptr_t() && +#if !defined(AARCH64) + // AArch64 supports the addressing mode: + // [base, 0], in which [base] is converted from a long value + offset != 0 && +#endif + offset != Type::OffsetBot) { // We cannot assert that the offset does not look oop-ish here. // Depending on the heap layout the cardmark base could land // inside some oopish region. It definitely does for Win2K. diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 4c2f208e445c1..4e2306602e6f7 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -2960,7 +2960,7 @@ bool Matcher::branches_to_uncommon_trap(const Node *n) { } if (call && - call->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point()) { + call->entry_point() == OptoRuntime::uncommon_trap_blob()->entry_point()) { const Type* trtype = call->in(TypeFunc::Parms)->bottom_type(); if (trtype->isa_int() && trtype->is_int()->is_con()) { jint tr_con = trtype->is_int()->get_con(); diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 438dc5c1f64e6..c7f0fb9fc3202 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -2723,13 +2723,13 @@ uint StoreNode::hash() const { // class ArrayPointer { private: - const bool _is_valid; // The parsing succeeded const Node* _pointer; // The final pointer to the position in the array const Node* _base; // Base address of the array const jlong _constant_offset; // Sum of collected constant offsets const Node* _int_offset; // (optional) Offset behind LShiftL and ConvI2L - const jint _int_offset_shift; // (optional) Shift value for int_offset const GrowableArray* _other_offsets; // List of other AddP offsets + const jint _int_offset_shift; // (optional) Shift value for int_offset + const bool _is_valid; // The parsing succeeded ArrayPointer(const bool is_valid, const Node* pointer, @@ -2738,13 +2738,13 @@ class ArrayPointer { const Node* int_offset, const jint int_offset_shift, const GrowableArray* other_offsets) : - _is_valid(is_valid), _pointer(pointer), _base(base), _constant_offset(constant_offset), _int_offset(int_offset), + _other_offsets(other_offsets), _int_offset_shift(int_offset_shift), - _other_offsets(other_offsets) + _is_valid(is_valid) { assert(_pointer != nullptr, "must always have pointer"); assert(is_valid == (_base != nullptr), "have base exactly if valid"); diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index eb0a914b99433..dc65afff26f0f 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -91,17 +91,20 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->type(in(IfTrue)) == Type::TOP) { return nullptr; } - // Canonicalize the node by moving constants to the right input. - if (in(Condition)->is_Bool() && phase->type(in(IfFalse))->singleton() && !phase->type(in(IfTrue))->singleton()) { - BoolNode* b = in(Condition)->as_Bool()->negate(phase); - return make(in(Control), phase->transform(b), in(IfTrue), in(IfFalse), _type); - } + // Check for Min/Max patterns. This is called before constants are pushed to the right input, as that transform can + // make BoolTests non-canonical. Node* minmax = Ideal_minmax(phase, this); if (minmax != nullptr) { return minmax; } + // Canonicalize the node by moving constants to the right input. + if (in(Condition)->is_Bool() && phase->type(in(IfFalse))->singleton() && !phase->type(in(IfTrue))->singleton()) { + BoolNode* b = in(Condition)->as_Bool()->negate(phase); + return make(in(Control), phase->transform(b), in(IfTrue), in(IfFalse), _type); + } + return nullptr; } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 6cf6ff2094e83..f36770e67ea6c 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -2766,6 +2766,10 @@ const RegMask &Node::in_RegMask(uint) const { } void Node_Array::grow(uint i) { + _nesting.check(_a); // Check if a potential reallocation in the arena is safe + if (i < _max) { + return; // No need to grow + } assert(_max > 0, "invariant"); uint old = _max; _max = next_power_of_2(i); @@ -2973,6 +2977,10 @@ void Unique_Node_List::remove_useless_nodes(VectorSet &useful) { //============================================================================= void Node_Stack::grow() { + _nesting.check(_a); // Check if a potential reallocation in the arena is safe + if (_inode_top < _inode_max) { + return; // No need to grow + } size_t old_top = pointer_delta(_inode_top,_inodes,sizeof(INode)); // save _top size_t old_max = pointer_delta(_inode_max,_inodes,sizeof(INode)); size_t max = old_max << 1; // max * 2 diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index ae379c4833a56..10e1e7b100696 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1596,6 +1596,8 @@ class Node_Array : public AnyObj { Arena* _a; // Arena to allocate in uint _max; Node** _nodes; + ReallocMark _nesting; // Safety checks for arena reallocation + void grow( uint i ); // Grow array node to fit public: Node_Array(Arena* a, uint max = OptoNodeListSize) : _a(a), _max(max) { @@ -1614,7 +1616,7 @@ class Node_Array : public AnyObj { Node* at(uint i) const { assert(i<_max,"oob"); return _nodes[i]; } Node** adr() { return _nodes; } // Extend the mapping: index i maps to Node *n. - void map( uint i, Node *n ) { if( i>=_max ) grow(i); _nodes[i] = n; } + void map( uint i, Node *n ) { grow(i); _nodes[i] = n; } void insert( uint i, Node *n ); void remove( uint i ); // Remove, preserving order // Clear all entries in _nodes to null but keep storage @@ -1844,6 +1846,7 @@ class Node_Stack { INode *_inode_max; // End of _inodes == _inodes + _max INode *_inodes; // Array storage for the stack Arena *_a; // Arena to allocate in + ReallocMark _nesting; // Safety checks for arena reallocation void grow(); public: Node_Stack(int size) { @@ -1867,7 +1870,7 @@ class Node_Stack { } void push(Node *n, uint i) { ++_inode_top; - if (_inode_top >= _inode_max) grow(); + grow(); INode *top = _inode_top; // optimization top->node = n; top->indx = i; diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 42374a0bcd4f8..b3f251bb361ba 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -716,7 +716,7 @@ ObjectValue* PhaseOutput::sv_for_node_id(GrowableArray *objs, int id) { for (int i = 0; i < objs->length(); i++) { assert(objs->at(i)->is_object(), "corrupt object cache"); - ObjectValue* sv = (ObjectValue*) objs->at(i); + ObjectValue* sv = objs->at(i)->as_ObjectValue(); if (sv->id() == id) { return sv; } @@ -755,7 +755,7 @@ void PhaseOutput::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local, if (local->is_SafePointScalarObject()) { SafePointScalarObjectNode* spobj = local->as_SafePointScalarObject(); - ObjectValue* sv = (ObjectValue*) sv_for_node_id(objs, spobj->_idx); + ObjectValue* sv = sv_for_node_id(objs, spobj->_idx); if (sv == nullptr) { ciKlass* cik = t->is_oopptr()->exact_klass(); assert(cik->is_instance_klass() || @@ -987,7 +987,7 @@ bool PhaseOutput::contains_as_scalarized_obj(JVMState* jvms, MachSafePointNode* continue; } - ObjectValue* other = (ObjectValue*) sv_for_node_id(objs, n->_idx); + ObjectValue* other = sv_for_node_id(objs, n->_idx); if (ov == other) { return true; } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 96e9f10880841..dae4a5c68a36f 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2351,6 +2351,7 @@ void Node::replace_by(Node *new_node) { //============================================================================= //----------------------------------------------------------------------------- void Type_Array::grow( uint i ) { + assert(_a == Compile::current()->comp_arena(), "Should be allocated in comp_arena"); if( !_max ) { _max = 1; _types = (const Type**)_a->Amalloc( _max * sizeof(Type*) ); diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 5b0de2e02d5f0..3887e8a5f6cdf 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -27,6 +27,7 @@ #include "opto/loopnode.hpp" #include "opto/node.hpp" #include "opto/predicates.hpp" +#include "opto/rootnode.hpp" // Walk over all Initialized Assertion Predicates and return the entry into the first Initialized Assertion Predicate // (i.e. not belonging to an Initialized Assertion Predicate anymore) @@ -239,27 +240,27 @@ class ReplaceInitAndStrideStrategy : public TransformStrategyForOpaqueLoopNodes } }; -// Creates an identical clone of this Template Assertion Predicate Expression (i.e.cloning all nodes from the Opaque4Node -// to and including the OpaqueLoop* nodes). The cloned nodes are rewired to reflect the same graph structure as found for -// this Template Assertion Predicate Expression. The cloned nodes get 'new_ctrl' as ctrl. There is no other update done -// for the cloned nodes. Return the newly cloned Opaque4Node. -Opaque4Node* TemplateAssertionPredicateExpression::clone(Node* new_ctrl, PhaseIdealLoop* phase) { +// Creates an identical clone of this Template Assertion Expression (i.e.cloning all nodes from the Opaque4Node to and +// including the OpaqueLoop* nodes). The cloned nodes are rewired to reflect the same graph structure as found for this +// Template Assertion Expression. The cloned nodes get 'new_ctrl' as ctrl. There is no other update done for the cloned +// nodes. Return the newly cloned Opaque4Node. +Opaque4Node* TemplateAssertionExpression::clone(Node* new_ctrl, PhaseIdealLoop* phase) { CloneStrategy clone_init_and_stride_strategy(phase, new_ctrl); return clone(clone_init_and_stride_strategy, new_ctrl, phase); } // Same as clone() but instead of cloning the OpaqueLoopInitNode, we replace it with the provided 'new_init' node. -Opaque4Node* TemplateAssertionPredicateExpression::clone_and_replace_init(Node* new_init, Node* new_ctrl, - PhaseIdealLoop* phase) { +Opaque4Node* TemplateAssertionExpression::clone_and_replace_init(Node* new_init, Node* new_ctrl, + PhaseIdealLoop* phase) { ReplaceInitAndCloneStrideStrategy replace_init_and_clone_stride_strategy(new_init, new_ctrl, phase); return clone(replace_init_and_clone_stride_strategy, new_ctrl, phase); } // Same as clone() but instead of cloning the OpaqueLoopInit and OpaqueLoopStride node, we replace them with the provided // 'new_init' and 'new_stride' nodes, respectively. -Opaque4Node* TemplateAssertionPredicateExpression::clone_and_replace_init_and_stride(Node* new_init, Node* new_stride, - Node* new_ctrl, - PhaseIdealLoop* phase) { +Opaque4Node* TemplateAssertionExpression::clone_and_replace_init_and_stride(Node* new_init, Node* new_stride, + Node* new_ctrl, + PhaseIdealLoop* phase) { ReplaceInitAndStrideStrategy replace_init_and_stride_strategy(new_init, new_stride); return clone(replace_init_and_stride_strategy, new_ctrl, phase); } @@ -307,8 +308,7 @@ class DataNodesOnPathsToTargets : public StackObj { // Do a BFS from the start_node to collect all target nodes. We can then do another BFS from the target nodes to // find all nodes on the paths from start->target(s). // Note: We could do a single DFS pass to search targets and backtrack in one walk. But this is much more complex. - // Given that the typical Template Assertion Predicate Expression only consists of a few nodes, we aim for - // simplicity here. + // Given that the typical Template Assertion Expression only consists of a few nodes, we aim for simplicity here. void collect_target_nodes(Node* start_node) { _nodes_to_visit.push(start_node); for (uint i = 0; i < _nodes_to_visit.size(); i++) { @@ -342,14 +342,14 @@ class DataNodesOnPathsToTargets : public StackObj { } }; -// Clones this Template Assertion Predicate Expression and applies the given strategy to transform the OpaqueLoop* nodes. -Opaque4Node* TemplateAssertionPredicateExpression::clone(const TransformStrategyForOpaqueLoopNodes& transform_strategy, - Node* new_ctrl, PhaseIdealLoop* phase) { +// Clones this Template Assertion Expression and applies the given strategy to transform the OpaqueLoop* nodes. +Opaque4Node* TemplateAssertionExpression::clone(const TransformStrategyForOpaqueLoopNodes& transform_strategy, + Node* new_ctrl, PhaseIdealLoop* phase) { ResourceMark rm; auto is_opaque_loop_node = [](const Node* node) { return node->is_Opaque1(); }; - DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionPredicateExpressionNode::is_maybe_in_expression, + DataNodesOnPathsToTargets data_nodes_on_path_to_targets(TemplateAssertionExpressionNode::is_maybe_in_expression, is_opaque_loop_node); const Unique_Node_List& collected_nodes = data_nodes_on_path_to_targets.collect(_opaque4_node); DataNodeGraph data_node_graph(collected_nodes, phase); @@ -359,8 +359,8 @@ Opaque4Node* TemplateAssertionPredicateExpression::clone(const TransformStrategy return opaque4_clone->as_Opaque4(); } -// Check if this node belongs a Template Assertion Predicate Expression (including OpaqueLoop* nodes). -bool TemplateAssertionPredicateExpressionNode::is_in_expression(Node* node) { +// Check if this node belongs a Template Assertion Expression (including OpaqueLoop* nodes). +bool TemplateAssertionExpressionNode::is_in_expression(Node* node) { if (is_maybe_in_expression(node)) { ResourceMark rm; Unique_Node_List list; @@ -377,10 +377,90 @@ bool TemplateAssertionPredicateExpressionNode::is_in_expression(Node* node) { return false; } -bool TemplateAssertionPredicateExpressionNode::is_template_assertion_predicate(Node* node) { +bool TemplateAssertionExpressionNode::is_template_assertion_predicate(Node* node) { return node->is_If() && node->in(1)->is_Opaque4(); } +InitializedAssertionPredicate::InitializedAssertionPredicate(IfNode* template_assertion_predicate, Node* new_init, + Node* new_stride, PhaseIdealLoop* phase) + : _template_assertion_predicate(template_assertion_predicate), + _new_init(new_init), + _new_stride(new_stride), + _phase(phase) {} + +// Create an Initialized Assertion Predicate at the provided control from the _template_assertion_predicate. +// We clone the Template Assertion Expression and replace: +// - Opaque4 with OpaqueInitializedAssertionPredicate +// - OpaqueLoop*Nodes with _new_init and _new_stride, respectively. +// +// / init stride +// | | | +// | OpaqueLoopInitNode OpaqueLoopStrideNode / _new_init _new_stride +// Template | \ / | \ / +// Assertion | ... Assertion | ... +// Expression | | Expression | | +// | Bool | new Bool +// | | | | +// \ Opaque4 ======> control \ OpaqueInitializedAssertionPredicate +// | \ / +// If new If +// / \ / \ +// success fail path new success new Halt +// proj (Halt or UCT) proj +// +IfTrueNode* InitializedAssertionPredicate::create(Node* control) { + IdealLoopTree* loop = _phase->get_loop(control); + OpaqueInitializedAssertionPredicateNode* assertion_expression = create_assertion_expression(control); + IfNode* if_node = create_if_node(control, assertion_expression, loop); + create_fail_path(if_node, loop); + return create_success_path(if_node, loop); +} + +// Create a new Assertion Expression to be used as bool input for the Initialized Assertion Predicate IfNode. +OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicate::create_assertion_expression(Node* control) { + Opaque4Node* template_opaque = _template_assertion_predicate->in(1)->as_Opaque4(); + TemplateAssertionExpression template_assertion_expression(template_opaque); + Opaque4Node* tmp_opaque = template_assertion_expression.clone_and_replace_init_and_stride(_new_init, _new_stride, + control, _phase); + OpaqueInitializedAssertionPredicateNode* assertion_expression = + new OpaqueInitializedAssertionPredicateNode(tmp_opaque->in(1)->as_Bool(), _phase->C); + _phase->register_new_node(assertion_expression, control); + return assertion_expression; +} + +IfNode* InitializedAssertionPredicate::create_if_node(Node* control, + OpaqueInitializedAssertionPredicateNode* assertion_expression, + IdealLoopTree* loop) { + const int if_opcode = _template_assertion_predicate->Opcode(); + NOT_PRODUCT(const AssertionPredicateType assertion_predicate_type = _template_assertion_predicate->assertion_predicate_type();) + IfNode* if_node = if_opcode == Op_If ? + new IfNode(control, assertion_expression, PROB_MAX, COUNT_UNKNOWN NOT_PRODUCT(COMMA assertion_predicate_type)) : + new RangeCheckNode(control, assertion_expression, PROB_MAX, COUNT_UNKNOWN NOT_PRODUCT(COMMA assertion_predicate_type)); + _phase->register_control(if_node, loop, control); + return if_node; +} + +IfTrueNode* InitializedAssertionPredicate::create_success_path(IfNode* if_node, IdealLoopTree* loop) { + IfTrueNode* success_proj = new IfTrueNode(if_node); + _phase->register_control(success_proj, loop, if_node); + return success_proj; +} + +void InitializedAssertionPredicate::create_fail_path(IfNode* if_node, IdealLoopTree* loop) { + IfFalseNode* fail_proj = new IfFalseNode(if_node); + _phase->register_control(fail_proj, loop, if_node); + create_halt_node(fail_proj, loop); +} + +void InitializedAssertionPredicate::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop) { + StartNode* start_node = _phase->C->start(); + Node* frame = new ParmNode(start_node, TypeFunc::FramePtr); + _phase->register_new_node(frame, start_node); + Node* halt = new HaltNode(fail_proj, frame, "Initialized Assertion Predicate cannot fail"); + _phase->igvn().add_input_to(_phase->C->root(), halt); + _phase->register_control(halt, loop, fail_proj); +} + // Is current node pointed to by iterator a predicate? bool PredicateEntryIterator::has_next() const { return ParsePredicate::is_predicate(_current) || diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index 08aa64f03e583..96f5c438b802f 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -29,6 +29,8 @@ #include "opto/connode.hpp" #include "opto/opaquenode.hpp" +class IdealLoopTree; + /* * There are different kinds of predicates throughout the code. We differentiate between the following predicates: * @@ -198,8 +200,8 @@ // value of a range check in the last iteration of a loop. enum class AssertionPredicateType { None, // Not an Assertion Predicate - Init_value, - Last_value + InitValue, + LastValue }; #endif // NOT PRODUCT @@ -294,20 +296,20 @@ class RuntimePredicate : public StackObj { static bool is_success_proj(Node* node, Deoptimization::DeoptReason deopt_reason); }; -// Interface to transform OpaqueLoopInit and OpaqueLoopStride nodes of a Template Assertion Predicate Expression. +// Interface to transform OpaqueLoopInit and OpaqueLoopStride nodes of a Template Assertion Expression. class TransformStrategyForOpaqueLoopNodes : public StackObj { public: virtual Node* transform_opaque_init(OpaqueLoopInitNode* opaque_init) const = 0; virtual Node* transform_opaque_stride(OpaqueLoopStrideNode* opaque_stride) const = 0; }; -// A Template Assertion Predicate Expression represents the Opaque4Node for the initial value or the last value of a +// A Template Assertion Predicate represents the Opaque4Node for the initial value or the last value of a // Template Assertion Predicate and all the nodes up to and including the OpaqueLoop* nodes. -class TemplateAssertionPredicateExpression : public StackObj { +class TemplateAssertionExpression : public StackObj { Opaque4Node* _opaque4_node; public: - explicit TemplateAssertionPredicateExpression(Opaque4Node* opaque4_node) : _opaque4_node(opaque4_node) {} + explicit TemplateAssertionExpression(Opaque4Node* opaque4_node) : _opaque4_node(opaque4_node) {} private: Opaque4Node* clone(const TransformStrategyForOpaqueLoopNodes& transform_strategy, Node* new_ctrl, PhaseIdealLoop* phase); @@ -318,29 +320,29 @@ class TemplateAssertionPredicateExpression : public StackObj { Opaque4Node* clone_and_replace_init_and_stride(Node* new_init, Node* new_stride, Node* new_ctrl, PhaseIdealLoop* phase); }; -// Class to represent a node being part of a Template Assertion Predicate Expression. +// Class to represent a node being part of a Template Assertion Expression. Note that this is not an IR node. // // The expression itself can belong to no, one, or two Template Assertion Predicates: // - None: This node is already dead (i.e. we replaced the Bool condition of the Template Assertion Predicate). // - Two: A OpaqueLoopInitNode could be part of two Template Assertion Predicates. // - One: In all other cases. -class TemplateAssertionPredicateExpressionNode : public StackObj { +class TemplateAssertionExpressionNode : public StackObj { Node* const _node; public: - explicit TemplateAssertionPredicateExpressionNode(Node* node) : _node(node) { + explicit TemplateAssertionExpressionNode(Node* node) : _node(node) { assert(is_in_expression(node), "must be valid"); } - NONCOPYABLE(TemplateAssertionPredicateExpressionNode); + NONCOPYABLE(TemplateAssertionExpressionNode); private: static bool is_template_assertion_predicate(Node* node); public: - // Check whether the provided node is part of a Template Assertion Predicate Expression or not. + // Check whether the provided node is part of a Template Assertion Expression or not. static bool is_in_expression(Node* node); - // Check if the opcode of node could be found in a Template Assertion Predicate Expression. + // Check if the opcode of node could be found in a Template Assertion Expression. // This also provides a fast check whether a node is unrelated. static bool is_maybe_in_expression(const Node* node) { const int opcode = node->Opcode(); @@ -377,21 +379,44 @@ class TemplateAssertionPredicateExpressionNode : public StackObj { callback(next->as_If()); DEBUG_ONLY(template_counter++;) } else { - assert(!next->is_CFG(), "no CFG expected in Template Assertion Predicate Expression"); + assert(!next->is_CFG(), "no CFG expected in Template Assertion Expression"); list.push_outputs_of(next); } } - // Each node inside a Template Assertion Predicate Expression is in between a Template Assertion Predicate and - // its OpaqueLoop* nodes (or an OpaqueLoop* node itself). The OpaqueLoop* nodes do not common up. Therefore, each - // Template Assertion Predicate Expression node belongs to a single expression - except for OpaqueLoopInitNodes. - // An OpaqueLoopInitNode is shared between the init and last value Template Assertion Predicate at creation. - // Later, when cloning the expressions, they are no longer shared. + // Each node inside a Template Assertion Expression is in between a Template Assertion Predicate and its OpaqueLoop* + // nodes (or an OpaqueLoop* node itself). The OpaqueLoop* nodes do not common up. Therefore, each Template Assertion + // Expression node belongs to a single expression - except for OpaqueLoopInitNodes. An OpaqueLoopInitNode is shared + // between the init and last value Template Assertion Predicate at creation. Later, when cloning the expressions, + // they are no longer shared. assert(template_counter <= 2, "a node cannot be part of more than two templates"); assert(template_counter <= 1 || _node->is_OpaqueLoopInit(), "only OpaqueLoopInit nodes can be part of two templates"); } }; +// This class creates a new Initialized Assertion Predicate. +class InitializedAssertionPredicate : public StackObj { + IfNode* const _template_assertion_predicate; + Node* const _new_init; + Node* const _new_stride; + PhaseIdealLoop* const _phase; + + public: + InitializedAssertionPredicate(IfNode* template_assertion_predicate, Node* new_init, Node* new_stride, + PhaseIdealLoop* phase); + NONCOPYABLE(InitializedAssertionPredicate); + + IfTrueNode* create(Node* control); + + private: + OpaqueInitializedAssertionPredicateNode* create_assertion_expression(Node* control); + IfNode* create_if_node(Node* control, OpaqueInitializedAssertionPredicateNode* assertion_expression, IdealLoopTree* loop); + void create_fail_path(IfNode* if_node, IdealLoopTree* loop); + void create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop); + IfTrueNode* create_success_path(IfNode* if_node, IdealLoopTree* loop); +}; + + // This class represents a Predicate Block (i.e. either a Loop Predicate Block, a Profiled Loop Predicate Block, // or a Loop Limit Check Predicate Block). It contains zero or more Regular Predicates followed by a Parse Predicate // which, however, does not need to exist (we could already have decided to remove Parse Predicates for this loop). diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 465404bb4693f..54408146d0c26 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -114,7 +114,8 @@ address OptoRuntime::_notify_jvmti_vthread_mount = nullptr; address OptoRuntime::_notify_jvmti_vthread_unmount = nullptr; #endif -ExceptionBlob* OptoRuntime::_exception_blob; +UncommonTrapBlob* OptoRuntime::_uncommon_trap_blob; +ExceptionBlob* OptoRuntime::_exception_blob; // This should be called in an assertion at the start of OptoRuntime routines // which are entered from compiled code (all of them) @@ -138,6 +139,7 @@ static bool check_compiled_frame(JavaThread* thread) { bool OptoRuntime::generate(ciEnv* env) { + generate_uncommon_trap_blob(); generate_exception_blob(); // Note: tls: Means fetching the return oop out of the thread-local storage diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index 6aadab9712243..34c2780a2f8d7 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -168,7 +168,10 @@ class OptoRuntime : public AllStatic { // CodeBlob support // =================================================================== + static UncommonTrapBlob* _uncommon_trap_blob; static ExceptionBlob* _exception_blob; + + static void generate_uncommon_trap_blob(void); static void generate_exception_blob(); static void register_finalizer(oopDesc* obj, JavaThread* current); @@ -208,6 +211,7 @@ class OptoRuntime : public AllStatic { static address notify_jvmti_vthread_unmount() { return _notify_jvmti_vthread_unmount; } #endif + static UncommonTrapBlob* uncommon_trap_blob() { return _uncommon_trap_blob; } static ExceptionBlob* exception_blob() { return _exception_blob; } // Implicit exception support diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index ad4053e2c98c4..1eff44ab7834f 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -95,7 +95,7 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { return true; } - clone_template_assertion_predicate_expression_down(n); + clone_template_assertion_expression_down(n); if (n->Opcode() == Op_OpaqueZeroTripGuard) { // If this Opaque1 is part of the zero trip guard for a loop: @@ -409,25 +409,25 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) return false; } -// 'n' could be a node belonging to a Template Assertion Predicate Expression (i.e. any node between a Template -// Assertion Predicate and its OpaqueLoop* nodes (included)). We cannot simply split this node up since this would -// create a phi node inside the Template Assertion Predicate Expression - making it unrecognizable as such. Therefore, -// we completely clone the entire Template Assertion Predicate Expression "down". This ensures that we have an -// untouched copy that is still recognized by the Template Assertion Predicate matching code. -void PhaseIdealLoop::clone_template_assertion_predicate_expression_down(Node* node) { - if (!TemplateAssertionPredicateExpressionNode::is_in_expression(node)) { +// 'n' could be a node belonging to a Template Assertion Expression (i.e. any node between a Template Assertion Predicate +// and its OpaqueLoop* nodes (included)). We cannot simply split this node up since this would create a phi node inside +// the Template Assertion Expression - making it unrecognizable as such. Therefore, we completely clone the entire +// Template Assertion Expression "down". This ensures that we have an untouched copy that is still recognized by the +// Template Assertion Predicate matching code. +void PhaseIdealLoop::clone_template_assertion_expression_down(Node* node) { + if (!TemplateAssertionExpressionNode::is_in_expression(node)) { return; } - TemplateAssertionPredicateExpressionNode template_assertion_predicate_expression_node(node); + TemplateAssertionExpressionNode template_assertion_expression_node(node); auto clone_expression = [&](IfNode* template_assertion_predicate) { Opaque4Node* opaque4_node = template_assertion_predicate->in(1)->as_Opaque4(); - TemplateAssertionPredicateExpression template_assertion_predicate_expression(opaque4_node); + TemplateAssertionExpression template_assertion_expression(opaque4_node); Node* new_ctrl = template_assertion_predicate->in(0); - Opaque4Node* cloned_opaque4_node = template_assertion_predicate_expression.clone(new_ctrl, this); + Opaque4Node* cloned_opaque4_node = template_assertion_expression.clone(new_ctrl, this); igvn().replace_input_of(template_assertion_predicate, 1, cloned_opaque4_node); }; - template_assertion_predicate_expression_node.for_each_template_assertion_predicate(clone_expression); + template_assertion_expression_node.for_each_template_assertion_predicate(clone_expression); } //------------------------------register_new_node------------------------------ diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index 2e227be765cfa..7bf75c93055a5 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -203,7 +203,7 @@ class StringConcat : public ResourceObj { Node* uct = _uncommon_traps.at(u); // Build a new call using the jvms state of the allocate - address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point(); + address call_addr = OptoRuntime::uncommon_trap_blob()->entry_point(); const TypeFunc* call_type = OptoRuntime::uncommon_trap_Type(); const TypePtr* no_memory_effects = nullptr; Compile* C = _stringopts->C; diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index 1b4d64e20b174..9ab62c282d124 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -184,7 +184,7 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { // Process merged VBAs if (EnableVectorAggressiveReboxing) { - Unique_Node_List calls(C->comp_arena()); + Unique_Node_List calls; for (DUIterator_Fast imax, i = vec_box->fast_outs(imax); i < imax; i++) { Node* use = vec_box->fast_out(i); if (use->is_CallJava()) { @@ -238,9 +238,9 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { } // Process debug uses at safepoints - Unique_Node_List safepoints(C->comp_arena()); + Unique_Node_List safepoints; - Unique_Node_List worklist(C->comp_arena()); + Unique_Node_List worklist; worklist.push(vec_box); while (worklist.size() > 0) { Node* n = worklist.pop(); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index b31f6ace5a6e2..cfcd903e79d95 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -2484,12 +2484,10 @@ bool LibraryCallKit::inline_vector_insert() { // Convert insert value back to its appropriate type. switch (elem_bt) { case T_BYTE: - insert_val = gvn().transform(new ConvL2INode(insert_val)); - insert_val = gvn().transform(new CastIINode(insert_val, TypeInt::BYTE)); + insert_val = gvn().transform(new ConvL2INode(insert_val, TypeInt::BYTE)); break; case T_SHORT: - insert_val = gvn().transform(new ConvL2INode(insert_val)); - insert_val = gvn().transform(new CastIINode(insert_val, TypeInt::SHORT)); + insert_val = gvn().transform(new ConvL2INode(insert_val, TypeInt::SHORT)); break; case T_INT: insert_val = gvn().transform(new ConvL2INode(insert_val)); diff --git a/src/hotspot/share/precompiled/precompiled.hpp b/src/hotspot/share/precompiled/precompiled.hpp index c53a78de87bca..07922d129697e 100644 --- a/src/hotspot/share/precompiled/precompiled.hpp +++ b/src/hotspot/share/precompiled/precompiled.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ // These header files are included in at least 130 C++ files, as of // measurements made in November 2018. This list excludes files named -// *.include.hpp, since including them decreased build performance. +// *.inline.hpp, since including them decreased build performance. #include "classfile/classLoaderData.hpp" #include "classfile/javaClasses.hpp" diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index ae040d661380e..12eba0ff623be 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -1074,7 +1074,7 @@ static jmethodID get_method_id(JNIEnv *env, jclass clazz, const char *name_str, TempNewSymbol signature = SymbolTable::probe(sig, (int)strlen(sig)); if (name == nullptr || signature == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), name_str); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchMethodError(), name_str); } oop mirror = JNIHandles::resolve_non_null(clazz); @@ -1084,7 +1084,7 @@ static jmethodID get_method_id(JNIEnv *env, jclass clazz, const char *name_str, // primitive java.lang.Class if (java_lang_Class::is_primitive(mirror)) { ResourceMark rm(THREAD); - THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), err_msg("%s%s.%s%s", is_static ? "static " : "", klass->signature_name(), name_str, sig)); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchMethodError(), err_msg("%s%s.%s%s", is_static ? "static " : "", klass->signature_name(), name_str, sig)); } // Make sure class is linked and initialized before handing id's out to @@ -1108,7 +1108,7 @@ static jmethodID get_method_id(JNIEnv *env, jclass clazz, const char *name_str, } if (m == nullptr || (m->is_static() != is_static)) { ResourceMark rm(THREAD); - THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), err_msg("%s%s.%s%s", is_static ? "static " : "", klass->signature_name(), name_str, sig)); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchMethodError(), err_msg("%s%s.%s%s", is_static ? "static " : "", klass->signature_name(), name_str, sig)); } return m->jmethod_id(); } @@ -1148,7 +1148,7 @@ JNI_ENTRY(ResultType, \ jni_Call##Result##Method(JNIEnv *env, jobject obj, jmethodID methodID, ...)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, Call##Result##Method, ResultType, \ (const ResultType&)ret);\ \ @@ -1203,7 +1203,7 @@ JNI_ENTRY(ResultType, \ jni_Call##Result##MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args)) \ \ EntryProbe;\ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, Call##Result##MethodV, ResultType, \ (const ResultType&)ret);\ \ @@ -1254,7 +1254,7 @@ DEFINE_CALLMETHODV(jdouble, Double, T_DOUBLE JNI_ENTRY(ResultType, \ jni_Call##Result##MethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args)) \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, Call##Result##MethodA, ResultType, \ (const ResultType&)ret);\ \ @@ -1546,7 +1546,7 @@ JNI_ENTRY(ResultType, \ jni_CallStatic##Result##Method(JNIEnv *env, jclass cls, jmethodID methodID, ...)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, CallStatic##Result##Method, ResultType, \ (const ResultType&)ret);\ \ @@ -1601,7 +1601,7 @@ JNI_ENTRY(ResultType, \ jni_CallStatic##Result##MethodV(JNIEnv *env, jclass cls, jmethodID methodID, va_list args)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, CallStatic##Result##MethodV, ResultType, \ (const ResultType&)ret);\ \ @@ -1657,7 +1657,7 @@ JNI_ENTRY(ResultType, \ jni_CallStatic##Result##MethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, CallStatic##Result##MethodA, ResultType, \ (const ResultType&)ret);\ \ @@ -1750,7 +1750,7 @@ DT_RETURN_MARK_DECL(GetFieldID, jfieldID JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig)) HOTSPOT_JNI_GETFIELDID_ENTRY(env, clazz, (char *) name, (char *) sig); - jfieldID ret = 0; + jfieldID ret = nullptr; DT_RETURN_MARK(GetFieldID, jfieldID, (const jfieldID&)ret); Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); @@ -1762,7 +1762,7 @@ JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, TempNewSymbol signame = SymbolTable::probe(sig, (int)strlen(sig)); if (fieldname == nullptr || signame == nullptr) { ResourceMark rm; - THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); } // Make sure class is initialized before handing id's out to fields @@ -1772,7 +1772,7 @@ JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, if (!k->is_instance_klass() || !InstanceKlass::cast(k)->find_field(fieldname, signame, false, &fd)) { ResourceMark rm; - THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig)); } // A jfieldID for a non-static field is simply the offset of the field within the instanceOop @@ -1986,7 +1986,7 @@ JNI_ENTRY(jfieldID, jni_GetStaticFieldID(JNIEnv *env, jclass clazz, TempNewSymbol fieldname = SymbolTable::probe(name, (int)strlen(name)); TempNewSymbol signame = SymbolTable::probe(sig, (int)strlen(sig)); if (fieldname == nullptr || signame == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); } Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); // Make sure class is initialized before handing id's out to static fields @@ -1995,7 +1995,7 @@ JNI_ENTRY(jfieldID, jni_GetStaticFieldID(JNIEnv *env, jclass clazz, fieldDescriptor fd; if (!k->is_instance_klass() || !InstanceKlass::cast(k)->find_field(fieldname, signame, true, &fd)) { - THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), (char*) name); } // A jfieldID for a static field is a JNIid specifying the field holder and the offset within the Klass* @@ -2309,7 +2309,7 @@ JNI_ENTRY(jobject, jni_GetObjectArrayElement(JNIEnv *env, jobjectArray array, js ResourceMark rm(THREAD); stringStream ss; ss.print("Index %d out of bounds for length %d", index, a->length()); - THROW_MSG_0(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string()); + THROW_MSG_NULL(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string()); } JNI_END @@ -3035,12 +3035,12 @@ extern "C" void* JNICALL jni_GetDirectBufferAddress(JNIEnv *env, jobject buf) if (!directBufferSupportInitializeEnded) { if (!initializeDirectBufferSupport(env, thread)) { - return 0; + return nullptr; } } if ((buf != nullptr) && (!env->IsInstanceOf(buf, directBufferClass))) { - return 0; + return nullptr; } ret = (void*)(intptr_t)env->GetLongField(buf, directBufferAddressField); @@ -3647,8 +3647,8 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) { } // Creation failed. We must reset vm_created - *vm = 0; - *(JNIEnv**)penv = 0; + *vm = nullptr; + *(JNIEnv**)penv = nullptr; // reset vm_created last to avoid race condition. Use OrderAccess to // control both compiler and architectural-based reordering. assert(vm_created == IN_PROGRESS, "must be"); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 7a8c07477dcb7..e40c11289661e 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -691,7 +691,7 @@ JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) (klass->is_instance_klass() && InstanceKlass::cast(klass)->reference_type() != REF_NONE)) { ResourceMark rm(THREAD); - THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); + THROW_MSG_NULL(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); } // Make shallow object copy @@ -791,7 +791,7 @@ JVM_ENTRY(jclass, JVM_FindPrimitiveClass(JNIEnv* env, const char* utf)) mirror = Universe::java_mirror(t); } if (mirror == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), (char*) utf); + THROW_MSG_NULL(vmSymbols::java_lang_ClassNotFoundException(), (char*) utf); } else { return (jclass) JNIHandles::make_local(THREAD, mirror); } @@ -952,7 +952,7 @@ static jclass jvm_lookup_define_class(jclass lookup, const char *name, Klass* lookup_k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(lookup)); // Lookup class must be a non-null instance if (lookup_k == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null"); } assert(lookup_k->is_instance_klass(), "Lookup class must be an instance klass"); @@ -979,20 +979,20 @@ static jclass jvm_lookup_define_class(jclass lookup, const char *name, if (!is_hidden) { // classData is only applicable for hidden classes if (classData != nullptr) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "classData is only applicable for hidden classes"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "classData is only applicable for hidden classes"); } if (is_nestmate) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "dynamic nestmate is only applicable for hidden classes"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "dynamic nestmate is only applicable for hidden classes"); } if (!is_strong) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "an ordinary class must be strongly referenced by its defining loader"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "an ordinary class must be strongly referenced by its defining loader"); } if (vm_annotations) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "vm annotations only allowed for hidden classes"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "vm annotations only allowed for hidden classes"); } if (flags != STRONG_LOADER_LINK) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), - err_msg("invalid flag 0x%x", flags)); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("invalid flag 0x%x", flags)); } } @@ -1032,7 +1032,7 @@ static jclass jvm_lookup_define_class(jclass lookup, const char *name, // The hidden class loader data has been artificially been kept alive to // this point. The mirror and any instances of this class have to keep // it alive afterwards. - ik->class_loader_data()->dec_keep_alive(); + ik->class_loader_data()->dec_keep_alive_ref_count(); if (is_nestmate && log_is_enabled(Debug, class, nestmates)) { ModuleEntry* module = ik->module(); @@ -1047,7 +1047,7 @@ static jclass jvm_lookup_define_class(jclass lookup, const char *name, if ((!is_hidden || is_nestmate) && !Reflection::is_same_class_package(lookup_k, ik)) { // non-hidden class or nestmate class must be in the same package as the Lookup class - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class and defined class are in different packages"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class and defined class are in different packages"); } if (init) { @@ -1078,7 +1078,7 @@ JVM_ENTRY(jclass, JVM_LookupDefineClass(JNIEnv *env, jclass lookup, const char * jsize len, jobject pd, jboolean initialize, int flags, jobject classData)) if (lookup == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null"); } assert(buf != nullptr, "buf must not be null"); @@ -1703,8 +1703,8 @@ JVM_ENTRY(jobjectArray, JVM_GetMethodParameters(JNIEnv *env, jobject method)) bounds_check(cp, index, CHECK_NULL); if (0 != index && !mh->constants()->tag_at(index).is_utf8()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), - "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + "Wrong type at constant pool index"); } } @@ -2133,7 +2133,7 @@ JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAt(JNIEnv *env, jobject obj, jobject u bounds_check(cp, index, CHECK_NULL); constantTag tag = cp->tag_at(index); if (!tag.is_klass() && !tag.is_unresolved_klass()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } Klass* k = cp->klass_at(index, CHECK_NULL); return (jclass) JNIHandles::make_local(THREAD, k->java_mirror()); @@ -2146,7 +2146,7 @@ JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAtIfLoaded(JNIEnv *env, jobject obj, j bounds_check(cp, index, CHECK_NULL); constantTag tag = cp->tag_at(index); if (!tag.is_klass() && !tag.is_unresolved_klass()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } Klass* k = ConstantPool::klass_at_if_loaded(cp, index); if (k == nullptr) return nullptr; @@ -2157,7 +2157,7 @@ JVM_END static jobject get_method_at_helper(const constantPoolHandle& cp, jint index, bool force_resolution, TRAPS) { constantTag tag = cp->tag_at(index); if (!tag.is_method() && !tag.is_interface_method()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } int klass_ref = cp->uncached_klass_ref_index_at(index); Klass* k_o; @@ -2172,7 +2172,7 @@ static jobject get_method_at_helper(const constantPoolHandle& cp, jint index, bo Symbol* sig = cp->uncached_signature_ref_at(index); methodHandle m (THREAD, k->find_method(name, sig)); if (m.is_null()) { - THROW_MSG_0(vmSymbols::java_lang_RuntimeException(), "Unable to look up method in target class"); + THROW_MSG_NULL(vmSymbols::java_lang_RuntimeException(), "Unable to look up method in target class"); } oop method; if (!m->is_initializer() || m->is_static()) { @@ -2206,7 +2206,7 @@ JVM_END static jobject get_field_at_helper(constantPoolHandle cp, jint index, bool force_resolution, TRAPS) { constantTag tag = cp->tag_at(index); if (!tag.is_field()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } int klass_ref = cp->uncached_klass_ref_index_at(index); Klass* k_o; @@ -2222,7 +2222,7 @@ static jobject get_field_at_helper(constantPoolHandle cp, jint index, bool force fieldDescriptor fd; Klass* target_klass = k->find_field(name, sig, &fd); if (target_klass == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_RuntimeException(), "Unable to look up field in target class"); + THROW_MSG_NULL(vmSymbols::java_lang_RuntimeException(), "Unable to look up field in target class"); } oop field = Reflection::new_field(&fd, CHECK_NULL); return JNIHandles::make_local(THREAD, field); @@ -2255,7 +2255,7 @@ JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetMemberRefInfoAt(JNIEnv *env, jobject bounds_check(cp, index, CHECK_NULL); constantTag tag = cp->tag_at(index); if (!tag.is_field_or_method()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } int klass_ref = cp->uncached_klass_ref_index_at(index); Symbol* klass_name = cp->klass_name_at(klass_ref); @@ -2306,7 +2306,7 @@ JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetNameAndTypeRefInfoAt(JNIEnv *env, job bounds_check(cp, index, CHECK_NULL); constantTag tag = cp->tag_at(index); if (!tag.is_name_and_type()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } Symbol* member_name = cp->symbol_at(cp->name_ref_index_at(index)); Symbol* member_sig = cp->symbol_at(cp->signature_ref_index_at(index)); @@ -2374,7 +2374,7 @@ JVM_ENTRY(jstring, JVM_ConstantPoolGetStringAt(JNIEnv *env, jobject obj, jobject bounds_check(cp, index, CHECK_NULL); constantTag tag = cp->tag_at(index); if (!tag.is_string()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } oop str = cp->string_at(index, CHECK_NULL); return (jstring) JNIHandles::make_local(THREAD, str); @@ -2388,7 +2388,7 @@ JVM_ENTRY(jstring, JVM_ConstantPoolGetUTF8At(JNIEnv *env, jobject obj, jobject u bounds_check(cp, index, CHECK_NULL); constantTag tag = cp->tag_at(index); if (!tag.is_symbol()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } Symbol* sym = cp->symbol_at(index); Handle str = java_lang_String::create_from_symbol(sym, CHECK_NULL); @@ -3119,14 +3119,6 @@ JVM_ENTRY(jobject, JVM_GetStackTrace(JNIEnv *env, jobject jthread)) return JNIHandles::make_local(THREAD, trace); JVM_END -JVM_ENTRY(void, JVM_DumpAllStacks(JNIEnv* env, jclass)) - VM_PrintThreads op; - VMThread::execute(&op); - if (JvmtiExport::should_post_data_dump()) { - JvmtiExport::post_data_dump(); - } -JVM_END - JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name)) // We don't use a ThreadsListHandle here because the current thread // must be alive. @@ -3300,13 +3292,13 @@ JVM_END // resolve array handle and check arguments static inline arrayOop check_array(JNIEnv *env, jobject arr, bool type_array_only, TRAPS) { if (arr == nullptr) { - THROW_0(vmSymbols::java_lang_NullPointerException()); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); } oop a = JNIHandles::resolve_non_null(arr); if (!a->is_array()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Argument is not an array"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Argument is not an array"); } else if (type_array_only && !a->is_typeArray()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Argument is not an array of primitive type"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Argument is not an array of primitive type"); } return arrayOop(a); } @@ -3543,7 +3535,7 @@ JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jo } return res; } else { - THROW_0(vmSymbols::java_lang_StackOverflowError()); + THROW_NULL(vmSymbols::java_lang_StackOverflowError()); } JVM_END @@ -3755,7 +3747,7 @@ JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobject // Check if threads is null if (threads == nullptr) { - THROW_(vmSymbols::java_lang_NullPointerException(), 0); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); } objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(threads)); @@ -3763,13 +3755,13 @@ JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobject int num_threads = ah->length(); // check if threads is non-empty array if (num_threads == 0) { - THROW_(vmSymbols::java_lang_IllegalArgumentException(), 0); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } // check if threads is not an array of objects of Thread class Klass* k = ObjArrayKlass::cast(ah->klass())->element_klass(); if (k != vmClasses::Thread_klass()) { - THROW_(vmSymbols::java_lang_IllegalArgumentException(), 0); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } ResourceMark rm(THREAD); diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp index 8d7ffdf4835ba..ceed1d8ad952a 100644 --- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp +++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -119,10 +119,10 @@ void JvmtiClassFileReconstituter::write_field_infos() { write_signature_attribute(generic_signature_index); } if (anno != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", anno); + write_annotations_attribute("RuntimeVisibleAnnotations", anno); } if (type_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", type_anno); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); } } } @@ -380,20 +380,6 @@ void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_n memcpy(writeable_address(length), annos->adr_at(0), length); } -void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_name, - const char* fallback_attr_name, - AnnotationArray* annos) { - TempNewSymbol sym = SymbolTable::probe(attr_name, (int)strlen(attr_name)); - if (sym != nullptr) { - if (symbol_to_cpool_index(sym) != 0) { - write_annotations_attribute(attr_name, annos); - return; - } - } - // use fallback name - write_annotations_attribute(fallback_attr_name, annos); -} - // BootstrapMethods_attribute { // u2 attribute_name_index; // u4 attribute_length; @@ -533,10 +519,10 @@ void JvmtiClassFileReconstituter::write_record_attribute() { write_signature_attribute(component->generic_signature_index()); } if (component->annotations() != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", component->annotations()); + write_annotations_attribute("RuntimeVisibleAnnotations", component->annotations()); } if (component->type_annotations() != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", component->type_annotations()); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", component->type_annotations()); } } } @@ -775,13 +761,13 @@ void JvmtiClassFileReconstituter::write_method_info(const methodHandle& method) write_signature_attribute(generic_signature_index); } if (anno != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", anno); + write_annotations_attribute("RuntimeVisibleAnnotations", anno); } if (param_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations", param_anno); + write_annotations_attribute("RuntimeVisibleParameterAnnotations", param_anno); } if (type_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", type_anno); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); } } @@ -841,10 +827,10 @@ void JvmtiClassFileReconstituter::write_class_attributes() { write_source_debug_extension_attribute(); } if (anno != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", anno); + write_annotations_attribute("RuntimeVisibleAnnotations", anno); } if (type_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", type_anno); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); } if (ik()->nest_host_index() != 0) { write_nest_host_attribute(); diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp index 2f36a2877d262..015042a8543a7 100644 --- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp +++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp @@ -117,13 +117,6 @@ class JvmtiClassFileReconstituter : public JvmtiConstantPoolReconstituter { void write_signature_attribute(u2 generic_signaure_index); void write_attribute_name_index(const char* name); void write_annotations_attribute(const char* attr_name, AnnotationArray* annos); - // With PreserveAllAnnotations option "runtime invisible" annotations - // (RuntimeInvisibleAnnotations/RuntimeInvisibleTypeAnnotations/RuntimeInvisibleParameterAnnotations) - // are considered "runtime visible" and ClassFileReconstituter writes them as - // RuntimeVisibleAnnotations/RuntimeVisibleTypeAnnotations/RuntimeVisibleParameterAnnotations. - // This helper method is for the corner case when "runtime visible" attribute name is not presents - // in the class constant pool and the annotations are written with fallback "runtime invisible" name. - void write_annotations_attribute(const char* attr_name, const char* fallback_attr_name, AnnotationArray* annos); void write_bootstrapmethod_attribute(); void write_nest_host_attribute(); void write_nest_members_attribute(); diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index d5c5d53ba8104..c9867ff164350 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -982,6 +982,8 @@ JvmtiEventControllerPrivate::change_field_watch(jvmtiEvent event_type, bool adde added? "add" : "remove", *count_addr)); + JvmtiVTMSTransitionDisabler disabler; + if (added) { (*count_addr)++; if (*count_addr == 1) { diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index c2b6d27986b94..95cc54d93134f 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -929,9 +929,8 @@ class JvmtiClassFileLoadHookPoster : public StackObj { _cached_class_file_ptr = cache_ptr; _has_been_modified = false; - if (_thread->is_in_any_VTMS_transition()) { - return; // no events should be posted if thread is in any VTMS transition - } + assert(!_thread->is_in_any_VTMS_transition(), "CFLH events are not allowed in any VTMS transition"); + _state = JvmtiExport::get_jvmti_thread_state(_thread); if (_state != nullptr) { _class_being_redefined = _state->get_class_being_redefined(); @@ -1091,8 +1090,9 @@ bool JvmtiExport::post_class_file_load_hook(Symbol* h_name, if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) { return false; } - if (JavaThread::current()->is_in_tmp_VTMS_transition()) { - return false; // skip CFLH events in tmp VTMS transition + + if (JavaThread::current()->is_in_any_VTMS_transition()) { + return false; // no events should be posted if thread is in any VTMS transition } JvmtiClassFileLoadHookPoster poster(h_name, class_loader, @@ -2068,7 +2068,7 @@ void JvmtiExport::post_exception_throw(JavaThread *thread, Method* method, addre jmethodID catch_jmethodID; if (current_bci < 0) { - catch_jmethodID = 0; + catch_jmethodID = nullptr; current_bci = 0; } else { catch_jmethodID = jem.to_jmethodID(current_mh); @@ -2105,8 +2105,8 @@ void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, Method* met JvmtiTrace::safe_get_thread_name(thread), (mh() == nullptr) ? "null" : mh()->klass_name()->as_C_string(), (mh() == nullptr) ? "null" : mh()->name()->as_C_string(), - location==0? "no location:" : "", - location==0? 0 : location - mh()->code_base(), + location == nullptr ? "no location:" : "", + location == nullptr ? 0 : location - mh()->code_base(), in_handler_frame? "in handler frame" : "not handler frame" )); if (state->is_exception_detected()) { diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index 36df28f271677..e98020aef1d3b 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -287,10 +287,10 @@ class JvmtiExport : public AllStatic { } // field access management - static address get_field_access_count_addr() NOT_JVMTI_RETURN_(0); + static address get_field_access_count_addr() NOT_JVMTI_RETURN_(nullptr); // field modification management - static address get_field_modification_count_addr() NOT_JVMTI_RETURN_(0); + static address get_field_modification_count_addr() NOT_JVMTI_RETURN_(nullptr); // ----------------- diff --git a/src/hotspot/share/prims/jvmtiTrace.cpp b/src/hotspot/share/prims/jvmtiTrace.cpp index 002f59957eab7..c5fb95c931b93 100644 --- a/src/hotspot/share/prims/jvmtiTrace.cpp +++ b/src/hotspot/share/prims/jvmtiTrace.cpp @@ -261,7 +261,7 @@ void JvmtiTrace::shutdown() { const char* JvmtiTrace::enum_name(const char** names, const jint* values, jint value) { - for (int index = 0; names[index] != 0; ++index) { + for (int index = 0; names[index] != nullptr; ++index) { if (values[index] == value) { return names[index]; } diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index fd10f0723bff0..4f33055d6a3fe 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -436,7 +436,6 @@ Symbol* MethodHandles::signature_polymorphic_intrinsic_name(vmIntrinsics::ID iid case vmIntrinsics::_linkToNative: return vmSymbols::linkToNative_name(); default: fatal("unexpected intrinsic id: %d %s", vmIntrinsics::as_int(iid), vmIntrinsics::name_at(iid)); - return 0; } } @@ -449,7 +448,6 @@ Bytecodes::Code MethodHandles::signature_polymorphic_intrinsic_bytecode(vmIntrin case vmIntrinsics::_invokeBasic: return Bytecodes::_invokehandle; default: fatal("unexpected id: (%d) %s", (uint)id, vmIntrinsics::name_at(id)); - return Bytecodes::_illegal; } } @@ -463,7 +461,6 @@ int MethodHandles::signature_polymorphic_intrinsic_ref_kind(vmIntrinsics::ID iid case vmIntrinsics::_linkToInterface: return JVM_REF_invokeInterface; default: fatal("unexpected intrinsic id: %d %s", vmIntrinsics::as_int(iid), vmIntrinsics::name_at(iid)); - return 0; } } @@ -1363,6 +1360,18 @@ JVM_ENTRY(jobject, MH_invokeExact_UOE(JNIEnv* env, jobject mh, jobjectArray args } JVM_END +/** + * Throws a java/lang/UnsupportedOperationException unconditionally. + * This is required by the specification of VarHandle.{access-mode} if + * invoked directly. + */ +JVM_ENTRY(jobject, VH_UOE(JNIEnv* env, jobject vh, jobjectArray args)) { + THROW_MSG_NULL(vmSymbols::java_lang_UnsupportedOperationException(), "VarHandle access mode methods cannot be invoked reflectively"); + return nullptr; +} +JVM_END + + /// JVM_RegisterMethodHandleMethods #define LANG "Ljava/lang/" @@ -1402,6 +1411,40 @@ static JNINativeMethod MH_methods[] = { {CC "invoke", CC "([" OBJ ")" OBJ, FN_PTR(MH_invoke_UOE)}, {CC "invokeExact", CC "([" OBJ ")" OBJ, FN_PTR(MH_invokeExact_UOE)} }; +static JNINativeMethod VH_methods[] = { + // UnsupportedOperationException throwers + {CC "get", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "set", CC "([" OBJ ")V", FN_PTR(VH_UOE)}, + {CC "getVolatile", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "setVolatile", CC "([" OBJ ")V", FN_PTR(VH_UOE)}, + {CC "getAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "setRelease", CC "([" OBJ ")V", FN_PTR(VH_UOE)}, + {CC "getOpaque", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "setOpaque", CC "([" OBJ ")V", FN_PTR(VH_UOE)}, + {CC "compareAndSet", CC "([" OBJ ")Z", FN_PTR(VH_UOE)}, + {CC "compareAndExchange", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "compareAndExchangeAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "compareAndExchangeRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "weakCompareAndSetPlain", CC "([" OBJ ")Z", FN_PTR(VH_UOE)}, + {CC "weakCompareAndSet", CC "([" OBJ ")Z", FN_PTR(VH_UOE)}, + {CC "weakCompareAndSetAcquire", CC "([" OBJ ")Z", FN_PTR(VH_UOE)}, + {CC "weakCompareAndSetRelease", CC "([" OBJ ")Z", FN_PTR(VH_UOE)}, + {CC "getAndSet", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndSetAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndSetRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndAdd", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndAddAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndAddRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseOr", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseOrAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseOrRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseAnd", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseAndAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseAndRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseXor", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseXorAcquire", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)}, + {CC "getAndBitwiseXorRelease", CC "([" OBJ ")" OBJ, FN_PTR(VH_UOE)} +}; /** * This one function is exported, used by NativeLookup. @@ -1409,9 +1452,12 @@ static JNINativeMethod MH_methods[] = { JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) { assert(!MethodHandles::enabled(), "must not be enabled"); assert(vmClasses::MethodHandle_klass() != nullptr, "should be present"); + assert(vmClasses::VarHandle_klass() != nullptr, "should be present"); - oop mirror = vmClasses::MethodHandle_klass()->java_mirror(); - jclass MH_class = (jclass) JNIHandles::make_local(THREAD, mirror); + oop mh_mirror = vmClasses::MethodHandle_klass()->java_mirror(); + oop vh_mirror = vmClasses::VarHandle_klass()->java_mirror(); + jclass MH_class = (jclass) JNIHandles::make_local(THREAD, mh_mirror); + jclass VH_class = (jclass) JNIHandles::make_local(THREAD, vh_mirror); { ThreadToNativeFromVM ttnfv(thread); @@ -1423,6 +1469,10 @@ JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) status = env->RegisterNatives(MH_class, MH_methods, sizeof(MH_methods)/sizeof(JNINativeMethod)); guarantee(status == JNI_OK && !env->ExceptionOccurred(), "register java.lang.invoke.MethodHandle natives"); + + status = env->RegisterNatives(VH_class, VH_methods, sizeof(VH_methods)/sizeof(JNINativeMethod)); + guarantee(status == JNI_OK && !env->ExceptionOccurred(), + "register java.lang.invoke.VarHandle natives"); } log_debug(methodhandles, indy)("MethodHandle support loaded (using LambdaForms)"); diff --git a/src/hotspot/share/prims/nativeLookup.cpp b/src/hotspot/share/prims/nativeLookup.cpp index e838c831ff1ab..78cf7481abfd0 100644 --- a/src/hotspot/share/prims/nativeLookup.cpp +++ b/src/hotspot/share/prims/nativeLookup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -414,7 +414,7 @@ address NativeLookup::lookup_base(const methodHandle& method, TRAPS) { ss.print("'"); method->print_external_name(&ss); ss.print("'"); - THROW_MSG_0(vmSymbols::java_lang_UnsatisfiedLinkError(), ss.as_string()); + THROW_MSG_NULL(vmSymbols::java_lang_UnsatisfiedLinkError(), ss.as_string()); } diff --git a/src/hotspot/share/prims/perf.cpp b/src/hotspot/share/prims/perf.cpp index f10854b529459..feef9760d8fdf 100644 --- a/src/hotspot/share/prims/perf.cpp +++ b/src/hotspot/share/prims/perf.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,7 @@ static char* jstr_to_utf(JNIEnv *env, jstring str, TRAPS) { char* utfstr = nullptr; if (str == nullptr) { - THROW_0(vmSymbols::java_lang_NullPointerException()); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); //throw_new(env,"NullPointerException"); } @@ -68,7 +68,7 @@ PERF_ENTRY(jobject, Perf_Attach(JNIEnv *env, jobject unused, int vmid)) PerfWrapper("Perf_Attach"); - char* address = 0; + char* address = nullptr; size_t capacity = 0; // attach to the PerfData memory region for the specified VM @@ -90,7 +90,7 @@ PERF_ENTRY(void, Perf_Detach(JNIEnv *env, jobject unused, jobject buffer)) return; } - void* address = 0; + void* address = nullptr; jlong capacity = 0; // get buffer address and capacity @@ -113,7 +113,7 @@ PERF_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name, if (units <= 0 || units > PerfData::U_Last) { debug_only(warning("unexpected units argument, units = %d", units)); - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } ResourceMark rm; @@ -128,7 +128,7 @@ PERF_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name, // check that the PerfData name doesn't already exist if (PerfDataManager::exists(name_utf)) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "PerfLong name already exists"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "PerfLong name already exists"); } switch(variability) { @@ -152,7 +152,7 @@ PERF_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name, default: /* Illegal Argument */ debug_only(warning("unexpected variability value: %d", variability)); - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); break; } @@ -174,21 +174,21 @@ PERF_ENTRY(jobject, Perf_CreateByteArray(JNIEnv *env, jobject perf, // check for valid byte array objects if (name == nullptr || value == nullptr) { - THROW_0(vmSymbols::java_lang_NullPointerException()); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); } // check for valid variability classification if (variability != PerfData::V_Constant && variability != PerfData::V_Variable) { debug_only(warning("unexpected variability value: %d", variability)); - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } // check for valid units if (units != PerfData::U_String) { // only String based ByteArray objects are currently supported debug_only(warning("unexpected units value: %d", variability)); - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } int value_length; @@ -211,7 +211,7 @@ PERF_ENTRY(jobject, Perf_CreateByteArray(JNIEnv *env, jobject perf, // check that the counter name doesn't already exist if (PerfDataManager::exists((char*)name_utf)) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "PerfByteArray name already exists"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "PerfByteArray name already exists"); } PerfByteArray* pbv = nullptr; diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index 942d9100c29ea..239ae480030f5 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -552,7 +552,7 @@ UNSAFE_ENTRY(jobject, Unsafe_StaticFieldBase0(JNIEnv *env, jobject unsafe, jobje int modifiers = java_lang_reflect_Field::modifiers(reflected); if ((modifiers & JVM_ACC_STATIC) == 0) { - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } return JNIHandles::make_local(THREAD, mirror); @@ -652,7 +652,7 @@ static jclass Unsafe_DefineClass_impl(JNIEnv *env, jstring name, jbyteArray data jbyte *body; char *utfName = nullptr; - jclass result = 0; + jclass result = nullptr; char buf[128]; assert(data != nullptr, "Class bytes must not be null"); @@ -665,7 +665,7 @@ static jclass Unsafe_DefineClass_impl(JNIEnv *env, jstring name, jbyteArray data body = NEW_C_HEAP_ARRAY_RETURN_NULL(jbyte, length, mtInternal); if (body == nullptr) { throw_new(env, "java/lang/OutOfMemoryError"); - return 0; + return nullptr; } env->GetByteArrayRegion(data, offset, length, body); diff --git a/src/hotspot/share/prims/wbtestmethods/parserTests.cpp b/src/hotspot/share/prims/wbtestmethods/parserTests.cpp index 2845aa9aa104e..efcf2b070d03b 100644 --- a/src/hotspot/share/prims/wbtestmethods/parserTests.cpp +++ b/src/hotspot/share/prims/wbtestmethods/parserTests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,6 +127,14 @@ static void fill_in_parser(DCmdParser* parser, oop argument) } else { parser->add_dcmd_option(argument); } + } else if (strcmp(type, "FILE") == 0) { + DCmdArgument* argument = + new DCmdArgument(name, desc, "FILE", mandatory); + if (isarg) { + parser->add_dcmd_argument(argument); + } else { + parser->add_dcmd_option(argument); + } } } diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 43e47409c4cb6..544eeabb5c212 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -590,7 +590,7 @@ WB_ENTRY(jobject, WB_G1AuxiliaryMemoryUsage(JNIEnv* env)) Handle h = MemoryService::create_MemoryUsage_obj(usage, CHECK_NULL); return JNIHandles::make_local(THREAD, h()); } - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1AuxiliaryMemoryUsage: G1 GC is not enabled"); + THROW_MSG_NULL(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1AuxiliaryMemoryUsage: G1 GC is not enabled"); WB_END WB_ENTRY(jint, WB_G1ActiveMemoryNodeCount(JNIEnv* env, jobject o)) @@ -799,6 +799,7 @@ WB_END WB_ENTRY(jboolean, WB_IsFrameDeoptimized(JNIEnv* env, jobject o, jint depth)) bool result = false; if (thread->has_last_Java_frame()) { + ResourceMark rm(THREAD); RegisterMap reg_map(thread, RegisterMap::UpdateMap::include, RegisterMap::ProcessFrames::include, diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index d353c5a162ae3..9086a5f6c7121 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -102,7 +102,7 @@ size_t Arguments::_default_SharedBaseAddress = SharedBaseAddress; bool Arguments::_enable_preview = false; -LegacyGCLogging Arguments::_legacyGCLogging = { 0, 0 }; +LegacyGCLogging Arguments::_legacyGCLogging = { nullptr, 0 }; // These are not set by the JDK's built-in launchers, but they can be set by // programs that embed the JVM using JNI_CreateJavaVM. See comments around @@ -503,8 +503,6 @@ static SpecialFlag const special_jvm_flags[] = { { "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "DontYieldALot", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, - { "PreserveAllAnnotations", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, - { "UseNotificationThread", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "LockingMode", JDK_Version::jdk(24), JDK_Version::jdk(26), JDK_Version::jdk(27) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -513,6 +511,8 @@ static SpecialFlag const special_jvm_flags[] = { { "MetaspaceReclaimPolicy", JDK_Version::undefined(), JDK_Version::jdk(21), JDK_Version::undefined() }, + { "UseNotificationThread", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, + { "PreserveAllAnnotations", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "UseEmptySlotsInSupers", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "OldSize", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, #if defined(X86) @@ -521,6 +521,8 @@ static SpecialFlag const special_jvm_flags[] = { { "RTMRetryCount", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, #endif // X86 + + { "BaseFootPrintEstimate", JDK_Version::undefined(), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "HeapFirstMaximumCompactionCount", JDK_Version::undefined(), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "UseVtableBasedCHA", JDK_Version::undefined(), JDK_Version::jdk(24), JDK_Version::jdk(25) }, #ifdef ASSERT @@ -1653,9 +1655,6 @@ jint Arguments::set_aggressive_heap_flags() { #endif // Increase some data structure sizes for efficiency - if (FLAG_SET_CMDLINE(BaseFootPrintEstimate, MaxHeapSize) != JVMFlag::SUCCESS) { - return JNI_EINVAL; - } if (FLAG_SET_CMDLINE(ResizeTLAB, false) != JVMFlag::SUCCESS) { return JNI_EINVAL; } @@ -1850,11 +1849,11 @@ bool Arguments::is_bad_option(const JavaVMOption* option, jboolean ignore, } static const char* user_assertion_options[] = { - "-da", "-ea", "-disableassertions", "-enableassertions", 0 + "-da", "-ea", "-disableassertions", "-enableassertions", nullptr }; static const char* system_assertion_options[] = { - "-dsa", "-esa", "-disablesystemassertions", "-enablesystemassertions", 0 + "-dsa", "-esa", "-disablesystemassertions", "-enablesystemassertions", nullptr }; bool Arguments::parse_uint(const char* value, diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index 399a78fd3fce1..bf198b5f5621f 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ #include "runtime/orderAccess.hpp" #include "utilities/align.hpp" #include "utilities/bytes.hpp" +#include "utilities/checkedCast.hpp" #include "utilities/macros.hpp" #include @@ -1118,7 +1119,7 @@ inline T Atomic::CmpxchgByteUsingInt::operator()(T volatile* dest, uint8_t canon_compare_value = compare_value; volatile uint32_t* aligned_dest = reinterpret_cast(align_down(dest, sizeof(uint32_t))); - size_t offset = pointer_delta(dest, aligned_dest, 1); + uint32_t offset = checked_cast(pointer_delta(dest, aligned_dest, 1)); uint32_t idx = (Endian::NATIVE == Endian::BIG) ? (sizeof(uint32_t) - 1 - offset) diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 87f60debc140a..0e4640b6a9f4d 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -1865,7 +1865,7 @@ int ThawBase::remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &arg const int frame_size = f.cb()->frame_size(); argsize = f.stack_argsize(); - f.next(SmallRegisterMap::instance, true /* stop */); + f.next(SmallRegisterMap::instance(), true /* stop */); empty = f.is_done(); assert(!empty || argsize == chunk->argsize(), ""); @@ -2075,7 +2075,7 @@ bool ThawBase::recurse_thaw_java_frame(frame& caller, int num_frames) { int argsize = _stream.stack_argsize(); - _stream.next(SmallRegisterMap::instance); + _stream.next(SmallRegisterMap::instance()); assert(_stream.to_frame().is_empty() == _stream.is_done(), ""); // we never leave a compiled caller of an interpreted frame as the top frame in the chunk @@ -2183,7 +2183,7 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c assert(hf.is_interpreted_frame(), ""); if (UNLIKELY(seen_by_gc())) { - _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance); + _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance()); } const bool is_bottom_frame = recurse_thaw_java_frame(caller, num_frames); @@ -2226,7 +2226,7 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c if (!is_bottom_frame) { // can only fix caller once this frame is thawed (due to callee saved regs) - _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance); + _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance()); } else if (_cont.tail()->has_bitmap() && locals > 0) { assert(hf.is_heap_frame(), "should be"); address start = (address)(heap_frame_bottom - locals); @@ -2243,7 +2243,7 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n assert(_cont.is_preempted() || !stub_caller, "stub caller not at preemption"); if (!stub_caller && UNLIKELY(seen_by_gc())) { // recurse_thaw_stub_frame already invoked our barriers with a full regmap - _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance); + _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance()); } const bool is_bottom_frame = recurse_thaw_java_frame(caller, num_frames); @@ -2302,7 +2302,7 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n if (!is_bottom_frame) { // can only fix caller once this frame is thawed (due to callee saved regs); this happens on the stack - _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance); + _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance()); } else if (_cont.tail()->has_bitmap() && added_argsize > 0) { address start = (address)(heap_frame_top + ContinuationHelper::CompiledFrame::size(hf) + frame::metadata_words_at_top); int stack_args_slots = f.cb()->as_nmethod()->num_stack_arg_slots(false /* rounded */); @@ -2384,7 +2384,7 @@ void ThawBase::finish_thaw(frame& f) { f.set_sp(align_down(f.sp(), frame::frame_alignment)); } push_return_frame(f); - chunk->fix_thawed_frame(f, SmallRegisterMap::instance); // can only fix caller after push_return_frame (due to callee saved regs) + chunk->fix_thawed_frame(f, SmallRegisterMap::instance()); // can only fix caller after push_return_frame (due to callee saved regs) assert(_cont.is_empty() == _cont.last_frame().is_empty(), ""); @@ -2450,8 +2450,8 @@ static inline intptr_t* thaw_internal(JavaThread* thread, const Continuation::th intptr_t* const sp = thw.thaw(kind); assert(is_aligned(sp, frame::frame_alignment), ""); - // All the frames have been thawed so we know they don't hold any monitors - assert(thread->held_monitor_count() == 0, "Must be"); + // All or part of the frames have been thawed so we know they don't hold any monitors except JNI monitors. + assert(thread->held_monitor_count() == thread->jni_monitor_count(), "Must be"); #ifdef ASSERT intptr_t* sp0 = sp; diff --git a/src/hotspot/share/runtime/flags/jvmFlag.cpp b/src/hotspot/share/runtime/flags/jvmFlag.cpp index 1917cd4b1980d..c465a942e5c68 100644 --- a/src/hotspot/share/runtime/flags/jvmFlag.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp @@ -31,6 +31,7 @@ #include "runtime/flags/jvmFlagAccess.hpp" #include "runtime/flags/jvmFlagLookup.hpp" #include "runtime/globals_extension.hpp" +#include "utilities/bitMap.hpp" #include "utilities/defaultStream.hpp" #include "utilities/stringUtils.hpp" @@ -692,7 +693,7 @@ void JVMFlag::printFlags(outputStream* out, bool withComments, bool printRanges, // called as part of error reporting, so handle native OOMs gracefully. // The last entry is the null entry. - const size_t length = JVMFlag::numFlags - 1; + constexpr size_t length = (sizeof(flagTable) / sizeof(JVMFlag)) - 1; // Print if (!printRanges) { @@ -701,26 +702,26 @@ void JVMFlag::printFlags(outputStream* out, bool withComments, bool printRanges, out->print_cr("[Global flags ranges]"); } - // Sort - JVMFlag** array = NEW_C_HEAP_ARRAY_RETURN_NULL(JVMFlag*, length, mtArguments); - if (array != nullptr) { - for (size_t i = 0; i < length; i++) { - array[i] = &flagTable[i]; - } - qsort(array, length, sizeof(JVMFlag*), compare_flags); - + BitMap::bm_word_t iteratorArray[BitMap::calc_size_in_words(length)]; + BitMapView iteratorMarkers(iteratorArray, length); + iteratorMarkers.clear_range(0, length); + // Print the flag with best sort value, then mark it. + for (size_t j = 0; j < length; j++) { + JVMFlag* bestFlag = nullptr; + size_t bestFlagIndex = 0; for (size_t i = 0; i < length; i++) { - if (array[i]->is_unlocked() && !(skipDefaults && array[i]->is_default())) { - array[i]->print_on(out, withComments, printRanges); + const bool skip = (skipDefaults && flagTable[i].is_default()); + const bool visited = iteratorMarkers.at(i); + if (!visited && flagTable[i].is_unlocked() && !skip) { + if ((bestFlag == nullptr) || (strcmp(bestFlag->name(), flagTable[i].name()) > 0)) { + bestFlag = &flagTable[i]; + bestFlagIndex = i; + } } } - FREE_C_HEAP_ARRAY(JVMFlag*, array); - } else { - // OOM? Print unsorted. - for (size_t i = 0; i < length; i++) { - if (flagTable[i].is_unlocked() && !(skipDefaults && flagTable[i].is_default())) { - flagTable[i].print_on(out, withComments, printRanges); - } + if (bestFlag != nullptr) { + bestFlag->print_on(out, withComments, printRanges); + iteratorMarkers.at_put(bestFlagIndex, true); } } } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 7a7a3769ac90f..61efc0b93764d 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -795,10 +795,6 @@ const int ObjectAlignmentInBytes = 8; "but not all -Xrun libraries may support the state of the VM " \ "at this time") \ \ - product(bool, PreserveAllAnnotations, false, \ - "(Deprecated) Preserve RuntimeInvisibleAnnotations as well " \ - "as RuntimeVisibleAnnotations") \ - \ develop(uintx, PreallocatedOutOfMemoryErrorCount, 4, \ "Number of OutOfMemoryErrors preallocated with backtrace") \ \ @@ -956,9 +952,6 @@ const int ObjectAlignmentInBytes = 8; product(bool, EnableThreadSMRStatistics, trueInDebug, DIAGNOSTIC, \ "Enable Thread SMR Statistics") \ \ - product(bool, UseNotificationThread, true, \ - "(Deprecated) Use Notification Thread") \ - \ product(bool, Inline, true, \ "Enable inlining") \ \ diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 7ffe56d9715a3..23df464aba8bb 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -484,7 +484,7 @@ void before_exit(JavaThread* thread, bool halt) { #ifdef LINUX if (DumpPerfMapAtExit) { - CodeCache::write_perf_map(); + CodeCache::write_perf_map(nullptr, tty); } if (PrintMemoryMapAtExit) { MemMapPrinter::print_all_mappings(tty); diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index a3eef07ba0ac9..b69ab708afa52 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -470,8 +470,8 @@ JavaThread::JavaThread(MEMFLAGS flags) : #endif // INCLUDE_JVMCI _exception_oop(oop()), - _exception_pc(0), - _exception_handler_pc(0), + _exception_pc(nullptr), + _exception_handler_pc(nullptr), _is_method_handle_return(0), _jni_active_critical(0), @@ -483,7 +483,7 @@ JavaThread::JavaThread(MEMFLAGS flags) : _frames_to_pop_failed_realloc(0), _cont_entry(nullptr), - _cont_fastpath(0), + _cont_fastpath(nullptr), _cont_fastpath_thread_state(1), _held_monitor_count(0), _jni_monitor_count(0), diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index add47738db0bc..8c1dff38051d9 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -235,13 +235,8 @@ void mutex_init() { MUTEX_DEFN(Patching_lock , PaddedMutex , nosafepoint); // used for safepointing and code patching. MUTEX_DEFN(MonitorDeflation_lock , PaddedMonitor, nosafepoint); // used for monitor deflation thread operations - MUTEX_DEFN(Service_lock , PaddedMonitor, service); // used for service thread operations - - if (UseNotificationThread) { - MUTEX_DEFN(Notification_lock , PaddedMonitor, service); // used for notification thread operations - } else { - Notification_lock = Service_lock; - } + MUTEX_DEFN(Service_lock , PaddedMonitor, service); // used for service thread operations + MUTEX_DEFN(Notification_lock , PaddedMonitor, service); // used for notification thread operations MUTEX_DEFN(JmethodIdCreation_lock , PaddedMutex , nosafepoint-2); // used for creating jmethodIDs. MUTEX_DEFN(InvokeMethodTypeTable_lock , PaddedMutex , safepoint); @@ -274,7 +269,6 @@ void mutex_init() { MUTEX_DEFN(JvmtiThreadState_lock , PaddedMutex , safepoint); // Used by JvmtiThreadState/JvmtiEventController MUTEX_DEFN(EscapeBarrier_lock , PaddedMonitor, nosafepoint); // Used to synchronize object reallocation/relocking triggered by JVMTI - MUTEX_DEFN(JvmtiVTMSTransition_lock , PaddedMonitor, safepoint); // used for Virtual Thread Mount State transition management MUTEX_DEFN(Management_lock , PaddedMutex , safepoint); // used for JVM management MUTEX_DEFN(ConcurrentGCBreakpoints_lock , PaddedMonitor, safepoint, true); @@ -360,6 +354,7 @@ void mutex_init() { // JVMCIRuntime_lock must be acquired before JVMCI_lock to avoid deadlock MUTEX_DEFL(JVMCI_lock , PaddedMonitor, JVMCIRuntime_lock); #endif + MUTEX_DEFL(JvmtiVTMSTransition_lock , PaddedMonitor, JvmtiThreadState_lock); // used for Virtual Thread Mount State transition management // Allocate RecursiveMutex MultiArray_lock = new RecursiveMutex(); diff --git a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp index 5e78baad3ab2a..657427943aa2f 100644 --- a/src/hotspot/share/runtime/perfData.cpp +++ b/src/hotspot/share/runtime/perfData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -361,7 +361,7 @@ PerfStringConstant* PerfDataManager::create_string_constant(CounterNS ns, if (!p->is_valid()) { // allocation of native resources failed. delete p; - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_NULL(vmSymbols::java_lang_OutOfMemoryError()); } add_item(p, false); @@ -379,7 +379,7 @@ PerfLongConstant* PerfDataManager::create_long_constant(CounterNS ns, if (!p->is_valid()) { // allocation of native resources failed. delete p; - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_NULL(vmSymbols::java_lang_OutOfMemoryError()); } add_item(p, false); @@ -402,7 +402,7 @@ PerfStringVariable* PerfDataManager::create_string_variable(CounterNS ns, if (!p->is_valid()) { // allocation of native resources failed. delete p; - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_NULL(vmSymbols::java_lang_OutOfMemoryError()); } add_item(p, false); @@ -420,7 +420,7 @@ PerfLongVariable* PerfDataManager::create_long_variable(CounterNS ns, if (!p->is_valid()) { // allocation of native resources failed. delete p; - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_NULL(vmSymbols::java_lang_OutOfMemoryError()); } add_item(p, false); @@ -442,7 +442,7 @@ PerfLongVariable* PerfDataManager::create_long_variable(CounterNS ns, if (!p->is_valid()) { // allocation of native resources failed. delete p; - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_NULL(vmSymbols::java_lang_OutOfMemoryError()); } add_item(p, true); @@ -460,7 +460,7 @@ PerfLongCounter* PerfDataManager::create_long_counter(CounterNS ns, if (!p->is_valid()) { // allocation of native resources failed. delete p; - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_NULL(vmSymbols::java_lang_OutOfMemoryError()); } add_item(p, false); @@ -482,7 +482,7 @@ PerfLongCounter* PerfDataManager::create_long_counter(CounterNS ns, if (!p->is_valid()) { // allocation of native resources failed. delete p; - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_NULL(vmSymbols::java_lang_OutOfMemoryError()); } add_item(p, true); diff --git a/src/hotspot/share/runtime/reflection.cpp b/src/hotspot/share/runtime/reflection.cpp index bc8779158d31d..865d25fa06b8a 100644 --- a/src/hotspot/share/runtime/reflection.cpp +++ b/src/hotspot/share/runtime/reflection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -325,7 +325,7 @@ static Klass* basic_type_mirror_to_arrayklass(oop basic_type_mirror, TRAPS) { assert(java_lang_Class::is_primitive(basic_type_mirror), "just checking"); BasicType type = java_lang_Class::primitive_type(basic_type_mirror); if (type == T_VOID) { - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } else { return Universe::typeArrayKlass(type); @@ -334,10 +334,10 @@ static Klass* basic_type_mirror_to_arrayklass(oop basic_type_mirror, TRAPS) { arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) { if (element_mirror == nullptr) { - THROW_0(vmSymbols::java_lang_NullPointerException()); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); } if (length < 0) { - THROW_MSG_0(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", length)); + THROW_MSG_NULL(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", length)); } if (java_lang_Class::is_primitive(element_mirror)) { Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL); @@ -345,7 +345,7 @@ arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) { } else { Klass* k = java_lang_Class::as_Klass(element_mirror); if (k->is_array_klass() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) { - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } return oopFactory::new_objArray(k, length, THREAD); } @@ -357,19 +357,19 @@ arrayOop Reflection::reflect_new_multi_array(oop element_mirror, typeArrayOop di assert(TypeArrayKlass::cast(dim_array->klass())->element_type() == T_INT, "just checking"); if (element_mirror == nullptr) { - THROW_0(vmSymbols::java_lang_NullPointerException()); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); } int len = dim_array->length(); if (len <= 0 || len > MAX_DIM) { - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } jint dimensions[MAX_DIM]; // C array copy of intArrayOop for (int i = 0; i < len; i++) { int d = dim_array->int_at(i); if (d < 0) { - THROW_MSG_0(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", d)); + THROW_MSG_NULL(vmSymbols::java_lang_NegativeArraySizeException(), err_msg("%d", d)); } dimensions[i] = d; } @@ -383,7 +383,7 @@ arrayOop Reflection::reflect_new_multi_array(oop element_mirror, typeArrayOop di if (klass->is_array_klass()) { int k_dim = ArrayKlass::cast(klass)->dimension(); if (k_dim + len > MAX_DIM) { - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } dim += k_dim; } @@ -977,11 +977,11 @@ static oop invoke(InstanceKlass* klass, } else { // check for null receiver if (receiver.is_null()) { - THROW_0(vmSymbols::java_lang_NullPointerException()); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); } // Check class of receiver against class declaring method if (!receiver->is_a(klass)) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "object is not an instance of declaring class"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "object is not an instance of declaring class"); } // target klass is receiver's klass target_klass = receiver->klass(); @@ -1047,15 +1047,15 @@ static oop invoke(InstanceKlass* klass, reflected_method->name(), reflected_method->signature()); ss.print("'"); - THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), ss.as_string()); + THROW_MSG_NULL(vmSymbols::java_lang_NoSuchMethodError(), ss.as_string()); } assert(ptypes->is_objArray(), "just checking"); int args_len = args.is_null() ? 0 : args->length(); // Check number of arguments if (ptypes->length() != args_len) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), - "wrong number of arguments"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + "wrong number of arguments"); } // Create object to contain parameters for the JavaCall @@ -1085,14 +1085,14 @@ static oop invoke(InstanceKlass* klass, case T_FLOAT: java_args.push_float(value.f); break; case T_DOUBLE: java_args.push_double(value.d); break; default: - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch"); } } else { if (arg != nullptr) { Klass* k = java_lang_Class::as_Klass(type_mirror); if (!arg->is_a(k)) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), - "argument type mismatch"); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), + "argument type mismatch"); } } Handle arg_handle(THREAD, arg); // Create handle for argument @@ -1148,7 +1148,7 @@ oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle InstanceKlass* klass = InstanceKlass::cast(java_lang_Class::as_Klass(mirror)); Method* m = klass->method_with_idnum(slot); if (m == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke"); + THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "invoke"); } methodHandle method(THREAD, m); @@ -1165,7 +1165,7 @@ oop Reflection::invoke_constructor(oop constructor_mirror, objArrayHandle args, InstanceKlass* klass = InstanceKlass::cast(java_lang_Class::as_Klass(mirror)); Method* m = klass->method_with_idnum(slot); if (m == nullptr) { - THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke"); + THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "invoke"); } methodHandle method(THREAD, m); assert(method->name() == vmSymbols::object_initializer_name(), "invalid constructor"); diff --git a/src/hotspot/share/runtime/serviceThread.cpp b/src/hotspot/share/runtime/serviceThread.cpp index f02e5062e672e..a81285ac97c0b 100644 --- a/src/hotspot/share/runtime/serviceThread.cpp +++ b/src/hotspot/share/runtime/serviceThread.cpp @@ -81,10 +81,7 @@ static void cleanup_oopstorages() { void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { while (true) { - bool sensors_changed = false; bool has_jvmti_events = false; - bool has_gc_notification_event = false; - bool has_dcmd_notification_event = false; bool stringtable_work = false; bool symboltable_work = false; bool finalizerservice_work = false; @@ -113,10 +110,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { // only the first recognized bit of work, to avoid frequently true early // tests from potentially starving later work. Hence the use of // arithmetic-or to combine results; we don't want short-circuiting. - while (((sensors_changed = (!UseNotificationThread && LowMemoryDetector::has_pending_requests())) | - (has_jvmti_events = _jvmti_service_queue.has_events()) | - (has_gc_notification_event = (!UseNotificationThread && GCNotifier::has_event())) | - (has_dcmd_notification_event = (!UseNotificationThread && DCmdFactory::has_pending_jmx_notification())) | + while (((has_jvmti_events = _jvmti_service_queue.has_events()) | (stringtable_work = StringTable::has_work()) | (symboltable_work = SymbolTable::has_work()) | (finalizerservice_work = FinalizerService::has_work()) | @@ -158,20 +152,6 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { _jvmti_event = nullptr; // reset } - if (!UseNotificationThread) { - if (sensors_changed) { - LowMemoryDetector::process_sensor_changes(jt); - } - - if(has_gc_notification_event) { - GCNotifier::sendNotification(CHECK); - } - - if(has_dcmd_notification_event) { - DCmdFactory::send_notification(CHECK); - } - } - if (resolved_method_table_work) { ResolvedMethodTable::do_concurrent_work(jt); } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 0e6d367586b9d..0eec7e4c34cae 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -100,10 +100,6 @@ SafepointBlob* SharedRuntime::_polling_page_vectors_safepoint_handler_blob; SafepointBlob* SharedRuntime::_polling_page_safepoint_handler_blob; SafepointBlob* SharedRuntime::_polling_page_return_handler_blob; -#ifdef COMPILER2 -UncommonTrapBlob* SharedRuntime::_uncommon_trap_blob; -#endif // COMPILER2 - nmethod* SharedRuntime::_cont_doYield_stub; //----------------------------generate_stubs----------------------------------- @@ -129,10 +125,6 @@ void SharedRuntime::generate_stubs() { _polling_page_return_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_RETURN); generate_deopt_blob(); - -#ifdef COMPILER2 - generate_uncommon_trap_blob(); -#endif // COMPILER2 } #include diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index da61883b2fe64..9eec8e079ec34 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -62,10 +62,6 @@ class SharedRuntime: AllStatic { static SafepointBlob* _polling_page_safepoint_handler_blob; static SafepointBlob* _polling_page_return_handler_blob; -#ifdef COMPILER2 - static UncommonTrapBlob* _uncommon_trap_blob; -#endif // COMPILER2 - static nmethod* _cont_doYield_stub; #ifndef PRODUCT @@ -223,11 +219,6 @@ class SharedRuntime: AllStatic { return _wrong_method_abstract_blob->entry_point(); } -#ifdef COMPILER2 - static void generate_uncommon_trap_blob(void); - static UncommonTrapBlob* uncommon_trap_blob() { return _uncommon_trap_blob; } -#endif // COMPILER2 - static address get_resolve_opt_virtual_call_stub() { assert(_resolve_opt_virtual_call_blob != nullptr, "oops"); return _resolve_opt_virtual_call_blob->entry_point(); diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index 047f6703b545c..2d45163d0f4a2 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -2074,7 +2074,7 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out, void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out, bool log_all) { if (_in_use_list.count() > 0) { stringStream ss; - out->print_cr("In-use monitor info:"); + out->print_cr("In-use monitor info%s:", log_all ? "" : " (eliding idle monitors)"); out->print_cr("(B -> is_busy, H -> has hash code, L -> lock status)"); out->print_cr("%18s %s %18s %18s", "monitor", "BHL", "object", "object type"); diff --git a/src/hotspot/share/runtime/vframeArray.cpp b/src/hotspot/share/runtime/vframeArray.cpp index d3bbbc283992a..1b26abf740287 100644 --- a/src/hotspot/share/runtime/vframeArray.cpp +++ b/src/hotspot/share/runtime/vframeArray.cpp @@ -321,7 +321,7 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, "should be held, after move_to"); } if (ProfileInterpreter) { - iframe()->interpreter_frame_set_mdp(0); // clear out the mdp. + iframe()->interpreter_frame_set_mdp(nullptr); // clear out the mdp. } iframe()->interpreter_frame_set_bcp(bcp); if (ProfileInterpreter) { diff --git a/src/hotspot/share/services/diagnosticArgument.cpp b/src/hotspot/share/services/diagnosticArgument.cpp index 94f2d3e1eb124..5fd565a605a44 100644 --- a/src/hotspot/share/services/diagnosticArgument.cpp +++ b/src/hotspot/share/services/diagnosticArgument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -189,9 +189,18 @@ template <> void DCmdArgument::parse_value(const char* str, destroy_value(); } else { // Use realloc as we may have a default set. - _value = REALLOC_C_HEAP_ARRAY(char, _value, len + 1, mtInternal); - int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str); - assert((size_t)n <= len, "Unexpected number of characters in string"); + if (strcmp(type(), "FILE") == 0) { + _value = REALLOC_C_HEAP_ARRAY(char, _value, JVM_MAXPATHLEN, mtInternal); + if (!Arguments::copy_expand_pid(str, len, _value, JVM_MAXPATHLEN)) { + stringStream error_msg; + error_msg.print("File path invalid or too long: %s", str); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), error_msg.base()); + } + } else { + _value = REALLOC_C_HEAP_ARRAY(char, _value, len + 1, mtInternal); + int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str); + assert((size_t)n <= len, "Unexpected number of characters in string"); + } } } diff --git a/src/hotspot/share/services/diagnosticArgument.hpp b/src/hotspot/share/services/diagnosticArgument.hpp index c9683ce4a2196..1451ea34f86ca 100644 --- a/src/hotspot/share/services/diagnosticArgument.hpp +++ b/src/hotspot/share/services/diagnosticArgument.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "classfile/vmSymbols.hpp" #include "memory/allocation.hpp" +#include "runtime/arguments.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" #include "utilities/exceptions.hpp" diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 7d2325c16b651..7a2c083814614 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -473,7 +473,7 @@ void FinalizerInfoDCmd::execute(DCmdSource source, TRAPS) { #if INCLUDE_SERVICES // Heap dumping/inspection supported HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), - _filename("filename","Name of the dump file", "STRING",true), + _filename("filename","Name of the dump file", "FILE",true), _all("-all", "Dump all objects, including unreachable objects", "BOOLEAN", false, "false"), _gzip("-gz", "If specified, the heap dump is written in gzipped format " @@ -852,20 +852,15 @@ void CodeCacheDCmd::execute(DCmdSource source, TRAPS) { } #ifdef LINUX -#define DEFAULT_PERFMAP_FILENAME "/tmp/perf-.map" - PerfMapDCmd::PerfMapDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), - _filename("filename", "Name of the map file", "STRING", false, DEFAULT_PERFMAP_FILENAME) + _filename("filename", "Name of the map file", "FILE", false, DEFAULT_PERFMAP_FILENAME) { _dcmdparser.add_dcmd_argument(&_filename); } void PerfMapDCmd::execute(DCmdSource source, TRAPS) { - // The check for _filename.is_set() is because we don't want to use - // DEFAULT_PERFMAP_FILENAME, since it is meant as a description - // of the default, not the actual default. - CodeCache::write_perf_map(_filename.is_set() ? _filename.value() : nullptr); + CodeCache::write_perf_map(_filename.value(), output()); } #endif // LINUX @@ -997,12 +992,12 @@ void ClassesDCmd::execute(DCmdSource source, TRAPS) { } #if INCLUDE_CDS -#define DEFAULT_CDS_ARCHIVE_FILENAME "java_pid_.jsa" +#define DEFAULT_CDS_ARCHIVE_FILENAME "java_pid%p_.jsa" DumpSharedArchiveDCmd::DumpSharedArchiveDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), _suboption("subcmd", "static_dump | dynamic_dump", "STRING", true), - _filename("filename", "Name of shared archive to be dumped", "STRING", false, + _filename("filename", "Name of shared archive to be dumped", "FILE", false, DEFAULT_CDS_ARCHIVE_FILENAME) { _dcmdparser.add_dcmd_argument(&_suboption); @@ -1040,7 +1035,7 @@ void DumpSharedArchiveDCmd::execute(DCmdSource source, TRAPS) { // call CDS.dumpSharedArchive Handle fileh; if (file != nullptr) { - fileh = java_lang_String::create_from_str(_filename.value(), CHECK); + fileh = java_lang_String::create_from_str(file, CHECK); } Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); Klass* cds_klass = SystemDictionary::resolve_or_fail(cds_name, true /*throw error*/, CHECK); @@ -1105,7 +1100,7 @@ ThreadDumpToFileDCmd::ThreadDumpToFileDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), _overwrite("-overwrite", "May overwrite existing file", "BOOLEAN", false, "false"), _format("-format", "Output format (\"plain\" or \"json\")", "STRING", false, "plain"), - _filepath("filepath", "The file path to the output file", "STRING", true) { + _filepath("filepath", "The file path to the output file", "FILE", true) { _dcmdparser.add_dcmd_option(&_overwrite); _dcmdparser.add_dcmd_option(&_format); _dcmdparser.add_dcmd_argument(&_filepath); @@ -1186,23 +1181,16 @@ void SystemMapDCmd::execute(DCmdSource source, TRAPS) { MemMapPrinter::print_all_mappings(output()); } -static constexpr char default_filename[] = "vm_memory_map_.txt"; +static constexpr char default_filename[] = "vm_memory_map_%p.txt"; SystemDumpMapDCmd::SystemDumpMapDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), - _filename("-F", "file path", "STRING", false, default_filename) { + _filename("-F", "file path", "FILE", false, default_filename) { _dcmdparser.add_dcmd_option(&_filename); } void SystemDumpMapDCmd::execute(DCmdSource source, TRAPS) { - stringStream defaultname; - const char* name = nullptr; - if (_filename.is_set()) { - name = _filename.value(); - } else { - defaultname.print("vm_memory_map_%d.txt", os::current_process_id()); - name = defaultname.base(); - } + const char* name = _filename.value(); fileStream fs(name); if (fs.is_open()) { if (!MemTracker::enabled()) { diff --git a/src/hotspot/share/services/diagnosticFramework.hpp b/src/hotspot/share/services/diagnosticFramework.hpp index e8881c2364611..357482ec5a14d 100644 --- a/src/hotspot/share/services/diagnosticFramework.hpp +++ b/src/hotspot/share/services/diagnosticFramework.hpp @@ -67,7 +67,7 @@ class CmdLine : public StackObj { const char* cmd_addr() const { return _cmd; } size_t cmd_len() const { return _cmd_len; } bool is_empty() const { return _cmd_len == 0; } - bool is_executable() const { return is_empty() || _cmd[0] != '#'; } + bool is_executable() const { return !is_empty() && _cmd[0] != '#'; } bool is_stop() const { return !is_empty() && strncmp("stop", _cmd, _cmd_len) == 0; } }; diff --git a/src/hotspot/share/services/lowMemoryDetector.hpp b/src/hotspot/share/services/lowMemoryDetector.hpp index 5c1201b4641a8..73491ea8412de 100644 --- a/src/hotspot/share/services/lowMemoryDetector.hpp +++ b/src/hotspot/share/services/lowMemoryDetector.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,8 +61,7 @@ // // May need to deal with hysteresis effect. // -// Memory detection code runs in the Notification thread or -// ServiceThread depending on UseNotificationThread flag. +// Memory detection code runs in the Notification thread. class OopClosure; class MemoryPool; diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index a9bb72a481a57..2c9f36c098d07 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -149,9 +149,8 @@ void Management::init() { } void Management::initialize(TRAPS) { - if (UseNotificationThread) { - NotificationThread::initialize(); - } + NotificationThread::initialize(); + if (ManagementServer) { ResourceMark rm(THREAD); HandleMark hm(THREAD); diff --git a/src/hotspot/share/utilities/bitMap.cpp b/src/hotspot/share/utilities/bitMap.cpp index 9bd5c8e000643..a07807070918f 100644 --- a/src/hotspot/share/utilities/bitMap.cpp +++ b/src/hotspot/share/utilities/bitMap.cpp @@ -197,11 +197,6 @@ bm_word_t* CHeapBitMap::reallocate(bm_word_t* map, size_t old_size_in_words, siz } #ifdef ASSERT -void BitMap::verify_size(idx_t size_in_bits) { - assert(size_in_bits <= max_size_in_bits(), - "out of bounds: " SIZE_FORMAT, size_in_bits); -} - void BitMap::verify_index(idx_t bit) const { assert(bit < _size, "BitMap index out of bounds: " SIZE_FORMAT " >= " SIZE_FORMAT, diff --git a/src/hotspot/share/utilities/bitMap.hpp b/src/hotspot/share/utilities/bitMap.hpp index 0d592de7cd077..5db12badbc95e 100644 --- a/src/hotspot/share/utilities/bitMap.hpp +++ b/src/hotspot/share/utilities/bitMap.hpp @@ -67,16 +67,16 @@ class BitMap { protected: // The maximum allowable size of a bitmap, in words or bits. // Limit max_size_in_bits so aligning up to a word boundary never overflows. - static idx_t max_size_in_words() { return raw_to_words_align_down(~idx_t(0)); } - static idx_t max_size_in_bits() { return max_size_in_words() * BitsPerWord; } + constexpr static idx_t max_size_in_words() { return raw_to_words_align_down(~idx_t(0)); } + constexpr static idx_t max_size_in_bits() { return max_size_in_words() * BitsPerWord; } // Assumes relevant validity checking for bit has already been done. - static idx_t raw_to_words_align_up(idx_t bit) { + constexpr static idx_t raw_to_words_align_up(idx_t bit) { return raw_to_words_align_down(bit + (BitsPerWord - 1)); } // Assumes relevant validity checking for bit has already been done. - static idx_t raw_to_words_align_down(idx_t bit) { + constexpr static idx_t raw_to_words_align_down(idx_t bit) { return bit >> LogBitsPerWord; } @@ -195,7 +195,7 @@ class BitMap { void pretouch(); // Accessing - static idx_t calc_size_in_words(size_t size_in_bits) { + constexpr static idx_t calc_size_in_words(size_t size_in_bits) { verify_size(size_in_bits); return raw_to_words_align_up(size_in_bits); } @@ -257,7 +257,13 @@ class BitMap { // Verification. // Verify size_in_bits does not exceed max_size_in_bits(). - static void verify_size(idx_t size_in_bits) NOT_DEBUG_RETURN; + constexpr static void verify_size(idx_t size_in_bits) { +#ifdef ASSERT + assert(size_in_bits <= max_size_in_bits(), + "out of bounds: " SIZE_FORMAT, size_in_bits); +#endif + } + // Verify bit is less than size(). void verify_index(idx_t bit) const NOT_DEBUG_RETURN; // Verify bit is not greater than size(). diff --git a/src/hotspot/share/utilities/byteswap.hpp b/src/hotspot/share/utilities/byteswap.hpp index fba0775cf4992..9c3b53630b39b 100644 --- a/src/hotspot/share/utilities/byteswap.hpp +++ b/src/hotspot/share/utilities/byteswap.hpp @@ -26,6 +26,7 @@ #define SHARE_UTILITIES_BYTESWAP_HPP #include "metaprogramming/enableIf.hpp" +#include "utilities/checkedCast.hpp" #include "utilities/globalDefinitions.hpp" #include @@ -63,7 +64,7 @@ struct ByteswapFallbackImpl; template struct ByteswapFallbackImpl { inline constexpr uint16_t operator()(uint16_t x) const { - return (((x & UINT16_C(0x00ff)) << 8) | ((x & UINT16_C(0xff00)) >> 8)); + return checked_cast(((x & UINT16_C(0x00ff)) << 8) | ((x & UINT16_C(0xff00)) >> 8)); } }; diff --git a/src/hotspot/share/utilities/concurrentHashTable.hpp b/src/hotspot/share/utilities/concurrentHashTable.hpp index 991ea9fe3c609..4e506d5fe84f1 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.hpp @@ -65,6 +65,7 @@ class ConcurrentHashTable : public CHeapObj { // the InternalTable or user-defined memory. class Node { private: + DEBUG_ONLY(size_t _saved_hash); Node * volatile _next; VALUE _value; public: @@ -77,6 +78,10 @@ class ConcurrentHashTable : public CHeapObj { Node* next() const; void set_next(Node* node) { _next = node; } Node* const volatile * next_ptr() { return &_next; } +#ifdef ASSERT + size_t saved_hash() const { return _saved_hash; } + void set_saved_hash(size_t hash) { _saved_hash = hash; } +#endif VALUE* value() { return &_value; } diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index 78c7e148bbb3c..f035aeae44847 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -679,7 +679,9 @@ inline bool ConcurrentHashTable:: // Keep in odd list odd = aux->next_ptr(); } else { - fatal("aux_index does not match even or odd indices"); + const char* msg = "Cannot resize table: Node hash code has changed possibly due to corruption of the contents."; + DEBUG_ONLY(fatal("%s Node hash code changed from " SIZE_FORMAT " to " SIZE_FORMAT, msg, aux->saved_hash(), aux_hash);) + NOT_DEBUG(fatal("%s", msg);) } } aux = aux_next; @@ -892,6 +894,7 @@ inline bool ConcurrentHashTable:: size_t i = 0; uintx hash = lookup_f.get_hash(); Node* new_node = Node::create_node(_context, value, nullptr); + DEBUG_ONLY(new_node->set_saved_hash(hash);) while (true) { { @@ -1117,6 +1120,7 @@ inline bool ConcurrentHashTable:: Bucket* bucket = get_bucket_in(table, hash); assert(!bucket->have_redirect() && !bucket->is_locked(), "bad"); Node* new_node = Node::create_node(_context, value, bucket->first()); + DEBUG_ONLY(new_node->set_saved_hash(hash);) if (!bucket->cas_first(new_node, bucket->first())) { assert(false, "bad"); } diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index c8f458dfa31ad..034444839ab50 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -43,6 +43,7 @@ #include "runtime/atomic.hpp" #include "utilities/events.hpp" #include "utilities/exceptions.hpp" +#include "utilities/utf8.hpp" // Limit exception message components to 64K (the same max as Symbols) #define MAX_LEN 65535 @@ -262,8 +263,32 @@ void Exceptions::fthrow(JavaThread* thread, const char* file, int line, Symbol* va_list ap; va_start(ap, format); char msg[max_msg_size]; - os::vsnprintf(msg, max_msg_size, format, ap); + int ret = os::vsnprintf(msg, max_msg_size, format, ap); va_end(ap); + + // If ret == -1 then either there was a format conversion error, or the required buffer size + // exceeds INT_MAX and so couldn't be returned (undocumented behaviour of vsnprintf). Depending + // on the platform the buffer may be filled to its capacity (Linux), filled to the conversion + // that encountered the overflow (macOS), or is empty (Windows), so it is possible we + // have a truncated UTF-8 sequence. Similarly, if the buffer was too small and ret >= max_msg_size + // we may also have a truncated UTF-8 sequence. In such cases we need to fix the buffer so the UTF-8 + // sequence is valid. + if (ret == -1 || ret >= max_msg_size) { + int len = (int) strlen(msg); + if (len > 0) { + // Truncation will only happen if the buffer was filled by vsnprintf, + // otherwise vsnprintf already terminated filling it at a well-defined point. + // But as this is not a clearly specified area we will perform our own UTF8 + // truncation anyway - though for those well-defined termination points it + // will be a no-op. + UTF8::truncate_to_legal_utf8((unsigned char*)msg, len + 1); + } + } + // UTF8::is_legal_utf8 should actually be called is_legal_utf8_class_name as the final + // parameter controls a check for a specific character appearing in the "name", which is only + // allowed for classfile versions <= 47. We pass `true` so that we allow such strings as this code + // know nothing about the actual string content. + assert(UTF8::is_legal_utf8((const unsigned char*)msg, (int)strlen(msg), true), "must be"); _throw_msg(thread, file, line, h_name, msg); } diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 74817a35e7706..b7be6dc04bfa5 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -222,9 +222,9 @@ FORBID_C_FUNCTION(void* reallocf(void *ptr, size_t size), "don't use"); const int LogBytesPerShort = 1; const int LogBytesPerInt = 2; #ifdef _LP64 -const int LogBytesPerWord = 3; +constexpr int LogBytesPerWord = 3; #else -const int LogBytesPerWord = 2; +constexpr int LogBytesPerWord = 2; #endif const int LogBytesPerLong = 3; @@ -233,16 +233,16 @@ const int BytesPerInt = 1 << LogBytesPerInt; const int BytesPerWord = 1 << LogBytesPerWord; const int BytesPerLong = 1 << LogBytesPerLong; -const int LogBitsPerByte = 3; +constexpr int LogBitsPerByte = 3; const int LogBitsPerShort = LogBitsPerByte + LogBytesPerShort; const int LogBitsPerInt = LogBitsPerByte + LogBytesPerInt; -const int LogBitsPerWord = LogBitsPerByte + LogBytesPerWord; +constexpr int LogBitsPerWord = LogBitsPerByte + LogBytesPerWord; const int LogBitsPerLong = LogBitsPerByte + LogBytesPerLong; const int BitsPerByte = 1 << LogBitsPerByte; const int BitsPerShort = 1 << LogBitsPerShort; const int BitsPerInt = 1 << LogBitsPerInt; -const int BitsPerWord = 1 << LogBitsPerWord; +constexpr int BitsPerWord = 1 << LogBitsPerWord; const int BitsPerLong = 1 << LogBitsPerLong; const int WordAlignmentMask = (1 << LogBytesPerWord) - 1; diff --git a/src/hotspot/share/utilities/istream.cpp b/src/hotspot/share/utilities/istream.cpp index cb082128c62ca..55fa443af69e9 100644 --- a/src/hotspot/share/utilities/istream.cpp +++ b/src/hotspot/share/utilities/istream.cpp @@ -78,7 +78,6 @@ bool inputStream::next() { void inputStream::set_done() { size_t end = _beg = _end = _content_end; _next = end + NEXT_PHANTOM; - _line_ending = 0; assert(definitely_done(), ""); } @@ -94,7 +93,6 @@ void inputStream::set_error(bool error_condition) { void inputStream::clear_buffer() { _content_end = _beg = _end = _next = 0; - _line_ending = 0; } const char* inputStream::next_content(size_t& next_content_length) const { @@ -146,7 +144,6 @@ bool inputStream::fill_buffer() { else { COV(FIB_L); } if (last_partial) { assert(have_current_line(), ""); - _line_ending = 0; _content_end -= 1; // reverse insertion of phantom newline assert(_next == _content_end + NEXT_PHANTOM, ""); assert(have_current_line(), ""); @@ -224,7 +221,6 @@ void inputStream::set_buffer_content(size_t content_start, if (nl == nullptr) { COV(SBC_N); _next = _end = content_end; - _line_ending = 0; assert(need_to_read(), ""); } else { COV(SBC_L); @@ -247,7 +243,6 @@ void inputStream::set_buffer_content(size_t content_start, // accept '\r' before '\n'. } _end = end; // now this->current_line() points to buf[beg..end] - _line_ending = (int)(_next - end); assert(have_current_line(), ""); assert(current_line() == &_buffer[_beg], ""); assert(current_line_length() == _end - _beg, ""); @@ -299,7 +294,7 @@ void inputStream::dump(const char* what) { bool ntr = (_next == _end), hcl = (_beg < _content_end && _end < _next), ddn = (_beg == _content_end && _next > _content_end); - tty->print_cr("%s%sistream %s%s%s%s%s [%d<%.*s>%d/%d..%d] LE=%d," + tty->print_cr("%s%sistream %s%s%s%s%s [%d<%.*s>%d/%d..%d] " " B=%llx%s[%d], LN=%d, CH=%d", what ? what : "", what ? ": " : "", _buffer == nullptr ? "U" : "", @@ -312,7 +307,6 @@ void inputStream::dump(const char* what) { diff < 0 ? 0 : diff > 10 ? 10 : diff, _buffer ? &_buffer[_beg] : "", (int)_end, (int)_next, (int)_content_end, - _line_ending, (unsigned long long)(intptr_t)_buffer, _buffer == _small_buffer ? "(SB)" : "", (int)_buffer_size, diff --git a/src/hotspot/share/utilities/istream.hpp b/src/hotspot/share/utilities/istream.hpp index b6a58055b937c..dd12028185956 100644 --- a/src/hotspot/share/utilities/istream.hpp +++ b/src/hotspot/share/utilities/istream.hpp @@ -96,7 +96,6 @@ class inputStream : public CHeapObjBase { Input* _input; // where the input comes from or else nullptr IState _input_state; // one of {NTR,EOF,ERR}_STATE - char _line_ending; // one of {0,1,2} for "", "\n", "\r\n" char* _buffer; // scratch buffer holding at least the current line size_t _buffer_size; // allocated size of buffer size_t _content_end; // offset to end of valid contents of buffer @@ -225,7 +224,6 @@ class inputStream : public CHeapObjBase { inputStream() : _input(nullptr), _input_state(IState::NTR_STATE), - _line_ending(0), _buffer(&_small_buffer[0]), _buffer_size(sizeof(_small_buffer)), _content_end(0), diff --git a/src/hotspot/share/utilities/utf8.cpp b/src/hotspot/share/utilities/utf8.cpp index 6fd877120df40..47cbb04da4b2c 100644 --- a/src/hotspot/share/utilities/utf8.cpp +++ b/src/hotspot/share/utilities/utf8.cpp @@ -392,6 +392,69 @@ bool UTF8::is_legal_utf8(const unsigned char* buffer, int length, return true; } +// Return true if `b` could be the starting byte of an encoded 2,3 or 6 +// byte sequence. +static bool is_starting_byte(unsigned char b) { + return b >= 0xC0 && b <= 0xEF; +} + +// Takes an incoming buffer that was valid UTF-8, but which has been truncated such that +// the last encoding may be partial, and returns the same buffer with a NUL-terminator +// inserted such that any partial encoding has gone. +// Note: if the incoming buffer is already valid then we may still drop the last encoding. +// To avoid that the caller can choose to check for validity first. +// The incoming buffer is still expected to be NUL-terminated. +// The incoming buffer is expected to be a realistic size - we assert if it is too small. +void UTF8::truncate_to_legal_utf8(unsigned char* buffer, int length) { + assert(length > 5, "invalid length"); + assert(buffer[length - 1] == '\0', "Buffer should be NUL-terminated"); + + if (buffer[length - 2] < 128) { // valid "ascii" - common case + return; + } + + // Modified UTF-8 encodes characters in sequences of 1, 2, 3 or 6 bytes. + // The last byte is invalid if it is: + // - the 1st byte of a 2, 3 or 6 byte sequence + // 0b110xxxxx + // 0b1110xxxx + // 0b11101101 + // - the 2nd byte of a 3 or 6 byte sequence + // 0b10xxxxxx + // 0b1010xxxx + // - the 3rd, 4th or 5th byte of a 6 byte sequence + // 0b10xxxxxx + // 0b11101101 + // 0b1011xxxx + // + // Rather than checking all possible situations we simplify things noting that as we have already + // got a truncated string, then dropping one more character is not significant. So we work from the + // end of the buffer looking for the first byte that can be the starting byte of a UTF-8 encoded sequence, + // then we insert NUL at that location to terminate the buffer. There is an added complexity with 6 byte + // encodings as the first and fourth bytes are the same and overlap with the 3 byte encoding. + + for (int index = length - 2; index > 0; index--) { + if (is_starting_byte(buffer[index])) { + if (buffer[index] == 0xED) { + // Could be first byte of 3 or 6, or fourth byte of 6. + // If fourth the previous three bytes will encode a high + // surrogate value in the range EDA080 to EDAFBF. We only + // need to check for EDA to establish this as the "missing" + // values in EDAxxx would not be valid 3 byte encodings. + if ((index - 3) >= 0 && + (buffer[index - 3] == 0xED) && + ((buffer[index - 2] & 0xF0) == 0xA0)) { + assert(buffer[index - 1] >= 0x80 && buffer[index - 1] <= 0xBF, "sanity check"); + // It was fourth byte so truncate 3 bytes earlier + index -= 3; + } + } + buffer[index] = '\0'; + break; + } + } +} + //------------------------------------------------------------------------------------- bool UNICODE::is_latin1(jchar c) { diff --git a/src/hotspot/share/utilities/utf8.hpp b/src/hotspot/share/utilities/utf8.hpp index 80346f7da7ddb..9a18dd0ff93b7 100644 --- a/src/hotspot/share/utilities/utf8.hpp +++ b/src/hotspot/share/utilities/utf8.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,6 +88,7 @@ class UTF8 : AllStatic { static bool is_legal_utf8(const unsigned char* buffer, int length, bool version_leq_47); + static void truncate_to_legal_utf8(unsigned char* buffer, int length); }; diff --git a/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java b/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java index cb55932fd7e99..a07a927e635ed 100644 --- a/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java +++ b/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java @@ -26,7 +26,6 @@ package sun.nio.ch; import java.io.IOException; -import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.SelectorProvider; @@ -92,11 +91,6 @@ class EPollSelectorImpl extends SelectorImpl { EPoll.ctl(epfd, EPOLL_CTL_ADD, eventfd.efd(), EPOLLIN); } - private void ensureOpen() { - if (!isOpen()) - throw new ClosedSelectorException(); - } - @Override protected int doSelect(Consumer action, long timeout) throws IOException @@ -243,7 +237,6 @@ protected void implDereg(SelectionKeyImpl ski) throws IOException { @Override public void setEventOps(SelectionKeyImpl ski) { - ensureOpen(); synchronized (updateLock) { updateKeys.addLast(ski); } diff --git a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c index 02d3c57f1f34b..efbd0ca568478 100644 --- a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c +++ b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c @@ -114,7 +114,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this, return n; } - n = sendfile64(dstFD, srcFD, &offset, (size_t)count); + n = sendfile(dstFD, srcFD, &offset, (size_t)count); if (n < 0) { if (errno == EAGAIN) return IOS_UNAVAILABLE; diff --git a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c index 95f78ffa135b1..c90e99dda07b3 100644 --- a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c +++ b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c @@ -211,7 +211,7 @@ Java_sun_nio_fs_LinuxNativeDispatcher_directCopy0 } do { - RESTARTABLE(sendfile64(dst, src, NULL, count), bytes_sent); + RESTARTABLE(sendfile(dst, src, NULL, count), bytes_sent); if (bytes_sent < 0) { if (errno == EAGAIN) return IOS_UNAVAILABLE; diff --git a/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java b/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java index 6c84984f515d4..6abc9e31879dd 100644 --- a/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java +++ b/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java @@ -26,7 +26,6 @@ package sun.nio.ch; import java.io.IOException; -import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.SelectorProvider; @@ -97,11 +96,6 @@ class KQueueSelectorImpl extends SelectorImpl { KQueue.register(kqfd, fd0, EVFILT_READ, EV_ADD); } - private void ensureOpen() { - if (!isOpen()) - throw new ClosedSelectorException(); - } - @Override protected int doSelect(Consumer action, long timeout) throws IOException @@ -285,7 +279,6 @@ protected void implDereg(SelectionKeyImpl ski) throws IOException { @Override public void setEventOps(SelectionKeyImpl ski) { - ensureOpen(); synchronized (updateLock) { updateKeys.addLast(ski); } diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java index 2dcf174cf5399..3650b10135356 100644 --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1272,7 +1272,7 @@ private void writeProxyDesc(ObjectStreamClass desc, boolean unshared) } bout.setBlockDataMode(true); - if (cl != null && isCustomSubclass()) { + if (isCustomSubclass()) { ReflectUtil.checkPackageAccess(cl); } annotateProxyClass(cl); diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index 7e5056a539b8c..e0c3aa19f5437 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -27,6 +27,7 @@ import jdk.internal.math.DoubleToDecimal; import jdk.internal.math.FloatToDecimal; +import jdk.internal.util.DecimalDigits; import java.io.IOException; import java.nio.CharBuffer; @@ -836,7 +837,7 @@ public AbstractStringBuilder append(char c) { */ public AbstractStringBuilder append(int i) { int count = this.count; - int spaceNeeded = count + Integer.stringSize(i); + int spaceNeeded = count + DecimalDigits.stringSize(i); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { StringLatin1.getChars(i, spaceNeeded, value); @@ -861,7 +862,7 @@ public AbstractStringBuilder append(int i) { */ public AbstractStringBuilder append(long l) { int count = this.count; - int spaceNeeded = count + Long.stringSize(l); + int spaceNeeded = count + DecimalDigits.stringSize(l); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { StringLatin1.getChars(l, spaceNeeded, value); diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index fb59de482ecf1..e2ec0909f4500 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -55,7 +55,9 @@ import java.lang.constant.Constable; import java.net.URL; import java.security.AccessController; +import java.security.Permissions; import java.security.PrivilegedAction; +import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -3220,10 +3222,6 @@ private boolean isOpenToCaller(String name, Class caller) { return true; } - - /** protection domain returned when the internal domain is null */ - private static java.security.ProtectionDomain allPermDomain; - /** * Returns the {@code ProtectionDomain} of this class. If there is a * security manager installed, this method first calls the security @@ -3244,7 +3242,7 @@ private boolean isOpenToCaller(String name, Class caller) { * @see java.lang.RuntimePermission * @since 1.2 */ - public java.security.ProtectionDomain getProtectionDomain() { + public ProtectionDomain getProtectionDomain() { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -3253,26 +3251,30 @@ public java.security.ProtectionDomain getProtectionDomain() { return protectionDomain(); } + /** Holder for the protection domain returned when the internal domain is null */ + private static class Holder { + private static final ProtectionDomain allPermDomain; + static { + Permissions perms = new Permissions(); + perms.add(SecurityConstants.ALL_PERMISSION); + allPermDomain = new ProtectionDomain(null, perms); + } + } + // package-private - java.security.ProtectionDomain protectionDomain() { - java.security.ProtectionDomain pd = getProtectionDomain0(); + ProtectionDomain protectionDomain() { + ProtectionDomain pd = getProtectionDomain0(); if (pd == null) { - if (allPermDomain == null) { - java.security.Permissions perms = - new java.security.Permissions(); - perms.add(SecurityConstants.ALL_PERMISSION); - allPermDomain = - new java.security.ProtectionDomain(null, perms); - } - pd = allPermDomain; + return Holder.allPermDomain; + } else { + return pd; } - return pd; } /** * Returns the ProtectionDomain of this class. */ - private native java.security.ProtectionDomain getProtectionDomain0(); + private native ProtectionDomain getProtectionDomain0(); /* * Return the Virtual Machine's Class object for the named diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 1c5b3c414bab6..7a65046181f00 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -27,6 +27,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.VM; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -427,7 +428,7 @@ private static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int l */ @IntrinsicCandidate public static String toString(int i) { - int size = stringSize(i); + int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; StringLatin1.getChars(i, size, buf); @@ -457,32 +458,6 @@ public static String toUnsignedString(int i) { return Long.toString(toUnsignedLong(i)); } - /** - * Returns the string representation size for a given int value. - * - * @param x int value - * @return string size - * - * @implNote There are other ways to compute this: e.g. binary search, - * but values are biased heavily towards zero, and therefore linear search - * wins. The iteration results are also routinely inlined in the generated - * code after loop unrolling. - */ - static int stringSize(int x) { - int d = 1; - if (x >= 0) { - d = 0; - x = -x; - } - int p = -10; - for (int i = 1; i < 10; i++) { - if (x > p) - return i + d; - p = 10 * p; - } - return 10 + d; - } - /** * Parses the string argument as a signed integer in the radix * specified by the second argument. The characters in the string diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 70bd2d62add0f..ee9533b29eb53 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -34,6 +34,7 @@ import java.util.Optional; import jdk.internal.misc.CDS; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -457,7 +458,7 @@ private static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, in * @return a string representation of the argument in base 10. */ public static String toString(long i) { - int size = stringSize(i); + int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; StringLatin1.getChars(i, size, buf); @@ -487,32 +488,6 @@ public static String toUnsignedString(long i) { return toUnsignedString(i, 10); } - /** - * Returns the string representation size for a given long value. - * - * @param x long value - * @return string size - * - * @implNote There are other ways to compute this: e.g. binary search, - * but values are biased heavily towards zero, and therefore linear search - * wins. The iteration results are also routinely inlined in the generated - * code after loop unrolling. - */ - static int stringSize(long x) { - int d = 1; - if (x >= 0) { - d = 0; - x = -x; - } - long p = -10; - for (int i = 1; i < 19; i++) { - if (x > p) - return i + d; - p = 10 * p; - } - return 19 + d; - } - /** * Parses the string argument as a signed {@code long} in the * radix specified by the second argument. The characters in the diff --git a/src/java.base/share/classes/java/lang/Process.java b/src/java.base/share/classes/java/lang/Process.java index 756705285d966..af6753a236bb2 100644 --- a/src/java.base/share/classes/java/lang/Process.java +++ b/src/java.base/share/classes/java/lang/Process.java @@ -32,6 +32,7 @@ import java.lang.ProcessBuilder.Redirect; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; +import java.time.Duration; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ForkJoinPool; @@ -441,10 +442,13 @@ public final BufferedWriter outputWriter(Charset charset) { * terminated and the timeout value is less than, or equal to, zero, then * this method returns immediately with the value {@code false}. * - *

The default implementation of this method polls the {@code exitValue} - * to check if the process has terminated. Concrete implementations of this - * class are strongly encouraged to override this method with a more - * efficient implementation. + * @implSpec + * The default implementation of this method polls the {@code exitValue} + * to check if the process has terminated. + * + * @implNote + * Concrete implementations of this class are strongly encouraged to + * override this method with a more efficient implementation. * * @param timeout the maximum time to wait * @param unit the time unit of the {@code timeout} argument @@ -475,6 +479,38 @@ public boolean waitFor(long timeout, TimeUnit unit) return false; } + /** + * Causes the current thread to wait, if necessary, until the + * process represented by this {@code Process} object has + * terminated, or the specified waiting duration elapses. + * + *

If the process has already terminated then this method returns + * immediately with the value {@code true}. If the process has not + * terminated and the duration is not positive, then + * this method returns immediately with the value {@code false}. + * + * @implSpec + * The default implementation of this method polls the {@code exitValue} + * to check if the process has terminated. + * + * @implNote + * Concrete implementations of this class are strongly encouraged to + * override this method with a more efficient implementation. + * + * @param duration the maximum duration to wait; if not positive, + * this method returns immediately. + * @return {@code true} if the process has exited and {@code false} if + * the waiting duration elapsed before the process has exited. + * @throws InterruptedException if the current thread is interrupted + * while waiting. + * @throws NullPointerException if duration is null + * @since 24 + */ + public boolean waitFor(Duration duration) throws InterruptedException { + Objects.requireNonNull(duration, "duration"); + return waitFor(TimeUnit.NANOSECONDS.convert(duration), TimeUnit.NANOSECONDS); + } + /** * Returns the exit value for the process. * @@ -577,8 +613,8 @@ public boolean isAlive() { /** * This is called from the default implementation of - * {@code waitFor(long, TimeUnit)}, which is specified to poll - * {@code exitValue()}. + * {@code waitFor(long, TimeUnit)} and {@code waitFor(Duration)}, + * which are specified to poll {@code exitValue()}. */ private boolean hasExited() { try { diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 68c16c93e4ba3..3d8481be1c3ad 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -2987,7 +2987,7 @@ public String concat(String str) { if (str.isEmpty()) { return this; } - return StringConcatHelper.simpleConcat(this, str); + return StringConcatHelper.doConcat(this, str); } /** diff --git a/src/java.base/share/classes/java/lang/StringConcatHelper.java b/src/java.base/share/classes/java/lang/StringConcatHelper.java index 49f556e5aa620..486e115369ea6 100644 --- a/src/java.base/share/classes/java/lang/StringConcatHelper.java +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.lang; import jdk.internal.misc.Unsafe; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import java.lang.invoke.MethodHandle; @@ -96,7 +97,7 @@ static long mix(long lengthCoder, char value) { * @return new length and coder */ static long mix(long lengthCoder, int value) { - return checkOverflow(lengthCoder + Integer.stringSize(value)); + return checkOverflow(lengthCoder + DecimalDigits.stringSize(value)); } /** @@ -107,7 +108,7 @@ static long mix(long lengthCoder, int value) { * @return new length and coder */ static long mix(long lengthCoder, long value) { - return checkOverflow(lengthCoder + Long.stringSize(value)); + return checkOverflow(lengthCoder + DecimalDigits.stringSize(value)); } /** @@ -119,51 +120,60 @@ static long mix(long lengthCoder, long value) { */ static long mix(long lengthCoder, String value) { lengthCoder += value.length(); - if (value.coder() == String.UTF16) { + if (!value.isLatin1()) { lengthCoder |= UTF16; } return checkOverflow(lengthCoder); } /** - * Prepends the stringly representation of boolean value into buffer, + * Prepends constant and the stringly representation of value into buffer, * given the coder and final index. Index is measured in chars, not in bytes! * * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to * @param value boolean value to encode + * @param prefix a constant to prepend before value * @return updated index (coder value retained) */ - static long prepend(long indexCoder, byte[] buf, boolean value) { + static long prepend(long indexCoder, byte[] buf, boolean value, String prefix) { int index = (int)indexCoder; if (indexCoder < UTF16) { if (value) { - buf[--index] = 'e'; - buf[--index] = 'u'; - buf[--index] = 'r'; - buf[--index] = 't'; + index -= 4; + buf[index] = 't'; + buf[index + 1] = 'r'; + buf[index + 2] = 'u'; + buf[index + 3] = 'e'; } else { - buf[--index] = 'e'; - buf[--index] = 's'; - buf[--index] = 'l'; - buf[--index] = 'a'; - buf[--index] = 'f'; + index -= 5; + buf[index] = 'f'; + buf[index + 1] = 'a'; + buf[index + 2] = 'l'; + buf[index + 3] = 's'; + buf[index + 4] = 'e'; } + index -= prefix.length(); + prefix.getBytes(buf, index, String.LATIN1); return index; } else { if (value) { - StringUTF16.putChar(buf, --index, 'e'); - StringUTF16.putChar(buf, --index, 'u'); - StringUTF16.putChar(buf, --index, 'r'); - StringUTF16.putChar(buf, --index, 't'); + index -= 4; + StringUTF16.putChar(buf, index, 't'); + StringUTF16.putChar(buf, index + 1, 'r'); + StringUTF16.putChar(buf, index + 2, 'u'); + StringUTF16.putChar(buf, index + 3, 'e'); } else { - StringUTF16.putChar(buf, --index, 'e'); - StringUTF16.putChar(buf, --index, 's'); - StringUTF16.putChar(buf, --index, 'l'); - StringUTF16.putChar(buf, --index, 'a'); - StringUTF16.putChar(buf, --index, 'f'); + index -= 5; + StringUTF16.putChar(buf, index, 'f'); + StringUTF16.putChar(buf, index + 1, 'a'); + StringUTF16.putChar(buf, index + 2, 'l'); + StringUTF16.putChar(buf, index + 3, 's'); + StringUTF16.putChar(buf, index + 4, 'e'); } + index -= prefix.length(); + prefix.getBytes(buf, index, String.UTF16); return index | UTF16; } } @@ -175,67 +185,22 @@ static long prepend(long indexCoder, byte[] buf, boolean value) { * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to - * @param value boolean value to encode - * @param prefix a constant to prepend before value - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, boolean value, String prefix) { - indexCoder = prepend(indexCoder, buf, value); - indexCoder = prepend(indexCoder, buf, prefix); - return indexCoder; - } - - /** - * Prepends the stringly representation of char value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to * @param value char value to encode - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, char value) { - if (indexCoder < UTF16) { - buf[(int)(--indexCoder)] = (byte) (value & 0xFF); - } else { - StringUTF16.putChar(buf, (int)(--indexCoder), value); - } - return indexCoder; - } - - /** - * Prepends constant and the stringly representation of value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value boolean value to encode * @param prefix a constant to prepend before value * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, char value, String prefix) { - indexCoder = prepend(indexCoder, buf, value); - indexCoder = prepend(indexCoder, buf, prefix); - return indexCoder; - } - - /** - * Prepends the stringly representation of integer value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value integer value to encode - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, int value) { + int index = (int)indexCoder; if (indexCoder < UTF16) { - return StringLatin1.getChars(value, (int)indexCoder, buf); + buf[--index] = (byte) (value & 0xFF); + index -= prefix.length(); + prefix.getBytes(buf, index, String.LATIN1); + return index; } else { - return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16; + StringUTF16.putChar(buf, --index, value); + index -= prefix.length(); + prefix.getBytes(buf, index, String.UTF16); + return index | UTF16; } } @@ -246,31 +211,22 @@ static long prepend(long indexCoder, byte[] buf, int value) { * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to - * @param value boolean value to encode + * @param value int value to encode * @param prefix a constant to prepend before value * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, int value, String prefix) { - indexCoder = prepend(indexCoder, buf, value); - indexCoder = prepend(indexCoder, buf, prefix); - return indexCoder; - } - - /** - * Prepends the stringly representation of long value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value long value to encode - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, long value) { + int index = (int)indexCoder; if (indexCoder < UTF16) { - return StringLatin1.getChars(value, (int)indexCoder, buf); + index = StringLatin1.getChars(value, index, buf); + index -= prefix.length(); + prefix.getBytes(buf, index, String.LATIN1); + return index; } else { - return StringUTF16.getChars(value, (int)indexCoder, buf) | UTF16; + index = StringUTF16.getChars(value, index, buf); + index -= prefix.length(); + prefix.getBytes(buf, index, String.UTF16); + return index | UTF16; } } @@ -281,34 +237,23 @@ static long prepend(long indexCoder, byte[] buf, long value) { * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to - * @param value boolean value to encode + * @param value long value to encode * @param prefix a constant to prepend before value * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, long value, String prefix) { - indexCoder = prepend(indexCoder, buf, value); - indexCoder = prepend(indexCoder, buf, prefix); - return indexCoder; - } - - /** - * Prepends the stringly representation of String value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - * - * @param indexCoder final char index in the buffer, along with coder packed - * into higher bits. - * @param buf buffer to append to - * @param value String value to encode - * @return updated index (coder value retained) - */ - static long prepend(long indexCoder, byte[] buf, String value) { - indexCoder -= value.length(); + int index = (int)indexCoder; if (indexCoder < UTF16) { - value.getBytes(buf, (int)indexCoder, String.LATIN1); + index = StringLatin1.getChars(value, index, buf); + index -= prefix.length(); + prefix.getBytes(buf, index, String.LATIN1); + return index; } else { - value.getBytes(buf, (int)indexCoder, String.UTF16); + index = StringUTF16.getChars(value, index, buf); + index -= prefix.length(); + prefix.getBytes(buf, index, String.UTF16); + return index | UTF16; } - return indexCoder; } /** @@ -323,9 +268,18 @@ static long prepend(long indexCoder, byte[] buf, String value) { * @return updated index (coder value retained) */ static long prepend(long indexCoder, byte[] buf, String value, String prefix) { - indexCoder = prepend(indexCoder, buf, value); - indexCoder = prepend(indexCoder, buf, prefix); - return indexCoder; + int index = ((int)indexCoder) - value.length(); + if (indexCoder < UTF16) { + value.getBytes(buf, index, String.LATIN1); + index -= prefix.length(); + prefix.getBytes(buf, index, String.LATIN1); + return index; + } else { + value.getBytes(buf, index, String.UTF16); + index -= prefix.length(); + prefix.getBytes(buf, index, String.UTF16); + return index | UTF16; + } } /** @@ -368,16 +322,24 @@ static String simpleConcat(Object first, Object second) { // newly created string required, see JLS 15.18.1 return new String(s1); } - // start "mixing" in length and coder or arguments, order is not - // important - long indexCoder = mix(initialCoder(), s1); - indexCoder = mix(indexCoder, s2); - byte[] buf = newArray(indexCoder); - // prepend each argument in reverse order, since we prepending - // from the end of the byte array - indexCoder = prepend(indexCoder, buf, s2); - indexCoder = prepend(indexCoder, buf, s1); - return newString(buf, indexCoder); + return doConcat(s1, s2); + } + + /** + * Perform a simple concatenation between two non-empty strings. + * + * @param s1 first argument + * @param s2 second argument + * @return String resulting string + */ + @ForceInline + static String doConcat(String s1, String s2) { + byte coder = (byte) (s1.coder() | s2.coder()); + int newLength = (s1.length() + s2.length()) << coder; + byte[] buf = newArray(newLength); + s1.getBytes(buf, 0, coder); + s2.getBytes(buf, s1.length(), coder); + return new String(buf, coder); } /** @@ -445,10 +407,20 @@ static byte[] newArrayWithSuffix(String suffix, long indexCoder) { static byte[] newArray(long indexCoder) { byte coder = (byte)(indexCoder >> 32); int index = ((int)indexCoder) << coder; - if (index < 0) { + return newArray(index); + } + + /** + * Allocates an uninitialized byte array based on the length + * @param length + * @return the newly allocated byte array + */ + @ForceInline + static byte[] newArray(int length) { + if (length < 0) { throw new OutOfMemoryError("Overflow: String length out of range"); } - return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index); + return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length); } /** diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 5e28b9ce95b7c..0947da8ded747 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -65,7 +65,6 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.WeakHashMap; -import java.util.concurrent.Callable; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2264,6 +2263,7 @@ private static class Out extends FileOutputStream { super(fd); } + @Override public void write(int b) throws IOException { boolean attempted = Blocker.begin(); try { @@ -2611,10 +2611,6 @@ public MethodHandle stringConcatHelper(String name, MethodType methodType) { return StringConcatHelper.lookupStatic(name, methodType); } - public long stringConcatHelperPrepend(long indexCoder, byte[] buf, String value) { - return StringConcatHelper.prepend(indexCoder, buf, value); - } - public long stringConcatInitialCoder() { return StringConcatHelper.initialCoder(); } @@ -2627,10 +2623,6 @@ public long stringConcatMix(long lengthCoder, char value) { return StringConcatHelper.mix(lengthCoder, value); } - public int stringSize(long i) { - return Long.stringSize(i); - } - public int getCharsLatin1(long i, int index, byte[] buf) { return StringLatin1.getChars(i, index, buf); } @@ -2681,14 +2673,6 @@ public Thread currentCarrierThread() { return Thread.currentCarrierThread(); } - public V executeOnCarrierThread(Callable task) throws Exception { - if (Thread.currentThread() instanceof VirtualThread vthread) { - return vthread.executeOnCarrierThread(task); - } else { - return task.call(); - } - } - public T getCarrierThreadLocal(CarrierThreadLocal local) { return ((ThreadLocal)local).getCarrierThreadLocal(); } diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index cf2fed3bf5d71..da05e77f12b53 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -40,6 +40,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import jdk.internal.event.VirtualThreadEndEvent; import jdk.internal.event.VirtualThreadPinnedEvent; import jdk.internal.event.VirtualThreadStartEvent; @@ -62,14 +63,13 @@ import static java.util.concurrent.TimeUnit.*; /** - * A thread that is scheduled by the Java virtual machine rather than the operating - * system. + * A thread that is scheduled by the Java virtual machine rather than the operating system. */ final class VirtualThread extends BaseVirtualThread { private static final Unsafe U = Unsafe.getUnsafe(); private static final ContinuationScope VTHREAD_SCOPE = new ContinuationScope("VirtualThreads"); private static final ForkJoinPool DEFAULT_SCHEDULER = createDefaultScheduler(); - private static final ScheduledExecutorService UNPARKER = createDelayedTaskScheduler(); + private static final ScheduledExecutorService[] DELAYED_TASK_SCHEDULERS = createDelayedTaskSchedulers(); private static final int TRACE_PINNING_MODE = tracePinningMode(); private static final long STATE = U.objectFieldOffset(VirtualThread.class, "state"); @@ -217,7 +217,7 @@ public void run() { * on the current thread before the task runs or continues. It unmounts when the * task completes or yields. */ - @ChangesCurrentThread + @ChangesCurrentThread // allow mount/unmount to be inlined private void runContinuation() { // the carrier must be a platform thread if (Thread.currentThread().isVirtual()) { @@ -257,42 +257,109 @@ private void runContinuation() { * Submits the runContinuation task to the scheduler. For the default scheduler, * and calling it on a worker thread, the task will be pushed to the local queue, * otherwise it will be pushed to an external submission queue. + * @param scheduler the scheduler + * @param retryOnOOME true to retry indefinitely if OutOfMemoryError is thrown * @throws RejectedExecutionException */ - private void submitRunContinuation() { - try { - scheduler.execute(runContinuation); - } catch (RejectedExecutionException ree) { - submitFailed(ree); - throw ree; + @ChangesCurrentThread + private void submitRunContinuation(Executor scheduler, boolean retryOnOOME) { + boolean done = false; + while (!done) { + try { + // The scheduler's execute method is invoked in the context of the + // carrier thread. For the default scheduler this ensures that the + // current thread is a ForkJoinWorkerThread so the task will be pushed + // to the local queue. For other schedulers, it avoids deadlock that + // would arise due to platform and virtual threads contending for a + // lock on the scheduler's submission queue. + if (currentThread() instanceof VirtualThread vthread) { + vthread.switchToCarrierThread(); + try { + scheduler.execute(runContinuation); + } finally { + switchToVirtualThread(vthread); + } + } else { + scheduler.execute(runContinuation); + } + done = true; + } catch (RejectedExecutionException ree) { + submitFailed(ree); + throw ree; + } catch (OutOfMemoryError e) { + if (retryOnOOME) { + U.park(false, 100_000_000); // 100ms + } else { + throw e; + } + } } } /** * Submits the runContinuation task to given scheduler with a lazy submit. + * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. * @throws RejectedExecutionException * @see ForkJoinPool#lazySubmit(ForkJoinTask) */ private void lazySubmitRunContinuation(ForkJoinPool pool) { + assert Thread.currentThread() instanceof CarrierThread; try { pool.lazySubmit(ForkJoinTask.adapt(runContinuation)); } catch (RejectedExecutionException ree) { submitFailed(ree); throw ree; + } catch (OutOfMemoryError e) { + submitRunContinuation(pool, true); } } /** * Submits the runContinuation task to the given scheduler as an external submit. + * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. * @throws RejectedExecutionException * @see ForkJoinPool#externalSubmit(ForkJoinTask) */ private void externalSubmitRunContinuation(ForkJoinPool pool) { + assert Thread.currentThread() instanceof CarrierThread; try { pool.externalSubmit(ForkJoinTask.adapt(runContinuation)); } catch (RejectedExecutionException ree) { submitFailed(ree); throw ree; + } catch (OutOfMemoryError e) { + submitRunContinuation(pool, true); + } + } + + /** + * Submits the runContinuation task to the scheduler. For the default scheduler, + * and calling it on a worker thread, the task will be pushed to the local queue, + * otherwise it will be pushed to an external submission queue. + * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. + * @throws RejectedExecutionException + */ + private void submitRunContinuation() { + submitRunContinuation(scheduler, true); + } + + /** + * Submits the runContinuation task to the scheduler. For the default scheduler, and + * calling it a virtual thread that uses the default scheduler, the task will be + * pushed to an external submission queue. This method may throw OutOfMemoryError. + * @throws RejectedExecutionException + * @throws OutOfMemoryError + */ + private void externalSubmitRunContinuationOrThrow() { + if (scheduler == DEFAULT_SCHEDULER && currentCarrierThread() instanceof CarrierThread ct) { + try { + ct.getPool().externalSubmit(ForkJoinTask.adapt(runContinuation)); + } catch (RejectedExecutionException ree) { + submitFailed(ree); + throw ree; + } + } else { + submitRunContinuation(scheduler, false); } } @@ -385,6 +452,8 @@ private void mount() { @ChangesCurrentThread @ReservedStackAccess private void unmount() { + assert !Thread.holdsLock(interruptLock); + // set Thread.currentThread() to return the platform thread Thread carrier = this.carrierThread; carrier.setCurrentThread(carrier); @@ -417,7 +486,7 @@ private void switchToCarrierThread() { */ @ChangesCurrentThread @JvmtiMountTransition - private void switchToVirtualThread(VirtualThread vthread) { + private static void switchToVirtualThread(VirtualThread vthread) { Thread carrier = vthread.carrierThread; assert carrier == Thread.currentCarrierThread(); carrier.setCurrentThread(vthread); @@ -474,13 +543,12 @@ private void afterYield() { // may have been unparked while parking if (parkPermit && compareAndSetState(newState, UNPARKED)) { - // lazy submit to continue on the current thread as carrier if possible - if (currentThread() instanceof CarrierThread ct) { + // lazy submit to continue on the current carrier if possible + if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) { lazySubmitRunContinuation(ct.getPool()); } else { submitRunContinuation(); } - } return; } @@ -561,8 +629,8 @@ void start(ThreadContainer container) { // scoped values may be inherited inheritScopedValueBindings(container); - // submit task to run thread - submitRunContinuation(); + // submit task to run thread, using externalSubmit if possible + externalSubmitRunContinuationOrThrow(); started = true; } finally { if (!started) { @@ -707,7 +775,7 @@ private Future scheduleUnpark(long nanos) { // need to switch to current carrier thread to avoid nested parking switchToCarrierThread(); try { - return UNPARKER.schedule(this::unpark, nanos, NANOSECONDS); + return schedule(this::unpark, nanos, NANOSECONDS); } finally { switchToVirtualThread(this); } @@ -718,6 +786,7 @@ private Future scheduleUnpark(long nanos) { */ @ChangesCurrentThread private void cancel(Future future) { + assert Thread.currentThread() == this; if (!future.isDone()) { // need to switch to current carrier thread to avoid nested parking switchToCarrierThread(); @@ -730,33 +799,26 @@ private void cancel(Future future) { } /** - * Re-enables this virtual thread for scheduling. If the virtual thread was - * {@link #park() parked} then it will be unblocked, otherwise its next call - * to {@code park} or {@linkplain #parkNanos(long) parkNanos} is guaranteed - * not to block. + * Re-enables this virtual thread for scheduling. If this virtual thread is parked + * then its task is scheduled to continue, otherwise its next call to {@code park} or + * {@linkplain #parkNanos(long) parkNanos} is guaranteed not to block. * @throws RejectedExecutionException if the scheduler cannot accept a task */ @Override - @ChangesCurrentThread void unpark() { - Thread currentThread = Thread.currentThread(); - if (!getAndSetParkPermit(true) && currentThread != this) { + if (!getAndSetParkPermit(true) && currentThread() != this) { int s = state(); - boolean parked = (s == PARKED) || (s == TIMED_PARKED); - if (parked && compareAndSetState(s, UNPARKED)) { - if (currentThread instanceof VirtualThread vthread) { - vthread.switchToCarrierThread(); - try { - submitRunContinuation(); - } finally { - switchToVirtualThread(vthread); - } - } else { - submitRunContinuation(); - } - } else if ((s == PINNED) || (s == TIMED_PINNED)) { + + // unparked while parked + if ((s == PARKED || s == TIMED_PARKED) && compareAndSetState(s, UNPARKED)) { + submitRunContinuation(); + return; + } + + // unparked while parked when pinned + if (s == PINNED || s == TIMED_PINNED) { // unpark carrier thread when pinned - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { synchronized (carrierThreadAccessLock()) { Thread carrier = carrierThread; @@ -765,8 +827,9 @@ void unpark() { } } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } + return; } } } @@ -859,11 +922,11 @@ boolean joinNanos(long nanos) throws InterruptedException { @Override void blockedOn(Interruptible b) { - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { super.blockedOn(b); } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } } @@ -874,9 +937,9 @@ public void interrupt() { checkAccess(); // if current thread is a virtual thread then prevent it from being - // suspended when entering or holding interruptLock + // suspended or unmounted when entering or holding interruptLock Interruptible blocker; - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { synchronized (interruptLock) { interrupted = true; @@ -890,18 +953,22 @@ public void interrupt() { if (carrier != null) carrier.setInterrupt(); } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } // notify blocker after releasing interruptLock if (blocker != null) { blocker.postInterrupt(); } + + // make available parking permit, unpark thread if parked + unpark(); + } else { interrupted = true; carrierThread.setInterrupt(); + setParkPermit(true); } - unpark(); } @Override @@ -914,14 +981,14 @@ boolean getAndClearInterrupt() { assert Thread.currentThread() == this; boolean oldValue = interrupted; if (oldValue) { - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { synchronized (interruptLock) { interrupted = false; carrierThread.clearInterrupt(); } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } } return oldValue; @@ -946,16 +1013,18 @@ Thread.State threadState() { return Thread.State.RUNNABLE; case RUNNING: // if mounted then return state of carrier thread - notifyJvmtiDisableSuspend(true); - try { - synchronized (carrierThreadAccessLock()) { - Thread carrierThread = this.carrierThread; - if (carrierThread != null) { - return carrierThread.threadState(); + if (Thread.currentThread() != this) { + disableSuspendAndPreempt(); + try { + synchronized (carrierThreadAccessLock()) { + Thread carrierThread = this.carrierThread; + if (carrierThread != null) { + return carrierThread.threadState(); + } } + } finally { + enableSuspendAndPreempt(); } - } finally { - notifyJvmtiDisableSuspend(false); } // runnable, mounted return Thread.State.RUNNABLE; @@ -1068,32 +1137,49 @@ public String toString() { sb.append(name); } sb.append("]/"); - Thread carrier = carrierThread; - if (carrier != null) { - // include the carrier thread state and name when mounted - notifyJvmtiDisableSuspend(true); + + // add the carrier state and thread name when mounted + boolean mounted; + if (Thread.currentThread() == this) { + mounted = appendCarrierInfo(sb); + } else { + disableSuspendAndPreempt(); try { synchronized (carrierThreadAccessLock()) { - carrier = carrierThread; - if (carrier != null) { - String stateAsString = carrier.threadState().toString(); - sb.append(stateAsString.toLowerCase(Locale.ROOT)); - sb.append('@'); - sb.append(carrier.getName()); - } + mounted = appendCarrierInfo(sb); } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } } - // include virtual thread state when not mounted - if (carrier == null) { + + // add virtual thread state when not mounted + if (!mounted) { String stateAsString = threadState().toString(); sb.append(stateAsString.toLowerCase(Locale.ROOT)); } + return sb.toString(); } + /** + * Appends the carrier state and thread name to the string buffer if mounted. + * @return true if mounted, false if not mounted + */ + private boolean appendCarrierInfo(StringBuilder sb) { + assert Thread.currentThread() == this || Thread.holdsLock(carrierThreadAccessLock()); + Thread carrier = carrierThread; + if (carrier != null) { + String stateAsString = carrier.threadState().toString(); + sb.append(stateAsString.toLowerCase(Locale.ROOT)); + sb.append('@'); + sb.append(carrier.getName()); + return true; + } else { + return false; + } + } + @Override public int hashCode() { return (int) threadId(); @@ -1127,6 +1213,22 @@ private Object carrierThreadAccessLock() { return interruptLock; } + /** + * Disallow the current thread be suspended or preempted. + */ + private void disableSuspendAndPreempt() { + notifyJvmtiDisableSuspend(true); + Continuation.pin(); + } + + /** + * Allow the current thread be suspended or preempted. + */ + private void enableSuspendAndPreempt() { + Continuation.unpin(); + notifyJvmtiDisableSuspend(false); + } + // -- wrappers for get/set of state, parking permit, and carrier thread -- private int state() { @@ -1188,10 +1290,16 @@ private void setCarrierThread(Thread carrier) { private static native void registerNatives(); static { registerNatives(); + + // ensure VTHREAD_GROUP is created, may be accessed by JVMTI + var group = Thread.virtualThreadGroup(); + + // ensure VirtualThreadPinnedEvent is loaded/initialized + U.ensureClassInitialized(VirtualThreadPinnedEvent.class); } /** - * Creates the default scheduler. + * Creates the default ForkJoinPool scheduler. */ @SuppressWarnings("removal") private static ForkJoinPool createDefaultScheduler() { @@ -1229,22 +1337,42 @@ private static ForkJoinPool createDefaultScheduler() { } /** - * Creates the ScheduledThreadPoolExecutor used for timed unpark. + * Schedule a runnable task to run after a delay. */ - private static ScheduledExecutorService createDelayedTaskScheduler() { - String propValue = GetPropertyAction.privilegedGetProperty("jdk.unparker.maxPoolSize"); - int poolSize; + private static Future schedule(Runnable command, long delay, TimeUnit unit) { + long tid = Thread.currentThread().threadId(); + int index = (int) tid & (DELAYED_TASK_SCHEDULERS.length - 1); + return DELAYED_TASK_SCHEDULERS[index].schedule(command, delay, unit); + } + + /** + * Creates the ScheduledThreadPoolExecutors used to execute delayed tasks. + */ + private static ScheduledExecutorService[] createDelayedTaskSchedulers() { + String propName = "jdk.virtualThreadScheduler.timerQueues"; + String propValue = GetPropertyAction.privilegedGetProperty(propName); + int queueCount; if (propValue != null) { - poolSize = Integer.parseInt(propValue); + queueCount = Integer.parseInt(propValue); + if (queueCount != Integer.highestOneBit(queueCount)) { + throw new RuntimeException("Value of " + propName + " must be power of 2"); + } } else { - poolSize = 1; + int ncpus = Runtime.getRuntime().availableProcessors(); + queueCount = Math.max(Integer.highestOneBit(ncpus / 4), 1); + } + var schedulers = new ScheduledExecutorService[queueCount]; + for (int i = 0; i < queueCount; i++) { + ScheduledThreadPoolExecutor stpe = (ScheduledThreadPoolExecutor) + Executors.newScheduledThreadPool(1, task -> { + Thread t = InnocuousThread.newThread("VirtualThread-unparker", task); + t.setDaemon(true); + return t; + }); + stpe.setRemoveOnCancelPolicy(true); + schedulers[i] = stpe; } - ScheduledThreadPoolExecutor stpe = (ScheduledThreadPoolExecutor) - Executors.newScheduledThreadPool(poolSize, task -> { - return InnocuousThread.newThread("VirtualThread-unparker", task); - }); - stpe.setRemoveOnCancelPolicy(true); - return stpe; + return schedulers; } /** diff --git a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java b/src/java.base/share/classes/java/lang/classfile/AccessFlags.java index 283094b81d629..6c6ac75e64896 100644 --- a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java +++ b/src/java.base/share/classes/java/lang/classfile/AccessFlags.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,52 +64,4 @@ public sealed interface AccessFlags * method, or field} */ AccessFlag.Location location(); - - /** - * {@return an {@linkplain AccessFlags} for a class} - * @param mask the flags to be set, as a bit mask - */ - static AccessFlags ofClass(int mask) { - return new AccessFlagsImpl(AccessFlag.Location.CLASS, mask); - } - - /** - * {@return an {@linkplain AccessFlags} for a class} - * @param flags the flags to be set - */ - static AccessFlags ofClass(AccessFlag... flags) { - return new AccessFlagsImpl(AccessFlag.Location.CLASS, flags); - } - - /** - * {@return an {@linkplain AccessFlags} for a field} - * @param mask the flags to be set, as a bit mask - */ - static AccessFlags ofField(int mask) { - return new AccessFlagsImpl(AccessFlag.Location.FIELD, mask); - } - - /** - * {@return an {@linkplain AccessFlags} for a field} - * @param flags the flags to be set - */ - static AccessFlags ofField(AccessFlag... flags) { - return new AccessFlagsImpl(AccessFlag.Location.FIELD, flags); - } - - /** - * {@return an {@linkplain AccessFlags} for a method} - * @param mask the flags to be set, as a bit mask - */ - static AccessFlags ofMethod(int mask) { - return new AccessFlagsImpl(AccessFlag.Location.METHOD, mask); - } - - /** - * {@return an {@linkplain AccessFlags} for a method} - * @param flags the flags to be set - */ - static AccessFlags ofMethod(AccessFlag... flags) { - return new AccessFlagsImpl(AccessFlag.Location.METHOD, flags); - } } diff --git a/src/java.base/share/classes/java/lang/classfile/Annotation.java b/src/java.base/share/classes/java/lang/classfile/Annotation.java index 3e7548d0859bc..28c3672bf914e 100644 --- a/src/java.base/share/classes/java/lang/classfile/Annotation.java +++ b/src/java.base/share/classes/java/lang/classfile/Annotation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,6 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface Annotation - extends WritableElement permits TypeAnnotation, AnnotationImpl { /** diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java b/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java index 41acb18e788e8..80adb07ec4b2a 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,6 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface AnnotationElement - extends WritableElement permits AnnotationImpl.AnnotationElementImpl { /** diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java index 2882296b6bc51..919c8a0441dde 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ * @since 22 */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) -public sealed interface AnnotationValue extends WritableElement +public sealed interface AnnotationValue permits AnnotationValue.OfAnnotation, AnnotationValue.OfArray, AnnotationValue.OfConstant, AnnotationValue.OfClass, AnnotationValue.OfEnum { diff --git a/src/java.base/share/classes/java/lang/classfile/Attribute.java b/src/java.base/share/classes/java/lang/classfile/Attribute.java index ad67bdf53652d..718f164e8ef7f 100644 --- a/src/java.base/share/classes/java/lang/classfile/Attribute.java +++ b/src/java.base/share/classes/java/lang/classfile/Attribute.java @@ -80,7 +80,7 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface Attribute> - extends WritableElement + extends ClassFileElement permits AnnotationDefaultAttribute, BootstrapMethodsAttribute, CharacterRangeTableAttribute, CodeAttribute, CompilationIDAttribute, ConstantValueAttribute, DeprecatedAttribute, EnclosingMethodAttribute, diff --git a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java index 63b2f1e03a939..d66806ca93b43 100644 --- a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java +++ b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ package java.lang.classfile; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -83,6 +84,6 @@ default > List findAttributes(AttributeMapper attr) list.add(t); } } - return list; + return Collections.unmodifiableList(list); } } diff --git a/src/java.base/share/classes/java/lang/classfile/BootstrapMethodEntry.java b/src/java.base/share/classes/java/lang/classfile/BootstrapMethodEntry.java index c472dca8530d7..5ff3c449fe9ae 100644 --- a/src/java.base/share/classes/java/lang/classfile/BootstrapMethodEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/BootstrapMethodEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,6 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface BootstrapMethodEntry - extends WritableElement permits BootstrapMethodEntryImpl { /** diff --git a/src/java.base/share/classes/java/lang/classfile/BufWriter.java b/src/java.base/share/classes/java/lang/classfile/BufWriter.java index 11d6a4501f0cb..c71b44e7c0286 100644 --- a/src/java.base/share/classes/java/lang/classfile/BufWriter.java +++ b/src/java.base/share/classes/java/lang/classfile/BufWriter.java @@ -24,8 +24,6 @@ */ package java.lang.classfile; -import java.util.List; - import java.lang.classfile.constantpool.ConstantPool; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.PoolEntry; @@ -110,13 +108,6 @@ public sealed interface BufWriter */ void writeBytes(byte[] arr); - /** - * Write the contents of another {@link BufWriter} to the buffer - * - * @param other the other {@linkplain BufWriter} - */ - void writeBytes(BufWriter other); - /** * Write a range of a byte array to the buffer * @@ -166,38 +157,8 @@ public sealed interface BufWriter */ void writeIndexOrZero(PoolEntry entry); - /** - * Write a list of entities to the buffer. The length of the list is - * written as a {@code u2}, followed by the bytes corresponding to each - * element in the list. Writing of the entities is delegated to the entry. - * - * @param list the entities - * @param the type of entity - */ - > void writeList(List list); - - /** - * Write a list of constant pool entry indexes to the buffer. The length - * of the list is written as a {@code u2}, followed by a {@code u2} for each - * entry in the list. - * - * @param list the list of entries - * @throws IllegalArgumentException if any entry has invalid index - */ - void writeListIndices(List list); - /** * {@return the number of bytes that have been written to the buffer} */ int size(); - - /** - * Copy the contents of the buffer into a byte array. - * - * @param array the byte array - * @param bufferOffset the offset into the array at which to write the - * contents of the buffer - * @throws IndexOutOfBoundsException if copying outside of the array bounds - */ - void copyTo(byte[] array, int bufferOffset); } diff --git a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java index f1b8bb13d27e5..7ad6e94df3fce 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,11 +30,12 @@ import java.lang.constant.MethodTypeDesc; import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.function.Consumer; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.Utf8Entry; + +import jdk.internal.classfile.impl.AccessFlagsImpl; import jdk.internal.classfile.impl.ChainedClassBuilder; import jdk.internal.classfile.impl.DirectClassBuilder; import jdk.internal.classfile.impl.Util; @@ -58,12 +59,6 @@ public sealed interface ClassBuilder extends ClassFileBuilder permits ChainedClassBuilder, DirectClassBuilder { - /** - * {@return the {@link ClassModel} representing the class being transformed, - * if this class builder represents the transformation of some {@link ClassModel}} - */ - Optional original(); - /** * Sets the classfile version. * @param major the major version number @@ -80,7 +75,7 @@ default ClassBuilder withVersion(int major, int minor) { * @return this builder */ default ClassBuilder withFlags(int flags) { - return with(AccessFlags.ofClass(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags)); } /** @@ -89,7 +84,7 @@ default ClassBuilder withFlags(int flags) { * @return this builder */ default ClassBuilder withFlags(AccessFlag... flags) { - return with(AccessFlags.ofClass(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags)); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java b/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java index 6452f52e04226..a4a8203038f15 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFileElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,6 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface ClassFileElement - permits AttributedElement, CompoundElement, WritableElement, + permits AttributedElement, CompoundElement, Attribute, ClassElement, CodeElement, FieldElement, MethodElement { } diff --git a/src/java.base/share/classes/java/lang/classfile/ClassReader.java b/src/java.base/share/classes/java/lang/classfile/ClassReader.java index ef4a36729e679..735aae444fc0c 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassReader.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassReader.java @@ -189,19 +189,4 @@ public sealed interface ClassReader extends ConstantPool * @param len the length of the range */ void copyBytesTo(BufWriter buf, int offset, int len); - - /** - * Compare a range of bytes from the classfile to a range of bytes within - * a {@link BufWriter}. - * - * @param bufWriter the {@linkplain BufWriter} - * @param bufWriterOffset the offset within the {@linkplain BufWriter} - * @param classReaderOffset the offset within the classfile - * @param length the length of the range - * @return whether the two ranges were identical - */ - boolean compare(BufWriter bufWriter, - int bufWriterOffset, - int classReaderOffset, - int length); } diff --git a/src/java.base/share/classes/java/lang/classfile/ClassSignature.java b/src/java.base/share/classes/java/lang/classfile/ClassSignature.java index 852d5f9baf4f1..5c55950547eb6 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassSignature.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassSignature.java @@ -76,7 +76,7 @@ public static ClassSignature of(List typeParameters, Signature.ClassTypeSig superclassSignature, Signature.ClassTypeSig... superinterfaceSignatures) { return new SignaturesImpl.ClassSignatureImpl( - requireNonNull(typeParameters), + List.copyOf(requireNonNull(typeParameters)), requireNonNull(superclassSignature), List.of(superinterfaceSignatures)); } 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 dffa9c7f89a85..64a677d63ade0 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java @@ -128,12 +128,6 @@ public sealed interface CodeBuilder extends ClassFileBuilder permits CodeBuilder.BlockCodeBuilder, ChainedCodeBuilder, TerminalCodeBuilder, NonterminalCodeBuilder { - /** - * {@return the {@link CodeModel} representing the method body being transformed, - * if this code builder represents the transformation of some {@link CodeModel}} - */ - Optional original(); - /** {@return a fresh unbound label} */ Label newLabel(); diff --git a/src/java.base/share/classes/java/lang/classfile/CodeModel.java b/src/java.base/share/classes/java/lang/classfile/CodeModel.java index 45488561c1ab3..ba816f2080581 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeModel.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeModel.java @@ -43,17 +43,7 @@ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface CodeModel extends CompoundElement, AttributedElement, MethodElement - permits CodeAttribute, BufferedCodeBuilder.Model, CodeImpl { - - /** - * {@return the maximum size of the local variable table} - */ - int maxLocals(); - - /** - * {@return the maximum size of the operand stack} - */ - int maxStack(); + permits CodeAttribute, BufferedCodeBuilder.Model { /** * {@return the enclosing method, if known} diff --git a/src/java.base/share/classes/java/lang/classfile/CompoundElement.java b/src/java.base/share/classes/java/lang/classfile/CompoundElement.java index 12904c9dd596e..9fcf02204cb13 100644 --- a/src/java.base/share/classes/java/lang/classfile/CompoundElement.java +++ b/src/java.base/share/classes/java/lang/classfile/CompoundElement.java @@ -25,6 +25,7 @@ package java.lang.classfile; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Spliterator; @@ -90,7 +91,7 @@ public void accept(E e) { list.add(e); } }); - return list; + return Collections.unmodifiableList(list); } } diff --git a/src/java.base/share/classes/java/lang/classfile/CustomAttribute.java b/src/java.base/share/classes/java/lang/classfile/CustomAttribute.java index 31544a0ba92bf..9fe492dc22c4c 100644 --- a/src/java.base/share/classes/java/lang/classfile/CustomAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/CustomAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,12 +59,6 @@ public final String attributeName() { return mapper.name(); } - @Override - @SuppressWarnings("unchecked") - public final void writeTo(BufWriter buf) { - mapper.writeAttribute(buf, (T) this); - } - @Override public String toString() { return String.format("CustomAttribute[name=%s]", mapper.name()); diff --git a/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java b/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java index 18a598398ec9a..d9a8f6b2fc325 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,12 @@ package java.lang.classfile; import java.lang.classfile.constantpool.Utf8Entry; + +import jdk.internal.classfile.impl.AccessFlagsImpl; import jdk.internal.classfile.impl.ChainedFieldBuilder; import jdk.internal.classfile.impl.TerminalFieldBuilder; import java.lang.reflect.AccessFlag; -import java.util.Optional; import java.util.function.Consumer; import jdk.internal.javac.PreviewFeature; @@ -56,7 +57,7 @@ public sealed interface FieldBuilder * @return this builder */ default FieldBuilder withFlags(int flags) { - return with(AccessFlags.ofField(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.FIELD, flags)); } /** @@ -65,12 +66,7 @@ default FieldBuilder withFlags(int flags) { * @return this builder */ default FieldBuilder withFlags(AccessFlag... flags) { - return with(AccessFlags.ofField(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.FIELD, flags)); } - /** - * {@return the {@link FieldModel} representing the field being transformed, - * if this field builder represents the transformation of some {@link FieldModel}} - */ - Optional original(); } diff --git a/src/java.base/share/classes/java/lang/classfile/FieldModel.java b/src/java.base/share/classes/java/lang/classfile/FieldModel.java index 35465dfe97d00..e14f264ca2a98 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldModel.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldModel.java @@ -42,7 +42,7 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface FieldModel - extends WritableElement, CompoundElement, AttributedElement, ClassElement + extends CompoundElement, AttributedElement, ClassElement permits BufferedFieldBuilder.Model, FieldImpl { /** {@return the access flags} */ diff --git a/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java b/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java index ece292c361b21..7c230760f6934 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,11 @@ package java.lang.classfile; -import java.util.Optional; import java.util.function.Consumer; import java.lang.classfile.constantpool.Utf8Entry; + +import jdk.internal.classfile.impl.AccessFlagsImpl; import jdk.internal.classfile.impl.ChainedMethodBuilder; import jdk.internal.classfile.impl.TerminalMethodBuilder; import java.lang.reflect.AccessFlag; @@ -50,19 +51,13 @@ public sealed interface MethodBuilder extends ClassFileBuilder permits ChainedMethodBuilder, TerminalMethodBuilder { - /** - * {@return the {@link MethodModel} representing the method being transformed, - * if this method builder represents the transformation of some {@link MethodModel}} - */ - Optional original(); - /** * Sets the method access flags. * @param flags the access flags, as a bit mask * @return this builder */ default MethodBuilder withFlags(int flags) { - return with(AccessFlags.ofMethod(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.METHOD, flags)); } /** @@ -71,7 +66,7 @@ default MethodBuilder withFlags(int flags) { * @return this builder */ default MethodBuilder withFlags(AccessFlag... flags) { - return with(AccessFlags.ofMethod(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.METHOD, flags)); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/MethodModel.java b/src/java.base/share/classes/java/lang/classfile/MethodModel.java index 3d91683e21889..651bc194ee3e0 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodModel.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodModel.java @@ -42,7 +42,7 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface MethodModel - extends WritableElement, CompoundElement, AttributedElement, ClassElement + extends CompoundElement, AttributedElement, ClassElement permits BufferedMethodBuilder.Model, MethodImpl { /** {@return the access flags} */ diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java index 02cbcee810f64..7a4f5886580cc 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java @@ -47,6 +47,16 @@ public sealed interface CodeAttribute extends Attribute, CodeModel permits BoundAttribute.BoundCodeAttribute { + /** + * {@return the maximum size of the local variable table} + */ + int maxLocals(); + + /** + * {@return the maximum size of the operand stack} + */ + int maxStack(); + /** * {@return The length of the code array in bytes} */ diff --git a/src/java.base/share/classes/java/lang/classfile/components/CodeStackTracker.java b/src/java.base/share/classes/java/lang/classfile/components/CodeStackTracker.java index 97ced9ab38562..1b711cfad0ec3 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/CodeStackTracker.java +++ b/src/java.base/share/classes/java/lang/classfile/components/CodeStackTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ * and calculating max stack size. *

* Sample use: - *

+ * * {@snippet lang=java : * var stackTracker = CodeStackTracker.of(); * codeBuilder.transforming(stackTracker, trackedBuilder -> { diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java index a43e6f102ed72..c092717547692 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java @@ -33,13 +33,11 @@ import java.util.List; import java.lang.classfile.BootstrapMethodEntry; -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassModel; import jdk.internal.classfile.impl.ClassReaderImpl; import java.lang.constant.ModuleDesc; import java.lang.constant.PackageDesc; -import java.lang.classfile.WritableElement; import jdk.internal.classfile.impl.AbstractPoolEntry.ClassEntryImpl; import jdk.internal.classfile.impl.AbstractPoolEntry.NameAndTypeEntryImpl; import jdk.internal.classfile.impl.SplitConstantPool; @@ -61,7 +59,7 @@ */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface ConstantPoolBuilder - extends ConstantPool, WritableElement + extends ConstantPool permits SplitConstantPool, TemporaryConstantPool { /** diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java index 49bd978e56a39..c8b74a139280e 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ */ package java.lang.classfile.constantpool; -import java.lang.classfile.WritableElement; import jdk.internal.javac.PreviewFeature; /** @@ -34,7 +33,7 @@ * @since 22 */ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) -public sealed interface PoolEntry extends WritableElement +public sealed interface PoolEntry permits AnnotationConstantValueEntry, DynamicConstantPoolEntry, LoadableConstantEntry, MemberRefEntry, ModuleEntry, NameAndTypeEntry, PackageEntry { diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java index cd37ab949926a..4d3d755386763 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,6 @@ */ package java.lang.classfile.instruction; -import java.lang.constant.ClassDesc; - -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeElement; import java.lang.classfile.CodeModel; @@ -34,6 +31,8 @@ import java.lang.classfile.PseudoInstruction; import java.lang.classfile.attribute.LocalVariableTableAttribute; import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.constant.ClassDesc; + import jdk.internal.classfile.impl.AbstractPseudoInstruction; import jdk.internal.classfile.impl.BoundLocalVariable; import jdk.internal.classfile.impl.TemporaryConstantPool; @@ -84,14 +83,6 @@ default ClassDesc typeSymbol() { */ Label endScope(); - /** - * Writes the local variable to the specified writer - * - * @param buf the writer - * @return true if the variable has been written - */ - boolean writeTo(BufWriter buf); - /** * {@return a local variable pseudo-instruction} * diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java index 1415d0bb57cdc..4409abe6db272 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ */ package java.lang.classfile.instruction; -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeElement; import java.lang.classfile.CodeModel; @@ -33,6 +32,7 @@ import java.lang.classfile.Signature; import java.lang.classfile.attribute.LocalVariableTypeTableAttribute; import java.lang.classfile.constantpool.Utf8Entry; + import jdk.internal.classfile.impl.AbstractPseudoInstruction; import jdk.internal.classfile.impl.BoundLocalVariableType; import jdk.internal.classfile.impl.TemporaryConstantPool; @@ -81,14 +81,6 @@ default Signature signatureSymbol() { */ Label endScope(); - /** - * Writes the local variable to the specified writer - * - * @param buf the writer - * @return true if the variable has been written - */ - boolean writeTo(BufWriter buf); - /** * {@return a local variable type pseudo-instruction} * diff --git a/src/java.base/share/classes/java/lang/classfile/package-info.java b/src/java.base/share/classes/java/lang/classfile/package-info.java index 49413d356607c..921cf9e1a3443 100644 --- a/src/java.base/share/classes/java/lang/classfile/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,18 +26,18 @@ /** *

Provides classfile parsing, generation, and transformation library.

* The {@code java.lang.classfile} package contains classes for reading, writing, and - * modifying Java class files, as specified in Chapter {@jvms 4} of the Java - * Java Virtual Machine Specification. + * modifying Java class files, as specified in Chapter {@jvms 4} of the + * Java Virtual Machine Specification. * *

Reading classfiles

* The main class for reading classfiles is {@link java.lang.classfile.ClassModel}; we * convert bytes into a {@link java.lang.classfile.ClassModel} with {@link * java.lang.classfile.ClassFile#parse(byte[])}: - *

+ * * {@snippet lang=java : * ClassModel cm = ClassFile.of().parse(bytes); * } - *

+ * * There are several additional overloads of {@code parse} that let you specify * various processing options. *

@@ -377,7 +377,7 @@ *

* Then we can compose {@code fooToBar} and {@code instrumentCalls} with {@link * java.lang.classfile.CodeTransform#andThen(java.lang.classfile.CodeTransform)}: - *

+ * * {@snippet lang=java : * var cc = ClassFile.of(); * byte[] newBytes = cc.transform(cc.parse(bytes), @@ -443,7 +443,7 @@ * or more, zero or one, exactly one), and a list of components. The elements * of a class are fields, methods, and the attributes that can appear on * classes: - *

+ * * {@snippet lang="text" : * ClassElement = * FieldModel*(UtfEntry name, Utf8Entry descriptor) @@ -468,7 +468,7 @@ * | PermittedSubclassesAttribute?(List permittedSubclasses) * | DeclarationElement* * } - *

+ * * where {@code DeclarationElement} are the elements that are common to all declarations * (classes, methods, fields) and so are factored out: * diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java index c9a79c9700db9..9585e613805c9 100644 --- a/src/java.base/share/classes/java/lang/foreign/Arena.java +++ b/src/java.base/share/classes/java/lang/foreign/Arena.java @@ -61,7 +61,7 @@ * by the garbage collector. The scope of an automatic arena is an automatic scope. As * such, the regions of memory backing memory segments allocated with the automatic arena * are deallocated at some unspecified time after the automatic arena (and all - * the segments allocated by it) becomes unreachable, + * the segments allocated by it) becomes {@linkplain java.lang.ref##reachability unreachable}, * as shown below: * {@snippet lang = java: * MemorySegment segment = Arena.ofAuto().allocate(100, 1); // @highlight regex='ofAuto()' diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 83a8b09f9d453..82f6b358a0752 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -517,7 +517,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * * @apiNote When using this method to pass a segment address to some external * operation (e.g. a JNI function), clients must ensure that the segment is - * kept reachable + * kept {@linkplain java.lang.ref##reachability reachable} * for the entire duration of the operation. A failure to do so might result * in the premature deallocation of the region of memory backing the memory * segment, in case the segment has been allocated with an @@ -785,7 +785,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * backing region of memory is no longer available. Furthermore, if the * provided scope is the scope of an {@linkplain Arena#ofAuto() automatic arena}, * the cleanup action must not prevent the scope from becoming - * unreachable. + * {@linkplain java.lang.ref##reachability unreachable}. * A failure to do so will permanently prevent the regions of memory * allocated by the automatic arena from being deallocated. * @@ -836,7 +836,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * backing region of memory is no longer available. Furthermore, if the * provided scope is the scope of an {@linkplain Arena#ofAuto() automatic arena}, * the cleanup action must not prevent the scope from becoming - * unreachable. + * {@linkplain java.lang.ref##reachability unreachable}. * A failure to do so will permanently prevent the regions of memory * allocated by the automatic arena from being deallocated. * @@ -2662,7 +2662,7 @@ static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOff * invalidated, either {@link Arena#close() explicitly}, or automatically, by the * garbage collector. A segment scope that is invalidated automatically is an * automatic scope. An automatic scope is always {@link #isAlive() alive} - * as long as it is reachable. + * as long as it is {@linkplain java.lang.ref##reachability reachable}. * Segments associated with an automatic scope are: *

    *
  • Segments obtained from an {@linkplain Arena#ofAuto() automatic arena};
  • diff --git a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java index 3bc416b9c6272..a8838ea715c92 100644 --- a/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java +++ b/src/java.base/share/classes/java/lang/foreign/SymbolLookup.java @@ -219,7 +219,7 @@ default SymbolLookup or(SymbolLookup other) { * were loaded after this method returned. *

    * Libraries associated with a class loader are unloaded when the class loader becomes - * unreachable. The + * {@linkplain java.lang.ref##reachability unreachable}. The * symbol lookup returned by this method is associated with an automatic * {@linkplain MemorySegment.Scope scope} which keeps the caller's class loader * reachable. Therefore, libraries associated with the caller's class loader are diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java index 09ea5df4a235f..3f6e521b27bd9 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -395,7 +395,7 @@ private static Name[] buildEmptyNames(int arity, MethodType mt, boolean isVoid) Name[] names = arguments(isVoid ? 0 : 1, mt); if (!isVoid) { Name zero = new Name(constantZero(basicType(mt.returnType()))); - names[arity] = zero.newIndex(arity); + names[arity] = zero.withIndex(arity); } assert(namesOK(arity, names)); return names; @@ -495,28 +495,18 @@ LambdaForm uncustomize() { * @return true if we can interpret */ private static boolean normalizeNames(int arity, Name[] names) { - Name[] oldNames = null; + Name[] oldNames = names.clone(); int maxOutArity = 0; - int changesStart = 0; for (int i = 0; i < names.length; i++) { Name n = names[i]; - if (!n.initIndex(i)) { - if (oldNames == null) { - oldNames = names.clone(); - changesStart = i; - } - names[i] = n.cloneWithIndex(i); - } + names[i] = n.withIndex(i); if (n.arguments != null && maxOutArity < n.arguments.length) maxOutArity = n.arguments.length; } if (oldNames != null) { - int startFixing = arity; - if (startFixing <= changesStart) - startFixing = changesStart+1; - for (int i = startFixing; i < names.length; i++) { - Name fixed = names[i].replaceNames(oldNames, names, changesStart, i); - names[i] = fixed.newIndex(i); + for (int i = Math.max(1, arity); i < names.length; i++) { + Name fixed = names[i].replaceNames(oldNames, names, 0, i); + names[i] = fixed.withIndex(i); } } int maxInterned = Math.min(arity, INTERNED_ARGUMENT_LIMIT); @@ -1339,30 +1329,24 @@ public static String shortenSignature(String signature) { static final class Name { final BasicType type; - @Stable short index; + final short index; final NamedFunction function; final Object constraint; // additional type information, if not null @Stable final Object[] arguments; private static final Object[] EMPTY_ARGS = new Object[0]; - private Name(int index, BasicType type, NamedFunction function, Object[] arguments) { + private Name(int index, BasicType type, NamedFunction function, Object[] arguments, Object constraint) { this.index = (short)index; this.type = type; this.function = function; this.arguments = arguments; - this.constraint = null; - assert(this.index == index && typesMatch(function, this.arguments)); - } - private Name(Name that, Object constraint) { - this.index = that.index; - this.type = that.type; - this.function = that.function; - this.arguments = that.arguments; this.constraint = constraint; + assert(this.index == index && typesMatch(function, arguments)); assert(constraint == null || isParam()); // only params have constraints assert(constraint == null || constraint instanceof ClassSpecializer.SpeciesData || constraint instanceof Class); } + Name(MethodHandle function, Object... arguments) { this(new NamedFunction(function), arguments); } @@ -1374,49 +1358,41 @@ private Name(Name that, Object constraint) { this(new NamedFunction(function), arguments); } Name(NamedFunction function) { - this(-1, function.returnType(), function, EMPTY_ARGS); + this(-1, function.returnType(), function, EMPTY_ARGS, null); } Name(NamedFunction function, Object arg) { - this(-1, function.returnType(), function, new Object[] { arg }); + this(-1, function.returnType(), function, new Object[] { arg }, null); } Name(NamedFunction function, Object arg0, Object arg1) { - this(-1, function.returnType(), function, new Object[] { arg0, arg1 }); + this(-1, function.returnType(), function, new Object[] { arg0, arg1 }, null); } Name(NamedFunction function, Object... arguments) { - this(-1, function.returnType(), function, Arrays.copyOf(arguments, arguments.length, Object[].class)); + this(-1, function.returnType(), function, Arrays.copyOf(arguments, arguments.length, Object[].class), null); } /** Create a raw parameter of the given type, with an expected index. */ Name(int index, BasicType type) { - this(index, type, null, null); + this(index, type, null, null, null); } /** Create a raw parameter of the given type. */ Name(BasicType type) { this(-1, type); } BasicType type() { return type; } int index() { return index; } - boolean initIndex(int i) { - if (index != i) { - if (index != -1) return false; - index = (short)i; - } - return true; - } + char typeChar() { return type.btChar; } - Name newIndex(int i) { - if (initIndex(i)) return this; - return cloneWithIndex(i); - } - Name cloneWithIndex(int i) { - Object[] newArguments = (arguments == null) ? null : arguments.clone(); - return new Name(i, type, function, newArguments).withConstraint(constraint); + Name withIndex(int i) { + if (i == this.index) return this; + return new Name(i, type, function, arguments, constraint); } + Name withConstraint(Object constraint) { if (constraint == this.constraint) return this; - return new Name(this, constraint); + return new Name(index, type, function, arguments, constraint); } + Name replaceName(Name oldName, Name newName) { // FIXME: use replaceNames uniformly if (oldName == newName) return this; @SuppressWarnings("LocalVariableHidesMemberVariable") diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index 29896fd8f93df..cf552c434be53 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -565,17 +565,17 @@ private static MethodHandle generateMHInlineCopy(MethodType mt, String[] constan // Fold in byte[] instantiation at argument 0 MethodHandle newArrayCombinator; - if (suffix != null) { - // newArray variant that deals with prepending any trailing constant - // - // initialLengthCoder is adjusted to have the correct coder - // and length: The newArrayWithSuffix method expects only the coder of the - // suffix to be encoded into indexCoder - initialLengthCoder -= suffix.length(); - newArrayCombinator = newArrayWithSuffix(suffix); - } else { - newArrayCombinator = newArray(); + if (suffix == null || suffix.isEmpty()) { + suffix = ""; } + // newArray variant that deals with prepending any trailing constant + // + // initialLengthCoder is adjusted to have the correct coder + // and length: The newArrayWithSuffix method expects only the coder of the + // suffix to be encoded into indexCoder + initialLengthCoder -= suffix.length(); + newArrayCombinator = newArrayWithSuffix(suffix); + mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, newArrayCombinator, 1 // index ); @@ -738,9 +738,7 @@ private static MethodHandle noPrefixPrepender(Class cl) { int idx = classIndex(cl); MethodHandle prepend = NO_PREFIX_PREPENDERS[idx]; if (prepend == null) { - NO_PREFIX_PREPENDERS[idx] = prepend = JLA.stringConcatHelper("prepend", - methodType(long.class, long.class, byte[].class, - Wrapper.asPrimitiveType(cl))).rebind(); + NO_PREFIX_PREPENDERS[idx] = prepend = MethodHandles.insertArguments(prepender(cl), 3, ""); } return prepend; } @@ -902,16 +900,6 @@ private static MethodHandle newArrayWithSuffix(String suffix) { return MethodHandles.insertArguments(mh, 0, suffix); } - private @Stable static MethodHandle NEW_ARRAY; - private static MethodHandle newArray() { - MethodHandle mh = NEW_ARRAY; - if (mh == null) { - NEW_ARRAY = mh = - JLA.stringConcatHelper("newArray", methodType(byte[].class, long.class)); - } - return mh; - } - /** * Public gateways to public "stringify" methods. These methods have the * form String apply(T obj), and normally delegate to {@code String.valueOf}, diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index ccb33a0e4dbd1..43f7339c75a96 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -56,6 +56,7 @@ import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import static java.util.Objects.requireNonNull; @@ -95,19 +96,13 @@ private SwitchBootstraps() {} private static final ClassDesc CD_Objects = ReferenceClassDescImpl.ofValidated("Ljava/util/Objects;"); private static class StaticHolders { - private static final MethodHandle NULL_CHECK; - private static final MethodHandle IS_ZERO; - private static final MethodHandle MAPPED_ENUM_LOOKUP; + private static final MethodHandle MAPPED_ENUM_SWITCH; static { try { - NULL_CHECK = LOOKUP.findStatic(Objects.class, "isNull", - MethodType.methodType(boolean.class, Object.class)); - IS_ZERO = LOOKUP.findStatic(SwitchBootstraps.class, "isZero", - MethodType.methodType(boolean.class, int.class)); - MAPPED_ENUM_LOOKUP = LOOKUP.findStatic(SwitchBootstraps.class, "mappedEnumLookup", - MethodType.methodType(int.class, Enum.class, MethodHandles.Lookup.class, - Class.class, EnumDesc[].class, EnumMap.class)); + MAPPED_ENUM_SWITCH = LOOKUP.findStatic(SwitchBootstraps.class, "mappedEnumSwitch", + MethodType.methodType(int.class, Enum.class, int.class, MethodHandles.Lookup.class, + Class.class, EnumDesc[].class, MappedEnumCache.class)); } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); @@ -211,10 +206,6 @@ private static void verifyLabel(Object label, Class selectorType) { } } - private static boolean isZero(int value) { - return value == 0; - } - /** * Bootstrap method for linking an {@code invokedynamic} call site that * implements a {@code switch} on a target of an enum type. The static @@ -286,23 +277,27 @@ public static CallSite enumSwitch(MethodHandles.Lookup lookup, labels = labels.clone(); Class enumClass = invocationType.parameterType(0); - labels = Stream.of(labels).map(l -> convertEnumConstants(lookup, enumClass, l)).toArray(); + boolean constantsOnly = true; + int len = labels.length; + + for (int i = 0; i < len; i++) { + Object convertedLabel = + convertEnumConstants(lookup, enumClass, labels[i]); + labels[i] = convertedLabel; + if (constantsOnly) + constantsOnly = convertedLabel instanceof EnumDesc; + } MethodHandle target; - boolean constantsOnly = Stream.of(labels).allMatch(l -> enumClass.isAssignableFrom(EnumDesc.class)); if (labels.length > 0 && constantsOnly) { //If all labels are enum constants, construct an optimized handle for repeat index 0: //if (selector == null) return -1 //else if (idx == 0) return mappingArray[selector.ordinal()]; //mapping array created lazily //else return "typeSwitch(labels)" - MethodHandle body = - MethodHandles.guardWithTest(MethodHandles.dropArguments(StaticHolders.NULL_CHECK, 0, int.class), - MethodHandles.dropArguments(MethodHandles.constant(int.class, -1), 0, int.class, Object.class), - MethodHandles.guardWithTest(MethodHandles.dropArguments(StaticHolders.IS_ZERO, 1, Object.class), - generateTypeSwitch(lookup, invocationType.parameterType(0), labels), - MethodHandles.insertArguments(StaticHolders.MAPPED_ENUM_LOOKUP, 1, lookup, enumClass, labels, new EnumMap()))); - target = MethodHandles.permuteArguments(body, MethodType.methodType(int.class, Object.class, int.class), 1, 0); + EnumDesc[] enumDescLabels = + Arrays.copyOf(labels, labels.length, EnumDesc[].class); + target = MethodHandles.insertArguments(StaticHolders.MAPPED_ENUM_SWITCH, 2, lookup, enumClass, enumDescLabels, new MappedEnumCache()); } else { target = generateTypeSwitch(lookup, invocationType.parameterType(0), labels); } @@ -331,26 +326,63 @@ private static > Object convertEnumConstants(MethodHandles.Loo } } - private static > int mappedEnumLookup(T value, MethodHandles.Lookup lookup, Class enumClass, EnumDesc[] labels, EnumMap enumMap) { - if (enumMap.map == null) { - T[] constants = SharedSecrets.getJavaLangAccess().getEnumConstantsShared(enumClass); - int[] map = new int[constants.length]; - int ordinal = 0; - - for (T constant : constants) { - map[ordinal] = labels.length; + private static > int mappedEnumSwitch(T value, int restartIndex, MethodHandles.Lookup lookup, Class enumClass, EnumDesc[] labels, MappedEnumCache enumCache) throws Throwable { + if (value == null) { + return -1; + } - for (int i = 0; i < labels.length; i++) { - if (Objects.equals(labels[i].constantName(), constant.name())) { - map[ordinal] = i; - break; + if (restartIndex != 0) { + MethodHandle generatedSwitch = enumCache.generatedSwitch; + if (generatedSwitch == null) { + synchronized (enumCache) { + generatedSwitch = enumCache.generatedSwitch; + + if (generatedSwitch == null) { + generatedSwitch = + generateTypeSwitch(lookup, enumClass, labels) + .asType(MethodType.methodType(int.class, + Enum.class, + int.class)); + enumCache.generatedSwitch = generatedSwitch; } } + } + + return (int) generatedSwitch.invokeExact(value, restartIndex); + } + + int[] constantsMap = enumCache.constantsMap; + + if (constantsMap == null) { + synchronized (enumCache) { + constantsMap = enumCache.constantsMap; - ordinal++; + if (constantsMap == null) { + T[] constants = SharedSecrets.getJavaLangAccess() + .getEnumConstantsShared(enumClass); + constantsMap = new int[constants.length]; + int ordinal = 0; + + for (T constant : constants) { + constantsMap[ordinal] = labels.length; + + for (int i = 0; i < labels.length; i++) { + if (Objects.equals(labels[i].constantName(), + constant.name())) { + constantsMap[ordinal] = i; + break; + } + } + + ordinal++; + } + + enumCache.constantsMap = constantsMap; + } } } - return enumMap.map[value.ordinal()]; + + return constantsMap[value.ordinal()]; } private static final class ResolvedEnumLabels implements BiPredicate { @@ -395,9 +427,11 @@ public boolean test(Integer labelIndex, Object value) { } } - private static final class EnumMap { + private static final class MappedEnumCache { + @Stable + public int[] constantsMap; @Stable - public int[] map; + public MethodHandle generatedSwitch; } /* diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index c69e291d0e2be..3a5fd1439373c 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2723,7 +2723,7 @@ public BigInteger sqrt() { throw new ArithmeticException("Negative BigInteger"); } - return new MutableBigInteger(this.mag).sqrt().toBigInteger(); + return new MutableBigInteger(this.mag).sqrtRem(false)[0].toBigInteger(); } /** @@ -2742,10 +2742,12 @@ public BigInteger sqrt() { * @since 9 */ public BigInteger[] sqrtAndRemainder() { - BigInteger s = sqrt(); - BigInteger r = this.subtract(s.square()); - assert r.compareTo(BigInteger.ZERO) >= 0; - return new BigInteger[] {s, r}; + if (this.signum < 0) { + throw new ArithmeticException("Negative BigInteger"); + } + + MutableBigInteger[] sqrtRem = new MutableBigInteger(this.mag).sqrtRem(true); + return new BigInteger[] { sqrtRem[0].toBigInteger(), sqrtRem[1].toBigInteger() }; } /** diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 30ea8e130fcca..b84e50f567eb7 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -109,9 +109,26 @@ class MutableBigInteger { * the int val. */ MutableBigInteger(int val) { - value = new int[1]; - intLen = 1; - value[0] = val; + init(val); + } + + /** + * Construct a new MutableBigInteger with a magnitude specified by + * the long val. + */ + MutableBigInteger(long val) { + int hi = (int) (val >>> 32); + if (hi == 0) { + init((int) val); + } else { + value = new int[] { hi, (int) val }; + intLen = 2; + } + } + + private void init(int val) { + value = new int[] { val }; + intLen = val != 0 ? 1 : 0; } /** @@ -260,6 +277,7 @@ void reset() { * Compare the magnitude of two MutableBigIntegers. Returns -1, 0 or 1 * as this MutableBigInteger is numerically less than, equal to, or * greater than {@code b}. + * Assumes no leading unnecessary zeros. */ final int compare(MutableBigInteger b) { int blen = b.intLen; @@ -285,6 +303,7 @@ final int compare(MutableBigInteger b) { /** * Returns a value equal to what {@code b.leftShift(32*ints); return compare(b);} * would return, but doesn't change the value of {@code b}. + * Assumes no leading unnecessary zeros. */ private int compareShifted(MutableBigInteger b, int ints) { int blen = b.intLen; @@ -538,6 +557,7 @@ void safeRightShift(int n) { /** * Right shift this MutableBigInteger n bits. The MutableBigInteger is left * in normal form. + * Assumes {@code Math.ceilDiv(n, 32) <= intLen || intLen == 0} */ void rightShift(int n) { if (intLen == 0) @@ -911,6 +931,58 @@ void addLower(MutableBigInteger addend, int n) { add(a); } + /** + * Shifts {@code this} of {@code n} ints to the left and adds {@code addend}. + * Assumes {@code n > 0} for speed. + */ + void shiftAdd(MutableBigInteger addend, int n) { + // Fast cases + if (addend.intLen <= n) { + shiftAddDisjoint(addend, n); + } else if (intLen == 0) { + copyValue(addend); + } else { + leftShift(n << 5); + add(addend); + } + } + + /** + * Shifts {@code this} of {@code n} ints to the left and adds {@code addend}. + * Assumes {@code addend.intLen <= n}. + */ + void shiftAddDisjoint(MutableBigInteger addend, int n) { + if (intLen == 0) { // Avoid unnormal values + copyValue(addend); + return; + } + + int[] res; + final int resLen = intLen + n, resOffset; + if (resLen > value.length) { + res = new int[resLen]; + System.arraycopy(value, offset, res, 0, intLen); + resOffset = 0; + } else { + res = value; + if (offset + resLen > value.length) { + System.arraycopy(value, offset, res, 0, intLen); + resOffset = 0; + } else { + resOffset = offset; + } + // Clear words where necessary + if (addend.intLen < n) + Arrays.fill(res, resOffset + intLen, resOffset + resLen - addend.intLen, 0); + } + + System.arraycopy(addend.value, addend.offset, res, resOffset + resLen - addend.intLen, addend.intLen); + + value = res; + offset = resOffset; + intLen = resLen; + } + /** * Subtracts the smaller of this and b from the larger and places the * result into this MutableBigInteger. @@ -1003,6 +1075,7 @@ private int difference(MutableBigInteger b) { /** * Multiply the contents of two MutableBigInteger objects. The result is * placed into MutableBigInteger z. The contents of y are not changed. + * Assume {@code intLen > 0} */ void multiply(MutableBigInteger y, MutableBigInteger z) { int xLen = intLen; @@ -1793,93 +1866,169 @@ private boolean unsignedLongCompare(long one, long two) { } /** - * Calculate the integer square root {@code floor(sqrt(this))} where - * {@code sqrt(.)} denotes the mathematical square root. The contents of - * {@code this} are not changed. The value of {@code this} is assumed - * to be non-negative. + * Calculate the integer square root {@code floor(sqrt(this))} and the remainder + * if needed, where {@code sqrt(.)} denotes the mathematical square root. + * The contents of {@code this} are not changed. + * The value of {@code this} is assumed to be non-negative. * - * @implNote The implementation is based on the material in Henry S. Warren, - * Jr., Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 279-282. - * - * @throws ArithmeticException if the value returned by {@code bitLength()} - * overflows the range of {@code int}. - * @return the integer square root of {@code this} - * @since 9 + * @return the integer square root of {@code this} and the remainder if needed */ - MutableBigInteger sqrt() { + MutableBigInteger[] sqrtRem(boolean needRemainder) { // Special cases. - if (this.isZero()) { - return new MutableBigInteger(0); - } else if (this.value.length == 1 - && (this.value[0] & LONG_MASK) < 4) { // result is unity - return ONE; - } - - if (bitLength() <= 63) { - // Initial estimate is the square root of the positive long value. - long v = new BigInteger(this.value, 1).longValueExact(); - long xk = (long)Math.floor(Math.sqrt(v)); - - // Refine the estimate. - do { - long xk1 = (xk + v/xk)/2; - - // Terminate when non-decreasing. - if (xk1 >= xk) { - return new MutableBigInteger(new int[] { - (int)(xk >>> 32), (int)(xk & LONG_MASK) - }); + if (this.intLen <= 2) { + final long x = this.toLong(); // unsigned + long s = unsignedLongSqrt(x); + + return new MutableBigInteger[] { + new MutableBigInteger((int) s), + needRemainder ? new MutableBigInteger(x - s * s) : null + }; + } + + // Normalize + MutableBigInteger x = this; + final int shift = (Integer.numberOfLeadingZeros(x.value[x.offset]) & ~1) // shift must be even + + ((x.intLen & 1) << 5); // x.intLen must be even + + if (shift != 0) { + x = new MutableBigInteger(x); + x.leftShift(shift); + } + + // Compute sqrt and remainder + MutableBigInteger[] sqrtRem = x.sqrtRemKaratsuba(x.intLen, needRemainder); + + // Unnormalize + if (shift != 0) { + final int halfShift = shift >> 1; + if (needRemainder) { + // shift <= 62, so s0 is at most 31 bit long + final long s0 = sqrtRem[0].value[sqrtRem[0].offset + sqrtRem[0].intLen - 1] + & (-1 >>> -halfShift); // Remove excess bits + if (s0 != 0L) { // An optimization + MutableBigInteger doubleProd = new MutableBigInteger(); + sqrtRem[0].mul((int) (s0 << 1), doubleProd); + + sqrtRem[1].add(doubleProd); + sqrtRem[1].subtract(new MutableBigInteger(s0 * s0)); } + sqrtRem[1].rightShift(shift); + } + sqrtRem[0].primitiveRightShift(halfShift); + } + return sqrtRem; + } - xk = xk1; - } while (true); - } else { - // Set up the initial estimate of the iteration. + private static long unsignedLongSqrt(long x) { + /* For every long value s in [0, 2^32) such that x == s * s, + * it is true that s - 1 <= (long) Math.sqrt(x >= 0 ? x : x + 0x1p64) <= s, + * and if x == 2^64 - 1, then (long) Math.sqrt(x >= 0 ? x : x + 0x1p64) == 2^32. + * Since both cast to long and `Math.sqrt()` are (weakly) increasing, + * this means that the value returned by Math.sqrt() + * for a long value in the range [0, 2^64) is either correct, + * or rounded up/down by one if the value is too high + * and too close to a perfect square. + */ + long s = (long) Math.sqrt(x >= 0 ? x : x + 0x1p64); + long s2 = s * s; // overflows iff s == 2^32 + return Long.compareUnsigned(x, s2) < 0 || s > LONG_MASK + ? s - 1 + : (Long.compareUnsigned(x, s2 + (s << 1)) <= 0 // x <= (s + 1)^2 - 1, does not overflow + ? s + : s + 1); + } - // Obtain the bitLength > 63. - int bitLength = (int) this.bitLength(); - if (bitLength != this.bitLength()) { - throw new ArithmeticException("bitLength() integer overflow"); - } + /** + * Assumes {@code 2 <= len <= intLen && len % 2 == 0 + * && Integer.numberOfLeadingZeros(value[offset]) <= 1} + * @implNote The implementation is based on Zimmermann's works available + * here and + * here + */ + private MutableBigInteger[] sqrtRemKaratsuba(int len, boolean needRemainder) { + if (len == 2) { // Base case + long x = ((value[offset] & LONG_MASK) << 32) | (value[offset + 1] & LONG_MASK); + long s = unsignedLongSqrt(x); - // Determine an even valued right shift into positive long range. - int shift = bitLength - 63; - if (shift % 2 == 1) { - shift++; - } + // Allocate sufficient space to hold the final square root, assuming intLen % 2 == 0 + MutableBigInteger sqrt = new MutableBigInteger(new int[intLen >> 1]); - // Shift the value into positive long range. - MutableBigInteger xk = new MutableBigInteger(this); - xk.rightShift(shift); - xk.normalize(); - - // Use the square root of the shifted value as an approximation. - double d = new BigInteger(xk.value, 1).doubleValue(); - BigInteger bi = BigInteger.valueOf((long)Math.ceil(Math.sqrt(d))); - xk = new MutableBigInteger(bi.mag); - - // Shift the approximate square root back into the original range. - xk.leftShift(shift / 2); - - // Refine the estimate. - MutableBigInteger xk1 = new MutableBigInteger(); - do { - // xk1 = (xk + n/xk)/2 - this.divide(xk, xk1, false); - xk1.add(xk); - xk1.rightShift(1); - - // Terminate when non-decreasing. - if (xk1.compare(xk) >= 0) { - return xk; - } + // Place the partial square root + sqrt.intLen = 1; + sqrt.value[0] = (int) s; + + return new MutableBigInteger[] { sqrt, new MutableBigInteger(x - s * s) }; + } - // xk = xk1 - xk.copyValue(xk1); + // Recursive step (len >= 4) - xk1.reset(); - } while (true); + final int halfLen = len >> 1; + // Recursive invocation + MutableBigInteger[] sr = sqrtRemKaratsuba(halfLen + (halfLen & 1), true); + + final int blockLen = halfLen >> 1; + MutableBigInteger dividend = sr[1]; + dividend.shiftAddDisjoint(getBlockForSqrt(1, len, blockLen), blockLen); + + // Compute dividend / (2*sqrt) + MutableBigInteger sqrt = sr[0]; + MutableBigInteger q = new MutableBigInteger(); + MutableBigInteger u = dividend.divide(sqrt, q); + if (q.isOdd()) + u.add(sqrt); + q.rightShift(1); + + sqrt.shiftAdd(q, blockLen); + // Corresponds to ub + a_0 in the paper + u.shiftAddDisjoint(getBlockForSqrt(0, len, blockLen), blockLen); + BigInteger qBig = q.toBigInteger(); // Cast to BigInteger to use fast multiplication + MutableBigInteger qSqr = new MutableBigInteger(qBig.multiply(qBig).mag); + + MutableBigInteger rem; + if (needRemainder) { + rem = u; + if (rem.subtract(qSqr) < 0) { + MutableBigInteger twiceSqrt = new MutableBigInteger(sqrt); + twiceSqrt.leftShift(1); + + // Since subtract() performs an absolute difference, to get the correct algebraic sum + // we must first add the sum of absolute values of addends concordant with the sign of rem + // and then subtract the sum of absolute values of addends that are discordant + rem.add(ONE); + rem.subtract(twiceSqrt); + sqrt.subtract(ONE); + } + } else { + rem = null; + if (u.compare(qSqr) < 0) + sqrt.subtract(ONE); } + + sr[1] = rem; + return sr; + } + + /** + * Returns a {@code MutableBigInteger} obtained by taking {@code blockLen} ints from + * {@code this} number, ending at {@code blockIndex*blockLen} (exclusive).
    + * Used in Karatsuba square root. + * @param blockIndex the block index, starting from the lowest + * @param len the logical length of the input value in units of 32 bits + * @param blockLen the length of the block in units of 32 bits + * + * @return a {@code MutableBigInteger} obtained by taking {@code blockLen} ints from + * {@code this} number, ending at {@code blockIndex*blockLen} (exclusive). + */ + private MutableBigInteger getBlockForSqrt(int blockIndex, int len, int blockLen) { + final int to = offset + len - blockIndex * blockLen; + + // Skip leading zeros + int from; + for (from = to - blockLen; from < to && value[from] == 0; from++); + + return from == to + ? new MutableBigInteger() + : new MutableBigInteger(Arrays.copyOfRange(value, from, to)); } /** diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index 7e0c39f9111ee..5ed8c73c919dd 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -142,12 +142,12 @@ * *

    * - * For IPv4 address format, please refer to Inet4Address#format; For IPv6 - * address format, please refer to Inet6Address#format. + * For IPv4 address format, please refer to the supported + * {@linkplain Inet4Address##format IPv4 address textual representations}; + * For IPv6 address format, please refer to the supported + * {@linkplain Inet6Address##format IPv6 address textual representations}. * - *

    There is a couple of + *

    There are a couple of * System Properties affecting how IPv4 and IPv6 addresses are used. * *

    Host Name Resolution

    diff --git a/src/java.base/share/classes/java/net/ServerSocket.java b/src/java.base/share/classes/java/net/ServerSocket.java index b3e570c858a62..bb0fae2b88d2b 100644 --- a/src/java.base/share/classes/java/net/ServerSocket.java +++ b/src/java.base/share/classes/java/net/ServerSocket.java @@ -328,8 +328,8 @@ private SocketImpl getImpl() throws SocketException { * an ephemeral port and a valid local address to bind the socket. * * @param endpoint The IP address and port number to bind to. - * @throws IOException if the bind operation fails, or if the socket - * is already bound. + * @throws IOException if the bind operation fails, the socket + * is already bound or the socket is closed. * @throws SecurityException if a {@code SecurityManager} is present and * its {@code checkListen} method doesn't allow the operation. * @throws IllegalArgumentException if endpoint is a @@ -357,8 +357,8 @@ public void bind(SocketAddress endpoint) throws IOException { * @param endpoint The IP address and port number to bind to. * @param backlog requested maximum length of the queue of * incoming connections. - * @throws IOException if the bind operation fails, or if the socket - * is already bound. + * @throws IOException if the bind operation fails, the socket + * is already bound or the socket is closed. * @throws SecurityException if a {@code SecurityManager} is present and * its {@code checkListen} method doesn't allow the operation. * @throws IllegalArgumentException if endpoint is a @@ -518,7 +518,7 @@ public SocketAddress getLocalSocketAddress() { * client socket implementation factory}, if one has been set. * * @throws IOException if an I/O error occurs when waiting for a - * connection. + * connection, the socket is not bound or the socket is closed. * @throws SecurityException if a security manager exists and its * {@code checkAccept} method doesn't allow the operation. * @throws SocketTimeoutException if a timeout was previously set with setSoTimeout and @@ -736,6 +736,9 @@ private void ensureCompatible(SocketImpl si) throws IOException { *

    If this socket has an associated channel then the channel is closed * as well. * + *

    Once closed, several of the methods defined by this class will throw + * an exception if invoked on the closed socket. + * * @throws IOException if an I/O error occurs when closing the socket. */ public void close() throws IOException { @@ -806,8 +809,8 @@ public boolean isClosed() { * operation to have effect. * * @param timeout the specified timeout, in milliseconds - * @throws SocketException if there is an error in the underlying protocol, - * such as a TCP error + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @throws IllegalArgumentException if {@code timeout} is negative * @since 1.1 * @see #getSoTimeout() @@ -824,7 +827,7 @@ public void setSoTimeout(int timeout) throws SocketException { * Retrieve setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}. * 0 returns implies that the option is disabled (i.e., timeout of infinity). * @return the {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} value - * @throws IOException if an I/O error occurs + * @throws IOException if an I/O error occurs or the socket is closed. * @since 1.1 * @see #setSoTimeout(int) */ @@ -887,8 +890,8 @@ public void setReuseAddress(boolean on) throws SocketException { * * @return a {@code boolean} indicating whether or not * {@code SO_REUSEADDR} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, such as a TCP error, + * or the socket is closed. * @since 1.4 * @see #setReuseAddress(boolean) */ @@ -1001,8 +1004,8 @@ public static synchronized void setSocketFactory(SocketImplFactory fac) throws I * requested value but the TCP receive window in sockets accepted from * this ServerSocket will be no larger than 64K bytes. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @param size the size to which to set the receive buffer * size. This value must be greater than 0. @@ -1029,8 +1032,8 @@ public void setReceiveBufferSize(int size) throws SocketException { *

    Note, the value actually set in the accepted socket is determined by * calling {@link Socket#getReceiveBufferSize()}. * @return the value of the {@code SO_RCVBUF} option for this {@code Socket}. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @see #setReceiveBufferSize(int) * @since 1.4 */ diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index 99a7bb6ca522e..23c225fbb2d29 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -683,7 +683,8 @@ void setConnected() { * * * @param endpoint the {@code SocketAddress} - * @throws IOException if an error occurs during the connection + * @throws IOException if an error occurs during the connection, the socket + * is already connected or the socket is closed * @throws java.nio.channels.IllegalBlockingModeException * if this socket has an associated channel, * and the channel is in non-blocking mode @@ -717,7 +718,8 @@ public void connect(SocketAddress endpoint) throws IOException { * * @param endpoint the {@code SocketAddress} * @param timeout the timeout value to be used in milliseconds. - * @throws IOException if an error occurs during the connection + * @throws IOException if an error occurs during the connection, the socket + * is already connected or the socket is closed * @throws SocketTimeoutException if timeout expires before connecting * @throws java.nio.channels.IllegalBlockingModeException * if this socket has an associated channel, @@ -780,8 +782,8 @@ public void connect(SocketAddress endpoint, int timeout) throws IOException { * an ephemeral port and a valid local address to bind the socket. * * @param bindpoint the {@code SocketAddress} to bind to - * @throws IOException if the bind operation fails, or if the socket - * is already bound. + * @throws IOException if the bind operation fails, the socket + * is already bound or the socket is closed. * @throws IllegalArgumentException if bindpoint is a * SocketAddress subclass not supported by this socket * @throws SecurityException if a security manager exists and its @@ -1174,8 +1176,8 @@ public void close() throws IOException { * will close the associated socket. * * @return an output stream for writing bytes to this socket. - * @throws IOException if an I/O error occurs when creating the - * output stream or if the socket is not connected. + * @throws IOException if an I/O error occurs when creating the + * output stream, the socket is not connected or the socket is closed. */ public OutputStream getOutputStream() throws IOException { int s = state; @@ -1251,8 +1253,8 @@ public void close() throws IOException { * @param on {@code true} to enable {@code TCP_NODELAY}, * {@code false} to disable. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @since 1.1 * @@ -1269,8 +1271,8 @@ public void setTcpNoDelay(boolean on) throws SocketException { * * @return a {@code boolean} indicating whether or not * {@code TCP_NODELAY} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.1 * @see #setTcpNoDelay(boolean) */ @@ -1289,9 +1291,9 @@ public boolean getTcpNoDelay() throws SocketException { * * @param on whether or not to linger on. * @param linger how long to linger for, if on is true. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. - * @throws IllegalArgumentException if the linger value is negative. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. + * @throws IllegalArgumentException if the linger value is negative. * @since 1.1 * @see #getSoLinger() */ @@ -1318,8 +1320,8 @@ public void setSoLinger(boolean on, int linger) throws SocketException { * The setting only affects socket close. * * @return the setting for {@code SO_LINGER}. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.1 * @see #setSoLinger(boolean, int) */ @@ -1368,8 +1370,8 @@ public void sendUrgentData(int data) throws IOException { * @param on {@code true} to enable {@code SO_OOBINLINE}, * {@code false} to disable. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @since 1.4 * @@ -1387,8 +1389,8 @@ public void setOOBInline(boolean on) throws SocketException { * @return a {@code boolean} indicating whether or not * {@code SO_OOBINLINE} is enabled. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.4 * @see #setOOBInline(boolean) */ @@ -1409,8 +1411,8 @@ public boolean getOOBInline() throws SocketException { * to have effect. * * @param timeout the specified timeout, in milliseconds. - * @throws SocketException if there is an error in the underlying protocol, - * such as a TCP error + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @throws IllegalArgumentException if {@code timeout} is negative * @since 1.1 * @see #getSoTimeout() @@ -1428,8 +1430,8 @@ public void setSoTimeout(int timeout) throws SocketException { * 0 returns implies that the option is disabled (i.e., timeout of infinity). * * @return the setting for {@code SO_TIMEOUT} - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @since 1.1 * @see #setSoTimeout(int) @@ -1455,14 +1457,12 @@ public int getSoTimeout() throws SocketException { *

    Because {@code SO_SNDBUF} is a hint, applications that want to verify * what size the buffers were set to should call {@link #getSendBufferSize()}. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. - * * @param size the size to which to set the send buffer * size. This value must be greater than 0. * - * @throws IllegalArgumentException if the - * value is 0 or is negative. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. + * @throws IllegalArgumentException if the value is 0 or is negative. * * @see #getSendBufferSize() * @since 1.2 @@ -1481,8 +1481,8 @@ public void setSendBufferSize(int size) throws SocketException { * for output on this {@code Socket}. * @return the value of the {@code SO_SNDBUF} option for this {@code Socket}. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @see #setSendBufferSize(int) * @since 1.2 @@ -1529,8 +1529,8 @@ public int getSendBufferSize() throws SocketException { * @throws IllegalArgumentException if the value is 0 or is * negative. * - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * * @see #getReceiveBufferSize() * @see ServerSocket#setReceiveBufferSize(int) @@ -1550,8 +1550,8 @@ public void setReceiveBufferSize(int size) throws SocketException { * for input on this {@code Socket}. * * @return the value of the {@code SO_RCVBUF} option for this {@code Socket}. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @see #setReceiveBufferSize(int) * @since 1.2 */ @@ -1570,8 +1570,8 @@ public int getReceiveBufferSize() throws SocketException { * Enable/disable {@link StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE}. * * @param on whether or not to have socket keep alive turned on. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.3 * @see #getKeepAlive() */ @@ -1586,8 +1586,8 @@ public void setKeepAlive(boolean on) throws SocketException { * * @return a {@code boolean} indicating whether or not * {@code SO_KEEPALIVE} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.3 * @see #setKeepAlive(boolean) */ @@ -1637,8 +1637,8 @@ public boolean getKeepAlive() throws SocketException { * would be placed into the sin6_flowinfo field of the IP header. * * @param tc an {@code int} value for the bitset. - * @throws SocketException if there is an error setting the - * traffic class or type-of-service + * @throws SocketException if there is an error setting the traffic class or type-of-service, + * or the socket is closed. * @since 1.4 * @see #getTrafficClass * @see StandardSocketOptions#IP_TOS @@ -1661,8 +1661,8 @@ public void setTrafficClass(int tc) throws SocketException { * set using the {@link #setTrafficClass(int)} method on this Socket. * * @return the traffic class or type-of-service already set - * @throws SocketException if there is an error obtaining the - * traffic class or type-of-service value. + * @throws SocketException if there is an error obtaining the traffic class + * or type-of-service value, or the socket is closed. * @since 1.4 * @see #setTrafficClass(int) * @see StandardSocketOptions#IP_TOS @@ -1715,8 +1715,8 @@ public void setReuseAddress(boolean on) throws SocketException { * * @return a {@code boolean} indicating whether or not * {@code SO_REUSEADDR} is enabled. - * @throws SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * @throws SocketException if there is an error in the underlying protocol, + * such as a TCP error, or the socket is closed. * @since 1.4 * @see #setReuseAddress(boolean) */ @@ -1733,8 +1733,9 @@ public boolean getReuseAddress() throws SocketException { * will throw a {@link SocketException}. *

    * Once a socket has been closed, it is not available for further networking - * use (i.e. can't be reconnected or rebound). A new socket needs to be - * created. + * use (i.e. can't be reconnected or rebound) and several of the methods defined + * by this class will throw an exception if invoked on the closed socket. A new + * socket needs to be created. * *

    Closing this socket will also close the socket's * {@link java.io.InputStream InputStream} and @@ -1767,8 +1768,8 @@ public void close() throws IOException { * socket, the stream's {@code available} method will return 0, and its * {@code read} methods will return {@code -1} (end of stream). * - * @throws IOException if an I/O error occurs when shutting down this - * socket. + * @throws IOException if an I/O error occurs when shutting down this socket, the + * socket is not connected or the socket is closed. * * @since 1.3 * @see java.net.Socket#shutdownOutput() @@ -1797,8 +1798,8 @@ public void shutdownInput() throws IOException { * shutdownOutput() on the socket, the stream will throw * an IOException. * - * @throws IOException if an I/O error occurs when shutting down this - * socket. + * @throws IOException if an I/O error occurs when shutting down this socket, the socket + * is not connected or the socket is closed. * * @since 1.3 * @see java.net.Socket#shutdownInput() diff --git a/src/java.base/share/classes/java/net/spi/InetAddressResolver.java b/src/java.base/share/classes/java/net/spi/InetAddressResolver.java index be931bc40e0cd..ee6eb50ae033d 100644 --- a/src/java.base/share/classes/java/net/spi/InetAddressResolver.java +++ b/src/java.base/share/classes/java/net/spi/InetAddressResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -92,7 +92,7 @@ public interface InetAddressResolver { * {@linkplain InetAddressResolver#lookupByName(String, LookupPolicy) looking up host addresses}. * *

    The default platform-wide lookup policy is constructed by consulting - * System Properties which affect + * System Properties which affect * how IPv4 and IPv6 addresses are returned. * * @since 18 diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index e8551bd78c6f7..6c0da82933160 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -56,14 +56,6 @@ class Direct$Type$Buffer$RW$$BO$ { #if[rw] - - // Cached unaligned-access capability - protected static final boolean UNALIGNED = Bits.unaligned(); - - // Base address, used in all indexing calculations - // NOTE: moved up to Buffer.java for speed in JNI GetDirectBufferAddress - // protected long address; - // An object attached to this buffer. If this buffer is a view of another // buffer then we use this field to keep a reference to that buffer to // ensure that its memory isn't freed before we are done with it. @@ -74,6 +66,8 @@ class Direct$Type$Buffer$RW$$BO$ } #if[byte] + // Cached unaligned-access capability + static final boolean UNALIGNED = Bits.unaligned(); private record Deallocator(long address, long size, int capacity) implements Runnable { private Deallocator { diff --git a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template index 8fbc1a3b69dfa..4820725d6c4ea 100644 --- a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template @@ -29,7 +29,6 @@ package java.nio; import java.lang.foreign.MemorySegment; import java.util.Objects; -import jdk.internal.util.ArraysSupport; /** #if[rw] @@ -706,9 +705,6 @@ class Heap$Type$Buffer$RW$ addr, segment))); } - public int hashCode() { - return ArraysSupport.hashCode(hb, ix(position()), remaining(), 1); - } #end[byte] @@ -737,9 +733,6 @@ class Heap$Type$Buffer$RW$ offset, segment); } - public int hashCode() { - return ArraysSupport.hashCode(hb, ix(position()), remaining(), 1); - } #end[char] diff --git a/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectableChannel.java b/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectableChannel.java index 558158cc852fb..4b342603caf63 100644 --- a/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectableChannel.java +++ b/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectableChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -137,6 +137,8 @@ private SelectionKey findKey(Selector sel) { void removeKey(SelectionKey k) { // package-private synchronized (keyLock) { + if (keys == null) + return; for (int i = 0; i < keys.length; i++) if (keys[i] == k) { keys[i] = null; @@ -233,7 +235,7 @@ public final SelectionKey register(Selector sel, int ops, Object att) k.interestOps(ops); } else { // New registration - k = ((AbstractSelector)sel).register(this, ops, att); + k = ((AbstractSelector) sel).register(this, ops, att); addKey(k); } return k; diff --git a/src/java.base/share/classes/java/text/CharacterIteratorFieldDelegate.java b/src/java.base/share/classes/java/text/CharacterIteratorFieldDelegate.java index 4d0788491acff..33b811e9ecdd5 100644 --- a/src/java.base/share/classes/java/text/CharacterIteratorFieldDelegate.java +++ b/src/java.base/share/classes/java/text/CharacterIteratorFieldDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ class CharacterIteratorFieldDelegate implements Format.FieldDelegate { } public void formatted(Format.Field attr, Object value, int start, int end, - StringBuffer buffer) { + Format.StringBuf buffer) { if (start != end) { if (start < size) { // Adjust attributes of existing runs @@ -93,7 +93,7 @@ public void formatted(Format.Field attr, Object value, int start, int end, } public void formatted(int fieldID, Format.Field attr, Object value, - int start, int end, StringBuffer buffer) { + int start, int end, Format.StringBuf buffer) { formatted(attr, value, start, end, buffer); } diff --git a/src/java.base/share/classes/java/text/ChoiceFormat.java b/src/java.base/share/classes/java/text/ChoiceFormat.java index ccb6cc4a04f47..cf951ebf81fda 100644 --- a/src/java.base/share/classes/java/text/ChoiceFormat.java +++ b/src/java.base/share/classes/java/text/ChoiceFormat.java @@ -514,7 +514,13 @@ public Object[] getFormats() { @Override public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition status) { - return format((double)number, toAppendTo, status); + return format((double) number, StringBufFactory.of(toAppendTo), status).asStringBuffer(); + } + + @Override + StringBuf format(long number, StringBuf toAppendTo, + FieldPosition status) { + return format((double) number, toAppendTo, status); } /** @@ -531,6 +537,12 @@ public StringBuffer format(long number, StringBuffer toAppendTo, @Override public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition status) { + return format(number, StringBufFactory.of(toAppendTo), status).asStringBuffer(); + } + + @Override + StringBuf format(double number, StringBuf toAppendTo, + FieldPosition status) { // find the number int i; for (i = 0; i < choiceLimits.length; ++i) { diff --git a/src/java.base/share/classes/java/text/CompactNumberFormat.java b/src/java.base/share/classes/java/text/CompactNumberFormat.java index 25fd9923b064a..0cb5eb88079e4 100644 --- a/src/java.base/share/classes/java/text/CompactNumberFormat.java +++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java @@ -539,29 +539,42 @@ public CompactNumberFormat(String decimalPattern, public final StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition fieldPosition) { + return switch (number) { + case Long l -> format(l.longValue(), toAppendTo, fieldPosition); + case Integer i -> format(i.longValue(), toAppendTo, fieldPosition); + case Short s -> format(s.longValue(), toAppendTo, fieldPosition); + case Byte b -> format(b.longValue(), toAppendTo, fieldPosition); + case AtomicInteger ai -> format(ai.longValue(), toAppendTo, fieldPosition); + case AtomicLong al -> format(al.longValue(), toAppendTo, fieldPosition); + case BigInteger bi when bi.bitLength() < 64 -> format(bi.longValue(), toAppendTo, fieldPosition); + case BigDecimal bd -> format(bd, StringBufFactory.of(toAppendTo), fieldPosition).asStringBuffer(); + case BigInteger bi -> format(bi, StringBufFactory.of(toAppendTo), fieldPosition).asStringBuffer(); + case Number n -> format(n.doubleValue(), toAppendTo, fieldPosition); + case null -> throw new IllegalArgumentException("Cannot format null as a number"); + default -> throw new IllegalArgumentException( + String.format("Cannot format %s as a number", number.getClass().getName())); + }; + } - if (number == null) { - throw new IllegalArgumentException("Cannot format null as a number"); - } - - if (number instanceof Long || number instanceof Integer - || number instanceof Short || number instanceof Byte - || number instanceof AtomicInteger - || number instanceof AtomicLong - || (number instanceof BigInteger - && ((BigInteger) number).bitLength() < 64)) { - return format(((Number) number).longValue(), toAppendTo, - fieldPosition); - } else if (number instanceof BigDecimal) { - return format((BigDecimal) number, toAppendTo, fieldPosition); - } else if (number instanceof BigInteger) { - return format((BigInteger) number, toAppendTo, fieldPosition); - } else if (number instanceof Number) { - return format(((Number) number).doubleValue(), toAppendTo, fieldPosition); - } else { - throw new IllegalArgumentException("Cannot format " - + number.getClass().getName() + " as a number"); - } + @Override + StringBuf format(Object number, + StringBuf toAppendTo, + FieldPosition fieldPosition) { + return switch (number) { + case Long l -> format(l.longValue(), toAppendTo, fieldPosition); + case Integer i -> format(i.longValue(), toAppendTo, fieldPosition); + case Short s -> format(s.longValue(), toAppendTo, fieldPosition); + case Byte b -> format(b.longValue(), toAppendTo, fieldPosition); + case AtomicInteger ai -> format(ai.longValue(), toAppendTo, fieldPosition); + case AtomicLong al -> format(al.longValue(), toAppendTo, fieldPosition); + case BigInteger bi when bi.bitLength() < 64 -> format(bi.longValue(), toAppendTo, fieldPosition); + case BigDecimal bd -> format(bd, toAppendTo, fieldPosition); + case BigInteger bi -> format(bi, toAppendTo, fieldPosition); + case Number n -> format(n.doubleValue(), toAppendTo, fieldPosition); + case null -> throw new IllegalArgumentException("Cannot format null as a number"); + default -> throw new IllegalArgumentException( + String.format("Cannot format %s as a number", number.getClass().getName())); + }; } /** @@ -591,12 +604,21 @@ public final StringBuffer format(Object number, public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) { + fieldPosition.setBeginIndex(0); + fieldPosition.setEndIndex(0); + return format(number, StringBufFactory.of(result), fieldPosition.getFieldDelegate()).asStringBuffer(); + } + + @Override + StringBuf format(double number, StringBuf result, + FieldPosition fieldPosition) { + fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); return format(number, result, fieldPosition.getFieldDelegate()); } - private StringBuffer format(double number, StringBuffer result, + private StringBuf format(double number, StringBuf result, FieldDelegate delegate) { boolean nanOrInfinity = decimalFormat.handleNaN(number, result, delegate); @@ -681,12 +703,21 @@ false, getMaximumIntegerDigits(), getMinimumIntegerDigits(), public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) { + fieldPosition.setBeginIndex(0); + fieldPosition.setEndIndex(0); + return format(number, StringBufFactory.of(result), fieldPosition.getFieldDelegate()).asStringBuffer(); + } + + @Override + StringBuf format(long number, StringBuf result, + FieldPosition fieldPosition) { + fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); return format(number, result, fieldPosition.getFieldDelegate()); } - private StringBuffer format(long number, StringBuffer result, FieldDelegate delegate) { + private StringBuf format(long number, StringBuf result, FieldDelegate delegate) { boolean isNegative = (number < 0); if (isNegative) { number = -number; @@ -757,15 +788,15 @@ isNegative, false, getMaximumIntegerDigits(), * of the prefix and the suffix fields can be * obtained using {@link NumberFormat.Field#PREFIX} * and {@link NumberFormat.Field#SUFFIX} respectively. - * @return the {@code StringBuffer} passed in as {@code result} + * @return the {@code StringBuf} passed in as {@code result} * @throws ArithmeticException if rounding is needed with rounding * mode being set to {@code RoundingMode.UNNECESSARY} * @throws NullPointerException if any of the given parameter * is {@code null} * @see FieldPosition */ - private StringBuffer format(BigDecimal number, StringBuffer result, - FieldPosition fieldPosition) { + private StringBuf format(BigDecimal number, StringBuf result, + FieldPosition fieldPosition) { Objects.requireNonNull(number); fieldPosition.setBeginIndex(0); @@ -773,7 +804,7 @@ private StringBuffer format(BigDecimal number, StringBuffer result, return format(number, result, fieldPosition.getFieldDelegate()); } - private StringBuffer format(BigDecimal number, StringBuffer result, + private StringBuf format(BigDecimal number, StringBuf result, FieldDelegate delegate) { boolean isNegative = number.signum() == -1; @@ -843,15 +874,15 @@ false, getMaximumIntegerDigits(), getMinimumIntegerDigits(), * prefix and the suffix fields can be obtained * using {@link NumberFormat.Field#PREFIX} and * {@link NumberFormat.Field#SUFFIX} respectively. - * @return the {@code StringBuffer} passed in as {@code result} + * @return the {@code StringBuf} passed in as {@code result} * @throws ArithmeticException if rounding is needed with rounding * mode being set to {@code RoundingMode.UNNECESSARY} * @throws NullPointerException if any of the given parameter * is {@code null} * @see FieldPosition */ - private StringBuffer format(BigInteger number, StringBuffer result, - FieldPosition fieldPosition) { + private StringBuf format(BigInteger number, StringBuf result, + FieldPosition fieldPosition) { Objects.requireNonNull(number); fieldPosition.setBeginIndex(0); @@ -859,7 +890,7 @@ private StringBuffer format(BigInteger number, StringBuffer result, return format(number, result, fieldPosition.getFieldDelegate(), false); } - private StringBuffer format(BigInteger number, StringBuffer result, + private StringBuf format(BigInteger number, StringBuf result, FieldDelegate delegate, boolean formatLong) { boolean isNegative = number.signum() == -1; @@ -936,7 +967,7 @@ private String getAffix(boolean isExpanded, boolean isPrefix, boolean isNegative * {@code NumberFormat.Field.SIGN} and * {@code NumberFormat.Field.PREFIX} fields */ - private void appendPrefix(StringBuffer result, String prefix, + private void appendPrefix(StringBuf result, String prefix, FieldDelegate delegate) { append(result, expandAffix(prefix), delegate, getFieldPositions(prefix, NumberFormat.Field.PREFIX)); @@ -952,7 +983,7 @@ private void appendPrefix(StringBuffer result, String prefix, * {@code NumberFormat.Field.SIGN} and * {@code NumberFormat.Field.SUFFIX} fields */ - private void appendSuffix(StringBuffer result, String suffix, + private void appendSuffix(StringBuf result, String suffix, FieldDelegate delegate) { append(result, expandAffix(suffix), delegate, getFieldPositions(suffix, NumberFormat.Field.SUFFIX)); @@ -968,7 +999,7 @@ private void appendSuffix(StringBuffer result, String suffix, * @param positions a list of {@code FieldPosition} in the given * string */ - private void append(StringBuffer result, String string, + private void append(StringBuf result, String string, FieldDelegate delegate, List positions) { if (!string.isEmpty()) { int start = result.length(); @@ -1134,23 +1165,21 @@ private int selectCompactPattern(BigInteger number) { public AttributedCharacterIterator formatToCharacterIterator(Object obj) { CharacterIteratorFieldDelegate delegate = new CharacterIteratorFieldDelegate(); - StringBuffer sb = new StringBuffer(); - - if (obj instanceof Double || obj instanceof Float) { - format(((Number) obj).doubleValue(), sb, delegate); - } else if (obj instanceof Long || obj instanceof Integer - || obj instanceof Short || obj instanceof Byte - || obj instanceof AtomicInteger || obj instanceof AtomicLong) { - format(((Number) obj).longValue(), sb, delegate); - } else if (obj instanceof BigDecimal) { - format((BigDecimal) obj, sb, delegate); - } else if (obj instanceof BigInteger) { - format((BigInteger) obj, sb, delegate, false); - } else if (obj == null) { - throw new NullPointerException( + StringBuf sb = StringBufFactory.of(); + switch (obj) { + case Double d -> format(d.doubleValue(), sb, delegate); + case Float f -> format(f.doubleValue(), sb, delegate); + case Long l -> format(l.longValue(), sb, delegate); + case Integer i -> format(i.longValue(), sb, delegate); + case Short s -> format(s.longValue(), sb, delegate); + case Byte b -> format(b.longValue(), sb, delegate); + case AtomicInteger ai -> format(ai.longValue(), sb, delegate); + case AtomicLong al -> format(al.longValue(), sb, delegate); + case BigDecimal bd -> format(bd, sb, delegate); + case BigInteger bi -> format(bi, sb, delegate, false); + case null -> throw new NullPointerException( "formatToCharacterIterator must be passed non-null object"); - } else { - throw new IllegalArgumentException( + default -> throw new IllegalArgumentException( "Cannot format given Object as a Number"); } return delegate.getIterator(sb.toString()); diff --git a/src/java.base/share/classes/java/text/DateFormat.java b/src/java.base/share/classes/java/text/DateFormat.java index a67ba6768366d..e29b3f0509f80 100644 --- a/src/java.base/share/classes/java/text/DateFormat.java +++ b/src/java.base/share/classes/java/text/DateFormat.java @@ -346,6 +346,19 @@ else if (obj instanceof Number) throw new IllegalArgumentException("Cannot format given Object as a Date"); } + @Override + final StringBuf format(Object obj, StringBuf toAppendTo, + FieldPosition fieldPosition) { + if (obj instanceof Date) { + return format((Date) obj, toAppendTo, fieldPosition); + } else if (obj instanceof Number) { + return format(new Date(((Number) obj).longValue()), + toAppendTo, fieldPosition); + } else { + throw new IllegalArgumentException("Cannot format given Object as a Date"); + } + } + /** * Formats a {@link Date} into a date-time string. The formatted * string is appended to the given {@code StringBuffer}. @@ -371,6 +384,11 @@ else if (obj instanceof Number) public abstract StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition); + StringBuf format(Date date, StringBuf toAppendTo, + FieldPosition fieldPosition) { + throw new UnsupportedOperationException("Subclasses should override this method"); + } + /** * Formats a {@link Date} into a date-time string. * @@ -379,8 +397,14 @@ public abstract StringBuffer format(Date date, StringBuffer toAppendTo, */ public final String format(Date date) { - return format(date, new StringBuffer(), - DontCareFieldPosition.INSTANCE).toString(); + if ("java.text".equals(getClass().getPackageName()) + && "java.text".equals(numberFormat.getClass().getPackageName())) { + return format(date, StringBufFactory.of(), + DontCareFieldPosition.INSTANCE).toString(); + } else { + return format(date, new StringBuffer(), + DontCareFieldPosition.INSTANCE).toString(); + } } /** diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index 04acca1ceb51d..c04abbd042f57 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -430,16 +430,15 @@ public class DecimalFormat extends NumberFormat { * for the default {@link java.util.Locale.Category#FORMAT FORMAT} locale. * This is a convenient way to obtain a * DecimalFormat when internationalization is not the main concern. - *

    - * To obtain standard formats for a given locale, use the factory methods - * on NumberFormat such as getNumberInstance. These factories will - * return the most appropriate sub-class of NumberFormat for a given - * locale. * - * @see java.text.NumberFormat#getInstance - * @see java.text.NumberFormat#getNumberInstance - * @see java.text.NumberFormat#getCurrencyInstance - * @see java.text.NumberFormat#getPercentInstance + * @apiNote To obtain standard formats for a given locale, use the + * {@code NumberFormat} factory methods such as {@link + * NumberFormat#getNumberInstance(Locale)}. These factories will return the most + * appropriate subclass of NumberFormat for a given locale. + * @see NumberFormat#getInstance(Locale) + * @see NumberFormat#getNumberInstance(Locale) + * @see NumberFormat#getCurrencyInstance(Locale) + * @see NumberFormat#getPercentInstance(Locale) */ @SuppressWarnings("this-escape") public DecimalFormat() { @@ -464,19 +463,18 @@ public DecimalFormat() { * DecimalFormat when internationalization is not the main concern. * The number of maximum integer digits is usually not derived from the pattern. * See the note in the {@link ##patterns Patterns} section for more detail. - *

    - * To obtain standard formats for a given locale, use the factory methods - * on NumberFormat such as getNumberInstance. These factories will - * return the most appropriate sub-class of NumberFormat for a given - * locale. * + * @apiNote To obtain standard formats for a given locale, use the + * {@code NumberFormat} factory methods such as {@link + * NumberFormat#getNumberInstance(Locale)}. These factories will return the most + * appropriate subclass of NumberFormat for a given locale. * @param pattern a non-localized pattern string. * @throws NullPointerException if {@code pattern} is null * @throws IllegalArgumentException if the given pattern is invalid. - * @see java.text.NumberFormat#getInstance - * @see java.text.NumberFormat#getNumberInstance - * @see java.text.NumberFormat#getCurrencyInstance - * @see java.text.NumberFormat#getPercentInstance + * @see NumberFormat#getInstance(Locale) + * @see NumberFormat#getNumberInstance(Locale) + * @see NumberFormat#getCurrencyInstance(Locale) + * @see NumberFormat#getPercentInstance(Locale) */ @SuppressWarnings("this-escape") public DecimalFormat(String pattern) { @@ -492,21 +490,20 @@ public DecimalFormat(String pattern) { * behavior of the format. * The number of maximum integer digits is usually not derived from the pattern. * See the note in the {@link ##patterns Patterns} section for more detail. - *

    - * To obtain standard formats for a given - * locale, use the factory methods on NumberFormat such as - * getInstance or getCurrencyInstance. If you need only minor adjustments - * to a standard format, you can modify the format returned by - * a NumberFormat factory method. * + * @apiNote To obtain standard formats for a given locale, use the + * {@code NumberFormat} factory methods such as {@link + * NumberFormat#getInstance(Locale)} or {@link NumberFormat#getCurrencyInstance(Locale)}. + * If you need only minor adjustments to a standard format, you can modify + * the format returned by a NumberFormat factory method. * @param pattern a non-localized pattern string * @param symbols the set of symbols to be used * @throws NullPointerException if any of the given arguments is null * @throws IllegalArgumentException if the given pattern is invalid - * @see java.text.NumberFormat#getInstance - * @see java.text.NumberFormat#getNumberInstance - * @see java.text.NumberFormat#getCurrencyInstance - * @see java.text.NumberFormat#getPercentInstance + * @see NumberFormat#getInstance(Locale) + * @see NumberFormat#getNumberInstance(Locale) + * @see NumberFormat#getCurrencyInstance(Locale) + * @see NumberFormat#getPercentInstance(Locale) * @see java.text.DecimalFormatSymbols */ @SuppressWarnings("this-escape") @@ -522,8 +519,8 @@ public DecimalFormat (String pattern, DecimalFormatSymbols symbols) { * Formats a number and appends the resulting text to the given string * buffer. * The number can be of any subclass of {@link java.lang.Number}. - *

    - * This implementation uses the maximum precision permitted. + * + * @implSpec This implementation uses the maximum precision permitted. * @param number the number to format * @param toAppendTo the {@code StringBuffer} to which the formatted * text is to be appended @@ -548,22 +545,38 @@ public DecimalFormat (String pattern, DecimalFormatSymbols symbols) { public final StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) { - if (number instanceof Long || number instanceof Integer || - number instanceof Short || number instanceof Byte || - number instanceof AtomicInteger || - number instanceof AtomicLong || - (number instanceof BigInteger && - ((BigInteger)number).bitLength () < 64)) { - return format(((Number)number).longValue(), toAppendTo, pos); - } else if (number instanceof BigDecimal) { - return format((BigDecimal)number, toAppendTo, pos); - } else if (number instanceof BigInteger) { - return format((BigInteger)number, toAppendTo, pos); - } else if (number instanceof Number) { - return format(((Number)number).doubleValue(), toAppendTo, pos); - } else { - throw new IllegalArgumentException("Cannot format given Object as a Number"); - } + return switch (number) { + case Long l -> format(l.longValue(), toAppendTo, pos); + case Integer i -> format(i.longValue(), toAppendTo, pos); + case Short s -> format(s.longValue(), toAppendTo, pos); + case Byte b -> format(b.longValue(), toAppendTo, pos); + case AtomicInteger ai -> format(ai.longValue(), toAppendTo, pos); + case AtomicLong al -> format(al.longValue(), toAppendTo, pos); + case BigInteger bi when bi.bitLength() < 64 -> format(bi.longValue(), toAppendTo, pos); + case BigDecimal bd -> format(bd, StringBufFactory.of(toAppendTo), pos).asStringBuffer(); + case BigInteger bi -> format(bi, StringBufFactory.of(toAppendTo), pos).asStringBuffer(); + case Number n -> format(n.doubleValue(), toAppendTo, pos); + case null, default -> throw new IllegalArgumentException("Cannot format given Object as a Number"); + }; + } + + @Override + final StringBuf format(Object number, + StringBuf toAppendTo, + FieldPosition pos) { + return switch (number) { + case Long l -> format(l.longValue(), toAppendTo, pos); + case Integer i -> format(i.longValue(), toAppendTo, pos); + case Short s -> format(s.longValue(), toAppendTo, pos); + case Byte b -> format(b.longValue(), toAppendTo, pos); + case AtomicInteger ai -> format(ai.longValue(), toAppendTo, pos); + case AtomicLong al -> format(al.longValue(), toAppendTo, pos); + case BigInteger bi when bi.bitLength() < 64 -> format(bi.longValue(), toAppendTo, pos); + case BigDecimal bd -> format(bd, toAppendTo, pos); + case BigInteger bi -> format(bi, toAppendTo, pos); + case Number n -> format(n.doubleValue(), toAppendTo, pos); + case null, default -> throw new IllegalArgumentException("Cannot format given Object as a Number"); + }; } /** @@ -588,6 +601,12 @@ public final StringBuffer format(Object number, @Override public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) { + return format(number, StringBufFactory.of(result), fieldPosition).asStringBuffer(); + } + + @Override + StringBuf format(double number, StringBuf result, + FieldPosition fieldPosition) { // If fieldPosition is a DontCareFieldPosition instance we can // try to go to fast-path code. boolean tryFastPath = false; @@ -619,8 +638,8 @@ public StringBuffer format(double number, StringBuffer result, * mode being set to RoundingMode.UNNECESSARY * @return The formatted number string */ - StringBuffer format(double number, StringBuffer result, - FieldDelegate delegate) { + StringBuf format(double number, StringBuf result, + FieldDelegate delegate) { boolean nanOrInfinity = handleNaN(number, result, delegate); if (nanOrInfinity) { @@ -666,7 +685,7 @@ StringBuffer format(double number, StringBuffer result, * @param delegate notified of locations of sub fields * @return true, if number is a NaN; false otherwise */ - boolean handleNaN(double number, StringBuffer result, + boolean handleNaN(double number, StringBuf result, FieldDelegate delegate) { if (Double.isNaN(number) || (Double.isInfinite(number) && multiplier == 0)) { @@ -691,7 +710,7 @@ boolean handleNaN(double number, StringBuffer result, * @return true, if number is a {@code Double.NEGATIVE_INFINITY} or * {@code Double.POSITIVE_INFINITY}; false otherwise */ - boolean handleInfinity(double number, StringBuffer result, + boolean handleInfinity(double number, StringBuf result, FieldDelegate delegate, boolean isNegative) { if (Double.isInfinite(number)) { if (isNegative) { @@ -720,7 +739,7 @@ boolean handleInfinity(double number, StringBuffer result, return false; } - StringBuffer doubleSubformat(double number, StringBuffer result, + StringBuf doubleSubformat(double number, StringBuf result, FieldDelegate delegate, boolean isNegative) { synchronized (digitList) { int maxIntDigits = super.getMaximumIntegerDigits(); @@ -761,6 +780,14 @@ public StringBuffer format(long number, StringBuffer result, fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); + return format(number, StringBufFactory.of(result), fieldPosition.getFieldDelegate()).asStringBuffer(); + } + + StringBuf format(long number, StringBuf result, + FieldPosition fieldPosition) { + fieldPosition.setBeginIndex(0); + fieldPosition.setEndIndex(0); + return format(number, result, fieldPosition.getFieldDelegate()); } @@ -774,8 +801,8 @@ public StringBuffer format(long number, StringBuffer result, * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ - StringBuffer format(long number, StringBuffer result, - FieldDelegate delegate) { + StringBuf format(long number, StringBuf result, + FieldDelegate delegate) { boolean isNegative = (number < 0); if (isNegative) { number = -number; @@ -849,8 +876,8 @@ StringBuffer format(long number, StringBuffer result, * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ - private StringBuffer format(BigDecimal number, StringBuffer result, - FieldPosition fieldPosition) { + private StringBuf format(BigDecimal number, StringBuf result, + FieldPosition fieldPosition) { fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); return format(number, result, fieldPosition.getFieldDelegate()); @@ -865,8 +892,8 @@ private StringBuffer format(BigDecimal number, StringBuffer result, * mode being set to RoundingMode.UNNECESSARY * @return The formatted number string */ - StringBuffer format(BigDecimal number, StringBuffer result, - FieldDelegate delegate) { + StringBuf format(BigDecimal number, StringBuf result, + FieldDelegate delegate) { if (multiplier != 1) { number = number.multiply(getBigDecimalMultiplier()); } @@ -908,8 +935,8 @@ StringBuffer format(BigDecimal number, StringBuffer result, * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ - private StringBuffer format(BigInteger number, StringBuffer result, - FieldPosition fieldPosition) { + private StringBuf format(BigInteger number, StringBuf result, + FieldPosition fieldPosition) { fieldPosition.setBeginIndex(0); fieldPosition.setEndIndex(0); @@ -926,8 +953,8 @@ private StringBuffer format(BigInteger number, StringBuffer result, * mode being set to RoundingMode.UNNECESSARY * @see java.text.FieldPosition */ - StringBuffer format(BigInteger number, StringBuffer result, - FieldDelegate delegate, boolean formatLong) { + StringBuf format(BigInteger number, StringBuf result, + FieldDelegate delegate, boolean formatLong) { if (multiplier != 1) { number = number.multiply(getBigIntegerMultiplier()); } @@ -985,25 +1012,23 @@ StringBuffer format(BigInteger number, StringBuffer result, @Override public AttributedCharacterIterator formatToCharacterIterator(Object obj) { CharacterIteratorFieldDelegate delegate = - new CharacterIteratorFieldDelegate(); - StringBuffer sb = new StringBuffer(); - - if (obj instanceof Double || obj instanceof Float) { - format(((Number)obj).doubleValue(), sb, delegate); - } else if (obj instanceof Long || obj instanceof Integer || - obj instanceof Short || obj instanceof Byte || - obj instanceof AtomicInteger || obj instanceof AtomicLong) { - format(((Number)obj).longValue(), sb, delegate); - } else if (obj instanceof BigDecimal) { - format((BigDecimal)obj, sb, delegate); - } else if (obj instanceof BigInteger) { - format((BigInteger)obj, sb, delegate, false); - } else if (obj == null) { - throw new NullPointerException( - "formatToCharacterIterator must be passed non-null object"); - } else { - throw new IllegalArgumentException( - "Cannot format given Object as a Number"); + new CharacterIteratorFieldDelegate(); + StringBuf sb = StringBufFactory.of(); + switch (obj) { + case Double d -> format(d.doubleValue(), sb, delegate); + case Float f -> format(f.doubleValue(), sb, delegate); + case Long l -> format(l.longValue(), sb, delegate); + case Integer i -> format(i.longValue(), sb, delegate); + case Short s -> format(s.longValue(), sb, delegate); + case Byte b -> format(b.longValue(), sb, delegate); + case AtomicInteger ai -> format(ai.longValue(), sb, delegate); + case AtomicLong al -> format(al.longValue(), sb, delegate); + case BigDecimal bd -> format(bd, sb, delegate); + case BigInteger bi -> format(bi, sb, delegate, false); + case null -> throw new NullPointerException( + "formatToCharacterIterator must be passed non-null object"); + default -> throw new IllegalArgumentException( + "Cannot format given Object as a Number"); } return delegate.getIterator(sb.toString()); } @@ -1754,22 +1779,23 @@ String fastFormat(double d) { } /** - * Sets the {@code DigitList} used by this {@code DecimalFormat} + * Utility method that sets the {@code DigitList} used by this {@code DecimalFormat} * instance. + * * @param number the number to format * @param isNegative true, if the number is negative; false otherwise * @param maxDigits the max digits + * @throws AssertionError if provided a Number subclass that is not supported + * by {@code DigitList} */ void setDigitList(Number number, boolean isNegative, int maxDigits) { - - if (number instanceof Double) { - digitList.set(isNegative, (Double) number, maxDigits, true); - } else if (number instanceof BigDecimal) { - digitList.set(isNegative, (BigDecimal) number, maxDigits, true); - } else if (number instanceof Long) { - digitList.set(isNegative, (Long) number, maxDigits); - } else if (number instanceof BigInteger) { - digitList.set(isNegative, (BigInteger) number, maxDigits); + switch (number) { + case Double d -> digitList.set(isNegative, d, maxDigits, true); + case BigDecimal bd -> digitList.set(isNegative, bd, maxDigits, true); + case Long l -> digitList.set(isNegative, l, maxDigits); + case BigInteger bi -> digitList.set(isNegative, bi, maxDigits); + default -> throw new AssertionError( + String.format("DigitList does not support %s", number.getClass().getName())); } } @@ -1779,7 +1805,7 @@ void setDigitList(Number number, boolean isNegative, int maxDigits) { * Complete the formatting of a finite number. On entry, the digitList must * be filled in with the correct digits. */ - private StringBuffer subformat(StringBuffer result, FieldDelegate delegate, + private StringBuf subformat(StringBuf result, FieldDelegate delegate, boolean isNegative, boolean isInteger, int maxIntDigits, int minIntDigits, int maxFraDigits, int minFraDigits) { @@ -1821,7 +1847,7 @@ private StringBuffer subformat(StringBuffer result, FieldDelegate delegate, * @param maxFraDigits maximum fraction digits * @param minFraDigits minimum fraction digits */ - void subformatNumber(StringBuffer result, FieldDelegate delegate, + void subformatNumber(StringBuf result, FieldDelegate delegate, boolean isNegative, boolean isInteger, int maxIntDigits, int minIntDigits, int maxFraDigits, int minFraDigits) { @@ -2108,7 +2134,7 @@ void subformatNumber(StringBuffer result, FieldDelegate delegate, *

    * This is used by {@code subformat} to add the prefix/suffix. */ - private void append(StringBuffer result, String string, + private void append(StringBuf result, String string, FieldDelegate delegate, FieldPosition[] positions, Format.Field signAttribute) { diff --git a/src/java.base/share/classes/java/text/DontCareFieldPosition.java b/src/java.base/share/classes/java/text/DontCareFieldPosition.java index 33c086d370bef..3117c67e78619 100644 --- a/src/java.base/share/classes/java/text/DontCareFieldPosition.java +++ b/src/java.base/share/classes/java/text/DontCareFieldPosition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,10 @@ class DontCareFieldPosition extends FieldPosition { private final Format.FieldDelegate noDelegate = new Format.FieldDelegate() { public void formatted(Format.Field attr, Object value, int start, - int end, StringBuffer buffer) { + int end, Format.StringBuf buffer) { } public void formatted(int fieldID, Format.Field attr, Object value, - int start, int end, StringBuffer buffer) { + int start, int end, Format.StringBuf buffer) { } }; diff --git a/src/java.base/share/classes/java/text/FieldPosition.java b/src/java.base/share/classes/java/text/FieldPosition.java index 7b9d23bafcbc5..92d0e9a2e6d3f 100644 --- a/src/java.base/share/classes/java/text/FieldPosition.java +++ b/src/java.base/share/classes/java/text/FieldPosition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -290,7 +290,7 @@ private class Delegate implements Format.FieldDelegate { private boolean encounteredField; public void formatted(Format.Field attr, Object value, int start, - int end, StringBuffer buffer) { + int end, Format.StringBuf buffer) { if (!encounteredField && matchesField(attr)) { setBeginIndex(start); setEndIndex(end); @@ -299,7 +299,7 @@ public void formatted(Format.Field attr, Object value, int start, } public void formatted(int fieldID, Format.Field attr, Object value, - int start, int end, StringBuffer buffer) { + int start, int end, Format.StringBuf buffer) { if (!encounteredField && matchesField(attr, fieldID)) { setBeginIndex(start); setEndIndex(end); diff --git a/src/java.base/share/classes/java/text/Format.java b/src/java.base/share/classes/java/text/Format.java index 81efc27acf4d2..9926bbfbb1507 100644 --- a/src/java.base/share/classes/java/text/Format.java +++ b/src/java.base/share/classes/java/text/Format.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -148,19 +148,24 @@ protected Format() { } /** - * Formats an object to produce a string. This is equivalent to + * Formats an object to produce a string. + * + * @implSpec This method returns a string that would be equal to the string returned by *

    * {@link #format(Object, StringBuffer, FieldPosition) format}(obj, * new StringBuffer(), new FieldPosition(0)).toString(); *
    - * * @param obj The object to format * @return Formatted string. * @throws IllegalArgumentException if the Format cannot format the given * object */ public final String format (Object obj) { - return format(obj, new StringBuffer(), new FieldPosition(0)).toString(); + if ("java.text".equals(getClass().getPackageName())) { + return format(obj, StringBufFactory.of(), new FieldPosition(0)).toString(); + } else { + return format(obj, new StringBuffer(), new FieldPosition(0)).toString(); + } } /** @@ -185,6 +190,12 @@ public abstract StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos); + StringBuf format(Object obj, + StringBuf toAppendTo, + FieldPosition pos) { + throw new UnsupportedOperationException("Subclasses should override this method"); + } + /** * Formats an Object producing an {@code AttributedCharacterIterator}. * You can use the returned {@code AttributedCharacterIterator} @@ -196,11 +207,11 @@ public abstract StringBuffer format(Object obj, * to define what the legal values are for each attribute in the * {@code AttributedCharacterIterator}, but typically the attribute * key is also used as the attribute value. - *

    The default implementation creates an - * {@code AttributedCharacterIterator} with no attributes. Subclasses - * that support fields should override this and create an - * {@code AttributedCharacterIterator} with meaningful attributes. * + * @apiNote Subclasses that support fields should override this and create an + * {@code AttributedCharacterIterator} with meaningful attributes. + * @implSpec The default implementation creates an + * {@code AttributedCharacterIterator} with no attributes. * @throws NullPointerException if obj is null. * @throws IllegalArgumentException when the Format cannot format the * given object. @@ -394,7 +405,7 @@ interface FieldDelegate { * NOT modify it. */ public void formatted(Format.Field attr, Object value, int start, - int end, StringBuffer buffer); + int end, StringBuf buffer); /** * Notified when a particular region of the String is formatted. @@ -408,6 +419,38 @@ public void formatted(Format.Field attr, Object value, int start, * NOT modify it. */ public void formatted(int fieldID, Format.Field attr, Object value, - int start, int end, StringBuffer buffer); + int start, int end, StringBuf buffer); + } + + /** + * StringBuf is the minimal common interface of {@code StringBuffer} and {@code StringBuilder}. + * It is used by the various {@code Format} implementations as the internal string buffer. + */ + sealed interface StringBuf + permits StringBufFactory.StringBufferImpl, StringBufFactory.StringBuilderImpl { + + int length(); + + String substring(int start, int end); + + String substring(int start); + + StringBuf append(char c); + + StringBuf append(String str); + + StringBuf append(int i); + + StringBuf append(char[] str, int offset, int len); + + StringBuf append(CharSequence s, int start, int end); + + StringBuf append(StringBuffer sb); + + boolean isProxyStringBuilder(); + + StringBuffer asStringBuffer(); + + StringBuilder asStringBuilder(); } } diff --git a/src/java.base/share/classes/java/text/ListFormat.java b/src/java.base/share/classes/java/text/ListFormat.java index bc155605934f5..5cd0e9e365126 100644 --- a/src/java.base/share/classes/java/text/ListFormat.java +++ b/src/java.base/share/classes/java/text/ListFormat.java @@ -356,7 +356,7 @@ public String[] getPatterns() { public String format(List input) { Objects.requireNonNull(input); - return format(input, new StringBuffer(), + return format(input, StringBufFactory.of(), DontCareFieldPosition.INSTANCE).toString(); } @@ -381,6 +381,18 @@ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition po Objects.requireNonNull(obj); Objects.requireNonNull(toAppendTo); + return format(obj, StringBufFactory.of(toAppendTo)).asStringBuffer(); + } + + @Override + StringBuf format(Object obj, StringBuf toAppendTo, FieldPosition pos) { + Objects.requireNonNull(obj); + Objects.requireNonNull(toAppendTo); + + return format(obj, toAppendTo); + } + + private StringBuf format(Object obj, StringBuf toAppendTo) { if (obj instanceof Object[] objs) { return generateMessageFormat(objs).format(objs, toAppendTo, DontCareFieldPosition.INSTANCE); } else if (obj instanceof List objs) { diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index 8f939f8af2669..b2dd7a51251ae 100644 --- a/src/java.base/share/classes/java/text/MessageFormat.java +++ b/src/java.base/share/classes/java/text/MessageFormat.java @@ -367,12 +367,11 @@ * *

    Usage Information

    * - *

    + * * The following example demonstrates a general usage of {@code MessageFormat}. * In internationalized programs, the message format pattern and other * static strings will likely be obtained from resource bundles. * - *

    * {@snippet lang=java : * int planet = 7; * String event = "a disturbance in the Force"; @@ -1027,12 +1026,13 @@ public Format[] getFormats() { public final StringBuffer format(Object[] arguments, StringBuffer result, FieldPosition pos) { - return subformat(arguments, result, pos, null); + return subformat(arguments, StringBufFactory.of(result), pos, null).asStringBuffer(); } /** * Creates a MessageFormat with the given pattern and uses it - * to format the given arguments. This is equivalent to + * to format the given arguments. + * This method returns a string that would be equal to the string returned by *

    * (new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString() *
    @@ -1076,6 +1076,12 @@ public static String format(String pattern, Object ... arguments) { public final StringBuffer format(Object arguments, StringBuffer result, FieldPosition pos) { + return subformat((Object[]) arguments, StringBufFactory.of(result), pos, null).asStringBuffer(); + } + + @Override + final StringBuf format(Object arguments, StringBuf result, + FieldPosition pos) { return subformat((Object[]) arguments, result, pos, null); } @@ -1116,7 +1122,7 @@ public final StringBuffer format(Object arguments, StringBuffer result, */ public AttributedCharacterIterator formatToCharacterIterator(Object arguments) { Objects.requireNonNull(arguments, "arguments must not be null"); - StringBuffer result = new StringBuffer(); + StringBuf result = StringBufFactory.of(); ArrayList iterators = new ArrayList<>(); subformat((Object[]) arguments, result, null, iterators); @@ -1472,7 +1478,7 @@ protected Object readResolve() throws InvalidObjectException { * {@code arguments} array is not of the type * expected by the format element(s) that use it. */ - private StringBuffer subformat(Object[] arguments, StringBuffer result, + private StringBuf subformat(Object[] arguments, StringBuf result, FieldPosition fp, List characterIterators) { // note: this implementation assumes a fast substring & index. // if this is not true, would be better to append chars one by one. @@ -1582,9 +1588,9 @@ private StringBuffer subformat(Object[] arguments, StringBuffer result, /** * Convenience method to append all the characters in - * {@code iterator} to the StringBuffer {@code result}. + * {@code iterator} to the StringBuf {@code result}. */ - private void append(StringBuffer result, CharacterIterator iterator) { + private void append(StringBuf result, CharacterIterator iterator) { if (iterator.first() != CharacterIterator.DONE) { char aChar; diff --git a/src/java.base/share/classes/java/text/NumberFormat.java b/src/java.base/share/classes/java/text/NumberFormat.java index 28f116042e9da..e397d4ba15d9d 100644 --- a/src/java.base/share/classes/java/text/NumberFormat.java +++ b/src/java.base/share/classes/java/text/NumberFormat.java @@ -302,17 +302,34 @@ protected NumberFormat() { public StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) { - if (number instanceof Long || number instanceof Integer || - number instanceof Short || number instanceof Byte || - number instanceof AtomicInteger || number instanceof AtomicLong || - (number instanceof BigInteger && - ((BigInteger)number).bitLength() < 64)) { - return format(((Number)number).longValue(), toAppendTo, pos); - } else if (number instanceof Number) { - return format(((Number)number).doubleValue(), toAppendTo, pos); - } else { - throw new IllegalArgumentException("Cannot format given Object as a Number"); - } + return switch (number) { + case Long l -> format(l.longValue(), toAppendTo, pos); + case Integer i -> format(i.longValue(), toAppendTo, pos); + case Short s -> format(s.longValue(), toAppendTo, pos); + case Byte b -> format(b.longValue(), toAppendTo, pos); + case AtomicInteger ai -> format(ai.longValue(), toAppendTo, pos); + case AtomicLong al -> format(al.longValue(), toAppendTo, pos); + case BigInteger bi when bi.bitLength() < 64 -> format(bi.longValue(), toAppendTo, pos); + case Number n -> format(n.doubleValue(), toAppendTo, pos); + case null, default -> throw new IllegalArgumentException("Cannot format given Object as a Number"); + }; + } + + @Override + StringBuf format(Object number, + StringBuf toAppendTo, + FieldPosition pos) { + return switch (number) { + case Long l -> format(l.longValue(), toAppendTo, pos); + case Integer i -> format(i.longValue(), toAppendTo, pos); + case Short s -> format(s.longValue(), toAppendTo, pos); + case Byte b -> format(b.longValue(), toAppendTo, pos); + case AtomicInteger ai -> format(ai.longValue(), toAppendTo, pos); + case AtomicLong al -> format(al.longValue(), toAppendTo, pos); + case BigInteger bi when bi.bitLength() < 64 -> format(bi.longValue(), toAppendTo, pos); + case Number n -> format(n.doubleValue(), toAppendTo, pos); + case null, default -> throw new IllegalArgumentException("Cannot format given Object as a Number"); + }; } /** @@ -347,8 +364,13 @@ public final String format(double number) { if (result != null) return result; - return format(number, new StringBuffer(), - DontCareFieldPosition.INSTANCE).toString(); + if ("java.text".equals(getClass().getPackageName())) { + return format(number, StringBufFactory.of(), + DontCareFieldPosition.INSTANCE).toString(); + } else { + return format(number, new StringBuffer(), + DontCareFieldPosition.INSTANCE).toString(); + } } /* @@ -367,8 +389,13 @@ public final String format(double number) { * @see java.text.Format#format */ public final String format(long number) { - return format(number, new StringBuffer(), - DontCareFieldPosition.INSTANCE).toString(); + if ("java.text".equals(getClass().getPackageName())) { + return format(number, StringBufFactory.of(), + DontCareFieldPosition.INSTANCE).toString(); + } else { + return format(number, new StringBuffer(), + DontCareFieldPosition.INSTANCE).toString(); + } } /** @@ -394,6 +421,12 @@ public abstract StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos); + StringBuf format(double number, + StringBuf toAppendTo, + FieldPosition pos) { + throw new UnsupportedOperationException("Subclasses should override this method"); + } + /** * Specialization of format. * @@ -417,6 +450,12 @@ public abstract StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos); + StringBuf format(long number, + StringBuf toAppendTo, + FieldPosition pos) { + throw new UnsupportedOperationException("Subclasses should override this method"); + } + /** * Parses text from the beginning of the given string to produce a {@code Number}. *

    diff --git a/src/java.base/share/classes/java/text/SimpleDateFormat.java b/src/java.base/share/classes/java/text/SimpleDateFormat.java index dbc7235cbc4e1..9f2513eb4b867 100644 --- a/src/java.base/share/classes/java/text/SimpleDateFormat.java +++ b/src/java.base/share/classes/java/text/SimpleDateFormat.java @@ -968,11 +968,18 @@ public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) { pos.beginIndex = pos.endIndex = 0; + return format(date, StringBufFactory.of(toAppendTo), pos.getFieldDelegate()).asStringBuffer(); + } + + @Override + final StringBuf format(Date date, StringBuf toAppendTo, + FieldPosition pos) { + pos.beginIndex = pos.endIndex = 0; return format(date, toAppendTo, pos.getFieldDelegate()); } // Called from Format after creating a FieldDelegate - private StringBuffer format(Date date, StringBuffer toAppendTo, + private StringBuf format(Date date, StringBuf toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date); @@ -1024,7 +1031,7 @@ private StringBuffer format(Date date, StringBuffer toAppendTo, */ @Override public AttributedCharacterIterator formatToCharacterIterator(Object obj) { - StringBuffer sb = new StringBuffer(); + StringBuf sb = StringBufFactory.of(); CharacterIteratorFieldDelegate delegate = new CharacterIteratorFieldDelegate(); @@ -1130,7 +1137,7 @@ else if (obj == null) { * Private member function that does the real date/time formatting. */ private void subFormat(int patternCharIndex, int count, - FieldDelegate delegate, StringBuffer buffer, + FieldDelegate delegate, StringBuf buffer, boolean useDateFormatSymbols) { int maxIntCount = Integer.MAX_VALUE; @@ -1320,7 +1327,11 @@ private void subFormat(int patternCharIndex, int count, } int num = (value / 60) * 100 + (value % 60); - CalendarUtils.sprintf0d(buffer, num, width); + if (buffer.isProxyStringBuilder()) { + CalendarUtils.sprintf0d(buffer.asStringBuilder(), num, width); + } else { + CalendarUtils.sprintf0d(buffer.asStringBuffer(), num, width); + } break; case PATTERN_ISO_ZONE: // 'X' @@ -1340,7 +1351,11 @@ private void subFormat(int patternCharIndex, int count, value = -value; } - CalendarUtils.sprintf0d(buffer, value / 60, 2); + if (buffer.isProxyStringBuilder()) { + CalendarUtils.sprintf0d(buffer.asStringBuilder(), value / 60, 2); + } else { + CalendarUtils.sprintf0d(buffer.asStringBuffer(), value / 60, 2); + } if (count == 1) { break; } @@ -1348,7 +1363,11 @@ private void subFormat(int patternCharIndex, int count, if (count == 3) { buffer.append(':'); } - CalendarUtils.sprintf0d(buffer, value % 60, 2); + if (buffer.isProxyStringBuilder()) { + CalendarUtils.sprintf0d(buffer.asStringBuilder(), value % 60, 2); + } else { + CalendarUtils.sprintf0d(buffer.asStringBuffer(), value % 60, 2); + } break; default: @@ -1382,7 +1401,7 @@ private void subFormat(int patternCharIndex, int count, /** * Formats a number with the specified minimum and maximum number of digits. */ - private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer) + private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuf buffer) { // Optimization for 1, 2 and 4 digit numbers. This should // cover most cases of formatting date/time related items. @@ -1425,7 +1444,17 @@ private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBu numberFormat.setMinimumIntegerDigits(minDigits); numberFormat.setMaximumIntegerDigits(maxDigits); - numberFormat.format((long)value, buffer, DontCareFieldPosition.INSTANCE); + if (buffer.isProxyStringBuilder()) { + //User can set numberFormat with a user-defined NumberFormat which + //not override format(long, StringBuf, FieldPosition). + if ("java.text".equals(numberFormat.getClass().getPackageName())) { + numberFormat.format((long) value, buffer, DontCareFieldPosition.INSTANCE); + } else { + buffer.append(numberFormat.format((long) value, new StringBuffer(), DontCareFieldPosition.INSTANCE)); + } + } else { + numberFormat.format((long) value, buffer.asStringBuffer(), DontCareFieldPosition.INSTANCE); + } } @@ -2565,5 +2594,4 @@ private void checkNegativeNumberExpression() { originalNumberFormat = numberFormat; } } - } diff --git a/src/java.base/share/classes/java/text/StringBufFactory.java b/src/java.base/share/classes/java/text/StringBufFactory.java new file mode 100644 index 0000000000000..bfad0c1093d32 --- /dev/null +++ b/src/java.base/share/classes/java/text/StringBufFactory.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 java.text; + +import java.text.Format.StringBuf; + +/** + * {@code StringBufFactory} creates implementations of {@code Format.StringBuf}, + * which is an interface with the minimum overlap required to support {@code StringBuffer} + * and {@code StringBuilder} in {@code Format}. This allows for {@code StringBuilder} to be used + * in place of {@code StringBuffer} to provide performance benefits for JDK internal + * {@code Format} subclasses. + */ +final class StringBufFactory { + + private StringBufFactory() { + } + + static StringBuf of(StringBuffer sb) { + return new StringBufferImpl(sb); + } + + static StringBuf of(StringBuilder sb) { + return new StringBuilderImpl(sb); + } + + static StringBuf of() { + return new StringBuilderImpl(); + } + + final static class StringBufferImpl implements StringBuf { + private final StringBuffer sb; + + StringBufferImpl(StringBuffer sb) { + this.sb = sb; + } + + @Override + public int length() { + return sb.length(); + } + + @Override + public String substring(int start, int end) { + return sb.substring(start, end); + } + + @Override + public String substring(int start) { + return sb.substring(start); + } + + @Override + public StringBuf append(char c) { + sb.append(c); + return this; + } + + @Override + public StringBuf append(String str) { + sb.append(str); + return this; + } + + @Override + public StringBuf append(int i) { + sb.append(i); + return this; + } + + @Override + public StringBuf append(char[] str, int offset, int len) { + sb.append(str, offset, len); + return this; + } + + @Override + public StringBuf append(CharSequence s, int start, int end) { + sb.append(s, start, end); + return this; + } + + @Override + public StringBuf append(StringBuffer asb) { + sb.append(asb); + return this; + } + + @Override + public boolean isProxyStringBuilder() { + return false; + } + + @Override + public StringBuffer asStringBuffer() { + return sb; + } + + @Override + public StringBuilder asStringBuilder() { + throw new AssertionError("Can't cast StringBuffer to StringBuilder"); + } + + @Override + public String toString() { + return sb.toString(); + } + } + + final static class StringBuilderImpl implements StringBuf { + private final StringBuilder sb; + + StringBuilderImpl(StringBuilder sb) { + this.sb = sb; + } + + StringBuilderImpl() { + this.sb = new StringBuilder(); + } + + @Override + public int length() { + return sb.length(); + } + + @Override + public String substring(int start, int end) { + return sb.substring(start, end); + } + + @Override + public String substring(int start) { + return sb.substring(start); + } + + @Override + public StringBuf append(char c) { + sb.append(c); + return this; + } + + @Override + public StringBuf append(String str) { + sb.append(str); + return this; + } + + @Override + public StringBuf append(int i) { + sb.append(i); + return this; + } + + @Override + public StringBuf append(char[] str, int offset, int len) { + sb.append(str, offset, len); + return this; + } + + @Override + public StringBuf append(CharSequence s, int start, int end) { + sb.append(s, start, end); + return this; + } + + @Override + public StringBuf append(StringBuffer asb) { + sb.append(asb); + return this; + } + + + @Override + public boolean isProxyStringBuilder() { + return true; + } + + @Override + public StringBuffer asStringBuffer() { + throw new AssertionError("Can't cast StringBuilder to StringBuffer"); + } + + @Override + public StringBuilder asStringBuilder() { + return sb; + } + + @Override + public String toString() { + return sb.toString(); + } + } +} diff --git a/src/java.base/share/classes/java/time/LocalDate.java b/src/java.base/share/classes/java/time/LocalDate.java index 2649c098a5b43..2d71fb1ceca54 100644 --- a/src/java.base/share/classes/java/time/LocalDate.java +++ b/src/java.base/share/classes/java/time/LocalDate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2147,28 +2147,36 @@ public int hashCode() { */ @Override public String toString() { + var buf = new StringBuilder(10); + formatTo(buf); + return buf.toString(); + } + + /** + * Prints the toString result to the given buf, avoiding extra string allocations. + * Requires extra capacity of 10 to avoid StringBuilder reallocation. + */ + void formatTo(StringBuilder buf) { int yearValue = year; int monthValue = month; int dayValue = day; int absYear = Math.abs(yearValue); - StringBuilder buf = new StringBuilder(10); if (absYear < 1000) { if (yearValue < 0) { - buf.append(yearValue - 10000).deleteCharAt(1); - } else { - buf.append(yearValue + 10000).deleteCharAt(0); + buf.append('-'); } + buf.repeat('0', absYear < 10 ? 3 : absYear < 100 ? 2 : 1); + buf.append(absYear); } else { if (yearValue > 9999) { buf.append('+'); } buf.append(yearValue); } - return buf.append(monthValue < 10 ? "-0" : "-") - .append(monthValue) - .append(dayValue < 10 ? "-0" : "-") - .append(dayValue) - .toString(); + buf.append(monthValue < 10 ? "-0" : "-") + .append(monthValue) + .append(dayValue < 10 ? "-0" : "-") + .append(dayValue); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/LocalDateTime.java b/src/java.base/share/classes/java/time/LocalDateTime.java index 024aa1dacbeda..d14afb7d78f9a 100644 --- a/src/java.base/share/classes/java/time/LocalDateTime.java +++ b/src/java.base/share/classes/java/time/LocalDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1965,7 +1965,11 @@ public int hashCode() { */ @Override public String toString() { - return date.toString() + 'T' + time.toString(); + var buf = new StringBuilder(29); + date.formatTo(buf); + buf.append('T'); + time.formatTo(buf); + return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/LocalTime.java b/src/java.base/share/classes/java/time/LocalTime.java index 499dca627e286..f28f8d3c5998a 100644 --- a/src/java.base/share/classes/java/time/LocalTime.java +++ b/src/java.base/share/classes/java/time/LocalTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -92,6 +92,8 @@ import java.time.temporal.ValueRange; import java.util.Objects; +import jdk.internal.util.DecimalDigits; + /** * A time without a time-zone in the ISO-8601 calendar system, * such as {@code 10:15:30}. @@ -1629,7 +1631,16 @@ public int hashCode() { */ @Override public String toString() { - StringBuilder buf = new StringBuilder(18); + var buf = new StringBuilder(18); + formatTo(buf); + return buf.toString(); + } + + /** + * Prints the toString result to the given buf, avoiding extra string allocations. + * Requires extra capacity of 18 to avoid StringBuilder reallocation. + */ + void formatTo(StringBuilder buf) { int hourValue = hour; int minuteValue = minute; int secondValue = second; @@ -1640,16 +1651,21 @@ public String toString() { buf.append(secondValue < 10 ? ":0" : ":").append(secondValue); if (nanoValue > 0) { buf.append('.'); - if (nanoValue % 1000_000 == 0) { - buf.append(Integer.toString((nanoValue / 1000_000) + 1000).substring(1)); + int zeros = 9 - DecimalDigits.stringSize(nanoValue); + if (zeros > 0) { + buf.repeat('0', zeros); + } + int digits; + if (nanoValue % 1_000_000 == 0) { + digits = nanoValue / 1_000_000; } else if (nanoValue % 1000 == 0) { - buf.append(Integer.toString((nanoValue / 1000) + 1000_000).substring(1)); + digits = nanoValue / 1000; } else { - buf.append(Integer.toString((nanoValue) + 1000_000_000).substring(1)); + digits = nanoValue; } + buf.append(digits); } } - return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 46a80deee00ec..33dea914b6690 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -121,6 +121,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import jdk.internal.util.DecimalDigits; + import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.locale.provider.CalendarDataUtility; import sun.util.locale.provider.LocaleProviderAdapter; @@ -2908,24 +2910,6 @@ NumberPrinterParser withSubsequentWidth(int subsequentWidth) { return new NumberPrinterParser(field, minWidth, maxWidth, signStyle, this.subsequentWidth + subsequentWidth); } - /* - * Copied from Long.stringSize - */ - private static int stringSize(long x) { - int d = 1; - if (x >= 0) { - d = 0; - x = -x; - } - long p = -10; - for (int i = 1; i < 19; i++) { - if (x > p) - return i + d; - p = 10 * p; - } - return 19 + d; - } - @Override public boolean format(DateTimePrintContext context, StringBuilder buf) { Long valueLong = context.getValue(field); @@ -2934,7 +2918,7 @@ public boolean format(DateTimePrintContext context, StringBuilder buf) { } long value = getValue(context, valueLong); DecimalStyle decimalStyle = context.getDecimalStyle(); - int size = stringSize(value); + int size = DecimalDigits.stringSize(value); if (value < 0) { size--; } @@ -3369,17 +3353,6 @@ boolean isFixedWidth(DateTimeParseContext context) { return false; } - // Simplified variant of Integer.stringSize that assumes positive values - private static int stringSize(int x) { - int p = 10; - for (int i = 1; i < 10; i++) { - if (x < p) - return i; - p = 10 * p; - } - return 10; - } - private static final int[] TENS = new int[] { 1, 10, @@ -3400,7 +3373,7 @@ public boolean format(DateTimePrintContext context, StringBuilder buf) { } int val = field.range().checkValidIntValue(value, field); DecimalStyle decimalStyle = context.getDecimalStyle(); - int stringSize = stringSize(val); + int stringSize = DecimalDigits.stringSize(val); char zero = decimalStyle.getZeroDigit(); if (val == 0 || stringSize < 10 - maxWidth) { // 0 or would round down to 0 diff --git a/src/java.base/share/classes/java/util/Date.java b/src/java.base/share/classes/java/util/Date.java index 1850564d8b290..aa16f2a75ee60 100644 --- a/src/java.base/share/classes/java/util/Date.java +++ b/src/java.base/share/classes/java/util/Date.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1012,7 +1012,7 @@ public int hashCode() { *

  • {@code mm} is the minute within the hour ({@code 00} through * {@code 59}), as two decimal digits. *
  • {@code ss} is the second within the minute ({@code 00} through - * {@code 61}, as two decimal digits. + * {@code 61}), as two decimal digits. *
  • {@code zzz} is the time zone (and may reflect daylight saving * time). Standard time zone abbreviations include those * recognized by the method {@code parse}. If time zone diff --git a/src/java.base/share/classes/java/util/Deque.java b/src/java.base/share/classes/java/util/Deque.java index ce40687989434..79e4187a586d8 100644 --- a/src/java.base/share/classes/java/util/Deque.java +++ b/src/java.base/share/classes/java/util/Deque.java @@ -163,7 +163,7 @@ * * * {@link #peek() peek()} - * {@link #getFirst() getFirst()} + * {@link #peekFirst() peekFirst()} * * * diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java index 3a72c4f4189a4..2a2fbc54d8618 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java @@ -189,14 +189,9 @@ public ConcurrentSkipListSet clone() { * contains more than {@code Integer.MAX_VALUE} elements, it * returns {@code Integer.MAX_VALUE}. * - *

    Beware that, unlike in most collections, this method is - * NOT a constant-time operation. Because of the - * asynchronous nature of these sets, determining the current - * number of elements requires traversing them all to count them. - * Additionally, it is possible for the size to change during - * execution of this method, in which case the returned result - * will be inaccurate. Thus, this method is typically not very - * useful in concurrent applications. + *

    It is possible for the size to change during execution of this method, + * in which case the returned result will be inaccurate. + * Thus, this method is typically not very useful in concurrent applications. * * @return the number of elements in this set */ diff --git a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java index 28fe0d4dca3d6..d6c45c7b04fd1 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java @@ -35,7 +35,9 @@ package java.util.concurrent.locks; -import jdk.internal.misc.VirtualThreads; +import java.util.concurrent.TimeUnit; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; /** @@ -176,7 +178,7 @@ public static void setCurrentBlocker(Object blocker) { public static void unpark(Thread thread) { if (thread != null) { if (thread.isVirtual()) { - VirtualThreads.unpark(thread); + JLA.unparkVirtualThread(thread); } else { U.unpark(thread); } @@ -216,7 +218,7 @@ public static void park(Object blocker) { setBlocker(t, blocker); try { if (t.isVirtual()) { - VirtualThreads.park(); + JLA.parkVirtualThread(); } else { U.park(false, 0L); } @@ -264,7 +266,7 @@ public static void parkNanos(Object blocker, long nanos) { setBlocker(t, blocker); try { if (t.isVirtual()) { - VirtualThreads.park(nanos); + JLA.parkVirtualThread(nanos); } else { U.park(false, nanos); } @@ -311,11 +313,7 @@ public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); try { - if (t.isVirtual()) { - VirtualThreads.parkUntil(deadline); - } else { - U.park(true, deadline); - } + parkUntil(deadline); } finally { setBlocker(t, null); } @@ -366,7 +364,7 @@ public static Object getBlocker(Thread t) { */ public static void park() { if (Thread.currentThread().isVirtual()) { - VirtualThreads.park(); + JLA.parkVirtualThread(); } else { U.park(false, 0L); } @@ -405,7 +403,7 @@ public static void park() { public static void parkNanos(long nanos) { if (nanos > 0) { if (Thread.currentThread().isVirtual()) { - VirtualThreads.park(nanos); + JLA.parkVirtualThread(nanos); } else { U.park(false, nanos); } @@ -444,7 +442,8 @@ public static void parkNanos(long nanos) { */ public static void parkUntil(long deadline) { if (Thread.currentThread().isVirtual()) { - VirtualThreads.parkUntil(deadline); + long millis = deadline - System.currentTimeMillis(); + JLA.parkVirtualThread(TimeUnit.MILLISECONDS.toNanos(millis)); } else { U.park(true, deadline); } @@ -462,4 +461,5 @@ static final long getThreadId(Thread thread) { private static final long PARKBLOCKER = U.objectFieldOffset(Thread.class, "parkBlocker"); + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); } diff --git a/src/java.base/share/classes/java/util/random/RandomGenerator.java b/src/java.base/share/classes/java/util/random/RandomGenerator.java index b38c4eee8f0cc..e019e073a2dac 100644 --- a/src/java.base/share/classes/java/util/random/RandomGenerator.java +++ b/src/java.base/share/classes/java/util/random/RandomGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -809,12 +809,11 @@ default int nextInt() { * * @throws IllegalArgumentException if {@code bound} is not positive * - * @implSpec The default implementation checks that {@code bound} is a - * positive {@code int}. Then invokes {@code nextInt()}, limiting the result - * to be greater than or equal zero and less than {@code bound}. If {@code bound} - * is a power of two then limiting is a simple masking operation. Otherwise, - * the result is re-calculated by invoking {@code nextInt()} until the - * result is greater than or equal zero and less than {@code bound}. + * @implSpec The default implementation checks that {@code bound} is positive. + * It then invokes {@link #nextInt()} one or more times to ensure a uniform + * distribution in the range 0 (inclusive) + * to {@code bound} (exclusive). + * It assumes the distribution of {@link #nextInt()} to be uniform. */ default int nextInt(int bound) { RandomSupport.checkBound(bound); @@ -835,13 +834,12 @@ default int nextInt(int bound) { * @throws IllegalArgumentException if {@code origin} is greater than * or equal to {@code bound} * - * @implSpec The default implementation checks that {@code origin} and - * {@code bound} are positive {@code ints}. Then invokes {@code nextInt()}, - * limiting the result to be greater that or equal {@code origin} and less - * than {@code bound}. If {@code bound} is a power of two then limiting is a - * simple masking operation. Otherwise, the result is re-calculated by - * invoking {@code nextInt()} until the result is greater than or equal - * {@code origin} and less than {@code bound}. + * @implSpec The default implementation checks that {@code origin} + * is less than {@code bound}. + * It then invokes {@link #nextInt()} one or more times to ensure a uniform + * distribution in the range {@code origin} (inclusive) + * to {@code bound} (exclusive). + * It assumes the distribution of {@link #nextInt()} to be uniform. */ default int nextInt(int origin, int bound) { RandomSupport.checkRange(origin, bound); @@ -868,13 +866,11 @@ default int nextInt(int origin, int bound) { * * @throws IllegalArgumentException if {@code bound} is not positive * - * @implSpec The default implementation checks that {@code bound} is a - * positive {@code long}. Then invokes {@code nextLong()}, limiting the - * result to be greater than or equal zero and less than {@code bound}. If - * {@code bound} is a power of two then limiting is a simple masking - * operation. Otherwise, the result is re-calculated by invoking - * {@code nextLong()} until the result is greater than or equal zero and - * less than {@code bound}. + * @implSpec The default implementation checks that {@code bound} is positive. + * It then invokes {@link #nextLong()} one or more times to ensure a uniform + * distribution in the range 0 (inclusive) + * to {@code bound} (exclusive). + * It assumes the distribution of {@link #nextLong()} to be uniform. */ default long nextLong(long bound) { RandomSupport.checkBound(bound); @@ -895,13 +891,12 @@ default long nextLong(long bound) { * @throws IllegalArgumentException if {@code origin} is greater than * or equal to {@code bound} * - * @implSpec The default implementation checks that {@code origin} and - * {@code bound} are positive {@code longs}. Then invokes {@code nextLong()}, - * limiting the result to be greater than or equal {@code origin} and less - * than {@code bound}. If {@code bound} is a power of two then limiting is a - * simple masking operation. Otherwise, the result is re-calculated by - * invoking {@code nextLong()} until the result is greater than or equal - * {@code origin} and less than {@code bound}. + * @implSpec The default implementation checks that {@code origin} + * is less than {@code bound}. + * It then invokes {@link #nextLong()} one or more times to ensure a uniform + * distribution in the range {@code origin} (inclusive) + * to {@code bound} (exclusive). + * It assumes the distribution of {@link #nextLong()} to be uniform. */ default long nextLong(long origin, long bound) { RandomSupport.checkRange(origin, bound); diff --git a/src/java.base/share/classes/java/util/zip/ZipConstants64.java b/src/java.base/share/classes/java/util/zip/ZipConstants64.java index d78eaf432d0b2..203e02ed9739c 100644 --- a/src/java.base/share/classes/java/util/zip/ZipConstants64.java +++ b/src/java.base/share/classes/java/util/zip/ZipConstants64.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,6 @@ class ZipConstants64 { static final int ZIP64_ENDHDR = 56; // ZIP64 end header size static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size static final int ZIP64_EXTHDR = 24; // EXT header size - static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID static final int ZIP64_MAGICCOUNT = 0xFFFF; static final long ZIP64_MAGICVAL = 0xFFFFFFFFL; diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index da8ab730873f2..6ab70d4bdb81d 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1324,7 +1324,7 @@ private void checkExtraFields(int cenPos, int startingOffset, tag, cenPos)); } - if (tag == ZIP64_EXTID) { + if (tag == EXTID_ZIP64) { // Get the compressed size; long csize = CENSIZ(cen, cenPos); // Get the uncompressed size; @@ -1360,7 +1360,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, long size, long locoff, int diskNo) throws ZipException { byte[] cen = this.cen; - // if ZIP64_EXTID blocksize == 0, which may occur with some older + // if EXTID_ZIP64 blocksize == 0, which may occur with some older // versions of Apache Ant and Commons Compress, validate csize and size // to make sure neither field == ZIP64_MAGICVAL if (blockSize == 0) { @@ -1368,7 +1368,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, locoff == ZIP64_MAGICVAL || diskNo == ZIP64_MAGICCOUNT) { zerror("Invalid CEN header (invalid zip64 extra data field size)"); } - // Only validate the ZIP64_EXTID data if the block size > 0 + // Only validate the EXTID_ZIP64 data if the block size > 0 return; } // Validate the Zip64 Extended Information Extra Field (0x0001) diff --git a/src/java.base/share/classes/java/util/zip/ZipInputStream.java b/src/java.base/share/classes/java/util/zip/ZipInputStream.java index 26f78b395c599..5302cf7516077 100644 --- a/src/java.base/share/classes/java/util/zip/ZipInputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipInputStream.java @@ -679,7 +679,7 @@ private boolean expect64BitDataDescriptor(byte[] extra, int flag, long csize, lo if (i + headerSize + dsize > extra.length) { return false; // Invalid size } - if (id == ZIP64_EXTID) { + if (id == EXTID_ZIP64) { return true; } i += headerSize + dsize; diff --git a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java index 231e6629a54ae..f68c27b265ee1 100644 --- a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java @@ -485,7 +485,7 @@ private void writeLOC(XEntry xentry) throws IOException { writeShort(elen); writeBytes(nameBytes, 0, nameBytes.length); if (hasZip64) { - writeShort(ZIP64_EXTID); + writeShort(EXTID_ZIP64); writeShort(16); writeLong(e.size); writeLong(e.csize); @@ -643,7 +643,7 @@ private void writeCEN(XEntry xentry) throws IOException { // take care of EXTID_ZIP64 and EXTID_EXTT if (hasZip64) { - writeShort(ZIP64_EXTID);// Zip64 extra + writeShort(EXTID_ZIP64);// Zip64 extra writeShort(elenZIP64); if (size == ZIP64_MAGICVAL) writeLong(e.size); diff --git a/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java b/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java index e4b2560415fc2..fd5ef0bcc5868 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLSessionContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,8 +30,8 @@ /** - * A SSLSessionContext represents a set of - * SSLSessions associated with a single entity. For example, + * A {@code SSLSessionContext} represents a set of + * {@code SSLSession}s associated with a single entity. For example, * it could be associated with a server or client who participates in many * sessions concurrently. *

    @@ -41,7 +41,7 @@ * Session contexts may not contain all sessions. For example, stateless * sessions are not stored in the session context. *

    - * There are SSLSessionContext parameters that affect how + * There are {@code SSLSessionContext} parameters that affect how * sessions are stored: *

      *
    • Sessions can be set to expire after a specified @@ -50,7 +50,7 @@ * can be limited. *
    * A session can be retrieved based on its session id, and all session id's - * in a SSLSessionContext can be listed. + * in a {@code SSLSessionContext} can be listed. * * @see SSLSession * @@ -61,19 +61,19 @@ public interface SSLSessionContext { /** - * Returns the SSLSession bound to the specified session id. + * Returns the {@code SSLSession} bound to the specified session id. * * @param sessionId the Session identifier - * @return the SSLSession or null if + * @return the {@code SSLSession} or null if * the specified session id does not refer to a valid SSLSession. * - * @throws NullPointerException if sessionId is null. + * @throws NullPointerException if {@code sessionId} is null. */ SSLSession getSession(byte[] sessionId); /** * Returns an Enumeration of all known session id's grouped under this - * SSLSessionContext. + * {@code SSLSessionContext}. *

    Session contexts may not contain all sessions. For example, * stateless sessions are not stored in the session context. * @@ -82,16 +82,17 @@ public interface SSLSessionContext { Enumeration getIds(); /** - * Sets the timeout limit for SSLSession objects grouped - * under this SSLSessionContext. + * Sets the timeout limit for {@code SSLSession} objects grouped + * under this {@code SSLSessionContext}. *

    * If the timeout limit is set to 't' seconds, a session exceeds the * timeout limit 't' seconds after its creation time. * When the timeout limit is exceeded for a session, the - * SSLSession object is invalidated and future connections - * cannot resume or rejoin the session. + * {@code SSLSession} object is marked so that future connections + * cannot resume or rejoin the session. Active sessions can continue + * to be used so long as resume and rejoin operations are not attempted. * A check for sessions exceeding the timeout is made immediately whenever - * the timeout limit is changed for this SSLSessionContext. + * the timeout limit is changed for this {@code SSLSessionContext}. * * @apiNote Note that the JDK Implementation uses default values for both * the session cache size and timeout. See @@ -109,17 +110,18 @@ public interface SSLSessionContext { void setSessionTimeout(int seconds); /** - * Returns the timeout limit of SSLSession objects grouped - * under this SSLSessionContext. + * Returns the timeout limit of {@code SSLSession} objects grouped + * under this {@code SSLSessionContext}. *

    * If the timeout limit is set to 't' seconds, a session exceeds the * timeout limit 't' seconds after its creation time. * When the timeout limit is exceeded for a session, the - * SSLSession object is invalidated and future connections - * cannot resume or rejoin the session. + * {@code SSLSession} object is marked so that future connections + * cannot resume or rejoin the session. Active sessions can continue + * to be used so long as resume and rejoin operations are not attempted. * A check for sessions exceeding the timeout limit is made immediately * whenever the timeout limit is changed for this - * SSLSessionContext. + * {@code SSLSessionContext}. * * @implNote The JDK implementation returns the session timeout as set by * the {@code setSessionTimeout} method, or if not set, a default @@ -133,8 +135,8 @@ public interface SSLSessionContext { int getSessionTimeout(); /** - * Sets the size of the cache used for storing SSLSession - * objects grouped under this SSLSessionContext. + * Sets the size of the cache used for storing {@code SSLSession} + * objects grouped under this {@code SSLSessionContext}. * * @apiNote Note that the JDK Implementation uses default values for both * the session cache size and timeout. See @@ -152,8 +154,8 @@ public interface SSLSessionContext { void setSessionCacheSize(int size); /** - * Returns the size of the cache used for storing SSLSession - * objects grouped under this SSLSessionContext. + * Returns the size of the cache used for storing {@code SSLSession} + * objects grouped under this {@code SSLSessionContext}. * * @implNote The JDK implementation returns the cache size as set by * the {@code setSessionCacheSize} method, or if not set, the diff --git a/src/java.base/share/classes/javax/security/auth/Subject.java b/src/java.base/share/classes/javax/security/auth/Subject.java index 277bbf434b00e..feb9e70b75280 100644 --- a/src/java.base/share/classes/javax/security/auth/Subject.java +++ b/src/java.base/share/classes/javax/security/auth/Subject.java @@ -108,9 +108,9 @@ * and {@code callAs} is similar to {@code doAs} except that the * input type and exceptions thrown are slightly different. * - *

    These methods behave differently depending on + *

    These methods behave differently depending on * whether a security manager is - * allowed or disallowed: + * {@linkplain SecurityManager##set-security-manager allowed or disallowed}: *

      *
    • If a security manager is allowed, which means it is either already set * or allowed to be set dynamically, a {@code Subject} object is associated 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 c31e745cd89a6..24aeabf6d3621 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -42,7 +42,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.RejectedExecutionException; import java.util.stream.Stream; @@ -432,12 +431,6 @@ public interface JavaLangAccess { */ MethodHandle stringConcatHelper(String name, MethodType methodType); - /** - * Prepends constant and the stringly representation of value into buffer, - * given the coder and final index. Index is measured in chars, not in bytes! - */ - long stringConcatHelperPrepend(long indexCoder, byte[] buf, String value); - /** * Get the string concat initial coder */ @@ -465,8 +458,6 @@ public interface JavaLangAccess { */ Object classData(Class c); - int stringSize(long i); - int getCharsLatin1(long i, int index, byte[] buf); int getCharsUTF16(long i, int index, byte[] buf); @@ -510,11 +501,6 @@ public interface JavaLangAccess { */ Thread currentCarrierThread(); - /** - * Executes the given value returning task on the current carrier thread. - */ - V executeOnCarrierThread(Callable task) throws Exception; - /** * Returns the value of the current carrier thread's copy of a thread-local. */ diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java index 9bb3f275ba1a2..0029b503d7a2d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,7 @@ public AnnotationDefaultAttribute readAttribute(AttributedElement e, ClassReader @Override protected void writeBody(BufWriter buf, AnnotationDefaultAttribute attr) { - attr.defaultValue().writeTo(buf); + AnnotationReader.writeAnnotationValue((BufWriterImpl) buf, attr.defaultValue()); } } @@ -119,7 +119,11 @@ public BootstrapMethodsAttribute readAttribute(AttributedElement e, ClassReader @Override protected void writeBody(BufWriter buf, BootstrapMethodsAttribute attr) { - buf.writeList(attr.bootstrapMethods()); + var b = (BufWriterImpl) buf; + b.writeU2(attr.bootstrapMethodsSize()); + for (var bsm : attr.bootstrapMethods()) { + ((BootstrapMethodEntryImpl) bsm).writeTo(b); + } } } @@ -254,7 +258,7 @@ public ExceptionsAttribute readAttribute(AttributedElement e, ClassReader cf, in @Override protected void writeBody(BufWriter buf, ExceptionsAttribute attr) { - buf.writeListIndices(attr.exceptions()); + Util.writeListIndices(buf, attr.exceptions()); } } @@ -408,19 +412,19 @@ protected void writeBody(BufWriter buf, ModuleAttribute attr) { for (ModuleExportInfo export : attr.exports()) { buf.writeIndex(export.exportedPackage()); buf.writeU2(export.exportsFlagsMask()); - buf.writeListIndices(export.exportsTo()); + Util.writeListIndices(buf, export.exportsTo()); } buf.writeU2(attr.opens().size()); for (ModuleOpenInfo open : attr.opens()) { buf.writeIndex(open.openedPackage()); buf.writeU2(open.opensFlagsMask()); - buf.writeListIndices(open.opensTo()); + Util.writeListIndices(buf, open.opensTo()); } - buf.writeListIndices(attr.uses()); + Util.writeListIndices(buf, attr.uses()); buf.writeU2(attr.provides().size()); for (ModuleProvideInfo provide : attr.provides()) { buf.writeIndex(provide.provides()); - buf.writeListIndices(provide.providesWith()); + Util.writeListIndices(buf, provide.providesWith()); } } } @@ -482,7 +486,7 @@ public ModulePackagesAttribute readAttribute(AttributedElement e, ClassReader cf @Override protected void writeBody(BufWriter buf, ModulePackagesAttribute attr) { - buf.writeListIndices(attr.packages()); + Util.writeListIndices(buf, attr.packages()); } } @@ -554,7 +558,7 @@ public NestMembersAttribute readAttribute(AttributedElement e, ClassReader cf, i @Override protected void writeBody(BufWriter buf, NestMembersAttribute attr) { - buf.writeListIndices(attr.nestMembers()); + Util.writeListIndices(buf, attr.nestMembers()); } } @@ -572,7 +576,7 @@ public PermittedSubclassesAttribute readAttribute(AttributedElement e, ClassRead @Override protected void writeBody(BufWriter buf, PermittedSubclassesAttribute attr) { - buf.writeListIndices(attr.permittedSubclasses()); + Util.writeListIndices(buf, attr.permittedSubclasses()); } } @@ -595,7 +599,7 @@ protected void writeBody(BufWriter buf, RecordAttribute attr) { for (RecordComponentInfo info : components) { buf.writeIndex(info.name()); buf.writeIndex(info.descriptor()); - buf.writeList(info.attributes()); + Util.writeAttributes((BufWriterImpl) buf, info.attributes()); } } } @@ -614,7 +618,7 @@ public RuntimeInvisibleAnnotationsAttribute readAttribute(AttributedElement encl @Override protected void writeBody(BufWriter buf, RuntimeInvisibleAnnotationsAttribute attr) { - buf.writeList(attr.annotations()); + AnnotationReader.writeAnnotations(buf, attr.annotations()); } } @@ -635,7 +639,7 @@ protected void writeBody(BufWriter buf, RuntimeInvisibleParameterAnnotationsAttr List> lists = attr.parameterAnnotations(); buf.writeU1(lists.size()); for (List list : lists) - buf.writeList(list); + AnnotationReader.writeAnnotations(buf, list); } } @@ -653,7 +657,7 @@ public RuntimeInvisibleTypeAnnotationsAttribute readAttribute(AttributedElement @Override protected void writeBody(BufWriter buf, RuntimeInvisibleTypeAnnotationsAttribute attr) { - buf.writeList(attr.annotations()); + AnnotationReader.writeAnnotations(buf, attr.annotations()); } } @@ -671,7 +675,7 @@ public RuntimeVisibleAnnotationsAttribute readAttribute(AttributedElement enclos @Override protected void writeBody(BufWriter buf, RuntimeVisibleAnnotationsAttribute attr) { - buf.writeList(attr.annotations()); + AnnotationReader.writeAnnotations(buf, attr.annotations()); } } @@ -692,7 +696,7 @@ protected void writeBody(BufWriter buf, RuntimeVisibleParameterAnnotationsAttrib List> lists = attr.parameterAnnotations(); buf.writeU1(lists.size()); for (List list : lists) - buf.writeList(list); + AnnotationReader.writeAnnotations(buf, list); } } @@ -710,7 +714,7 @@ public RuntimeVisibleTypeAnnotationsAttribute readAttribute(AttributedElement e, @Override protected void writeBody(BufWriter buf, RuntimeVisibleTypeAnnotationsAttribute attr) { - buf.writeList(attr.annotations()); + AnnotationReader.writeAnnotations(buf, attr.annotations()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java index d3f49e3168afb..dcbd8c8fee83d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java @@ -24,12 +24,11 @@ */ package jdk.internal.classfile.impl; -import java.lang.classfile.BufWriter; import java.lang.classfile.Label; import java.lang.classfile.constantpool.Utf8Entry; public class AbstractBoundLocalVariable - extends AbstractElement { + extends AbstractElement implements Util.WritableLocalVariable { protected final CodeImpl code; protected final int offset; private Utf8Entry nameEntry; @@ -80,8 +79,9 @@ public int slot() { return code.classReader.readU2(offset + 8); } - public boolean writeTo(BufWriter b) { - var lc = ((BufWriterImpl)b).labelContext(); + @Override + public boolean writeLocalTo(BufWriterImpl b) { + var lc = b.labelContext(); int startBci = lc.labelToBci(startScope()); int endBci = lc.labelToBci(endScope()); if (startBci == -1 || endBci == -1) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java index 2feba5f9e0bb2..f574627491f6e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractDirectBuilder.java @@ -24,10 +24,8 @@ */ package jdk.internal.classfile.impl; -import java.lang.classfile.constantpool.ConstantPool; -import java.util.Optional; - import java.lang.classfile.Attribute; +import java.lang.classfile.constantpool.ConstantPool; public class AbstractDirectBuilder { protected final SplitConstantPool constantPool; @@ -48,10 +46,6 @@ public boolean canWriteDirect(ConstantPool source) { return constantPool().canWriteDirect(source); } - public Optional original() { - return Optional.ofNullable(original); - } - public void setOriginal(M original) { this.original = original; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 48e1f0990b2ee..23bf3769608fd 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -317,7 +317,7 @@ static int size(CodeImpl code, int codeStart, int pos) { int pad = ap - (pos + 1); int low = code.classReader.readInt(ap + 4); int high = code.classReader.readInt(ap + 8); - if (high < low || high - low > code.codeLength >> 2) { + if (high < low || (long)high - low > code.codeLength >> 2) { throw new IllegalArgumentException("Invalid tableswitch values low: " + low + " high: " + high); } int cnt = high - low + 1; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index f846ca6fa9011..c6e6c4dce57a7 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -34,7 +34,6 @@ import java.lang.classfile.constantpool.ConstantDynamicEntry; import java.lang.classfile.constantpool.ConstantPool; import java.lang.classfile.constantpool.ConstantPoolBuilder; -import java.lang.classfile.BufWriter; import java.lang.classfile.constantpool.DoubleEntry; import java.lang.classfile.constantpool.FieldRefEntry; import java.lang.classfile.constantpool.FloatEntry; @@ -123,6 +122,8 @@ public int width() { return (tag == ClassFile.TAG_LONG || tag == ClassFile.TAG_DOUBLE) ? 2 : 1; } + abstract void writeTo(BufWriterImpl buf); + abstract PoolEntry clone(ConstantPoolBuilder cp); public static final class Utf8EntryImpl extends AbstractPoolEntry implements Utf8Entry { @@ -407,7 +408,7 @@ public boolean equalsString(String s) { } @Override - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { if (rawBytes != null) { pool.writeU1(tag); pool.writeU2(rawLen); @@ -478,7 +479,7 @@ public T ref1() { return ref1; } - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeU2(ref1.index()); } @@ -508,7 +509,7 @@ public U ref2() { return ref2; } - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeU2(ref1.index()); pool.writeU2(ref2.index()); @@ -814,7 +815,7 @@ public NameAndTypeEntryImpl nameAndType() { return nameAndType; } - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeU2(bsmIndex); pool.writeU2(nameAndType.index()); @@ -919,7 +920,7 @@ public DirectMethodHandleDesc asSymbol() { } @Override - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeU1(refKind); pool.writeU2(reference.index()); @@ -1069,7 +1070,7 @@ public static final class IntegerEntryImpl extends PrimitiveEntry } @Override - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeInt(val); } @@ -1102,7 +1103,7 @@ public static final class FloatEntryImpl extends PrimitiveEntry } @Override - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeFloat(val); } @@ -1134,7 +1135,7 @@ public static final class LongEntryImpl extends PrimitiveEntry implements } @Override - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeLong(val); } @@ -1166,7 +1167,7 @@ public static final class DoubleEntryImpl extends PrimitiveEntry impleme } @Override - public void writeTo(BufWriter pool) { + void writeTo(BufWriterImpl pool) { pool.writeU1(tag); pool.writeDouble(val); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java index ffc360379059c..00d817b8cb0cb 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPseudoInstruction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -154,7 +154,8 @@ public void writeTo(DirectCodeBuilder writer) { } - private abstract static sealed class AbstractLocalPseudo extends AbstractPseudoInstruction { + private abstract static sealed class AbstractLocalPseudo extends AbstractPseudoInstruction + implements Util.WritableLocalVariable { protected final int slot; protected final Utf8Entry name; protected final Utf8Entry descriptor; @@ -189,8 +190,9 @@ public Label endScope() { return endScope; } - public boolean writeTo(BufWriter b) { - var lc = ((BufWriterImpl)b).labelContext(); + @Override + public boolean writeLocalTo(BufWriterImpl b) { + var lc = b.labelContext(); int startBci = lc.labelToBci(startScope()); int endBci = lc.labelToBci(endScope()); if (startBci == -1 || endBci == -1) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java index 5aebec8ab40f6..9502ff7b246c9 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java @@ -24,6 +24,7 @@ */ package jdk.internal.classfile.impl; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; @@ -37,11 +38,11 @@ public abstract sealed class AbstractUnboundModel extends AbstractElement implements CompoundElement, AttributedElement permits BufferedCodeBuilder.Model, BufferedFieldBuilder.Model, BufferedMethodBuilder.Model { - private final List elements; + final List elements; private List> attributes; public AbstractUnboundModel(List elements) { - this.elements = elements; + this.elements = Collections.unmodifiableList(elements); } @Override @@ -63,8 +64,11 @@ public List elementList() { public List> attributes() { if (attributes == null) attributes = elements.stream() - .filter(e -> e instanceof Attribute) - .>map(e -> (Attribute) e) + .>mapMulti((e, sink) -> { + if (e instanceof Attribute attr) { + sink.accept(attr); + } + }) .toList(); return attributes; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java index 4fc2d5cf63bfe..03353e272a675 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,30 +32,20 @@ import static java.lang.classfile.ClassFile.*; -public final class AnnotationImpl implements Annotation { - private final Utf8Entry className; - private final List elements; - - public AnnotationImpl(Utf8Entry className, - List elems) { - this.className = className; - this.elements = List.copyOf(elems); - } - - @Override - public Utf8Entry className() { - return className; +public record AnnotationImpl(Utf8Entry className, List elements) + implements Annotation, Util.Writable { + public AnnotationImpl { + elements = List.copyOf(elements); } @Override - public List elements() { - return elements; - } - - @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { buf.writeIndex(className()); - buf.writeList(elements()); + buf.writeU2(elements().size()); + for (var e : elements) { + buf.writeIndex(e.name()); + AnnotationReader.writeAnnotationValue(buf, e.value()); + } } @Override @@ -81,16 +71,16 @@ public String toString() { public record AnnotationElementImpl(Utf8Entry name, AnnotationValue value) - implements AnnotationElement { + implements AnnotationElement, Util.Writable { @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { buf.writeIndex(name()); - value().writeTo(buf); + AnnotationReader.writeAnnotationValue(buf, value()); } } - public sealed interface OfConstantImpl extends AnnotationValue.OfConstant + public sealed interface OfConstantImpl extends AnnotationValue.OfConstant, Util.Writable permits AnnotationImpl.OfStringImpl, AnnotationImpl.OfDoubleImpl, AnnotationImpl.OfFloatImpl, AnnotationImpl.OfLongImpl, AnnotationImpl.OfIntegerImpl, AnnotationImpl.OfShortImpl, @@ -98,7 +88,7 @@ public sealed interface OfConstantImpl extends AnnotationValue.OfConstant AnnotationImpl.OfBooleanImpl { @Override - default void writeTo(BufWriter buf) { + default void writeTo(BufWriterImpl buf) { buf.writeU1(tag()); buf.writeIndex(constant()); } @@ -237,7 +227,7 @@ public boolean booleanValue() { } public record OfArrayImpl(List values) - implements AnnotationValue.OfArray { + implements AnnotationValue.OfArray, Util.Writable { public OfArrayImpl(List values) { this.values = List.copyOf(values); @@ -249,22 +239,25 @@ public char tag() { } @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { buf.writeU1(tag()); - buf.writeList(values); + buf.writeU2(values.size()); + for (var e : values) { + AnnotationReader.writeAnnotationValue(buf, e); + } } } public record OfEnumImpl(Utf8Entry className, Utf8Entry constantName) - implements AnnotationValue.OfEnum { + implements AnnotationValue.OfEnum, Util.Writable { @Override public char tag() { return AEV_ENUM; } @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { buf.writeU1(tag()); buf.writeIndex(className); buf.writeIndex(constantName); @@ -273,29 +266,29 @@ public void writeTo(BufWriter buf) { } public record OfAnnotationImpl(Annotation annotation) - implements AnnotationValue.OfAnnotation { + implements AnnotationValue.OfAnnotation, Util.Writable { @Override public char tag() { return AEV_ANNOTATION; } @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { buf.writeU1(tag()); - annotation.writeTo(buf); + AnnotationReader.writeAnnotation(buf, annotation); } } public record OfClassImpl(Utf8Entry className) - implements AnnotationValue.OfClass { + implements AnnotationValue.OfClass, Util.Writable { @Override public char tag() { return AEV_CLASS; } @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { buf.writeU1(tag()); buf.writeIndex(className); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java index 7b5920b3e7853..2ada13aaba82c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java @@ -28,6 +28,7 @@ import java.lang.classfile.Annotation; import java.lang.classfile.AnnotationElement; import java.lang.classfile.AnnotationValue; +import java.lang.classfile.BufWriter; import java.lang.classfile.ClassReader; import java.lang.classfile.constantpool.*; import java.lang.classfile.TypeAnnotation; @@ -39,7 +40,7 @@ import java.lang.classfile.constantpool.Utf8Entry; import jdk.internal.access.SharedSecrets; -class AnnotationReader { +public final class AnnotationReader { private AnnotationReader() { } public static List readAnnotations(ClassReader classReader, int p) { @@ -280,4 +281,24 @@ private static int skipTypeAnnotation(ClassReader classReader, int p) { p = skipElementValuePairs(classReader, p); return p; } + + public static void writeAnnotation(BufWriterImpl buf, Annotation annotation) { + // handles annotations and type annotations + // TODO annotation cleanup later + ((Util.Writable) annotation).writeTo(buf); + } + + public static void writeAnnotations(BufWriter buf, List list) { + // handles annotations and type annotations + var internalBuf = (BufWriterImpl) buf; + internalBuf.writeU2(list.size()); + for (var e : list) { + writeAnnotation(internalBuf, e); + } + } + + public static void writeAnnotationValue(BufWriterImpl buf, AnnotationValue value) { + // TODO annotation cleanup later + ((Util.Writable) value).writeTo(buf); + } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java index be5176ad1df08..ba51474c131e2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ import java.lang.classfile.Attribute; import java.lang.classfile.AttributeMapper; -import java.lang.classfile.BufWriter; public class AttributeHolder { private final List> attributes = new ArrayList<>(); @@ -50,10 +49,8 @@ public int size() { return attributes.size(); } - public void writeTo(BufWriter buf) { - buf.writeU2(attributes.size()); - for (Attribute a : attributes) - a.writeTo(buf); + public void writeTo(BufWriterImpl buf) { + Util.writeAttributes(buf, attributes); } boolean isPresent(AttributeMapper am) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java index d6876a82a9c2b..267c11c584578 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BootstrapMethodEntryImpl.java @@ -28,7 +28,6 @@ import java.lang.classfile.constantpool.ConstantPool; import java.lang.classfile.BootstrapMethodEntry; -import java.lang.classfile.BufWriter; import java.lang.classfile.constantpool.LoadableConstantEntry; import java.lang.classfile.constantpool.MethodHandleEntry; @@ -87,9 +86,8 @@ public int hashCode() { return hash; } - @Override - public void writeTo(BufWriter writer) { + void writeTo(BufWriterImpl writer) { writer.writeIndex(bootstrapMethod()); - writer.writeListIndices(arguments()); + Util.writeListIndices(writer, arguments()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java index 6a23be54f4855..d5ed0c14bcef4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -48,7 +49,7 @@ public abstract sealed class BoundAttribute> extends AbstractElement - implements Attribute { + implements Attribute, Util.Writable { static final int NAME_AND_LENGTH_PREFIX = 6; private final AttributeMapper mapper; @@ -101,7 +102,7 @@ public void writeTo(DirectFieldBuilder builder) { @Override @SuppressWarnings("unchecked") - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { if (!buf.canWriteDirect(classReader)) attributeMapper().writeAttribute(buf, (T) this); else @@ -134,7 +135,6 @@ public static List> readAttributes(AttributedElement enclosing, Cla var filled = new ArrayList>(size); int p = pos + 2; int cfLen = reader.classfileLength(); - var apo = ((ClassReaderImpl)reader).context().attributesProcessingOption(); for (int i = 0; i < size; ++i) { Utf8Entry name = reader.readEntry(p, Utf8Entry.class); int len = reader.readInt(p + 2); @@ -148,7 +148,7 @@ public static List> readAttributes(AttributedElement enclosing, Cla mapper = customAttributes.apply(name); } if (mapper != null) { - filled.add((Attribute)mapper.readAttribute(enclosing, reader, p)); + filled.add((Attribute) Objects.requireNonNull(mapper.readAttribute(enclosing, reader, p))); } else { AttributeMapper fakeMapper = new AttributeMapper<>() { @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index 87161588203a3..ac34d78e0e586 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -27,10 +27,8 @@ import java.nio.ByteBuffer; import java.util.Arrays; -import java.util.List; import java.lang.classfile.BufWriter; -import java.lang.classfile.WritableElement; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPool; import java.lang.classfile.constantpool.ConstantPoolBuilder; @@ -126,10 +124,8 @@ public void writeBytes(byte[] arr) { writeBytes(arr, 0, arr.length); } - @Override - public void writeBytes(BufWriter other) { - BufWriterImpl o = (BufWriterImpl) other; - writeBytes(o.elems, 0, o.offset); + public void writeBytes(BufWriterImpl other) { + writeBytes(other.elems, 0, other.offset); } @Override @@ -175,7 +171,6 @@ public ByteBuffer asByteBuffer() { return ByteBuffer.wrap(elems, 0, offset).slice(); } - @Override public void copyTo(byte[] array, int bufferOffset) { System.arraycopy(elems, 0, array, bufferOffset, size()); } @@ -198,20 +193,4 @@ public void writeIndexOrZero(PoolEntry entry) { else writeIndex(entry); } - - @Override - public> void writeList(List list) { - writeU2(list.size()); - for (T t : list) { - t.writeTo(this); - } - } - - @Override - public void writeListIndices(List list) { - writeU2(list.size()); - for (PoolEntry info : list) { - writeIndex(info); - } - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java index e9a8f2e1e1c89..b690f8dbfe70a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java @@ -24,7 +24,6 @@ */ package jdk.internal.classfile.impl; -import java.lang.classfile.BufWriter; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeElement; import java.lang.classfile.CodeModel; @@ -33,9 +32,6 @@ import java.lang.classfile.Label; import java.lang.classfile.MethodModel; import java.lang.classfile.instruction.ExceptionCatch; -import java.lang.classfile.instruction.IncrementInstruction; -import java.lang.classfile.instruction.LoadInstruction; -import java.lang.classfile.instruction.StoreInstruction; import java.util.ArrayList; import java.util.List; @@ -48,7 +44,6 @@ public final class BufferedCodeBuilder private final ClassFileImpl context; private final List elements = new ArrayList<>(); private final LabelImpl startLabel, endLabel; - private final CodeModel original; private final MethodInfo methodInfo; private boolean finished; private int maxLocals; @@ -61,20 +56,11 @@ public BufferedCodeBuilder(MethodInfo methodInfo, this.context = context; this.startLabel = new LabelImpl(this, -1); this.endLabel = new LabelImpl(this, -1); - this.original = original; this.methodInfo = methodInfo; - this.maxLocals = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); - if (original != null) - this.maxLocals = Math.max(this.maxLocals, original.maxLocals()); - + this.maxLocals = TerminalCodeBuilder.setupTopLocal(methodInfo, original); elements.add(startLabel); } - @Override - public Optional original() { - return Optional.ofNullable(original); - } - @Override public Label newLabel() { return new LabelImpl(this, -1); @@ -162,33 +148,22 @@ public final class Model implements CodeModel { private Model() { - super(elements); + super(BufferedCodeBuilder.this.elements); } @Override public List exceptionHandlers() { return elements.stream() - .filter(x -> x instanceof ExceptionCatch) - .map(x -> (ExceptionCatch) x) + .mapMulti((x, sink) -> { + if (x instanceof ExceptionCatch ec) { + sink.accept(ec); + } + }) .toList(); } - @Override - public int maxLocals() { - for (CodeElement element : elements) { - if (element instanceof LoadInstruction i) - maxLocals = Math.max(maxLocals, i.slot() + i.typeKind().slotSize()); - else if (element instanceof StoreInstruction i) - maxLocals = Math.max(maxLocals, i.slot() + i.typeKind().slotSize()); - else if (element instanceof IncrementInstruction i) - maxLocals = Math.max(maxLocals, i.slot() + 1); - } - return maxLocals; - } - - @Override - public int maxStack() { - throw new UnsupportedOperationException("nyi"); + int curTopLocal() { + return BufferedCodeBuilder.this.curTopLocal(); } @Override @@ -206,10 +181,6 @@ public void accept(CodeBuilder cb) { }); } - public void writeTo(BufWriter buf) { - DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, context, null).writeTo(buf); - } - @Override public String toString() { return String.format("CodeModel[id=%s]", Integer.toHexString(System.identityHashCode(this))); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java index 2ba7ef14d0d3c..d165ddd3928fd 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ */ package jdk.internal.classfile.impl; +import java.lang.reflect.AccessFlag; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -41,19 +42,16 @@ public final class BufferedFieldBuilder private final Utf8Entry desc; private final List elements = new ArrayList<>(); private AccessFlags flags; - private final FieldModel original; public BufferedFieldBuilder(SplitConstantPool constantPool, ClassFileImpl context, Utf8Entry name, - Utf8Entry type, - FieldModel original) { + Utf8Entry type) { this.constantPool = constantPool; this.context = context; this.name = name; this.desc = type; - this.flags = AccessFlags.ofField(); - this.original = original; + this.flags = new AccessFlagsImpl(AccessFlag.Location.FIELD); } @Override @@ -61,11 +59,6 @@ public ConstantPoolBuilder constantPool() { return constantPool; } - @Override - public Optional original() { - return Optional.ofNullable(original); - } - @Override public FieldBuilder with(FieldElement element) { elements.add(element); @@ -86,13 +79,12 @@ public final class Model extends AbstractUnboundModel implements FieldModel { public Model() { - super(elements); + super(BufferedFieldBuilder.this.elements); } @Override public Optional parent() { - FieldModel fm = original().orElse(null); - return fm == null? Optional.empty() : fm.parent(); + return Optional.empty(); } @Override @@ -112,7 +104,7 @@ public Utf8Entry fieldType() { @Override public void writeTo(DirectClassBuilder builder) { - builder.withField(name, desc, new Consumer() { + builder.withField(name, desc, new Consumer<>() { @Override public void accept(FieldBuilder fieldBuilder) { elements.forEach(fieldBuilder); @@ -120,13 +112,6 @@ public void accept(FieldBuilder fieldBuilder) { }); } - @Override - public void writeTo(BufWriter buf) { - DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, context, name, desc, null); - elements.forEach(fb); - fb.writeTo(buf); - } - @Override public String toString() { return String.format("FieldModel[fieldName=%s, fieldType=%s, flags=%d]", name.stringValue(), desc.stringValue(), flags.flagsMask()); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java index eaa2e2b710e76..4a9e9b111ce08 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java @@ -33,7 +33,6 @@ import java.lang.classfile.AccessFlags; -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassModel; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeModel; @@ -67,7 +66,7 @@ public BufferedMethodBuilder(SplitConstantPool constantPool, this.context = context; this.name = nameInfo; this.desc = typeInfo; - this.flags = AccessFlags.ofMethod(flags); + this.flags = new AccessFlagsImpl(AccessFlag.Location.METHOD, flags); this.original = original; } @@ -91,11 +90,6 @@ public ConstantPoolBuilder constantPool() { return constantPool; } - @Override - public Optional original() { - return Optional.ofNullable(original); - } - @Override public Utf8Entry methodName() { return name; @@ -162,7 +156,7 @@ public final class Model extends AbstractUnboundModel implements MethodModel, MethodInfo { public Model() { - super(elements); + super(BufferedMethodBuilder.this.elements); } @Override @@ -172,7 +166,7 @@ public AccessFlags flags() { @Override public Optional parent() { - return original().flatMap(MethodModel::parent); + return Optional.empty(); } @Override @@ -202,7 +196,11 @@ public int parameterSlot(int paramNo) { @Override public Optional code() { - throw new UnsupportedOperationException("nyi"); + return elements.stream().mapMulti((e, sink) -> { + if (e instanceof CodeModel cm) { + sink.accept(cm); + } + }).findFirst(); } @Override @@ -215,13 +213,6 @@ public void accept(MethodBuilder mb) { }); } - @Override - public void writeTo(BufWriter buf) { - DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, context, name, desc, methodFlags(), null); - elements.forEach(mb); - mb.writeTo(buf); - } - @Override public String toString() { return String.format("MethodModel[methodName=%s, methodType=%s, flags=%d]", diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java index 045652782171f..d31debf6f35a5 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java @@ -24,7 +24,6 @@ */ package jdk.internal.classfile.impl; -import java.util.Optional; import java.util.function.Consumer; import java.lang.classfile.*; @@ -51,15 +50,10 @@ public ClassBuilder with(ClassElement element) { return this; } - @Override - public Optional original() { - return terminal.original(); - } - @Override public ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, Consumer handler) { consumer.accept(new BufferedFieldBuilder(terminal.constantPool, terminal.context, - name, descriptor, null) + name, descriptor) .run(handler) .toModel()); return this; @@ -68,8 +62,7 @@ public ClassBuilder withField(Utf8Entry name, Utf8Entry descriptor, Consumer original() { - return terminal.original(); - } - @Override public FieldBuilder with(FieldElement element) { consumer.accept(element); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java index 866dda2c5bc53..a7084116b9bd3 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java @@ -24,7 +24,6 @@ */ package jdk.internal.classfile.impl; -import java.util.Optional; import java.util.function.Consumer; import java.lang.classfile.CodeBuilder; @@ -32,7 +31,6 @@ import java.lang.classfile.CodeTransform; import java.lang.classfile.MethodBuilder; import java.lang.classfile.MethodElement; -import java.lang.classfile.MethodModel; import java.lang.classfile.constantpool.ConstantPoolBuilder; public final class ChainedMethodBuilder implements MethodBuilder { @@ -75,9 +73,4 @@ public ConstantPoolBuilder constantPool() { return terminal.constantPool(); } - @Override - public Optional original() { - return terminal.original(); - } - } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java index eab283a8c6030..c6b9be5c76a99 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java @@ -101,7 +101,7 @@ public int classfileLength() { @Override public AccessFlags flags() { - return AccessFlags.ofClass(reader.flags()); + return new AccessFlagsImpl(AccessFlag.Location.CLASS, reader.flags()); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java index 25d4e2e68e8e8..9695b16ad515c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java @@ -321,12 +321,13 @@ ClassModel getContainedClass() { return containedClass; } - boolean writeBootstrapMethods(BufWriter buf) { + boolean writeBootstrapMethods(BufWriterImpl buf) { Optional a = containedClass.findAttribute(Attributes.bootstrapMethods()); if (a.isEmpty()) return false; - a.get().writeTo(buf); + // BootstrapMethodAttribute implementations are all internal writable + ((Util.Writable) a.get()).writeTo(buf); return true; } @@ -465,13 +466,12 @@ public T readEntryOrNull(int offset, Class cls) { return entryByIndex(index, cls); } - @Override - public boolean compare(BufWriter bufWriter, + public boolean compare(BufWriterImpl bufWriter, int bufWriterOffset, int classReaderOffset, int length) { try { - return Arrays.equals(((BufWriterImpl) bufWriter).elems, + return Arrays.equals(bufWriter.elems, bufWriterOffset, bufWriterOffset + length, buffer, classReaderOffset, classReaderOffset + length); } catch (IndexOutOfBoundsException e) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 4b2a21072f013..d4719fad0b1c2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -43,7 +43,7 @@ public final class CodeImpl extends BoundAttribute.BoundCodeAttribute - implements CodeModel, LabelContext { + implements LabelContext { static final Instruction[] SINGLETON_INSTRUCTIONS = new Instruction[256]; @@ -141,7 +141,7 @@ public List> attributes() { } @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { if (buf.canWriteDirect(classReader)) { super.writeTo(buf); } @@ -154,7 +154,7 @@ public void accept(CodeBuilder cb) { } }, (SplitConstantPool)buf.constantPool(), - ((BufWriterImpl)buf).context(), + buf.context(), null).writeTo(buf); } } @@ -210,7 +210,7 @@ public void accept(int s, int e, int h, int c) { return exceptionTable; } - public boolean compareCodeBytes(BufWriter buf, int offset, int len) { + public boolean compareCodeBytes(BufWriterImpl buf, int offset, int len) { return codeLength == len && classReader.compare(buf, offset, codeStart, codeLength); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 915c3ad8cf4d9..21fde0f6002df 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,6 @@ import java.util.List; import java.util.function.Consumer; -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassElement; import java.lang.classfile.ClassModel; @@ -44,7 +43,6 @@ import java.lang.classfile.MethodBuilder; import java.lang.classfile.MethodModel; import java.lang.classfile.MethodTransform; -import java.lang.classfile.WritableElement; import java.lang.classfile.constantpool.Utf8Entry; public final class DirectClassBuilder @@ -52,8 +50,8 @@ public final class DirectClassBuilder implements ClassBuilder { final ClassEntry thisClassEntry; - private final List> fields = new ArrayList<>(); - private final List> methods = new ArrayList<>(); + private final List fields = new ArrayList<>(); + private final List methods = new ArrayList<>(); private ClassEntry superclassEntry; private List interfaceEntries; private int majorVersion; @@ -78,7 +76,7 @@ public ClassBuilder with(ClassElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute)element); + writeAttribute((CustomAttribute) element); } return this; } @@ -120,12 +118,12 @@ public ClassBuilder transformMethod(MethodModel method, MethodTransform transfor // internal / for use by elements - public ClassBuilder withField(WritableElement field) { + ClassBuilder withField(Util.Writable field) { fields.add(field); return this; } - public ClassBuilder withMethod(WritableElement method) { + ClassBuilder withMethod(Util.Writable method) { methods.add(method); return this; } @@ -172,13 +170,13 @@ else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisC // We maintain two writers, and then we join them at the end int size = sizeHint == 0 ? 256 : sizeHint; - BufWriter head = new BufWriterImpl(constantPool, context, size); + BufWriterImpl head = new BufWriterImpl(constantPool, context, size); BufWriterImpl tail = new BufWriterImpl(constantPool, context, size, thisClassEntry, majorVersion); // The tail consists of fields and methods, and attributes // This should trigger all the CP/BSM mutation - tail.writeList(fields); - tail.writeList(methods); + Util.writeList(tail, fields); + Util.writeList(tail, methods); int attributesOffset = tail.size(); attributes.writeTo(tail); @@ -197,7 +195,7 @@ else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisC head.writeU2(flags); head.writeIndex(thisClassEntry); head.writeIndexOrZero(superclass); - head.writeListIndices(ies); + Util.writeListIndices(head, ies); // Join head and tail into an exact-size buffer byte[] result = new byte[head.size() + tail.size()]; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 0b6549a82da9e..a7652ac369b16 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -24,7 +24,6 @@ */ package jdk.internal.classfile.impl; -import java.lang.constant.MethodTypeDesc; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -36,20 +35,17 @@ import java.lang.classfile.Attribute; import java.lang.classfile.Attributes; -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeElement; import java.lang.classfile.CodeModel; import java.lang.classfile.CustomAttribute; -import java.lang.classfile.Instruction; import java.lang.classfile.Label; import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; import java.lang.classfile.instruction.SwitchCase; import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.attribute.LineNumberTableAttribute; -import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.DoubleEntry; @@ -83,7 +79,7 @@ public final class DirectCodeBuilder private final boolean transformFwdJumps, transformBackJumps; private final Label startLabel, endLabel; final MethodInfo methodInfo; - final BufWriter bytecodesBufWriter; + final BufWriterImpl bytecodesBufWriter; private CodeAttribute mruParent; private int[] mruParentTable; private Map parentMap; @@ -100,7 +96,7 @@ or model maxLocals (for transformation) allocLocal(TypeKind) bumps by nSlots */ - public static Attribute build(MethodInfo methodInfo, + public static UnboundAttribute build(MethodInfo methodInfo, Consumer handler, SplitConstantPool constantPool, ClassFileImpl context, @@ -131,12 +127,10 @@ private DirectCodeBuilder(MethodInfo methodInfo, this.transformFwdJumps = transformFwdJumps; this.transformBackJumps = context.shortJumpsOption() == ClassFile.ShortJumpsOption.FIX_SHORT_JUMPS; bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength()) - : new BufWriterImpl(constantPool, context); + : new BufWriterImpl(constantPool, context); this.startLabel = new LabelImpl(this, 0); this.endLabel = new LabelImpl(this, -1); - this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); - if (original != null) - this.topLocal = Math.max(this.topLocal, original.maxLocals()); + this.topLocal = TerminalCodeBuilder.setupTopLocal(methodInfo, original); } @Override @@ -144,7 +138,7 @@ public CodeBuilder with(CodeElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute)element); + writeAttribute((CustomAttribute) element); } return this; } @@ -193,9 +187,9 @@ public MethodInfo methodInfo() { return methodInfo; } - private Attribute content = null; + private UnboundAttribute content = null; - private void writeExceptionHandlers(BufWriter buf) { + private void writeExceptionHandlers(BufWriterImpl buf) { int pos = buf.size(); int handlersSize = handlers.size(); buf.writeU2(handlersSize); @@ -233,7 +227,7 @@ private void buildContent() { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { int pos = b.size(); int crSize = characterRanges.size(); b.writeU2(crSize); @@ -264,12 +258,12 @@ public void writeBody(BufWriter b) { if (!localVariables.isEmpty()) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { int pos = b.size(); int lvSize = localVariables.size(); b.writeU2(lvSize); for (LocalVariable l : localVariables) { - if (!l.writeTo(b)) { + if (!Util.writeLocalVariable(b, l)) { if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) { lvSize--; } else { @@ -287,12 +281,12 @@ public void writeBody(BufWriter b) { if (!localVariableTypes.isEmpty()) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTypeTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { int pos = b.size(); int lvtSize = localVariableTypes.size(); b.writeU2(localVariableTypes.size()); for (LocalVariableType l : localVariableTypes) { - if (!l.writeTo(b)) { + if (!Util.writeLocalVariable(b, l)) { if (context.deadLabelsOption() == ClassFile.DeadLabelsOption.DROP_DEAD_LABELS) { lvtSize--; } else { @@ -316,8 +310,9 @@ public void writeBody(BufWriter b) { private void writeCounters(boolean codeMatch, BufWriterImpl buf) { if (codeMatch) { - buf.writeU2(original.maxStack()); - buf.writeU2(original.maxLocals()); + var originalAttribute = (CodeImpl) original; + buf.writeU2(originalAttribute.maxStack()); + buf.writeU2(originalAttribute.maxLocals()); } else { StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); buf.writeU2(cntr.maxStack()); @@ -352,8 +347,7 @@ private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) { } @Override - public void writeBody(BufWriter b) { - BufWriterImpl buf = (BufWriterImpl) b; + public void writeBody(BufWriterImpl buf) { buf.setLabelContext(DirectCodeBuilder.this); int codeLength = curPc(); @@ -389,8 +383,8 @@ public void writeBody(BufWriter b) { buf.writeInt(codeLength); buf.writeBytes(bytecodesBufWriter); - writeExceptionHandlers(b); - attributes.writeTo(b); + writeExceptionHandlers(buf); + attributes.writeTo(buf); buf.setLabelContext(null); } }; @@ -427,12 +421,12 @@ public void writeLineNumber(int pc, int lineNo) { } @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { throw new UnsupportedOperationException(); } @Override - public void writeTo(BufWriter b) { + public void writeTo(BufWriterImpl b) { b.writeIndex(b.constantPool().utf8Entry(Attributes.NAME_LINE_NUMBER_TABLE)); push(); b.writeInt(buf.size() + 2); @@ -447,7 +441,7 @@ private boolean codeAndExceptionsMatch(int codeLength) { codeAttributesMatch = cai.codeLength == curPc() && cai.compareCodeBytes(bytecodesBufWriter, 0, codeLength); if (codeAttributesMatch) { - BufWriter bw = new BufWriterImpl(constantPool, context); + var bw = new BufWriterImpl(constantPool, context); writeExceptionHandlers(bw); codeAttributesMatch = cai.classReader.compare(bw, 0, cai.exceptionHandlerPos, bw.size()); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java index 7bdab531e4493..ce51bb1d26bc4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,15 @@ import java.util.function.Consumer; -import java.lang.classfile.BufWriter; import java.lang.classfile.CustomAttribute; import java.lang.classfile.FieldBuilder; import java.lang.classfile.FieldElement; import java.lang.classfile.FieldModel; -import java.lang.classfile.WritableElement; import java.lang.classfile.constantpool.Utf8Entry; public final class DirectFieldBuilder extends AbstractDirectBuilder - implements TerminalFieldBuilder, WritableElement { + implements TerminalFieldBuilder, Util.Writable { private final Utf8Entry name; private final Utf8Entry desc; private int flags; @@ -59,7 +57,7 @@ public FieldBuilder with(FieldElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute)element); + writeAttribute((CustomAttribute) element); } return this; } @@ -74,7 +72,7 @@ void setFlags(int flags) { } @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { buf.writeU2(flags); buf.writeIndex(name); buf.writeIndex(desc); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index a4b3bbe1b84b8..f79eb0af0cb53 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -28,7 +28,6 @@ import java.lang.constant.MethodTypeDesc; import java.util.function.Consumer; -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeModel; @@ -37,12 +36,11 @@ import java.lang.classfile.MethodBuilder; import java.lang.classfile.MethodElement; import java.lang.classfile.MethodModel; -import java.lang.classfile.WritableElement; import java.lang.classfile.constantpool.Utf8Entry; public final class DirectMethodBuilder extends AbstractDirectBuilder - implements TerminalMethodBuilder, WritableElement { + implements TerminalMethodBuilder, Util.Writable { final Utf8Entry name; final Utf8Entry desc; @@ -115,7 +113,7 @@ public MethodBuilder with(MethodElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute)element); + writeAttribute((CustomAttribute) element); } return this; } @@ -148,8 +146,7 @@ public DirectMethodBuilder run(Consumer handler) { } @Override - public void writeTo(BufWriter b) { - BufWriterImpl buf = (BufWriterImpl) b; + public void writeTo(BufWriterImpl buf) { buf.writeU2(flags); buf.writeIndex(name); buf.writeIndex(desc); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java index ba424c862fdc9..a71593a57120e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java @@ -24,6 +24,7 @@ */ package jdk.internal.classfile.impl; +import java.lang.reflect.AccessFlag; import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -33,7 +34,7 @@ public final class FieldImpl extends AbstractElement - implements FieldModel { + implements FieldModel, Util.Writable { private final ClassReader reader; private final int startPos, endPos, attributesPos; @@ -48,7 +49,7 @@ public FieldImpl(ClassReader reader, int startPos, int endPos, int attributesPos @Override public AccessFlags flags() { - return AccessFlags.ofField(reader.readU2(startPos)); + return new AccessFlagsImpl(AccessFlag.Location.FIELD, reader.readU2(startPos)); } @Override @@ -78,7 +79,7 @@ public List> attributes() { } @Override - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { if (buf.canWriteDirect(reader)) { reader.copyBytesTo(buf, startPos, endPos - startPos); } @@ -86,7 +87,7 @@ public void writeTo(BufWriter buf) { buf.writeU2(flags().flagsMask()); buf.writeIndex(fieldName()); buf.writeIndex(fieldType()); - buf.writeList(attributes()); + Util.writeAttributes(buf, attributes()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java index 62a83dffc6e87..032b18600a897 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java @@ -28,13 +28,14 @@ import java.lang.classfile.*; import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.reflect.AccessFlag; import java.util.List; import java.util.Optional; import java.util.function.Consumer; public final class MethodImpl extends AbstractElement - implements MethodModel, MethodInfo { + implements MethodModel, MethodInfo, Util.Writable { private final ClassReader reader; private final int startPos, endPos, attributesPos; @@ -51,7 +52,7 @@ public MethodImpl(ClassReader reader, int startPos, int endPos, int attrStart) { @Override public AccessFlags flags() { - return AccessFlags.ofMethod(reader.readU2(startPos)); + return new AccessFlagsImpl(AccessFlag.Location.METHOD, reader.readU2(startPos)); } @Override @@ -101,8 +102,7 @@ public List> attributes() { } @Override - public void writeTo(BufWriter b) { - BufWriterImpl buf = (BufWriterImpl) b; + public void writeTo(BufWriterImpl buf) { if (buf.canWriteDirect(reader)) { reader.copyBytesTo(buf, startPos, endPos - startPos); } @@ -110,7 +110,7 @@ public void writeTo(BufWriter b) { buf.writeU2(flags().flagsMask()); buf.writeIndex(methodName()); buf.writeIndex(methodType()); - buf.writeList(attributes()); + Util.writeAttributes(buf, attributes()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java index 9c27f0f5f9f72..f40da3a2beac6 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,7 @@ */ package jdk.internal.classfile.impl; -import java.util.Optional; - import java.lang.classfile.CodeBuilder; -import java.lang.classfile.CodeModel; import java.lang.classfile.Label; import java.lang.classfile.constantpool.ConstantPoolBuilder; @@ -59,11 +56,6 @@ public ConstantPoolBuilder constantPool() { return terminal.constantPool(); } - @Override - public Optional original() { - return terminal.original(); - } - @Override public Label newLabel() { return terminal.newLabel(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 6c5a8a266c0d1..f7905712b18d6 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -29,12 +29,10 @@ import java.util.Arrays; import java.util.List; -import java.lang.classfile.Attribute; import java.lang.classfile.Attributes; import java.lang.classfile.ClassReader; import java.lang.classfile.ClassFile; import java.lang.classfile.BootstrapMethodEntry; -import java.lang.classfile.BufWriter; import java.lang.classfile.attribute.BootstrapMethodsAttribute; import java.lang.classfile.constantpool.*; import java.util.Objects; @@ -135,7 +133,7 @@ public boolean canWriteDirect(ConstantPool other) { return this == other || parent == other; } - public boolean writeBootstrapMethods(BufWriter buf) { + public boolean writeBootstrapMethods(BufWriterImpl buf) { if (bsmSize == 0) return false; int pos = buf.size(); @@ -148,11 +146,11 @@ public boolean writeBootstrapMethods(BufWriter buf) { buf.patchInt(pos + 6, 2, bsmSize); } else { - Attribute a + UnboundAttribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.bootstrapMethods()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { buf.writeU2(bsmSize); for (int i = 0; i < bsmSize; i++) bootstrapMethodEntry(i).writeTo(buf); @@ -163,8 +161,7 @@ public void writeBody(BufWriter b) { return true; } - @Override - public void writeTo(BufWriter buf) { + void writeTo(BufWriterImpl buf) { int writeFrom = 1; if (size() >= 65536) { throw new IllegalArgumentException(String.format("Constant pool is too large %d", size())); @@ -175,7 +172,7 @@ public void writeTo(BufWriter buf) { writeFrom = parent.size(); } for (int i = writeFrom; i < size(); ) { - PoolEntry info = entryByIndex(i); + var info = (AbstractPoolEntry) entryByIndex(i); info.writeTo(buf); i += info.width(); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java index 899a43571a3f7..876cb201b7954 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java @@ -51,7 +51,7 @@ static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) { dcb.methodInfo.methodName().stringValue(), dcb.methodInfo.methodTypeSymbol(), (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, - ((BufWriterImpl) dcb.bytecodesBufWriter).asByteBuffer(), + dcb.bytecodesBufWriter.asByteBuffer(), dcb.constantPool, dcb.handlers); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 2ea8922d3267c..d0bf91171acfe 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -285,5 +285,9 @@ public static record StackMapFrameImpl(int frameType, List locals, List stack) implements StackMapFrameInfo { + public StackMapFrameImpl { + locals = List.copyOf(locals); + stack = List.copyOf(stack); + } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 5b103a21f8a73..514c1d32f4138 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -152,7 +152,7 @@ static StackMapGenerator of(DirectCodeBuilder dcb, BufWriterImpl buf) { dcb.methodInfo.methodName().stringValue(), dcb.methodInfo.methodTypeSymbol(), (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, - ((BufWriterImpl) dcb.bytecodesBufWriter).asByteBuffer(), + dcb.bytecodesBufWriter.asByteBuffer(), dcb.constantPool, dcb.context, dcb.handlers); @@ -383,7 +383,7 @@ private void removeRangeFromExcTable(int rangeStart, int rangeEnd) { public Attribute stackMapTableAttribute() { return frames.isEmpty() ? null : new UnboundAttribute.AdHocAttribute<>(Attributes.stackMapTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(frames.size()); Frame prevFrame = new Frame(classHierarchy); prevFrame.setLocalsFromArg(methodName, methodDesc, isStatic, thisType); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java index e5f7d9cca799f..784e844b712f2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TemporaryConstantPool.java @@ -189,9 +189,4 @@ public int bootstrapMethodCount() { public boolean canWriteDirect(ConstantPool constantPool) { return false; } - - @Override - public void writeTo(BufWriter buf) { - throw new UnsupportedOperationException(); - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java index 6e3ca516bf480..054ea91992869 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java @@ -25,8 +25,24 @@ package jdk.internal.classfile.impl; import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeModel; +import java.lang.classfile.attribute.CodeAttribute; public sealed interface TerminalCodeBuilder extends CodeBuilder, LabelContext permits DirectCodeBuilder, BufferedCodeBuilder { int curTopLocal(); + + static int setupTopLocal(MethodInfo methodInfo, CodeModel original) { + int paramSlots = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); + if (original == null) { + return paramSlots; + } + if (original instanceof CodeAttribute attr) { + return Math.max(paramSlots, attr.maxLocals()); + } + if (original instanceof BufferedCodeBuilder.Model buffered) { + return Math.max(paramSlots, buffered.curTopLocal()); + } + throw new InternalError("Unknown code model " + original); + } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java index 70b58e423f69a..6b64511fd5a13 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ import java.lang.classfile.AttributeMapper; import java.lang.classfile.Attributes; import java.lang.classfile.BootstrapMethodEntry; -import java.lang.classfile.BufWriter; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.Label; import java.lang.classfile.TypeAnnotation; @@ -93,9 +92,11 @@ import java.lang.classfile.constantpool.PackageEntry; import java.lang.classfile.constantpool.Utf8Entry; +import jdk.internal.access.SharedSecrets; + public abstract sealed class UnboundAttribute> extends AbstractElement - implements Attribute { + implements Attribute, Util.Writable { protected final AttributeMapper mapper; public UnboundAttribute(AttributeMapper mapper) { @@ -114,7 +115,7 @@ public String attributeName() { @Override @SuppressWarnings("unchecked") - public void writeTo(BufWriter buf) { + public void writeTo(BufWriterImpl buf) { mapper.writeAttribute(buf, (T) this); } @@ -628,7 +629,13 @@ public static final class UnboundRuntimeInvisibleParameterAnnotationsAttribute public UnboundRuntimeInvisibleParameterAnnotationsAttribute(List> elements) { super(Attributes.runtimeInvisibleParameterAnnotations()); - this.elements = List.copyOf(elements); + // deep copy + var array = elements.toArray().clone(); + for (int i = 0; i < array.length; i++) { + array[i] = List.copyOf((List) array[i]); + } + + this.elements = SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(array); } @Override @@ -752,7 +759,7 @@ public UnboundRecordComponentInfo(Utf8Entry name, Utf8Entry descriptor, List targetPath, Utf8Entry className, - List elements) implements TypeAnnotation { + List elements) implements TypeAnnotation, Util.Writable { public UnboundTypeAnnotation(TargetInfo targetInfo, List targetPath, Utf8Entry className, List elements) { @@ -769,8 +776,8 @@ private int labelToBci(LabelContext lr, Label label) { } @Override - public void writeTo(BufWriter buf) { - LabelContext lr = ((BufWriterImpl) buf).labelContext(); + public void writeTo(BufWriterImpl buf) { + LabelContext lr = buf.labelContext(); // target_type buf.writeU1(targetInfo.targetType().targetTypeValue()); @@ -818,7 +825,7 @@ public void writeTo(BufWriter buf) { buf.writeU2(elements.size()); for (AnnotationElement pair : elements()) { buf.writeIndex(pair.name()); - pair.value().writeTo(buf); + AnnotationReader.writeAnnotationValue(buf, pair.value()); } } } @@ -904,10 +911,10 @@ public AdHocAttribute(AttributeMapper mapper) { super(mapper); } - public abstract void writeBody(BufWriter b); + public abstract void writeBody(BufWriterImpl b); @Override - public void writeTo(BufWriter b) { + public void writeTo(BufWriterImpl b) { b.writeIndex(b.constantPool().utf8Entry(mapper.name())); b.writeInt(0); int start = b.size(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 0e969272c4028..079ac9551aba6 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -24,6 +24,9 @@ */ package jdk.internal.classfile.impl; +import java.lang.classfile.CustomAttribute; +import java.lang.classfile.PseudoInstruction; +import java.lang.classfile.constantpool.PoolEntry; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.AbstractList; @@ -47,7 +50,6 @@ import static java.lang.classfile.ClassFile.ACC_STATIC; import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.components.ClassPrinter; -import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.nio.ByteBuffer; import java.util.function.Consumer; @@ -188,6 +190,31 @@ public static MethodTypeDesc methodTypeSymbol(NameAndTypeEntry nat) { return ((AbstractPoolEntry.NameAndTypeEntryImpl)nat).methodTypeSymbol(); } + @SuppressWarnings("unchecked") + private static void writeAttribute(BufWriterImpl writer, Attribute attr) { + if (attr instanceof CustomAttribute ca) { + var mapper = (AttributeMapper) ca.attributeMapper(); + mapper.writeAttribute(writer, (T) ca); + } else { + assert attr instanceof BoundAttribute || attr instanceof UnboundAttribute; + ((Writable) attr).writeTo(writer); + } + } + + public static void writeAttributes(BufWriterImpl buf, List> list) { + buf.writeU2(list.size()); + for (var e : list) { + writeAttribute(buf, e); + } + } + + static void writeList(BufWriterImpl buf, List list) { + buf.writeU2(list.size()); + for (var e : list) { + e.writeTo(buf); + } + } + public static int slotSize(ClassDesc desc) { return switch (desc.descriptorString().charAt(0)) { case 'V' -> 0; @@ -216,7 +243,7 @@ public static void dumpMethod(SplitConstantPool cp, clb.withMethod(methodName, methodDesc, acc, mb -> ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.code()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(-1);//max stack b.writeU2(-1);//max locals b.writeInt(bytecode.limit()); @@ -237,4 +264,28 @@ public void writeBody(BufWriter b) { } } } + + public static void writeListIndices(BufWriter writer, List list) { + writer.writeU2(list.size()); + for (PoolEntry info : list) { + writer.writeIndex(info); + } + } + + public static boolean writeLocalVariable(BufWriterImpl buf, PseudoInstruction lvOrLvt) { + return ((WritableLocalVariable) lvOrLvt).writeLocalTo(buf); + } + + /** + * A generic interface for objects to write to a + * buf writer. Do not implement unless necessary, + * as this writeTo is public, which can be troublesome. + */ + interface Writable { + void writeTo(BufWriterImpl writer); + } + + interface WritableLocalVariable { + boolean writeLocalTo(BufWriterImpl buf); + } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java index 33733c5572a73..74cdb881a744a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java @@ -26,6 +26,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.lang.classfile.ClassHierarchyResolver; @@ -133,7 +134,7 @@ public static List verify(ClassModel classModel, ClassHierarchyReso errors.addAll(inference_verify(klass)); } } - return errors; + return Collections.unmodifiableList(errors); } finally { log_info(logger, "End class verification for: %s", clsName); } diff --git a/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java index 0a75416fe73b4..5061c95cb423f 100644 --- a/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java @@ -213,16 +213,25 @@ public String descriptorString() { @Override public MethodType resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException { - @SuppressWarnings("removal") - MethodType mtype = AccessController.doPrivileged(new PrivilegedAction<>() { - @Override - public MethodType run() { - return MethodType.fromMethodDescriptorString(descriptorString(), - lookup.lookupClass().getClassLoader()); - } - }); - - // let's check that the lookup has access to all the types in the method type + MethodType mtype; + try { + @SuppressWarnings("removal") + MethodType mt = AccessController.doPrivileged(new PrivilegedAction<>() { + @Override + public MethodType run() { + return MethodType.fromMethodDescriptorString(descriptorString(), + lookup.lookupClass().getClassLoader()); + } + }); + mtype = mt; + } catch (TypeNotPresentException ex) { + throw (ClassNotFoundException) ex.getCause(); + } + + // Some method types, like ones containing a package private class not accessible + // to the overriding method, can be valid method descriptors and obtained from + // MethodType.fromMethodDescriptor, but ldc instruction will fail to resolve such + // MethodType constants due to access control (JVMS 5.4.3.1 and 5.4.3.5) lookup.accessClass(mtype.returnType()); for (Class paramType: mtype.parameterArray()) { lookup.accessClass(paramType); diff --git a/src/java.base/share/classes/jdk/internal/misc/VirtualThreads.java b/src/java.base/share/classes/jdk/internal/misc/VirtualThreads.java deleted file mode 100644 index 04a93afad9473..0000000000000 --- a/src/java.base/share/classes/jdk/internal/misc/VirtualThreads.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. 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.internal.misc; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.RejectedExecutionException; -import jdk.internal.access.JavaLangAccess; -import jdk.internal.access.SharedSecrets; - -/** - * Defines static methods to support execution in the context of a virtual thread. - */ -public final class VirtualThreads { - private static final JavaLangAccess JLA; - static { - JLA = SharedSecrets.getJavaLangAccess(); - if (JLA == null) { - throw new InternalError("JavaLangAccess not setup"); - } - } - private VirtualThreads() { } - - /** - * Parks the current virtual thread until it is unparked or interrupted. - * If already unparked then the parking permit is consumed and this method - * completes immediately (meaning it doesn't yield). It also completes - * immediately if the interrupt status is set. - * @throws WrongThreadException if the current thread is not a virtual thread - */ - public static void park() { - JLA.parkVirtualThread(); - } - - /** - * Parks the current virtual thread up to the given waiting time or until it - * is unparked or interrupted. If already unparked then the parking permit is - * consumed and this method completes immediately (meaning it doesn't yield). - * It also completes immediately if the interrupt status is set or the waiting - * time is {@code <= 0}. - * @param nanos the maximum number of nanoseconds to wait - * @throws WrongThreadException if the current thread is not a virtual thread - */ - public static void park(long nanos) { - JLA.parkVirtualThread(nanos); - } - - /** - * Parks the current virtual thread until the given deadline or until is is - * unparked or interrupted. If already unparked then the parking permit is - * consumed and this method completes immediately (meaning it doesn't yield). - * It also completes immediately if the interrupt status is set or the - * deadline has past. - * @param deadline absolute time, in milliseconds, from the epoch - * @throws WrongThreadException if the current thread is not a virtual thread - */ - public static void parkUntil(long deadline) { - long millis = deadline - System.currentTimeMillis(); - long nanos = TimeUnit.NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS); - park(nanos); - } - - /** - * Re-enables a virtual thread for scheduling. If the thread was parked then - * it will be unblocked, otherwise its next attempt to park will not block - * @param thread the virtual thread to unpark - * @throws IllegalArgumentException if the thread is not a virtual thread - * @throws RejectedExecutionException if the scheduler cannot accept a task - */ - public static void unpark(Thread thread) { - JLA.unparkVirtualThread(thread); - } -} diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index bdcbe15ea844f..83438e59b8276 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,4 +84,56 @@ private DecimalDigits() { public static short digitPair(int i) { return DIGITS[i]; } + + /** + * Returns the string representation size for a given int value. + * + * @param x int value + * @return string size + * + * @implNote There are other ways to compute this: e.g. binary search, + * but values are biased heavily towards zero, and therefore linear search + * wins. The iteration results are also routinely inlined in the generated + * code after loop unrolling. + */ + public static int stringSize(int x) { + int d = 1; + if (x >= 0) { + d = 0; + x = -x; + } + int p = -10; + for (int i = 1; i < 10; i++) { + if (x > p) + return i + d; + p = 10 * p; + } + return 10 + d; + } + + /** + * Returns the string representation size for a given long value. + * + * @param x long value + * @return string size + * + * @implNote There are other ways to compute this: e.g. binary search, + * but values are biased heavily towards zero, and therefore linear search + * wins. The iteration results are also routinely inlined in the generated + * code after loop unrolling. + */ + public static int stringSize(long x) { + int d = 1; + if (x >= 0) { + d = 0; + x = -x; + } + long p = -10; + for (int i = 1; i < 19; i++) { + if (x > p) + return i + d; + p = 10 * p; + } + return 19 + d; + } } diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java index 9c364cd781350..810d43ae38a3a 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -219,8 +219,14 @@ public boolean containsValue(Object value) { @Override public V get(Object key) { - Objects.requireNonNull(key, "key must not be null"); removeStaleReferences(); + return getNoCheckStale(key); + } + + // Internal get(key) without removing stale references that would modify the keyset. + // Use when iterating or streaming over the keys to avoid ConcurrentModificationException. + private V getNoCheckStale(Object key) { + Objects.requireNonNull(key, "key must not be null"); return map.get(lookupKey(key)); } @@ -291,7 +297,7 @@ public Collection values() { public Set> entrySet() { removeStaleReferences(); return filterKeySet() - .map(k -> new AbstractMap.SimpleEntry<>(k, get(k))) + .map(k -> new AbstractMap.SimpleEntry<>(k, getNoCheckStale(k))) .collect(Collectors.toSet()); } @@ -335,7 +341,7 @@ public V replace(K key, V value) { public String toString() { removeStaleReferences(); return filterKeySet() - .map(k -> k + "=" + get(k)) + .map(k -> k + "=" + getNoCheckStale(k)) .collect(Collectors.joining(", ", "{", "}")); } diff --git a/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java b/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java index bb0d603790fe6..de7a1f98daf31 100644 --- a/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SelectorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -188,11 +188,11 @@ public final void implCloseSelector() throws IOException { // Deregister channels Iterator i = keys.iterator(); while (i.hasNext()) { - SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); + SelectionKeyImpl ski = (SelectionKeyImpl) i.next(); deregister(ski); SelectableChannel selch = ski.channel(); if (!selch.isOpen() && !selch.isRegistered()) - ((SelChImpl)selch).kill(); + ((SelChImpl) selch).kill(); selectedKeys.remove(ski); i.remove(); } @@ -221,13 +221,14 @@ protected final SelectionKey register(AbstractSelectableChannel ch, keys.add(k); try { k.interestOps(ops); - } catch (ClosedSelectorException e) { + } catch (CancelledKeyException e) { + // key observed and cancelled. Okay to return a cancelled key. + } + if (!isOpen()) { assert ch.keyFor(this) == null; keys.remove(k); k.cancel(); - throw e; - } catch (CancelledKeyException e) { - // key observed and cancelled. Okay to return a cancelled key. + throw new ClosedSelectorException(); } return k; } @@ -277,7 +278,7 @@ protected final void processDeregisterQueue() throws IOException { SelectableChannel ch = ski.channel(); if (!ch.isOpen() && !ch.isRegistered()) - ((SelChImpl)ch).kill(); + ((SelChImpl) ch).kill(); } } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index 2a9239fac95ac..2cb74fb105697 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,7 +181,7 @@ enum SSLExtension implements SSLStringizer { USE_SRTP (0x000E, "use_srtp"), // Extensions defined in RFC 6520 (TLS and DTLS Heartbeat Extension) - HEARTBEAT (0x000E, "heartbeat"), + HEARTBEAT (0x000F, "heartbeat"), // Extensions defined in RFC 7301 (TLS Application-Layer Protocol Negotiation Extension) CH_ALPN (0x0010, "application_layer_protocol_negotiation", @@ -406,7 +406,7 @@ enum SSLExtension implements SSLStringizer { CertificateAuthoritiesExtension.ssStringizer), OID_FILTERS (0x0030, "oid_filters"), - POST_HANDSHAKE_AUTH (0x0030, "post_handshake_auth"), + POST_HANDSHAKE_AUTH (0x0031, "post_handshake_auth"), CH_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert", SSLHandshake.CLIENT_HELLO, diff --git a/src/java.base/share/classes/sun/security/util/DerInputStream.java b/src/java.base/share/classes/sun/security/util/DerInputStream.java index 8df4e80eb97ac..e0519f0fa0980 100644 --- a/src/java.base/share/classes/sun/security/util/DerInputStream.java +++ b/src/java.base/share/classes/sun/security/util/DerInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -114,7 +114,7 @@ public DerValue getDerValue() throws IOException { // to the end of return value by DerIndefLenConverter::convertBytes // and stay inside result.buffer. int unused = result.buffer.length - result.end; - this.pos = this.data.length - unused; + this.pos = this.end - unused; } else { this.pos = result.end; } diff --git a/src/java.base/share/classes/sun/security/util/ECParameters.java b/src/java.base/share/classes/sun/security/util/ECParameters.java index 429b022cc1baa..79b38ead6fa74 100644 --- a/src/java.base/share/classes/sun/security/util/ECParameters.java +++ b/src/java.base/share/classes/sun/security/util/ECParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -203,8 +203,7 @@ protected void engineInit(byte[] params, String decodingMethod) } if (spec.isAssignableFrom(ECGenParameterSpec.class)) { - // Ensure the name is the Object ID - String name = namedCurve.getObjectId(); + String name = namedCurve.getNameAndAliases()[0]; return spec.cast(new ECGenParameterSpec(name)); } diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java index e83f2896304af..ee6ec0f72baa8 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,6 +62,7 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import jdk.internal.util.StaticProperty; import sun.security.action.GetPropertyAction; import sun.util.resources.LocaleData; import sun.util.resources.OpenListResourceBundle; @@ -289,6 +290,16 @@ public String getCurrencyName(String key) { } public String getLocaleName(String key) { + // Get names for old ISO codes with new ISO code resources + if (StaticProperty.javaLocaleUseOldISOCodes().equalsIgnoreCase("true")) { + key = switch (key) { + case "iw" -> "he"; + case "in" -> "id"; + case "ji" -> "yi"; + default -> key; + }; + } + Object localeName = null; String cacheKey = LOCALE_NAMES + key; diff --git a/src/java.base/share/native/launcher/main.c b/src/java.base/share/native/launcher/main.c index d3898d7adc634..c504c13154f5b 100644 --- a/src/java.base/share/native/launcher/main.c +++ b/src/java.base/share/native/launcher/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -110,7 +110,25 @@ main(int argc, char **argv) } } } - JLI_CmdToArgs(GetCommandLine()); + + // Obtain the command line in UTF-16, then convert it to ANSI code page + // without the "best-fit" option + LPWSTR wcCmdline = GetCommandLineW(); + int mbSize = WideCharToMultiByte(CP_ACP, + WC_NO_BEST_FIT_CHARS | WC_COMPOSITECHECK | WC_DEFAULTCHAR, + wcCmdline, -1, NULL, 0, NULL, NULL); + // If the call to WideCharToMultiByte() fails, it returns 0, which + // will then make the following JLI_MemAlloc() to issue exit(1) + LPSTR mbCmdline = JLI_MemAlloc(mbSize); + if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS | WC_COMPOSITECHECK | WC_DEFAULTCHAR, + wcCmdline, -1, mbCmdline, mbSize, NULL, NULL) == 0) { + perror("command line encoding conversion failure"); + exit(1); + } + + JLI_CmdToArgs(mbCmdline); + JLI_MemFree(mbCmdline); + margc = JLI_GetStdArgc(); // add one more to mark the end margv = (char **)JLI_MemAlloc((margc + 1) * (sizeof(char *))); diff --git a/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java b/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java index a3bff8d1e6523..d29eacb81c1b0 100644 --- a/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java +++ b/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java @@ -231,7 +231,6 @@ protected void implDereg(SelectionKeyImpl ski) throws IOException { @Override public void setEventOps(SelectionKeyImpl ski) { - ensureOpen(); synchronized (updateLock) { updateKeys.addLast(ski); } diff --git a/src/java.base/windows/classes/sun/nio/ch/WEPollSelectorImpl.java b/src/java.base/windows/classes/sun/nio/ch/WEPollSelectorImpl.java index bbcfab9bbf19c..f2bff88675fdb 100644 --- a/src/java.base/windows/classes/sun/nio/ch/WEPollSelectorImpl.java +++ b/src/java.base/windows/classes/sun/nio/ch/WEPollSelectorImpl.java @@ -28,7 +28,6 @@ import java.io.FileDescriptor; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.channels.ClosedSelectorException; import java.nio.channels.Pipe; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; @@ -89,11 +88,6 @@ class WEPollSelectorImpl extends SelectorImpl { WEPoll.ctl(eph, EPOLL_CTL_ADD, fd0Val, WEPoll.EPOLLIN); } - private void ensureOpen() { - if (!isOpen()) - throw new ClosedSelectorException(); - } - @Override protected int doSelect(Consumer action, long timeout) throws IOException @@ -228,7 +222,6 @@ protected void implDereg(SelectionKeyImpl ski) throws IOException { @Override public void setEventOps(SelectionKeyImpl ski) { - ensureOpen(); synchronized (updateLock) { updateKeys.addLast(ski); } diff --git a/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java b/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java index 977a1653fdd31..eb681bc5ab935 100644 --- a/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java +++ b/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -606,7 +606,6 @@ protected void implDereg(SelectionKeyImpl ski) { @Override public void setEventOps(SelectionKeyImpl ski) { - ensureOpen(); synchronized (updateLock) { updateKeys.addLast(ski); } diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java b/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java index 19e3e62412bb6..9bcd800e13cb3 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,8 @@ package sun.nio.fs; -import jdk.internal.misc.PreviewFeatures; import jdk.internal.vm.Continuation; +import jdk.internal.vm.ContinuationSupport; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; @@ -106,7 +106,7 @@ static Privilege enablePrivilege(String priv) { final boolean needToRevert = elevated; // prevent yielding with privileges - if (PreviewFeatures.isEnabled()) + if (ContinuationSupport.isSupported()) Continuation.pin(); return () -> { @@ -126,7 +126,7 @@ else if (needToRevert) } } finally { LocalFree(pLuid); - if (PreviewFeatures.isEnabled()) + if (ContinuationSupport.isSupported()) Continuation.unpin(); } }; diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaProgressBarUI.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaProgressBarUI.java index bf66a1b4b0878..71500f3a95c92 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaProgressBarUI.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaProgressBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -256,7 +256,7 @@ protected Point getStringPlacement(final Graphics g, final String progressString y = oldX; } - return new Point(x + Math.round(width / 2 - stringWidth / 2), y + ((height + fontSizer.getAscent() - fontSizer.getLeading() - fontSizer.getDescent()) / 2) - 1); + return new Point(x + (width / 2 - stringWidth / 2), y + ((height + fontSizer.getAscent() - fontSizer.getLeading() - fontSizer.getDescent()) / 2) - 1); } static Dimension getCircularPreferredSize() { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java index 1326cee140371..f9f6307678108 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKEngine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ class GTKEngine { /** Size of the image cache */ private static final int CACHE_SIZE = 50; - /** This enum mirrors that in gtk2_interface.h */ + /** This enum mirrors that in gtk_interface.h */ static enum WidgetType { BUTTON, CHECK_BOX, CHECK_BOX_MENU_ITEM, COLOR_CHOOSER, COMBO_BOX, COMBO_BOX_ARROW_BUTTON, COMBO_BOX_TEXT_FIELD, @@ -493,13 +493,13 @@ public void paintShadow(Graphics g, SynthContext context, GTKLookAndFeel.synthStateToGTKStateType(state).ordinal(); int synthState = context.getComponentState(); Container parent = context.getComponent().getParent(); - if(GTKLookAndFeel.is3()) { - if (parent != null && parent.getParent() instanceof JComboBox) { - if (parent.getParent().hasFocus()) { - synthState |= SynthConstants.FOCUSED; - } + + if (parent != null && parent.getParent() instanceof JComboBox) { + if (parent.getParent().hasFocus()) { + synthState |= SynthConstants.FOCUSED; } } + int dir = getTextDirection(context); int widget = getWidgetType(context.getComponent(), id).ordinal(); native_paint_shadow(widget, gtkState, shadowType.ordinal(), detail, @@ -628,7 +628,7 @@ public void themeChanged() { cache.flush(); } - /* GtkSettings enum mirrors that in gtk2_interface.h */ + /* GtkSettings enum mirrors that in gtk_interface.h */ public Object getSetting(Settings property) { synchronized(sun.awt.UNIXToolkit.GTK_LOCK) { return native_get_gtk_setting(property.ordinal()); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java index 46f8d3ff189c0..4f04d729099f7 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ class GTKIconFactory { static final int CHECK_ICON_EXTRA_INSET = 1; static final int DEFAULT_ICON_SPACING = 2; static final int DEFAULT_ICON_SIZE = 13; - static final int DEFAULT_TOGGLE_MENU_ITEM_SIZE = 12; // For pre-gtk2.4 + static final int DEFAULT_TOGGLE_MENU_ITEM_SIZE = 12; private static final String RADIO_BUTTON_ICON = "paintRadioButtonIcon"; private static final String CHECK_BOX_ICON = "paintCheckBoxIcon"; @@ -214,7 +214,7 @@ int getIconDimension(SynthContext context) { Region region = context.getRegion(); GTKStyle style = (GTKStyle) context.getStyle(); - if (GTKLookAndFeel.is3() && region == Region.MENU) { + if (region == Region.MENU) { Object value = style.getClassSpecificValue("arrow-scaling"); if (value instanceof Number) { iconDimension = (int)(((Number) value).floatValue() * diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java index 93ba22d8dd3da..681c0e5195838 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java @@ -77,7 +77,6 @@ */ @SuppressWarnings("serial") // Superclass not serializable public class GTKLookAndFeel extends SynthLookAndFeel { - private static boolean IS_22; private static boolean IS_3; /** @@ -124,17 +123,6 @@ public class GTKLookAndFeel extends SynthLookAndFeel { */ private static String gtkThemeName = "Default"; - /** - * Returns true if running on system containing at least 2.2. - */ - static boolean is2_2() { - // NOTE: We're currently hard coding to use 2.2. - // If we want to support both GTK 2.0 and 2.2, we'll - // need to get the major/minor/micro version from the .so. - // Refer to bug 4912613 for details. - return IS_22; - } - static boolean is3() { return IS_3; } @@ -1454,17 +1442,7 @@ public void initialize() { throw new InternalError("Unable to load native GTK libraries"); } - if (UNIXToolkit.getGtkVersion() == UNIXToolkit.GtkVersions.GTK2) { - @SuppressWarnings("removal") - String version = AccessController.doPrivileged( - new GetPropertyAction("jdk.gtk.version")); - if (version != null) { - IS_22 = version.equals("2.2"); - } else { - IS_22 = true; - } - } else if (UNIXToolkit.getGtkVersion() == - UNIXToolkit.GtkVersions.GTK3) { + if (UNIXToolkit.getGtkVersion() == UNIXToolkit.GtkVersions.GTK3) { IS_3 = true; } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java index e643d3d28ff3d..ea27a2a6e0d9b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java @@ -211,20 +211,7 @@ public void paintRadioButtonMenuItemBackground(SynthContext context, int gtkState = GTKLookAndFeel.synthStateToGTKState( id, context.getComponentState()); if (gtkState == SynthConstants.MOUSE_OVER) { - if (GTKLookAndFeel.is3()) { - paintComponentBackground(context, g, x, y, w, h); - return; - } - synchronized (UNIXToolkit.GTK_LOCK) { - if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { - ShadowType shadow = (GTKLookAndFeel.is2_2() ? - ShadowType.NONE : ShadowType.OUT); - ENGINE.startPainting(g, x, y, w, h, id); - ENGINE.paintBox(g, context, id, gtkState, - shadow, "menuitem", x, y, w, h); - ENGINE.finishPainting(); - } - } + paintComponentBackground(context, g, x, y, w, h); } } @@ -570,21 +557,7 @@ public void paintMenuItemBackground(SynthContext context, int gtkState = GTKLookAndFeel.synthStateToGTKState( context.getRegion(), context.getComponentState()); if (gtkState == SynthConstants.MOUSE_OVER) { - if (GTKLookAndFeel.is3()) { - paintComponentBackground(context, g, x, y, w, h); - return; - } - Region id = Region.MENU_ITEM; - synchronized (UNIXToolkit.GTK_LOCK) { - if (! ENGINE.paintCachedImage(g, x, y, w, h, id)) { - ShadowType shadow = (GTKLookAndFeel.is2_2() ? - ShadowType.NONE : ShadowType.OUT); - ENGINE.startPainting(g, x, y, w, h, id); - ENGINE.paintBox(g, context, id, gtkState, shadow, - "menuitem", x, y, w, h); - ENGINE.finishPainting(); - } - } + paintComponentBackground(context, g, x, y, w, h); } } @@ -610,10 +583,10 @@ public void paintPopupMenuBackground(SynthContext context, Graphics g, x + insets.left, y + insets.top, w - insets.left - insets.right, h - insets.top - insets.bottom); BufferedImage img = ENGINE.finishPainting(); - if(!isHW) { + if (!isHW && img != null) { int border = img.getRGB(0, h / 2); - if (img != null && border == img.getRGB(w / 2, h / 2)) { - // fix no menu borders in Adwaita theme + if (border == img.getRGB(w / 2, h / 2)) { + // fix no menu borders Graphics g2 = img.getGraphics(); Color c = new Color(border); g2.setColor(new Color(Math.max((int) (c.getRed() * 0.8), 0), @@ -698,17 +671,14 @@ public void paintSeparatorBackground(SynthContext context, } else { h -= (insets.top + insets.bottom); } - if (GTKLookAndFeel.is3()) { - if (id == Region.POPUP_MENU_SEPARATOR) { - detail = "menuitem"; - h -= (insets.top + insets.bottom); - } else { - detail = "separator"; - } + + if (id == Region.POPUP_MENU_SEPARATOR) { + detail = "menuitem"; + h -= (insets.top + insets.bottom); } else { - detail = orientation == JSeparator.HORIZONTAL ? - "hseparator" : "vseparator"; + detail = "separator"; } + synchronized (UNIXToolkit.GTK_LOCK) { if (! ENGINE.paintCachedImage(g, x, y, w, h, id, state, detail, orientation)) { @@ -823,15 +793,15 @@ public void paintSliderTrackBackground(SynthContext context, // The ubuntulooks engine paints slider troughs differently depending // on the current slider value and its component orientation. JSlider slider = (JSlider)context.getComponent(); - if (GTKLookAndFeel.is3()) { - if (slider.getOrientation() == JSlider.VERTICAL) { - y += 1; - h -= 2; - } else { - x += 1; - w -= 2; - } + + if (slider.getOrientation() == JSlider.VERTICAL) { + y += 1; + h -= 2; + } else { + x += 1; + w -= 2; } + double value = slider.getValue(); double min = slider.getMinimum(); double max = slider.getMaximum(); @@ -865,7 +835,7 @@ public void paintSliderThumbBackground(SynthContext context, Region id = context.getRegion(); int gtkState = GTKLookAndFeel.synthStateToGTKState( id, context.getComponentState()); - boolean hasFocus = GTKLookAndFeel.is3() && + boolean hasFocus = ((context.getComponentState() & SynthConstants.FOCUSED) != 0); synchronized (UNIXToolkit.GTK_LOCK) { if (! ENGINE.paintCachedImage(g, x, y, w, h, id, gtkState, dir, @@ -965,25 +935,27 @@ public void paintTabbedPaneTabBackground(SynthContext context, Graphics g, int x, int y, int w, int h, int tabIndex) { - Region id = context.getRegion(); - int state = context.getComponentState(); - int gtkState = ((state & SynthConstants.SELECTED) != 0 ? - SynthConstants.ENABLED : SynthConstants.PRESSED); JTabbedPane pane = (JTabbedPane)context.getComponent(); - int placement = pane.getTabPlacement(); + if (UIManager.getBoolean("TabbedPane.tabsOpaque") || pane.isOpaque()) { + Region id = context.getRegion(); + int state = context.getComponentState(); + int gtkState = ((state & SynthConstants.SELECTED) != 0 ? + SynthConstants.ENABLED : SynthConstants.PRESSED); + int placement = pane.getTabPlacement(); - // Fill the tab rect area - g.fillRect(x, y, w, h); + // Fill the tab rect area + g.fillRect(x, y, w, h); - synchronized (UNIXToolkit.GTK_LOCK) { - if (! ENGINE.paintCachedImage(g, x, y, w, h, - id, gtkState, placement, tabIndex)) { - PositionType side = POSITIONS[placement - 1]; - ENGINE.startPainting(g, x, y, w, h, - id, gtkState, placement, tabIndex); - ENGINE.paintExtension(g, context, id, gtkState, - ShadowType.OUT, "tab", x, y, w, h, side, tabIndex); - ENGINE.finishPainting(); + synchronized (UNIXToolkit.GTK_LOCK) { + if (!ENGINE.paintCachedImage(g, x, y, w, h, + id, gtkState, placement, tabIndex)) { + PositionType side = POSITIONS[placement - 1]; + ENGINE.startPainting(g, x, y, w, h, + id, gtkState, placement, tabIndex); + ENGINE.paintExtension(g, context, id, gtkState, + ShadowType.OUT, "tab", x, y, w, h, side, tabIndex); + ENGINE.finishPainting(); + } } } } @@ -1059,21 +1031,10 @@ private void paintTextBackground(SynthContext context, Graphics g, int yThickness = style.getYThickness(); ENGINE.startPainting(g, x, y, w, h, id, state); - if (GTKLookAndFeel.is3()) { - ENGINE.paintBackground(g, context, id, gtkState, null, - x, y, w, h); - } + ENGINE.paintBackground(g, context, id, gtkState, null, + x, y, w, h); ENGINE.paintShadow(g, context, id, gtkState, ShadowType.IN, "entry", x, y, w, h); - if (!GTKLookAndFeel.is3()) { - ENGINE.paintFlatBox(g, context, id, - gtkState, ShadowType.NONE, "entry_bg", - x + xThickness, - y + yThickness, - w - (2 * xThickness), - h - (2 * yThickness), - ColorType.TEXT_BACKGROUND); - } if (focusSize > 0 && (state & SynthConstants.FOCUSED) != 0) { if (!interiorFocus) { @@ -1084,14 +1045,14 @@ private void paintTextBackground(SynthContext context, Graphics g, } else { if (containerParent instanceof JComboBox) { x += (focusSize + 2); - y += focusSize + (GTKLookAndFeel.is3() ? 3 : 1); - w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 1); - h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 6 : 2); + y += focusSize + 3; + w -= 2 * focusSize + 4; + h -= 2 * focusSize + 6; } else { - x += focusSize + (GTKLookAndFeel.is3() ? 2 : 0); - y += focusSize + (GTKLookAndFeel.is3() ? 2 :0 ); - w -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0); - h -= 2 * focusSize + (GTKLookAndFeel.is3() ? 4 : 0); + x += focusSize + 2; + y += focusSize + 2; + w -= 2 * focusSize + 4; + h -= 2 * focusSize + 4; } } ENGINE.paintFocus(g, context, id, gtkState, @@ -1437,11 +1398,6 @@ public void paintMenuArrowIcon(SynthContext context, Graphics g, if (gtkState == SynthConstants.MOUSE_OVER) { shadow = ShadowType.IN; } - if (!GTKLookAndFeel.is3()) { - x += 3; - y += 3; - w = h = 7; - } ENGINE.paintArrow(g, context, Region.MENU_ITEM, gtkState, shadow, dir, "menuitem", x, y, w, h); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java index 3ba534ea2a723..90c051353686e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java @@ -751,14 +751,7 @@ public boolean isOpaque(SynthContext context) { region == Region.EDITOR_PANE) { return true; } - if (!GTKLookAndFeel.is3()) { - if (region == Region.FORMATTED_TEXT_FIELD || - region == Region.PASSWORD_FIELD || - region == Region.SPINNER || - region == Region.TEXT_FIELD) { - return true; - } - } + Component c = context.getComponent(); String name = c.getName(); if (name == "ComboBox.renderer" || name == "ComboBox.listRenderer") { @@ -884,7 +877,7 @@ else if ("CheckBox.iconTextGap".equals(key) || int focusPad = getClassSpecificIntValue(context, "focus-padding", 1); return indicatorSpacing + focusSize + focusPad; - } else if (GTKLookAndFeel.is3() && "ComboBox.forceOpaque".equals(key)) { + } else if ("ComboBox.forceOpaque".equals(key)) { return true; } else if ("Tree.expanderSize".equals(key)) { Object value = getClassSpecificValue("expander-size"); diff --git a/src/java.desktop/share/classes/javax/swing/JOptionPane.java b/src/java.desktop/share/classes/javax/swing/JOptionPane.java index 930acc90b35c3..1c1f3ee9ba355 100644 --- a/src/java.desktop/share/classes/javax/swing/JOptionPane.java +++ b/src/java.desktop/share/classes/javax/swing/JOptionPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -883,6 +883,11 @@ public static int showOptionDialog(Component parentComponent, Object selectedValue = pane.getValue(); + if (parentComponent != null) { + parentComponent.revalidate(); + parentComponent.repaint(); + } + if(selectedValue == null) return CLOSED_OPTION; if(options == null) { diff --git a/src/java.desktop/share/classes/javax/swing/JSplitPane.java b/src/java.desktop/share/classes/javax/swing/JSplitPane.java index 7d4482af07ea8..90f3efb3e69cf 100644 --- a/src/java.desktop/share/classes/javax/swing/JSplitPane.java +++ b/src/java.desktop/share/classes/javax/swing/JSplitPane.java @@ -26,6 +26,7 @@ package javax.swing; import java.awt.Component; +import java.awt.ComponentOrientation; import java.awt.Graphics; import java.beans.BeanProperty; import java.beans.ConstructorProperties; @@ -362,6 +363,32 @@ public JSplitPane(int newOrientation, } + /** + * {@inheritDoc} + * @param orientation {@inheritDoc} + */ + @Override + public void setComponentOrientation(ComponentOrientation orientation) { + super.setComponentOrientation(orientation); + Component leftComponent = this.getLeftComponent(); + Component rightComponent = this.getRightComponent(); + if (!this.getComponentOrientation().isLeftToRight()) { + if (rightComponent != null) { + setLeftComponent(rightComponent); + } + if (leftComponent != null) { + setRightComponent(leftComponent); + } + } else { + if (leftComponent != null) { + setLeftComponent(leftComponent); + } + if (rightComponent != null) { + setRightComponent(rightComponent); + } + } + } + /** * {@inheritDoc} * @param enabled {@inheritDoc} diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java index c990c968ff6fa..9dfd422f6dddb 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/SynthPainterImpl.java @@ -2098,24 +2098,27 @@ public void paintTabbedPaneTabBackground(SynthContext context, Graphics g, public void paintTabbedPaneTabBackground(SynthContext context, Graphics g, int x, int y, int w, int h, int tabIndex, int orientation) { - if (orientation == JTabbedPane.LEFT) { - AffineTransform transform = new AffineTransform(); - transform.scale(-1, 1); - transform.rotate(Math.toRadians(90)); - paintBackground(context, g, y, x, h, w, transform); - } else if (orientation == JTabbedPane.RIGHT) { - AffineTransform transform = new AffineTransform(); - transform.rotate(Math.toRadians(90)); - transform.translate(0, -(x + w)); - paintBackground(context, g, y, 0, h, w, transform); - } else if (orientation == JTabbedPane.BOTTOM) { - AffineTransform transform = new AffineTransform(); - transform.translate(x,y); - transform.scale(1, -1); - transform.translate(0,-h); - paintBackground(context, g, 0, 0, w, h, transform); - } else { - paintBackground(context, g, x, y, w, h, null); + JTabbedPane pane = (JTabbedPane)context.getComponent(); + if (UIManager.getBoolean("TabbedPane.tabsOpaque") || pane.isOpaque()) { + if (orientation == JTabbedPane.LEFT) { + AffineTransform transform = new AffineTransform(); + transform.scale(-1, 1); + transform.rotate(Math.toRadians(90)); + paintBackground(context, g, y, x, h, w, transform); + } else if (orientation == JTabbedPane.RIGHT) { + AffineTransform transform = new AffineTransform(); + transform.rotate(Math.toRadians(90)); + transform.translate(0, -(x + w)); + paintBackground(context, g, y, 0, h, w, transform); + } else if (orientation == JTabbedPane.BOTTOM) { + AffineTransform transform = new AffineTransform(); + transform.translate(x, y); + transform.scale(1, -1); + transform.translate(0, -h); + paintBackground(context, g, 0, 0, w, h, transform); + } else { + paintBackground(context, g, x, y, w, h, null); + } } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java index 837c4d8298f9e..3378ef9e8b3de 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java @@ -127,7 +127,6 @@ public class SynthTabbedPaneUI extends BasicTabbedPaneUI // Background color for unselected tabs private Color unselectedBackground; private boolean contentOpaque = true; - private boolean tabsOpaque = true; /** * @@ -156,7 +155,6 @@ private boolean scrollableTabLayoutEnabled() { protected void installDefaults() { selectColor = UIManager.getColor("TabbedPane.selected"); contentOpaque = UIManager.getBoolean("TabbedPane.contentOpaque"); - tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); unselectedBackground = UIManager.getColor("TabbedPane.unselectedBackground"); updateStyle(tabPane); } @@ -655,10 +653,8 @@ private void paintTab(SynthContext ss, Graphics g, g.setColor(getUnselectedBackgroundAt(tabIndex)); } - if (tabsOpaque || tabPane.isOpaque()) { - tabContext.getPainter().paintTabbedPaneTabBackground(tabContext, g, - x, y, width, height, tabIndex, placement); - } + tabContext.getPainter().paintTabbedPaneTabBackground(tabContext, g, + x, y, width, height, tabIndex, placement); tabContext.getPainter().paintTabbedPaneTabBorder(tabContext, g, x, y, width, height, tabIndex, placement); diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index 595799926a307..8239565a53f21 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -844,6 +844,10 @@ Object getInternalCSSValue(CSS.Attribute key, String value) { } static Object mergeTextDecoration(String value) { + if (value.startsWith("none")) { + return null; + } + boolean underline = value.contains("underline"); boolean strikeThrough = value.contains("line-through"); if (!underline && !strikeThrough) { diff --git a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java index 0790060f5e8c8..64d2e0e3bbf8a 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java @@ -724,7 +724,7 @@ public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs) { * to return an AttributeSet that provides some sort of * attribute conversion. * - * @param a The set of attributes to be represented in the + * @param a The set of attributes to be represented in * the compact form. */ protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) { @@ -740,7 +740,7 @@ protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) { * to return a MutableAttributeSet that provides some sort of * attribute conversion. * - * @param a The set of attributes to be represented in the + * @param a The set of attributes to be represented in * the larger form. */ protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) { @@ -2173,7 +2173,7 @@ public static final class ListPainter implements Serializable { /** * Returns a string that represents the value * of the HTML.Attribute.TYPE attribute. - * If this attributes is not defined, then + * If this attributes is not defined, * then the type defaults to "disc" unless * the tag is on Ordered list. In the case * of the latter, the default type is "decimal". @@ -2391,7 +2391,7 @@ void drawShape(Graphics g, CSS.Value type, int ax, int ay, int aw, // Position shape to the middle of the html text. int gap = isLeftToRight ? - (bulletgap + size/3) : (aw + bulletgap); int x = ax + gap; - int y = Math.max(ay, ay + (int)Math.ceil(ah/2)); + int y = Math.max(ay, ay + ah/2); if (type == CSS.Value.SQUARE) { g.drawRect(x, y, size/3, size/3); diff --git a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index 0dfa5e42d9a89..82f47824bfae7 100644 --- a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -2474,11 +2474,7 @@ public void cancel() { } } - /** - * Returns true is a print job is ongoing but will - * be cancelled and the next opportunity. false is - * returned otherwise. - */ + @Override public boolean isCancelled() { boolean cancelled = false; diff --git a/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java b/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java index 191a12092a1ff..d509fe802b028 100644 --- a/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java +++ b/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java @@ -70,19 +70,12 @@ public abstract class UNIXToolkit extends SunToolkit private static final int[] BAND_OFFSETS_ALPHA = { 0, 1, 2, 3 }; private static final int DEFAULT_DATATRANSFER_TIMEOUT = 10000; - private static final String GTK2_DEPRECATION_MESSAGE = - "WARNING: the GTK 2 library is deprecated and " + - "its support will be removed in a future release"; - private static volatile boolean gtk2WarningIssued = false; - // Allowed GTK versions public enum GtkVersions { ANY(0), - GTK2(Constants.GTK2_MAJOR_NUMBER), GTK3(Constants.GTK3_MAJOR_NUMBER); static class Constants { - static final int GTK2_MAJOR_NUMBER = 2; static final int GTK3_MAJOR_NUMBER = 3; } @@ -94,8 +87,6 @@ static class Constants { public static GtkVersions getVersion(int number) { switch (number) { - case Constants.GTK2_MAJOR_NUMBER: - return GTK2; case Constants.GTK3_MAJOR_NUMBER: return GTK3; default: @@ -498,15 +489,7 @@ public static GtkVersions getEnabledGtkVersion() { @SuppressWarnings("removal") String version = AccessController.doPrivileged( new GetPropertyAction("jdk.gtk.version")); - if (version == null) { - return GtkVersions.ANY; - } else if (version.startsWith("2")) { - if (!gtk2WarningIssued) { - System.err.println(GTK2_DEPRECATION_MESSAGE); - gtk2WarningIssued = true; - } - return GtkVersions.GTK2; - } else if("3".equals(version) ){ + if ("3".equals(version)) { return GtkVersions.GTK3; } return GtkVersions.ANY; diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XCheckboxPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XCheckboxPeer.java index 88f1784889e73..c66cda1644372 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XCheckboxPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XCheckboxPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -362,12 +362,15 @@ public void paintCheckbox(Graphics g, if (armed | selected) { //Paint the check + AffineTransform af = g2.getTransform(); + double scaleX = af.getScaleX(); + double scaleY = af.getScaleY(); // FIXME: is this the right color? g2.setColor(getPeerForeground()); - AffineTransform af = g2.getTransform(); - g2.setTransform(AffineTransform.getTranslateInstance(rx,ry)); + g2.setTransform(AffineTransform.getTranslateInstance(rx * scaleX, ry * scaleY)); + g2.scale(scaleX, scaleY); g2.fill(myCheckMark); g2.setTransform(af); } diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java index 3daf9b2a8b8e8..b05ff7f8c4add 100644 --- a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java +++ b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java @@ -60,35 +60,33 @@ * The restore token allows the ScreenCast session to be restored * with previously granted screen access permissions. */ -@SuppressWarnings("removal") final class TokenStorage { private TokenStorage() {} private static final String REL_NAME = + ".java/robot/screencast-tokens.properties"; + private static final String REL_NAME_SECONDARY = ".awt/robot/screencast-tokens.properties"; private static final Properties PROPS = new Properties(); private static final Path PROPS_PATH; private static final Path PROP_FILENAME; + @SuppressWarnings("removal") private static void doPrivilegedRunnable(Runnable runnable) { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - runnable.run(); - return null; - } + AccessController.doPrivileged((PrivilegedAction) () -> { + runnable.run(); + return null; }); } static { - PROPS_PATH = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Path run() { - return setupPath(); - } - }); + @SuppressWarnings("removal") + Path propsPath = AccessController + .doPrivileged((PrivilegedAction) () -> setupPath()); + + PROPS_PATH = propsPath; if (PROPS_PATH != null) { PROP_FILENAME = PROPS_PATH.getFileName(); @@ -110,25 +108,32 @@ private static Path setupPath() { } Path path = Path.of(userHome, REL_NAME); + Path secondaryPath = Path.of(userHome, REL_NAME_SECONDARY); + + boolean copyFromSecondary = !Files.isWritable(path) + && Files.isWritable(secondaryPath); + Path workdir = path.getParent(); - if (!Files.exists(workdir)) { - try { - Files.createDirectories(workdir); - } catch (Exception e) { - if (SCREENCAST_DEBUG) { - System.err.printf("Token storage: cannot create" + - " directory %s %s\n", workdir, e); + if (!Files.isWritable(path)) { + if (!Files.exists(workdir)) { + try { + Files.createDirectories(workdir); + } catch (Exception e) { + if (SCREENCAST_DEBUG) { + System.err.printf("Token storage: cannot create" + + " directory %s %s\n", workdir, e); + } + return null; } - return null; } - } - if (!Files.isWritable(workdir)) { - if (SCREENCAST_DEBUG) { - System.err.printf("Token storage: %s is not writable\n", workdir); + if (!Files.isWritable(workdir)) { + if (SCREENCAST_DEBUG) { + System.err.printf("Token storage: %s is not writable\n", workdir); + } + return null; } - return null; } try { @@ -145,7 +150,17 @@ private static Path setupPath() { } } - if (Files.exists(path)) { + if (copyFromSecondary) { + if (SCREENCAST_DEBUG) { + System.out.println("Token storage: copying from the secondary location " + + secondaryPath); + } + synchronized (PROPS) { + if (readTokens(secondaryPath)) { + store(path, "copy from the secondary location"); + } + } + } else if (Files.exists(path)) { if (!setFilePermission(path)) { return null; } @@ -302,7 +317,7 @@ private static void storeTokenFromNative(String oldToken, } if (changed) { - doPrivilegedRunnable(() -> store("save tokens")); + doPrivilegedRunnable(() -> store(PROPS_PATH, "save tokens")); } } } @@ -315,7 +330,7 @@ private static boolean readTokens(Path path) { PROPS.clear(); PROPS.load(reader); } - } catch (IOException e) { + } catch (IOException | IllegalArgumentException e) { if (SCREENCAST_DEBUG) { System.err.printf(""" Token storage: failed to load property file %s @@ -410,7 +425,7 @@ static Set getTokens(List affectedScreenBounds) { } private static void removeMalformedRecords(Set malformedRecords) { - if (!isWritable() + if (!isWritable(PROPS_PATH) || malformedRecords == null || malformedRecords.isEmpty()) { return; @@ -424,17 +439,17 @@ private static void removeMalformedRecords(Set malformedRecords) { } } - store("remove malformed records"); + store(PROPS_PATH, "remove malformed records"); } } - private static void store(String failMsg) { - if (!isWritable()) { + private static void store(Path path, String failMsg) { + if (!isWritable(path)) { return; } synchronized (PROPS) { - try (BufferedWriter writer = Files.newBufferedWriter(PROPS_PATH)) { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { PROPS.store(writer, null); } catch (IOException e) { if (SCREENCAST_DEBUG) { @@ -445,13 +460,13 @@ private static void store(String failMsg) { } } - private static boolean isWritable() { - if (PROPS_PATH == null - || (Files.exists(PROPS_PATH) && !Files.isWritable(PROPS_PATH))) { + private static boolean isWritable(Path path) { + if (path == null + || (Files.exists(path) && !Files.isWritable(path))) { if (SCREENCAST_DEBUG) { System.err.printf( - "Token storage: %s is not writable\n", PROPS_PATH); + "Token storage: %s is not writable\n", path); } return false; } else { diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c deleted file mode 100644 index 7dba83e9024d2..0000000000000 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c +++ /dev/null @@ -1,2603 +0,0 @@ -/* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifdef HEADLESS - #error This file should not be included in headless library -#endif - -#include -#include -#include -#include -#include -#include -#include "gtk2_interface.h" -#include "java_awt_Transparency.h" -#include "jvm_md.h" -#include "sizecalc.h" -#include -#include "awt.h" - -#define GTK_TYPE_BORDER ((*fp_gtk_border_get_type)()) - -#define G_TYPE_FUNDAMENTAL_SHIFT (2) -#define G_TYPE_MAKE_FUNDAMENTAL(x) ((GType) ((x) << G_TYPE_FUNDAMENTAL_SHIFT)) - -#define CONV_BUFFER_SIZE 128 - -#define NO_SYMBOL_EXCEPTION 1 - -static void *gtk2_libhandle = NULL; -static void *gthread_libhandle = NULL; - -static jmp_buf j; - -/* Widgets */ -static GtkWidget *gtk2_widget = NULL; -static GtkWidget *gtk2_window = NULL; -static GtkFixed *gtk2_fixed = NULL; - -/* Paint system */ -static GdkPixmap *gtk2_white_pixmap = NULL; -static GdkPixmap *gtk2_black_pixmap = NULL; -static GdkPixbuf *gtk2_white_pixbuf = NULL; -static GdkPixbuf *gtk2_black_pixbuf = NULL; -static int gtk2_pixbuf_width = 0; -static int gtk2_pixbuf_height = 0; - -/* Static buffer for conversion from java.lang.String to UTF-8 */ -static char convertionBuffer[CONV_BUFFER_SIZE]; - -static gboolean new_combo = TRUE; -const char ENV_PREFIX[] = "GTK_MODULES="; - - -static GtkWidget *gtk2_widgets[_GTK_WIDGET_TYPE_SIZE]; - -/************************* - * Glib function pointers - *************************/ - -static gboolean (*fp_g_main_context_iteration)(GMainContext *context, - gboolean may_block); - -static GValue* (*fp_g_value_init)(GValue *value, GType g_type); -static gboolean (*fp_g_type_is_a)(GType type, GType is_a_type); -static gboolean (*fp_g_value_get_boolean)(const GValue *value); -static gchar (*fp_g_value_get_char)(const GValue *value); -static guchar (*fp_g_value_get_uchar)(const GValue *value); -static gint (*fp_g_value_get_int)(const GValue *value); -static guint (*fp_g_value_get_uint)(const GValue *value); -static glong (*fp_g_value_get_long)(const GValue *value); -static gulong (*fp_g_value_get_ulong)(const GValue *value); -static gint64 (*fp_g_value_get_int64)(const GValue *value); -static guint64 (*fp_g_value_get_uint64)(const GValue *value); -static gfloat (*fp_g_value_get_float)(const GValue *value); -static gdouble (*fp_g_value_get_double)(const GValue *value); -static const gchar* (*fp_g_value_get_string)(const GValue *value); -static gint (*fp_g_value_get_enum)(const GValue *value); -static guint (*fp_g_value_get_flags)(const GValue *value); -static GParamSpec* (*fp_g_value_get_param)(const GValue *value); -static gpointer* (*fp_g_value_get_boxed)(const GValue *value); -static gpointer* (*fp_g_value_get_pointer)(const GValue *value); -static GObject* (*fp_g_value_get_object)(const GValue *value); -static GParamSpec* (*fp_g_param_spec_int)(const gchar *name, - const gchar *nick, const gchar *blurb, - gint minimum, gint maximum, gint default_value, - GParamFlags flags); -static void (*fp_g_object_get)(gpointer object, - const gchar* fpn, ...); -static void (*fp_g_object_set)(gpointer object, - const gchar *first_property_name, - ...); -/************************ - * GDK function pointers - ************************/ -static GdkPixmap *(*fp_gdk_pixmap_new)(GdkDrawable *drawable, - gint width, gint height, gint depth); -static GdkGC *(*fp_gdk_gc_new)(GdkDrawable*); -static void (*fp_gdk_rgb_gc_set_foreground)(GdkGC*, guint32); -static void (*fp_gdk_draw_rectangle)(GdkDrawable*, GdkGC*, gboolean, - gint, gint, gint, gint); -static GdkPixbuf *(*fp_gdk_pixbuf_new)(GdkColorspace colorspace, - gboolean has_alpha, int bits_per_sample, int width, int height); -static void (*fp_gdk_drawable_get_size)(GdkDrawable *drawable, - gint* width, gint* height); - -/************************ - * Gtk function pointers - ************************/ -static gboolean (*fp_gtk_init_check)(int* argc, char** argv); - -/* Painting */ -static void (*fp_gtk_paint_hline)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GdkRectangle* area, GtkWidget* widget, - const gchar* detail, gint x1, gint x2, gint y); -static void (*fp_gtk_paint_vline)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GdkRectangle* area, GtkWidget* widget, - const gchar* detail, gint y1, gint y2, gint x); -static void (*fp_gtk_paint_shadow)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height); -static void (*fp_gtk_paint_arrow)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - GtkArrowType arrow_type, gboolean fill, gint x, gint y, - gint width, gint height); -static void (*fp_gtk_paint_box)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height); -static void (*fp_gtk_paint_flat_box)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height); -static void (*fp_gtk_paint_check)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height); -static void (*fp_gtk_paint_option)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height); -static void (*fp_gtk_paint_box_gap)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height, - GtkPositionType gap_side, gint gap_x, gint gap_width); -static void (*fp_gtk_paint_extension)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height, GtkPositionType gap_side); -static void (*fp_gtk_paint_focus)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GdkRectangle* area, GtkWidget* widget, - const gchar* detail, gint x, gint y, gint width, gint height); -static void (*fp_gtk_paint_slider)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height, GtkOrientation orientation); -static void (*fp_gtk_paint_handle)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height, GtkOrientation orientation); -static void (*fp_gtk_paint_expander)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GdkRectangle* area, GtkWidget* widget, - const gchar* detail, gint x, gint y, GtkExpanderStyle expander_style); -static void (*fp_gtk_style_apply_default_background)(GtkStyle* style, - GdkWindow* window, gboolean set_bg, GtkStateType state_type, - GdkRectangle* area, gint x, gint y, gint width, gint height); - -/* Widget creation */ -static GtkWidget* (*fp_gtk_arrow_new)(GtkArrowType arrow_type, - GtkShadowType shadow_type); -static GtkWidget* (*fp_gtk_button_new)(); -static GtkWidget* (*fp_gtk_check_button_new)(); -static GtkWidget* (*fp_gtk_check_menu_item_new)(); -static GtkWidget* (*fp_gtk_color_selection_dialog_new)(const gchar* title); -static GtkWidget* (*fp_gtk_combo_box_new)(); -static GtkWidget* (*fp_gtk_combo_box_entry_new)(); -static GtkWidget* (*fp_gtk_entry_new)(); -static GtkWidget* (*fp_gtk_fixed_new)(); -static GtkWidget* (*fp_gtk_handle_box_new)(); -static GtkWidget* (*fp_gtk_hpaned_new)(); -static GtkWidget* (*fp_gtk_vpaned_new)(); -static GtkWidget* (*fp_gtk_hscale_new)(GtkAdjustment* adjustment); -static GtkWidget* (*fp_gtk_vscale_new)(GtkAdjustment* adjustment); -static GtkWidget* (*fp_gtk_hscrollbar_new)(GtkAdjustment* adjustment); -static GtkWidget* (*fp_gtk_vscrollbar_new)(GtkAdjustment* adjustment); -static GtkWidget* (*fp_gtk_hseparator_new)(); -static GtkWidget* (*fp_gtk_vseparator_new)(); -static GtkWidget* (*fp_gtk_image_new)(); -static GtkWidget* (*fp_gtk_label_new)(const gchar* str); -static GtkWidget* (*fp_gtk_menu_new)(); -static GtkWidget* (*fp_gtk_menu_bar_new)(); -static GtkWidget* (*fp_gtk_menu_item_new)(); -static GtkWidget* (*fp_gtk_notebook_new)(); -static GtkWidget* (*fp_gtk_progress_bar_new)(); -static GtkWidget* (*fp_gtk_progress_bar_set_orientation)( - GtkProgressBar *pbar, - GtkProgressBarOrientation orientation); -static GtkWidget* (*fp_gtk_radio_button_new)(GSList *group); -static GtkWidget* (*fp_gtk_radio_menu_item_new)(GSList *group); -static GtkWidget* (*fp_gtk_scrolled_window_new)(GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); -static GtkWidget* (*fp_gtk_separator_menu_item_new)(); -static GtkWidget* (*fp_gtk_separator_tool_item_new)(); -static GtkWidget* (*fp_gtk_text_view_new)(); -static GtkWidget* (*fp_gtk_toggle_button_new)(); -static GtkWidget* (*fp_gtk_toolbar_new)(); -static GtkWidget* (*fp_gtk_tree_view_new)(); -static GtkWidget* (*fp_gtk_viewport_new)(GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); -static GtkWidget* (*fp_gtk_window_new)(GtkWindowType type); -static GtkWidget* (*fp_gtk_dialog_new)(); -static GtkWidget* (*fp_gtk_spin_button_new)(GtkAdjustment *adjustment, - gdouble climb_rate, guint digits); -static GtkWidget* (*fp_gtk_frame_new)(const gchar *label); - -/* Other widget operations */ -static GtkObject* (*fp_gtk_adjustment_new)(gdouble value, - gdouble lower, gdouble upper, gdouble step_increment, - gdouble page_increment, gdouble page_size); -static void (*fp_gtk_container_add)(GtkContainer *window, GtkWidget *widget); -static void (*fp_gtk_menu_shell_append)(GtkMenuShell *menu_shell, - GtkWidget *child); -static void (*fp_gtk_menu_item_set_submenu)(GtkMenuItem *menu_item, - GtkWidget *submenu); -static void (*fp_gtk_widget_realize)(GtkWidget *widget); -static GdkPixbuf* (*fp_gtk_widget_render_icon)(GtkWidget *widget, - const gchar *stock_id, GtkIconSize size, const gchar *detail); -static void (*fp_gtk_widget_set_name)(GtkWidget *widget, const gchar *name); -static void (*fp_gtk_widget_set_parent)(GtkWidget *widget, GtkWidget *parent); -static void (*fp_gtk_widget_set_direction)(GtkWidget *widget, - GtkTextDirection direction); -static void (*fp_gtk_widget_style_get)(GtkWidget *widget, - const gchar *first_property_name, ...); -static void (*fp_gtk_widget_class_install_style_property)( - GtkWidgetClass* class, GParamSpec *pspec); -static GParamSpec* (*fp_gtk_widget_class_find_style_property)( - GtkWidgetClass* class, const gchar* property_name); -static void (*fp_gtk_widget_style_get_property)(GtkWidget* widget, - const gchar* property_name, GValue* value); -static char* (*fp_pango_font_description_to_string)( - const PangoFontDescription* fd); -static GtkSettings* (*fp_gtk_settings_get_default)(); -static GtkSettings* (*fp_gtk_widget_get_settings)(GtkWidget *widget); -static GType (*fp_gtk_border_get_type)(); -static void (*fp_gtk_arrow_set)(GtkWidget* arrow, - GtkArrowType arrow_type, - GtkShadowType shadow_type); -static void (*fp_gtk_widget_size_request)(GtkWidget *widget, - GtkRequisition *requisition); -static GtkAdjustment* (*fp_gtk_range_get_adjustment)(GtkRange* range); - -/* Method bodies */ - -static void throw_exception(JNIEnv *env, const char* name, const char* message) -{ - jclass class = (*env)->FindClass(env, name); - - if (class != NULL) - (*env)->ThrowNew(env, class, message); - - (*env)->DeleteLocalRef(env, class); -} - -/* This is a workaround for the bug: - * http://sourceware.org/bugzilla/show_bug.cgi?id=1814 - * (dlsym/dlopen clears dlerror state) - * This bug is specific to Linux, but there is no harm in - * applying this workaround on Solaris as well. - */ -static void* dl_symbol(const char* name) -{ - void* result = dlsym(gtk2_libhandle, name); - if (!result) - longjmp(j, NO_SYMBOL_EXCEPTION); - - return result; -} - -static void* dl_symbol_gthread(const char* name) -{ - void* result = dlsym(gthread_libhandle, name); - if (!result) - longjmp(j, NO_SYMBOL_EXCEPTION); - - return result; -} - -gboolean gtk2_check(const char* lib_name, gboolean load) -{ - if (gtk2_libhandle != NULL) { - /* We've already successfully opened the GTK libs, so return true. */ - return TRUE; - } else { - void *lib = NULL; - -#ifdef RTLD_NOLOAD - /* Just check if gtk libs are already in the process space */ - lib = dlopen(lib_name, RTLD_LAZY | RTLD_NOLOAD); - if (!load || lib != NULL) { - return lib != NULL; - } -#else -#ifdef _AIX - /* On AIX we could implement this with the help of loadquery(L_GETINFO, ..) */ - /* (see reload_table() in hotspot/src/os/aix/vm/loadlib_aix.cpp) but it is */ - /* probably not worth it because most AIX servers don't have GTK libs anyway */ -#endif -#endif - - lib = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL); - if (lib == NULL) { - return FALSE; - } - - fp_gtk_check_version = dlsym(lib, "gtk_check_version"); - /* Check for GTK 2.2+ */ - if (!fp_gtk_check_version(2, 2, 0)) { - return TRUE; - } - - // 8048289: workaround for https://bugzilla.gnome.org/show_bug.cgi?id=733065 - // dlclose(lib); - - return FALSE; - } -} - -#define ADD_SUPPORTED_ACTION(actionStr) \ -do { \ - jfieldID fld_action = (*env)->GetStaticFieldID(env, cls_action, actionStr, "Ljava/awt/Desktop$Action;"); \ - if (!(*env)->ExceptionCheck(env)) { \ - jobject action = (*env)->GetStaticObjectField(env, cls_action, fld_action); \ - (*env)->CallBooleanMethod(env, supportedActions, mid_arrayListAdd, action); \ - } else { \ - (*env)->ExceptionClear(env); \ - } \ -} while(0); - - -static void update_supported_actions(JNIEnv *env) { - GVfs * (*fp_g_vfs_get_default) (void); - const gchar * const * (*fp_g_vfs_get_supported_uri_schemes) (GVfs * vfs); - const gchar * const * schemes = NULL; - - jclass cls_action = (*env)->FindClass(env, "java/awt/Desktop$Action"); - CHECK_NULL(cls_action); - jclass cls_xDesktopPeer = (*env)->FindClass(env, "sun/awt/X11/XDesktopPeer"); - CHECK_NULL(cls_xDesktopPeer); - jfieldID fld_supportedActions = (*env)->GetStaticFieldID(env, cls_xDesktopPeer, "supportedActions", "Ljava/util/List;"); - CHECK_NULL(fld_supportedActions); - jobject supportedActions = (*env)->GetStaticObjectField(env, cls_xDesktopPeer, fld_supportedActions); - - jclass cls_arrayList = (*env)->FindClass(env, "java/util/ArrayList"); - CHECK_NULL(cls_arrayList); - jmethodID mid_arrayListAdd = (*env)->GetMethodID(env, cls_arrayList, "add", "(Ljava/lang/Object;)Z"); - CHECK_NULL(mid_arrayListAdd); - jmethodID mid_arrayListClear = (*env)->GetMethodID(env, cls_arrayList, "clear", "()V"); - CHECK_NULL(mid_arrayListClear); - - (*env)->CallVoidMethod(env, supportedActions, mid_arrayListClear); - - ADD_SUPPORTED_ACTION("OPEN"); - - /** - * gtk_show_uri() documentation says: - * - * > you need to install gvfs to get support for uri schemes such as http:// - * > or ftp://, as only local files are handled by GIO itself. - * - * So OPEN action was safely added here. - * However, it looks like Solaris 11 have gvfs support only for 32-bit - * applications only by default. - */ - - fp_g_vfs_get_default = dl_symbol("g_vfs_get_default"); - fp_g_vfs_get_supported_uri_schemes = dl_symbol("g_vfs_get_supported_uri_schemes"); - dlerror(); - - if (fp_g_vfs_get_default && fp_g_vfs_get_supported_uri_schemes) { - GVfs * vfs = fp_g_vfs_get_default(); - schemes = vfs ? fp_g_vfs_get_supported_uri_schemes(vfs) : NULL; - if (schemes) { - int i = 0; - while (schemes[i]) { - if (strcmp(schemes[i], "http") == 0) { - ADD_SUPPORTED_ACTION("BROWSE"); - ADD_SUPPORTED_ACTION("MAIL"); - break; - } - i++; - } - } - } else { -#ifdef DEBUG - fprintf(stderr, "Cannot load g_vfs_get_supported_uri_schemes\n"); -#endif /* DEBUG */ - } - -} -/** - * Functions for awt_Desktop.c - */ -static gboolean gtk2_show_uri_load(JNIEnv *env) { - gboolean success = FALSE; - dlerror(); - const char *gtk_version = fp_gtk_check_version(2, 14, 0); - if (gtk_version != NULL) { - // The gtk_show_uri is available from GTK+ 2.14 -#ifdef DEBUG - fprintf (stderr, "The version of GTK is %s. " - "The gtk_show_uri function is supported " - "since GTK+ 2.14.\n", gtk_version); -#endif /* DEBUG */ - } else { - // Loading symbols only if the GTK version is 2.14 and higher - fp_gtk_show_uri = dl_symbol("gtk_show_uri"); - const char *dlsym_error = dlerror(); - if (dlsym_error) { -#ifdef DEBUG - fprintf (stderr, "Cannot load symbol: %s \n", dlsym_error); -#endif /* DEBUG */ - } else if (fp_gtk_show_uri == NULL) { -#ifdef DEBUG - fprintf(stderr, "dlsym(gtk_show_uri) returned NULL\n"); -#endif /* DEBUG */ - } else { - gtk->gtk_show_uri = fp_gtk_show_uri; - update_supported_actions(env); - success = TRUE; - } - } - return success; -} - -/** - * Functions for sun_awt_X11_GtkFileDialogPeer.c - */ -static void gtk2_file_chooser_load() -{ - fp_gtk_file_chooser_get_filename = dl_symbol( - "gtk_file_chooser_get_filename"); - fp_gtk_file_chooser_dialog_new = dl_symbol("gtk_file_chooser_dialog_new"); - fp_gtk_file_chooser_set_current_folder = dl_symbol( - "gtk_file_chooser_set_current_folder"); - fp_gtk_file_chooser_set_filename = dl_symbol( - "gtk_file_chooser_set_filename"); - fp_gtk_file_chooser_set_current_name = dl_symbol( - "gtk_file_chooser_set_current_name"); - fp_gtk_file_filter_add_custom = dl_symbol("gtk_file_filter_add_custom"); - fp_gtk_file_chooser_set_filter = dl_symbol("gtk_file_chooser_set_filter"); - fp_gtk_file_chooser_get_type = dl_symbol("gtk_file_chooser_get_type"); - fp_gtk_file_filter_new = dl_symbol("gtk_file_filter_new"); - if (fp_gtk_check_version(2, 8, 0) == NULL) { - fp_gtk_file_chooser_set_do_overwrite_confirmation = dl_symbol( - "gtk_file_chooser_set_do_overwrite_confirmation"); - } - fp_gtk_file_chooser_set_select_multiple = dl_symbol( - "gtk_file_chooser_set_select_multiple"); - fp_gtk_file_chooser_get_current_folder = dl_symbol( - "gtk_file_chooser_get_current_folder"); - fp_gtk_file_chooser_get_filenames = dl_symbol( - "gtk_file_chooser_get_filenames"); - fp_gtk_g_slist_length = dl_symbol("g_slist_length"); - fp_gdk_x11_drawable_get_xid = dl_symbol("gdk_x11_drawable_get_xid"); -} - -GtkApi* gtk2_load(JNIEnv *env, const char* lib_name) -{ - gboolean result; - int i; - int (*handler)(); - int (*io_handler)(); - char *gtk_modules_env; - - gtk2_libhandle = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL); - if (gtk2_libhandle == NULL) { - return FALSE; - } - - gthread_libhandle = dlopen(GTHREAD_LIB_VERSIONED, RTLD_LAZY | RTLD_LOCAL); - if (gthread_libhandle == NULL) { - gthread_libhandle = dlopen(GTHREAD_LIB, RTLD_LAZY | RTLD_LOCAL); - if (gthread_libhandle == NULL) - return FALSE; - } - - if (setjmp(j) == 0) - { - fp_gtk_check_version = dl_symbol("gtk_check_version"); - /* Check for GTK 2.2+ */ - if (fp_gtk_check_version(2, 2, 0)) { - longjmp(j, NO_SYMBOL_EXCEPTION); - } - - /* GLib */ - fp_glib_check_version = dlsym(gtk2_libhandle, "glib_check_version"); - if (!fp_glib_check_version) { - dlerror(); - } - fp_g_free = dl_symbol("g_free"); - fp_g_object_unref = dl_symbol("g_object_unref"); - - fp_g_main_context_iteration = - dl_symbol("g_main_context_iteration"); - - fp_g_value_init = dl_symbol("g_value_init"); - fp_g_type_is_a = dl_symbol("g_type_is_a"); - - fp_g_value_get_boolean = dl_symbol("g_value_get_boolean"); - fp_g_value_get_char = dl_symbol("g_value_get_char"); - fp_g_value_get_uchar = dl_symbol("g_value_get_uchar"); - fp_g_value_get_int = dl_symbol("g_value_get_int"); - fp_g_value_get_uint = dl_symbol("g_value_get_uint"); - fp_g_value_get_long = dl_symbol("g_value_get_long"); - fp_g_value_get_ulong = dl_symbol("g_value_get_ulong"); - fp_g_value_get_int64 = dl_symbol("g_value_get_int64"); - fp_g_value_get_uint64 = dl_symbol("g_value_get_uint64"); - fp_g_value_get_float = dl_symbol("g_value_get_float"); - fp_g_value_get_double = dl_symbol("g_value_get_double"); - fp_g_value_get_string = dl_symbol("g_value_get_string"); - fp_g_value_get_enum = dl_symbol("g_value_get_enum"); - fp_g_value_get_flags = dl_symbol("g_value_get_flags"); - fp_g_value_get_param = dl_symbol("g_value_get_param"); - fp_g_value_get_boxed = dl_symbol("g_value_get_boxed"); - fp_g_value_get_pointer = dl_symbol("g_value_get_pointer"); - fp_g_value_get_object = dl_symbol("g_value_get_object"); - fp_g_param_spec_int = dl_symbol("g_param_spec_int"); - fp_g_object_get = dl_symbol("g_object_get"); - fp_g_object_set = dl_symbol("g_object_set"); - - /* GDK */ - fp_gdk_get_default_root_window = - dl_symbol("gdk_get_default_root_window"); - fp_gdk_pixmap_new = dl_symbol("gdk_pixmap_new"); - fp_gdk_pixbuf_get_from_drawable = - dl_symbol("gdk_pixbuf_get_from_drawable"); - fp_gdk_pixbuf_scale_simple = - dl_symbol("gdk_pixbuf_scale_simple"); - fp_gdk_gc_new = dl_symbol("gdk_gc_new"); - fp_gdk_rgb_gc_set_foreground = - dl_symbol("gdk_rgb_gc_set_foreground"); - fp_gdk_draw_rectangle = dl_symbol("gdk_draw_rectangle"); - fp_gdk_drawable_get_size = dl_symbol("gdk_drawable_get_size"); - - /* Pixbuf */ - fp_gdk_pixbuf_new = dl_symbol("gdk_pixbuf_new"); - fp_gdk_pixbuf_new_from_file = - dl_symbol("gdk_pixbuf_new_from_file"); - fp_gdk_pixbuf_get_width = dl_symbol("gdk_pixbuf_get_width"); - fp_gdk_pixbuf_get_height = dl_symbol("gdk_pixbuf_get_height"); - fp_gdk_pixbuf_get_pixels = dl_symbol("gdk_pixbuf_get_pixels"); - fp_gdk_pixbuf_get_rowstride = - dl_symbol("gdk_pixbuf_get_rowstride"); - fp_gdk_pixbuf_get_has_alpha = - dl_symbol("gdk_pixbuf_get_has_alpha"); - fp_gdk_pixbuf_get_bits_per_sample = - dl_symbol("gdk_pixbuf_get_bits_per_sample"); - fp_gdk_pixbuf_get_n_channels = - dl_symbol("gdk_pixbuf_get_n_channels"); - fp_gdk_pixbuf_get_colorspace = - dl_symbol("gdk_pixbuf_get_colorspace"); - - /* GTK painting */ - fp_gtk_init_check = dl_symbol("gtk_init_check"); - fp_gtk_paint_hline = dl_symbol("gtk_paint_hline"); - fp_gtk_paint_vline = dl_symbol("gtk_paint_vline"); - fp_gtk_paint_shadow = dl_symbol("gtk_paint_shadow"); - fp_gtk_paint_arrow = dl_symbol("gtk_paint_arrow"); - fp_gtk_paint_box = dl_symbol("gtk_paint_box"); - fp_gtk_paint_flat_box = dl_symbol("gtk_paint_flat_box"); - fp_gtk_paint_check = dl_symbol("gtk_paint_check"); - fp_gtk_paint_option = dl_symbol("gtk_paint_option"); - fp_gtk_paint_box_gap = dl_symbol("gtk_paint_box_gap"); - fp_gtk_paint_extension = dl_symbol("gtk_paint_extension"); - fp_gtk_paint_focus = dl_symbol("gtk_paint_focus"); - fp_gtk_paint_slider = dl_symbol("gtk_paint_slider"); - fp_gtk_paint_handle = dl_symbol("gtk_paint_handle"); - fp_gtk_paint_expander = dl_symbol("gtk_paint_expander"); - fp_gtk_style_apply_default_background = - dl_symbol("gtk_style_apply_default_background"); - - /* GTK widgets */ - fp_gtk_arrow_new = dl_symbol("gtk_arrow_new"); - fp_gtk_button_new = dl_symbol("gtk_button_new"); - fp_gtk_spin_button_new = dl_symbol("gtk_spin_button_new"); - fp_gtk_check_button_new = dl_symbol("gtk_check_button_new"); - fp_gtk_check_menu_item_new = - dl_symbol("gtk_check_menu_item_new"); - fp_gtk_color_selection_dialog_new = - dl_symbol("gtk_color_selection_dialog_new"); - fp_gtk_entry_new = dl_symbol("gtk_entry_new"); - fp_gtk_fixed_new = dl_symbol("gtk_fixed_new"); - fp_gtk_handle_box_new = dl_symbol("gtk_handle_box_new"); - fp_gtk_image_new = dl_symbol("gtk_image_new"); - fp_gtk_hpaned_new = dl_symbol("gtk_hpaned_new"); - fp_gtk_vpaned_new = dl_symbol("gtk_vpaned_new"); - fp_gtk_hscale_new = dl_symbol("gtk_hscale_new"); - fp_gtk_vscale_new = dl_symbol("gtk_vscale_new"); - fp_gtk_hscrollbar_new = dl_symbol("gtk_hscrollbar_new"); - fp_gtk_vscrollbar_new = dl_symbol("gtk_vscrollbar_new"); - fp_gtk_hseparator_new = dl_symbol("gtk_hseparator_new"); - fp_gtk_vseparator_new = dl_symbol("gtk_vseparator_new"); - fp_gtk_label_new = dl_symbol("gtk_label_new"); - fp_gtk_menu_new = dl_symbol("gtk_menu_new"); - fp_gtk_menu_bar_new = dl_symbol("gtk_menu_bar_new"); - fp_gtk_menu_item_new = dl_symbol("gtk_menu_item_new"); - fp_gtk_menu_item_set_submenu = - dl_symbol("gtk_menu_item_set_submenu"); - fp_gtk_notebook_new = dl_symbol("gtk_notebook_new"); - fp_gtk_progress_bar_new = - dl_symbol("gtk_progress_bar_new"); - fp_gtk_progress_bar_set_orientation = - dl_symbol("gtk_progress_bar_set_orientation"); - fp_gtk_radio_button_new = - dl_symbol("gtk_radio_button_new"); - fp_gtk_radio_menu_item_new = - dl_symbol("gtk_radio_menu_item_new"); - fp_gtk_scrolled_window_new = - dl_symbol("gtk_scrolled_window_new"); - fp_gtk_separator_menu_item_new = - dl_symbol("gtk_separator_menu_item_new"); - fp_gtk_text_view_new = dl_symbol("gtk_text_view_new"); - fp_gtk_toggle_button_new = - dl_symbol("gtk_toggle_button_new"); - fp_gtk_toolbar_new = dl_symbol("gtk_toolbar_new"); - fp_gtk_tree_view_new = dl_symbol("gtk_tree_view_new"); - fp_gtk_viewport_new = dl_symbol("gtk_viewport_new"); - fp_gtk_window_new = dl_symbol("gtk_window_new"); - fp_gtk_window_present = dl_symbol("gtk_window_present"); - fp_gtk_window_move = dl_symbol("gtk_window_move"); - fp_gtk_window_resize = dl_symbol("gtk_window_resize"); - - fp_gtk_dialog_new = dl_symbol("gtk_dialog_new"); - fp_gtk_frame_new = dl_symbol("gtk_frame_new"); - - fp_gtk_adjustment_new = dl_symbol("gtk_adjustment_new"); - fp_gtk_container_add = dl_symbol("gtk_container_add"); - fp_gtk_menu_shell_append = - dl_symbol("gtk_menu_shell_append"); - fp_gtk_widget_realize = dl_symbol("gtk_widget_realize"); - fp_gtk_widget_destroy = dl_symbol("gtk_widget_destroy"); - fp_gtk_widget_render_icon = - dl_symbol("gtk_widget_render_icon"); - fp_gtk_widget_set_name = - dl_symbol("gtk_widget_set_name"); - fp_gtk_widget_set_parent = - dl_symbol("gtk_widget_set_parent"); - fp_gtk_widget_set_direction = - dl_symbol("gtk_widget_set_direction"); - fp_gtk_widget_style_get = - dl_symbol("gtk_widget_style_get"); - fp_gtk_widget_class_install_style_property = - dl_symbol("gtk_widget_class_install_style_property"); - fp_gtk_widget_class_find_style_property = - dl_symbol("gtk_widget_class_find_style_property"); - fp_gtk_widget_style_get_property = - dl_symbol("gtk_widget_style_get_property"); - fp_pango_font_description_to_string = - dl_symbol("pango_font_description_to_string"); - fp_gtk_settings_get_default = - dl_symbol("gtk_settings_get_default"); - fp_gtk_widget_get_settings = - dl_symbol("gtk_widget_get_settings"); - fp_gtk_border_get_type = dl_symbol("gtk_border_get_type"); - fp_gtk_arrow_set = dl_symbol("gtk_arrow_set"); - fp_gtk_widget_size_request = - dl_symbol("gtk_widget_size_request"); - fp_gtk_range_get_adjustment = - dl_symbol("gtk_range_get_adjustment"); - - fp_gtk_widget_hide = dl_symbol("gtk_widget_hide"); - fp_gtk_main_quit = dl_symbol("gtk_main_quit"); - fp_g_signal_connect_data = dl_symbol("g_signal_connect_data"); - fp_gtk_widget_show = dl_symbol("gtk_widget_show"); - fp_gtk_main = dl_symbol("gtk_main"); - - fp_g_path_get_dirname = dl_symbol("g_path_get_dirname"); - - /** - * GLib thread system - */ - if (GLIB_CHECK_VERSION(2, 20, 0)) { - fp_g_thread_get_initialized = dl_symbol_gthread("g_thread_get_initialized"); - } - fp_g_thread_init = dl_symbol_gthread("g_thread_init"); - fp_gdk_threads_init = dl_symbol("gdk_threads_init"); - fp_gdk_threads_enter = dl_symbol("gdk_threads_enter"); - fp_gdk_threads_leave = dl_symbol("gdk_threads_leave"); - - /** - * Functions for sun_awt_X11_GtkFileDialogPeer.c - */ - if (fp_gtk_check_version(2, 4, 0) == NULL) { - // The current GtkFileChooser is available from GTK+ 2.4 - gtk2_file_chooser_load(); - } - - /* Some functions may be missing in pre-2.4 GTK. - We handle them specially here. - */ - fp_gtk_combo_box_new = dlsym(gtk2_libhandle, "gtk_combo_box_new"); - if (fp_gtk_combo_box_new == NULL) { - fp_gtk_combo_box_new = dl_symbol("gtk_combo_new"); - } - - fp_gtk_combo_box_entry_new = - dlsym(gtk2_libhandle, "gtk_combo_box_entry_new"); - if (fp_gtk_combo_box_entry_new == NULL) { - fp_gtk_combo_box_entry_new = dl_symbol("gtk_combo_new"); - new_combo = FALSE; - } - - fp_gtk_separator_tool_item_new = - dlsym(gtk2_libhandle, "gtk_separator_tool_item_new"); - if (fp_gtk_separator_tool_item_new == NULL) { - fp_gtk_separator_tool_item_new = - dl_symbol("gtk_vseparator_new"); - } - - fp_g_list_append = dl_symbol("g_list_append"); - fp_g_list_free = dl_symbol("g_list_free"); - fp_g_list_free_full = dl_symbol("g_list_free_full"); - } - /* Now we have only one kind of exceptions: NO_SYMBOL_EXCEPTION - * Otherwise we can check the return value of setjmp method. - */ - else - { - dlclose(gtk2_libhandle); - gtk2_libhandle = NULL; - - dlclose(gthread_libhandle); - gthread_libhandle = NULL; - - return FALSE; - } - - /* - * Strip the AT-SPI GTK_MODULES if present - */ - gtk_modules_env = getenv ("GTK_MODULES"); - if ((gtk_modules_env && strstr(gtk_modules_env, "atk-bridge")) || - (gtk_modules_env && strstr(gtk_modules_env, "gail"))) { - /* careful, strtok modifies its args */ - gchar *tmp_env = strdup(gtk_modules_env); - if (tmp_env) { - /* the new env will be smaller than the old one */ - gchar *s, *new_env = SAFE_SIZE_STRUCT_ALLOC(malloc, - sizeof(ENV_PREFIX), 1, strlen (gtk_modules_env)); - - if (new_env) { - strcpy(new_env, ENV_PREFIX); - - /* strip out 'atk-bridge' and 'gail' */ - size_t PREFIX_LENGTH = strlen(ENV_PREFIX); - gchar *tmp_ptr = NULL; - for (s = strtok_r(tmp_env, ":", &tmp_ptr); s; - s = strtok_r(NULL, ":", &tmp_ptr)) { - if ((!strstr(s, "atk-bridge")) && (!strstr(s, "gail"))) { - if (strlen(new_env) > PREFIX_LENGTH) { - new_env = strcat(new_env, ":"); - } - new_env = strcat(new_env, s); - } - } - if (putenv(new_env) != 0) { - /* no free() on success, putenv() doesn't copy string */ - free(new_env); - } - } - free(tmp_env); - } - } - /* - * GTK should be initialized with gtk_init_check() before use. - * - * gtk_init_check installs its own error handlers. It is critical that - * we preserve error handler set from AWT. Otherwise we'll crash on - * BadMatch errors which we would normally ignore. The IO error handler - * is preserved here, too, just for consistency. - */ - AWT_LOCK(); - handler = XSetErrorHandler(NULL); - io_handler = XSetIOErrorHandler(NULL); - - if (fp_gtk_check_version(2, 2, 0) == NULL) { - - // Calling g_thread_init() multiple times leads to crash on GLib < 2.24 - // We can use g_thread_get_initialized () but it is available only for - // GLib >= 2.20. - gboolean is_g_thread_get_initialized = FALSE; - if (GLIB_CHECK_VERSION(2, 20, 0)) { - is_g_thread_get_initialized = fp_g_thread_get_initialized(); - } - - if (!is_g_thread_get_initialized) { - fp_g_thread_init(NULL); - } - - //According the GTK documentation, gdk_threads_init() should be - //called before gtk_init() or gtk_init_check() - fp_gdk_threads_init(); - } - result = (*fp_gtk_init_check)(NULL, NULL); - - XSetErrorHandler(handler); - XSetIOErrorHandler(io_handler); - AWT_UNLOCK(); - - /* Initialize widget array. */ - for (i = 0; i < _GTK_WIDGET_TYPE_SIZE; i++) - { - gtk2_widgets[i] = NULL; - } - if (result) { - GtkApi* gtk = (GtkApi*)malloc(sizeof(GtkApi)); - gtk2_init(gtk); - return gtk; - } - return NULL; -} - -int gtk2_unload() -{ - int i; - char *gtk2_error; - - if (!gtk2_libhandle) - return TRUE; - - /* Release painting objects */ - if (gtk2_white_pixmap != NULL) { - (*fp_g_object_unref)(gtk2_white_pixmap); - (*fp_g_object_unref)(gtk2_black_pixmap); - (*fp_g_object_unref)(gtk2_white_pixbuf); - (*fp_g_object_unref)(gtk2_black_pixbuf); - gtk2_white_pixmap = gtk2_black_pixmap = - gtk2_white_pixbuf = gtk2_black_pixbuf = NULL; - } - gtk2_pixbuf_width = 0; - gtk2_pixbuf_height = 0; - - if (gtk2_window != NULL) { - /* Destroying toplevel widget will destroy all contained widgets */ - (*fp_gtk_widget_destroy)(gtk2_window); - - /* Unset some static data so they get reinitialized on next load */ - gtk2_window = NULL; - } - - dlerror(); - dlclose(gtk2_libhandle); - dlclose(gthread_libhandle); - if ((gtk2_error = dlerror()) != NULL) - { - return FALSE; - } - return TRUE; -} - -/* Dispatch all pending events from the GTK event loop. - * This is needed to catch theme change and update widgets' style. - */ -static void flush_gtk_event_loop() -{ - while( (*fp_g_main_context_iteration)(NULL, FALSE)); -} - -/* - * Initialize components of containment hierarchy. This creates a GtkFixed - * inside a GtkWindow. All widgets get realized. - */ -static void init_containers() -{ - if (gtk2_window == NULL) - { - gtk2_window = (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL); - gtk2_fixed = (GtkFixed *)(*fp_gtk_fixed_new)(); - (*fp_gtk_container_add)((GtkContainer*)gtk2_window, - (GtkWidget *)gtk2_fixed); - (*fp_gtk_widget_realize)(gtk2_window); - (*fp_gtk_widget_realize)((GtkWidget *)gtk2_fixed); - } -} - -/* - * Ensure everything is ready for drawing an element of the specified width - * and height. - * - * We should somehow handle translucent images. GTK can draw to X Drawables - * only, which don't support alpha. When we retrieve the image back from - * the server, translucency information is lost. There're several ways to - * work around this: - * 1) Subclass GdkPixmap and cache translucent objects on client side. This - * requires us to implement parts of X server drawing logic on client side. - * Many X requests can potentially be "translucent"; e.g. XDrawLine with - * fill=tile and a translucent tile is a "translucent" operation, whereas - * XDrawLine with fill=solid is an "opaque" one. Moreover themes can (and some - * do) intermix transparent and opaque operations which makes caching even - * more problematic. - * 2) Use Xorg 32bit ARGB visual when available. GDK has no native support - * for it (as of version 2.6). Also even in JDS 3 Xorg does not support - * these visuals by default, which makes optimizing for them pointless. - * We can consider doing this at a later point when ARGB visuals become more - * popular. - * 3') GTK has plans to use Cairo as its graphical backend (presumably in - * 2.8), and Cairo supports alpha. With it we could also get rid of the - * unnecessary round trip to server and do all the drawing on client side. - * 4) For now we draw to two different pixmaps and restore alpha channel by - * comparing results. This can be optimized by using subclassed pixmap and - * doing the second drawing only if necessary. -*/ -static void gtk2_init_painting(JNIEnv *env, gint width, gint height) -{ - GdkGC *gc; - GdkPixbuf *white, *black; - - init_containers(); - - if (gtk2_pixbuf_width < width || gtk2_pixbuf_height < height) - { - white = (*fp_gdk_pixbuf_new)(GDK_COLORSPACE_RGB, TRUE, 8, width, height); - black = (*fp_gdk_pixbuf_new)(GDK_COLORSPACE_RGB, TRUE, 8, width, height); - - if (white == NULL || black == NULL) - { - snprintf(convertionBuffer, CONV_BUFFER_SIZE, "Couldn't create pixbuf of size %dx%d", width, height); - throw_exception(env, "java/lang/RuntimeException", convertionBuffer); - fp_gdk_threads_leave(); - return; - } - - if (gtk2_white_pixmap != NULL) { - /* free old stuff */ - (*fp_g_object_unref)(gtk2_white_pixmap); - (*fp_g_object_unref)(gtk2_black_pixmap); - (*fp_g_object_unref)(gtk2_white_pixbuf); - (*fp_g_object_unref)(gtk2_black_pixbuf); - } - - gtk2_white_pixmap = (*fp_gdk_pixmap_new)(gtk2_window->window, width, height, -1); - gtk2_black_pixmap = (*fp_gdk_pixmap_new)(gtk2_window->window, width, height, -1); - - gtk2_white_pixbuf = white; - gtk2_black_pixbuf = black; - - gtk2_pixbuf_width = width; - gtk2_pixbuf_height = height; - } - - /* clear the pixmaps */ - gc = (*fp_gdk_gc_new)(gtk2_white_pixmap); - (*fp_gdk_rgb_gc_set_foreground)(gc, 0xffffff); - (*fp_gdk_draw_rectangle)(gtk2_white_pixmap, gc, TRUE, 0, 0, width, height); - (*fp_g_object_unref)(gc); - - gc = (*fp_gdk_gc_new)(gtk2_black_pixmap); - (*fp_gdk_rgb_gc_set_foreground)(gc, 0x000000); - (*fp_gdk_draw_rectangle)(gtk2_black_pixmap, gc, TRUE, 0, 0, width, height); - (*fp_g_object_unref)(gc); -} - -/* - * Restore image from white and black pixmaps and copy it into destination - * buffer. This method compares two pixbufs taken from white and black - * pixmaps and decodes color and alpha components. Pixbufs are RGB without - * alpha, destination buffer is ABGR. - * - * The return value is the transparency type of the resulting image, either - * one of java_awt_Transparency_OPAQUE, java_awt_Transparency_BITMASK, and - * java_awt_Transparency_TRANSLUCENT. - */ -static gint gtk2_copy_image(gint *dst, gint width, gint height) -{ - gint i, j, r, g, b; - guchar *white, *black; - gint stride, padding; - gboolean is_opaque = TRUE; - gboolean is_bitmask = TRUE; - - (*fp_gdk_pixbuf_get_from_drawable)(gtk2_white_pixbuf, gtk2_white_pixmap, - NULL, 0, 0, 0, 0, width, height); - (*fp_gdk_pixbuf_get_from_drawable)(gtk2_black_pixbuf, gtk2_black_pixmap, - NULL, 0, 0, 0, 0, width, height); - - white = (*fp_gdk_pixbuf_get_pixels)(gtk2_white_pixbuf); - black = (*fp_gdk_pixbuf_get_pixels)(gtk2_black_pixbuf); - stride = (*fp_gdk_pixbuf_get_rowstride)(gtk2_black_pixbuf); - padding = stride - width * 4; - if (padding >= 0 && stride > 0) { - for (i = 0; i < height; i++) { - for (j = 0; j < width; j++) { - int r1 = *white++; - int r2 = *black++; - int alpha = 0xff + r2 - r1; - - switch (alpha) { - case 0: /* transparent pixel */ - r = g = b = 0; - black += 3; - white += 3; - is_opaque = FALSE; - break; - - case 0xff: /* opaque pixel */ - r = r2; - g = *black++; - b = *black++; - black++; - white += 3; - break; - - default: /* translucent pixel */ - r = 0xff * r2 / alpha; - g = 0xff * *black++ / alpha; - b = 0xff * *black++ / alpha; - black++; - white += 3; - is_opaque = FALSE; - is_bitmask = FALSE; - break; - } - - *dst++ = (alpha << 24 | r << 16 | g << 8 | b); - } - - white += padding; - black += padding; - } - } - return is_opaque ? java_awt_Transparency_OPAQUE : - (is_bitmask ? java_awt_Transparency_BITMASK : - java_awt_Transparency_TRANSLUCENT); -} - -static void -gtk2_set_direction(GtkWidget *widget, GtkTextDirection dir) -{ - /* - * Some engines (inexplicably) look at the direction of the widget's - * parent, so we need to set the direction of both the widget and its - * parent. - */ - (*fp_gtk_widget_set_direction)(widget, dir); - if (widget->parent != NULL) { - (*fp_gtk_widget_set_direction)(widget->parent, dir); - } -} - -/* - * Initializes the widget to correct state for some engines. - * This is a pure empirical method. - */ -static void init_toggle_widget(WidgetType widget_type, gint synth_state) -{ - gboolean is_active = ((synth_state & SELECTED) != 0); - - if (widget_type == RADIO_BUTTON || - widget_type == CHECK_BOX || - widget_type == TOGGLE_BUTTON) { - ((GtkToggleButton*)gtk2_widget)->active = is_active; - } - - if ((synth_state & FOCUSED) != 0) { - ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_FOCUS; - } else { - ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_FOCUS; - } - - if ((((synth_state & MOUSE_OVER) != 0) && ((synth_state & PRESSED) == 0)) || - (((synth_state & FOCUSED) != 0) && ((synth_state & PRESSED) != 0))) { - gtk2_widget->state = GTK_STATE_PRELIGHT; - } else if ((synth_state & DISABLED) != 0) { - gtk2_widget->state = GTK_STATE_INSENSITIVE; - } else { - gtk2_widget->state = is_active ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL; - } -} - -/* GTK state_type filter */ -static GtkStateType get_gtk_state_type(WidgetType widget_type, gint synth_state) -{ - GtkStateType result = GTK_STATE_NORMAL; - - if ((synth_state & DISABLED) != 0) { - result = GTK_STATE_INSENSITIVE; - } else if ((synth_state & PRESSED) != 0) { - result = GTK_STATE_ACTIVE; - } else if ((synth_state & MOUSE_OVER) != 0) { - result = GTK_STATE_PRELIGHT; - } - return result; -} - -/* GTK shadow_type filter */ -static GtkShadowType get_gtk_shadow_type(WidgetType widget_type, gint synth_state) -{ - GtkShadowType result = GTK_SHADOW_OUT; - - if ((synth_state & SELECTED) != 0) { - result = GTK_SHADOW_IN; - } - return result; -} - - -static GtkWidget* gtk2_get_arrow(GtkArrowType arrow_type, GtkShadowType shadow_type) -{ - GtkWidget *arrow = NULL; - if (NULL == gtk2_widgets[_GTK_ARROW_TYPE]) - { - gtk2_widgets[_GTK_ARROW_TYPE] = (*fp_gtk_arrow_new)(arrow_type, shadow_type); - (*fp_gtk_container_add)((GtkContainer *)gtk2_fixed, gtk2_widgets[_GTK_ARROW_TYPE]); - (*fp_gtk_widget_realize)(gtk2_widgets[_GTK_ARROW_TYPE]); - } - arrow = gtk2_widgets[_GTK_ARROW_TYPE]; - - (*fp_gtk_arrow_set)(arrow, arrow_type, shadow_type); - return arrow; -} - -static GtkAdjustment* create_adjustment() -{ - return (GtkAdjustment *) - (*fp_gtk_adjustment_new)(50.0, 0.0, 100.0, 10.0, 20.0, 20.0); -} - -/** - * Returns a pointer to the cached native widget for the specified widget - * type. - */ -static GtkWidget *gtk2_get_widget(WidgetType widget_type) -{ - gboolean init_result = FALSE; - GtkWidget *result = NULL; - switch (widget_type) - { - case BUTTON: - case TABLE_HEADER: - if (init_result = (NULL == gtk2_widgets[_GTK_BUTTON_TYPE])) - { - gtk2_widgets[_GTK_BUTTON_TYPE] = (*fp_gtk_button_new)(); - } - result = gtk2_widgets[_GTK_BUTTON_TYPE]; - break; - case CHECK_BOX: - if (init_result = (NULL == gtk2_widgets[_GTK_CHECK_BUTTON_TYPE])) - { - gtk2_widgets[_GTK_CHECK_BUTTON_TYPE] = - (*fp_gtk_check_button_new)(); - } - result = gtk2_widgets[_GTK_CHECK_BUTTON_TYPE]; - break; - case CHECK_BOX_MENU_ITEM: - if (init_result = (NULL == gtk2_widgets[_GTK_CHECK_MENU_ITEM_TYPE])) - { - gtk2_widgets[_GTK_CHECK_MENU_ITEM_TYPE] = - (*fp_gtk_check_menu_item_new)(); - } - result = gtk2_widgets[_GTK_CHECK_MENU_ITEM_TYPE]; - break; - /************************************************************ - * Creation a dedicated color chooser is dangerous because - * it deadlocks the EDT - ************************************************************/ -/* case COLOR_CHOOSER: - if (init_result = - (NULL == gtk2_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE])) - { - gtk2_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE] = - (*fp_gtk_color_selection_dialog_new)(NULL); - } - result = gtk2_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE]; - break;*/ - case COMBO_BOX: - if (init_result = (NULL == gtk2_widgets[_GTK_COMBO_BOX_TYPE])) - { - gtk2_widgets[_GTK_COMBO_BOX_TYPE] = - (*fp_gtk_combo_box_new)(); - } - result = gtk2_widgets[_GTK_COMBO_BOX_TYPE]; - break; - case COMBO_BOX_ARROW_BUTTON: - if (init_result = - (NULL == gtk2_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE])) - { - gtk2_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE] = - (*fp_gtk_toggle_button_new)(); - } - result = gtk2_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE]; - break; - case COMBO_BOX_TEXT_FIELD: - if (init_result = - (NULL == gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE])) - { - result = gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE] = - (*fp_gtk_entry_new)(); - } - result = gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE]; - break; - case DESKTOP_ICON: - case INTERNAL_FRAME_TITLE_PANE: - case LABEL: - if (init_result = (NULL == gtk2_widgets[_GTK_LABEL_TYPE])) - { - gtk2_widgets[_GTK_LABEL_TYPE] = - (*fp_gtk_label_new)(NULL); - } - result = gtk2_widgets[_GTK_LABEL_TYPE]; - break; - case DESKTOP_PANE: - case PANEL: - case ROOT_PANE: - if (init_result = (NULL == gtk2_widgets[_GTK_CONTAINER_TYPE])) - { - /* There is no constructor for a container type. I've - * chosen GtkFixed container since it has a default - * constructor. - */ - gtk2_widgets[_GTK_CONTAINER_TYPE] = - (*fp_gtk_fixed_new)(); - } - result = gtk2_widgets[_GTK_CONTAINER_TYPE]; - break; - case EDITOR_PANE: - case TEXT_AREA: - case TEXT_PANE: - if (init_result = (NULL == gtk2_widgets[_GTK_TEXT_VIEW_TYPE])) - { - gtk2_widgets[_GTK_TEXT_VIEW_TYPE] = - (*fp_gtk_text_view_new)(); - } - result = gtk2_widgets[_GTK_TEXT_VIEW_TYPE]; - break; - case FORMATTED_TEXT_FIELD: - case PASSWORD_FIELD: - case TEXT_FIELD: - if (init_result = (NULL == gtk2_widgets[_GTK_ENTRY_TYPE])) - { - gtk2_widgets[_GTK_ENTRY_TYPE] = - (*fp_gtk_entry_new)(); - } - result = gtk2_widgets[_GTK_ENTRY_TYPE]; - break; - case HANDLE_BOX: - if (init_result = (NULL == gtk2_widgets[_GTK_HANDLE_BOX_TYPE])) - { - gtk2_widgets[_GTK_HANDLE_BOX_TYPE] = - (*fp_gtk_handle_box_new)(); - } - result = gtk2_widgets[_GTK_HANDLE_BOX_TYPE]; - break; - case HSCROLL_BAR: - case HSCROLL_BAR_BUTTON_LEFT: - case HSCROLL_BAR_BUTTON_RIGHT: - case HSCROLL_BAR_TRACK: - case HSCROLL_BAR_THUMB: - if (init_result = (NULL == gtk2_widgets[_GTK_HSCROLLBAR_TYPE])) - { - gtk2_widgets[_GTK_HSCROLLBAR_TYPE] = - (*fp_gtk_hscrollbar_new)(create_adjustment()); - } - result = gtk2_widgets[_GTK_HSCROLLBAR_TYPE]; - break; - case HSEPARATOR: - if (init_result = (NULL == gtk2_widgets[_GTK_HSEPARATOR_TYPE])) - { - gtk2_widgets[_GTK_HSEPARATOR_TYPE] = - (*fp_gtk_hseparator_new)(); - } - result = gtk2_widgets[_GTK_HSEPARATOR_TYPE]; - break; - case HSLIDER: - case HSLIDER_THUMB: - case HSLIDER_TRACK: - if (init_result = (NULL == gtk2_widgets[_GTK_HSCALE_TYPE])) - { - gtk2_widgets[_GTK_HSCALE_TYPE] = - (*fp_gtk_hscale_new)(NULL); - } - result = gtk2_widgets[_GTK_HSCALE_TYPE]; - break; - case HSPLIT_PANE_DIVIDER: - case SPLIT_PANE: - if (init_result = (NULL == gtk2_widgets[_GTK_HPANED_TYPE])) - { - gtk2_widgets[_GTK_HPANED_TYPE] = (*fp_gtk_hpaned_new)(); - } - result = gtk2_widgets[_GTK_HPANED_TYPE]; - break; - case IMAGE: - if (init_result = (NULL == gtk2_widgets[_GTK_IMAGE_TYPE])) - { - gtk2_widgets[_GTK_IMAGE_TYPE] = (*fp_gtk_image_new)(); - } - result = gtk2_widgets[_GTK_IMAGE_TYPE]; - break; - case INTERNAL_FRAME: - if (init_result = (NULL == gtk2_widgets[_GTK_WINDOW_TYPE])) - { - gtk2_widgets[_GTK_WINDOW_TYPE] = - (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL); - } - result = gtk2_widgets[_GTK_WINDOW_TYPE]; - break; - case TOOL_TIP: - if (init_result = (NULL == gtk2_widgets[_GTK_TOOLTIP_TYPE])) - { - result = (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL); - (*fp_gtk_widget_set_name)(result, "gtk-tooltips"); - gtk2_widgets[_GTK_TOOLTIP_TYPE] = result; - } - result = gtk2_widgets[_GTK_TOOLTIP_TYPE]; - break; - case LIST: - case TABLE: - case TREE: - case TREE_CELL: - if (init_result = (NULL == gtk2_widgets[_GTK_TREE_VIEW_TYPE])) - { - gtk2_widgets[_GTK_TREE_VIEW_TYPE] = - (*fp_gtk_tree_view_new)(); - } - result = gtk2_widgets[_GTK_TREE_VIEW_TYPE]; - break; - case TITLED_BORDER: - if (init_result = (NULL == gtk2_widgets[_GTK_FRAME_TYPE])) - { - gtk2_widgets[_GTK_FRAME_TYPE] = fp_gtk_frame_new(NULL); - } - result = gtk2_widgets[_GTK_FRAME_TYPE]; - break; - case POPUP_MENU: - if (init_result = (NULL == gtk2_widgets[_GTK_MENU_TYPE])) - { - gtk2_widgets[_GTK_MENU_TYPE] = - (*fp_gtk_menu_new)(); - } - result = gtk2_widgets[_GTK_MENU_TYPE]; - break; - case MENU: - case MENU_ITEM: - case MENU_ITEM_ACCELERATOR: - if (init_result = (NULL == gtk2_widgets[_GTK_MENU_ITEM_TYPE])) - { - gtk2_widgets[_GTK_MENU_ITEM_TYPE] = - (*fp_gtk_menu_item_new)(); - } - result = gtk2_widgets[_GTK_MENU_ITEM_TYPE]; - break; - case MENU_BAR: - if (init_result = (NULL == gtk2_widgets[_GTK_MENU_BAR_TYPE])) - { - gtk2_widgets[_GTK_MENU_BAR_TYPE] = - (*fp_gtk_menu_bar_new)(); - } - result = gtk2_widgets[_GTK_MENU_BAR_TYPE]; - break; - case COLOR_CHOOSER: - case OPTION_PANE: - if (init_result = (NULL == gtk2_widgets[_GTK_DIALOG_TYPE])) - { - gtk2_widgets[_GTK_DIALOG_TYPE] = - (*fp_gtk_dialog_new)(); - } - result = gtk2_widgets[_GTK_DIALOG_TYPE]; - break; - case POPUP_MENU_SEPARATOR: - if (init_result = - (NULL == gtk2_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE])) - { - gtk2_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE] = - (*fp_gtk_separator_menu_item_new)(); - } - result = gtk2_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE]; - break; - case HPROGRESS_BAR: - if (init_result = (NULL == gtk2_widgets[_GTK_HPROGRESS_BAR_TYPE])) - { - gtk2_widgets[_GTK_HPROGRESS_BAR_TYPE] = - (*fp_gtk_progress_bar_new)(); - } - result = gtk2_widgets[_GTK_HPROGRESS_BAR_TYPE]; - break; - case VPROGRESS_BAR: - if (init_result = (NULL == gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE])) - { - gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE] = - (*fp_gtk_progress_bar_new)(); - /* - * Vertical JProgressBars always go bottom-to-top, - * regardless of the ComponentOrientation. - */ - (*fp_gtk_progress_bar_set_orientation)( - (GtkProgressBar *)gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE], - GTK_PROGRESS_BOTTOM_TO_TOP); - } - result = gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE]; - break; - case RADIO_BUTTON: - if (init_result = (NULL == gtk2_widgets[_GTK_RADIO_BUTTON_TYPE])) - { - gtk2_widgets[_GTK_RADIO_BUTTON_TYPE] = - (*fp_gtk_radio_button_new)(NULL); - } - result = gtk2_widgets[_GTK_RADIO_BUTTON_TYPE]; - break; - case RADIO_BUTTON_MENU_ITEM: - if (init_result = - (NULL == gtk2_widgets[_GTK_RADIO_MENU_ITEM_TYPE])) - { - gtk2_widgets[_GTK_RADIO_MENU_ITEM_TYPE] = - (*fp_gtk_radio_menu_item_new)(NULL); - } - result = gtk2_widgets[_GTK_RADIO_MENU_ITEM_TYPE]; - break; - case SCROLL_PANE: - if (init_result = - (NULL == gtk2_widgets[_GTK_SCROLLED_WINDOW_TYPE])) - { - gtk2_widgets[_GTK_SCROLLED_WINDOW_TYPE] = - (*fp_gtk_scrolled_window_new)(NULL, NULL); - } - result = gtk2_widgets[_GTK_SCROLLED_WINDOW_TYPE]; - break; - case SPINNER: - case SPINNER_ARROW_BUTTON: - case SPINNER_TEXT_FIELD: - if (init_result = (NULL == gtk2_widgets[_GTK_SPIN_BUTTON_TYPE])) - { - result = gtk2_widgets[_GTK_SPIN_BUTTON_TYPE] = - (*fp_gtk_spin_button_new)(NULL, 0, 0); - } - result = gtk2_widgets[_GTK_SPIN_BUTTON_TYPE]; - break; - case TABBED_PANE: - case TABBED_PANE_TAB_AREA: - case TABBED_PANE_CONTENT: - case TABBED_PANE_TAB: - if (init_result = (NULL == gtk2_widgets[_GTK_NOTEBOOK_TYPE])) - { - gtk2_widgets[_GTK_NOTEBOOK_TYPE] = - (*fp_gtk_notebook_new)(); - } - result = gtk2_widgets[_GTK_NOTEBOOK_TYPE]; - break; - case TOGGLE_BUTTON: - if (init_result = (NULL == gtk2_widgets[_GTK_TOGGLE_BUTTON_TYPE])) - { - gtk2_widgets[_GTK_TOGGLE_BUTTON_TYPE] = - (*fp_gtk_toggle_button_new)(); - } - result = gtk2_widgets[_GTK_TOGGLE_BUTTON_TYPE]; - break; - case TOOL_BAR: - case TOOL_BAR_DRAG_WINDOW: - if (init_result = (NULL == gtk2_widgets[_GTK_TOOLBAR_TYPE])) - { - gtk2_widgets[_GTK_TOOLBAR_TYPE] = - (*fp_gtk_toolbar_new)(); - } - result = gtk2_widgets[_GTK_TOOLBAR_TYPE]; - break; - case TOOL_BAR_SEPARATOR: - if (init_result = - (NULL == gtk2_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE])) - { - gtk2_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE] = - (*fp_gtk_separator_tool_item_new)(); - } - result = gtk2_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE]; - break; - case VIEWPORT: - if (init_result = (NULL == gtk2_widgets[_GTK_VIEWPORT_TYPE])) - { - GtkAdjustment *adjustment = create_adjustment(); - gtk2_widgets[_GTK_VIEWPORT_TYPE] = - (*fp_gtk_viewport_new)(adjustment, adjustment); - } - result = gtk2_widgets[_GTK_VIEWPORT_TYPE]; - break; - case VSCROLL_BAR: - case VSCROLL_BAR_BUTTON_UP: - case VSCROLL_BAR_BUTTON_DOWN: - case VSCROLL_BAR_TRACK: - case VSCROLL_BAR_THUMB: - if (init_result = (NULL == gtk2_widgets[_GTK_VSCROLLBAR_TYPE])) - { - gtk2_widgets[_GTK_VSCROLLBAR_TYPE] = - (*fp_gtk_vscrollbar_new)(create_adjustment()); - } - result = gtk2_widgets[_GTK_VSCROLLBAR_TYPE]; - break; - case VSEPARATOR: - if (init_result = (NULL == gtk2_widgets[_GTK_VSEPARATOR_TYPE])) - { - gtk2_widgets[_GTK_VSEPARATOR_TYPE] = - (*fp_gtk_vseparator_new)(); - } - result = gtk2_widgets[_GTK_VSEPARATOR_TYPE]; - break; - case VSLIDER: - case VSLIDER_THUMB: - case VSLIDER_TRACK: - if (init_result = (NULL == gtk2_widgets[_GTK_VSCALE_TYPE])) - { - gtk2_widgets[_GTK_VSCALE_TYPE] = - (*fp_gtk_vscale_new)(NULL); - } - result = gtk2_widgets[_GTK_VSCALE_TYPE]; - /* - * Vertical JSliders start at the bottom, while vertical - * GtkVScale widgets start at the top (by default), so to fix - * this we set the "inverted" flag to get the Swing behavior. - */ - ((GtkRange*)result)->inverted = 1; - break; - case VSPLIT_PANE_DIVIDER: - if (init_result = (NULL == gtk2_widgets[_GTK_VPANED_TYPE])) - { - gtk2_widgets[_GTK_VPANED_TYPE] = (*fp_gtk_vpaned_new)(); - } - result = gtk2_widgets[_GTK_VPANED_TYPE]; - break; - default: - result = NULL; - break; - } - - if (result != NULL && init_result) - { - if (widget_type == RADIO_BUTTON_MENU_ITEM || - widget_type == CHECK_BOX_MENU_ITEM || - widget_type == MENU_ITEM || - widget_type == MENU || - widget_type == POPUP_MENU_SEPARATOR) - { - GtkWidget *menu = gtk2_get_widget(POPUP_MENU); - (*fp_gtk_menu_shell_append)((GtkMenuShell *)menu, result); - } - else if (widget_type == POPUP_MENU) - { - GtkWidget *menu_bar = gtk2_get_widget(MENU_BAR); - GtkWidget *root_menu = (*fp_gtk_menu_item_new)(); - (*fp_gtk_menu_item_set_submenu)((GtkMenuItem*)root_menu, result); - (*fp_gtk_menu_shell_append)((GtkMenuShell *)menu_bar, root_menu); - } - else if (widget_type == COMBO_BOX_ARROW_BUTTON || - widget_type == COMBO_BOX_TEXT_FIELD) - { - /* - * We add a regular GtkButton/GtkEntry to a GtkComboBoxEntry - * in order to trick engines into thinking it's a real combobox - * arrow button/text field. - */ - GtkWidget *combo = (*fp_gtk_combo_box_entry_new)(); - - if (new_combo && widget_type == COMBO_BOX_ARROW_BUTTON) { - (*fp_gtk_widget_set_parent)(result, combo); - ((GtkBin*)combo)->child = result; - } else { - (*fp_gtk_container_add)((GtkContainer *)combo, result); - } - (*fp_gtk_container_add)((GtkContainer *)gtk2_fixed, combo); - } - else if (widget_type != TOOL_TIP && - widget_type != INTERNAL_FRAME && - widget_type != OPTION_PANE) - { - (*fp_gtk_container_add)((GtkContainer *)gtk2_fixed, result); - } - (*fp_gtk_widget_realize)(result); - } - return result; -} - -void gtk2_paint_arrow(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, - GtkArrowType arrow_type, gboolean fill) -{ - static int w, h; - static GtkRequisition size; - - if (widget_type == COMBO_BOX_ARROW_BUTTON || widget_type == TABLE) - gtk2_widget = gtk2_get_arrow(arrow_type, shadow_type); - else - gtk2_widget = gtk2_get_widget(widget_type); - - switch (widget_type) - { - case SPINNER_ARROW_BUTTON: - x = 1; - y = ((arrow_type == GTK_ARROW_UP) ? 2 : 0); - height -= 2; - width -= 3; - - w = width / 2; - w -= w % 2 - 1; - h = (w + 1) / 2; - break; - - case HSCROLL_BAR_BUTTON_LEFT: - case HSCROLL_BAR_BUTTON_RIGHT: - case VSCROLL_BAR_BUTTON_UP: - case VSCROLL_BAR_BUTTON_DOWN: - w = width / 2; - h = height / 2; - break; - - case COMBO_BOX_ARROW_BUTTON: - case TABLE: - x = 1; - (*fp_gtk_widget_size_request)(gtk2_widget, &size); - w = size.width - ((GtkMisc*)gtk2_widget)->xpad * 2; - h = size.height - ((GtkMisc*)gtk2_widget)->ypad * 2; - w = h = MIN(MIN(w, h), MIN(width,height)) * 0.7; - break; - - default: - w = width; - h = height; - break; - } - x += (width - w) / 2; - y += (height - h) / 2; - - (*fp_gtk_paint_arrow)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, arrow_type, fill, - x, y, w, h); - (*fp_gtk_paint_arrow)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, arrow_type, fill, - x, y, w, h); -} - -static void gtk2_paint_box(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, - gint synth_state, GtkTextDirection dir) -{ - gtk2_widget = gtk2_get_widget(widget_type); - - /* - * The clearlooks engine sometimes looks at the widget's state field - * instead of just the state_type variable that we pass in, so to account - * for those cases we set the widget's state field accordingly. The - * flags field is similarly important for things like focus/default state. - */ - gtk2_widget->state = state_type; - - if (widget_type == HSLIDER_TRACK) { - /* - * For horizontal JSliders with right-to-left orientation, we need - * to set the "inverted" flag to match the native GTK behavior where - * the foreground highlight is on the right side of the slider thumb. - * This is needed especially for the ubuntulooks engine, which looks - * exclusively at the "inverted" flag to determine on which side of - * the thumb to paint the highlight... - */ - ((GtkRange*)gtk2_widget)->inverted = (dir == GTK_TEXT_DIR_RTL); - - /* - * Note however that other engines like clearlooks will look at both - * the "inverted" field and the text direction to determine how - * the foreground highlight is painted: - * !inverted && ltr --> paint highlight on left side - * !inverted && rtl --> paint highlight on right side - * inverted && ltr --> paint highlight on right side - * inverted && rtl --> paint highlight on left side - * So the only way to reliably get the desired results for horizontal - * JSlider (i.e., highlight on left side for LTR ComponentOrientation - * and highlight on right side for RTL ComponentOrientation) is to - * always override text direction as LTR, and then set the "inverted" - * flag accordingly (as we have done above). - */ - dir = GTK_TEXT_DIR_LTR; - } - - /* - * Some engines (e.g. clearlooks) will paint the shadow of certain - * widgets (e.g. COMBO_BOX_ARROW_BUTTON) differently depending on the - * the text direction. - */ - gtk2_set_direction(gtk2_widget, dir); - - switch (widget_type) { - case BUTTON: - if (synth_state & DEFAULT) { - ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_DEFAULT; - } else { - ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_DEFAULT; - } - break; - case TOGGLE_BUTTON: - init_toggle_widget(widget_type, synth_state); - break; - case HSCROLL_BAR_BUTTON_LEFT: - /* - * The clearlooks engine will draw a "left" button when: - * x == w->allocation.x - * - * The ubuntulooks engine will draw a "left" button when: - * [x,y,width,height] - * intersects - * [w->alloc.x,w->alloc.y,width,height] - * - * The values that are set below should ensure that a "left" - * button is rendered for both of these (and other) engines. - */ - gtk2_widget->allocation.x = x; - gtk2_widget->allocation.y = y; - gtk2_widget->allocation.width = width; - gtk2_widget->allocation.height = height; - break; - case HSCROLL_BAR_BUTTON_RIGHT: - /* - * The clearlooks engine will draw a "right" button when: - * x + width == w->allocation.x + w->allocation.width - * - * The ubuntulooks engine will draw a "right" button when: - * [x,y,width,height] - * does not intersect - * [w->alloc.x,w->alloc.y,width,height] - * but does intersect - * [w->alloc.x+width,w->alloc.y,width,height] - * - * The values that are set below should ensure that a "right" - * button is rendered for both of these (and other) engines. - */ - gtk2_widget->allocation.x = x+width; - gtk2_widget->allocation.y = 0; - gtk2_widget->allocation.width = 0; - gtk2_widget->allocation.height = height; - break; - case VSCROLL_BAR_BUTTON_UP: - /* - * The clearlooks engine will draw an "up" button when: - * y == w->allocation.y - * - * The ubuntulooks engine will draw an "up" button when: - * [x,y,width,height] - * intersects - * [w->alloc.x,w->alloc.y,width,height] - * - * The values that are set below should ensure that an "up" - * button is rendered for both of these (and other) engines. - */ - gtk2_widget->allocation.x = x; - gtk2_widget->allocation.y = y; - gtk2_widget->allocation.width = width; - gtk2_widget->allocation.height = height; - break; - case VSCROLL_BAR_BUTTON_DOWN: - /* - * The clearlooks engine will draw a "down" button when: - * y + height == w->allocation.y + w->allocation.height - * - * The ubuntulooks engine will draw a "down" button when: - * [x,y,width,height] - * does not intersect - * [w->alloc.x,w->alloc.y,width,height] - * but does intersect - * [w->alloc.x,w->alloc.y+height,width,height] - * - * The values that are set below should ensure that a "down" - * button is rendered for both of these (and other) engines. - */ - gtk2_widget->allocation.x = x; - gtk2_widget->allocation.y = y+height; - gtk2_widget->allocation.width = width; - gtk2_widget->allocation.height = 0; - break; - default: - break; - } - - (*fp_gtk_paint_box)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, x, y, width, height); - (*fp_gtk_paint_box)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, x, y, width, height); - - /* - * Reset the text direction to the default value so that we don't - * accidentally affect other operations and widgets. - */ - gtk2_set_direction(gtk2_widget, GTK_TEXT_DIR_LTR); -} - -void gtk2_paint_box_gap(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, - GtkPositionType gap_side, gint gap_x, gint gap_width) -{ - /* Clearlooks needs a real clip area to paint the gap properly */ - GdkRectangle area = { x, y, width, height }; - - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_box_gap)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, &area, gtk2_widget, detail, - x, y, width, height, gap_side, gap_x, gap_width); - (*fp_gtk_paint_box_gap)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, &area, gtk2_widget, detail, - x, y, width, height, gap_side, gap_x, gap_width); -} - -static void gtk2_paint_check(WidgetType widget_type, gint synth_state, - const gchar *detail, gint x, gint y, gint width, gint height) -{ - GtkStateType state_type = get_gtk_state_type(widget_type, synth_state); - GtkShadowType shadow_type = get_gtk_shadow_type(widget_type, synth_state); - - gtk2_widget = gtk2_get_widget(widget_type); - init_toggle_widget(widget_type, synth_state); - - (*fp_gtk_paint_check)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); - (*fp_gtk_paint_check)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); -} - -static void gtk2_paint_expander(WidgetType widget_type, GtkStateType state_type, - const gchar *detail, gint x, gint y, gint width, gint height, - GtkExpanderStyle expander_style) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_expander)(gtk2_widget->style, gtk2_white_pixmap, - state_type, NULL, gtk2_widget, detail, - x + width / 2, y + height / 2, expander_style); - (*fp_gtk_paint_expander)(gtk2_widget->style, gtk2_black_pixmap, - state_type, NULL, gtk2_widget, detail, - x + width / 2, y + height / 2, expander_style); -} - -static void gtk2_paint_extension(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, GtkPositionType gap_side) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_extension)(gtk2_widget->style, gtk2_white_pixmap, - state_type, shadow_type, NULL, gtk2_widget, detail, - x, y, width, height, gap_side); - (*fp_gtk_paint_extension)(gtk2_widget->style, gtk2_black_pixmap, - state_type, shadow_type, NULL, gtk2_widget, detail, - x, y, width, height, gap_side); -} - -static void gtk2_paint_flat_box(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, gboolean has_focus) -{ - gtk2_widget = gtk2_get_widget(widget_type); - - if (has_focus) - ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_FOCUS; - else - ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_FOCUS; - - (*fp_gtk_paint_flat_box)(gtk2_widget->style, gtk2_white_pixmap, - state_type, shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); - (*fp_gtk_paint_flat_box)(gtk2_widget->style, gtk2_black_pixmap, - state_type, shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); -} - -static void gtk2_paint_focus(WidgetType widget_type, GtkStateType state_type, - const char *detail, gint x, gint y, gint width, gint height) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_focus)(gtk2_widget->style, gtk2_white_pixmap, state_type, - NULL, gtk2_widget, detail, x, y, width, height); - (*fp_gtk_paint_focus)(gtk2_widget->style, gtk2_black_pixmap, state_type, - NULL, gtk2_widget, detail, x, y, width, height); -} - -static void gtk2_paint_handle(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, GtkOrientation orientation) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_handle)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height, orientation); - (*fp_gtk_paint_handle)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height, orientation); -} - -static void gtk2_paint_hline(WidgetType widget_type, GtkStateType state_type, - const gchar *detail, gint x, gint y, gint width, gint height) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_hline)(gtk2_widget->style, gtk2_white_pixmap, state_type, - NULL, gtk2_widget, detail, x, x + width, y); - (*fp_gtk_paint_hline)(gtk2_widget->style, gtk2_black_pixmap, state_type, - NULL, gtk2_widget, detail, x, x + width, y); -} - -static void gtk2_paint_option(WidgetType widget_type, gint synth_state, - const gchar *detail, gint x, gint y, gint width, gint height) -{ - GtkStateType state_type = get_gtk_state_type(widget_type, synth_state); - GtkShadowType shadow_type = get_gtk_shadow_type(widget_type, synth_state); - - gtk2_widget = gtk2_get_widget(widget_type); - init_toggle_widget(widget_type, synth_state); - - (*fp_gtk_paint_option)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); - (*fp_gtk_paint_option)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); -} - -static void gtk2_paint_shadow(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, - gint synth_state, GtkTextDirection dir) -{ - gtk2_widget = gtk2_get_widget(widget_type); - - /* - * The clearlooks engine sometimes looks at the widget's state field - * instead of just the state_type variable that we pass in, so to account - * for those cases we set the widget's state field accordingly. The - * flags field is similarly important for things like focus state. - */ - gtk2_widget->state = state_type; - - /* - * Some engines (e.g. clearlooks) will paint the shadow of certain - * widgets (e.g. COMBO_BOX_TEXT_FIELD) differently depending on the - * the text direction. - */ - gtk2_set_direction(gtk2_widget, dir); - - switch (widget_type) { - case COMBO_BOX_TEXT_FIELD: - case FORMATTED_TEXT_FIELD: - case PASSWORD_FIELD: - case SPINNER_TEXT_FIELD: - case TEXT_FIELD: - if (synth_state & FOCUSED) { - ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_FOCUS; - } else { - ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_FOCUS; - } - break; - default: - break; - } - - (*fp_gtk_paint_shadow)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, x, y, width, height); - (*fp_gtk_paint_shadow)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, x, y, width, height); - - /* - * Reset the text direction to the default value so that we don't - * accidentally affect other operations and widgets. - */ - gtk2_set_direction(gtk2_widget, GTK_TEXT_DIR_LTR); -} - -static void gtk2_paint_slider(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height, GtkOrientation orientation, - gboolean has_focus) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_slider)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height, orientation); - (*fp_gtk_paint_slider)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height, orientation); -} - -static void gtk2_paint_vline(WidgetType widget_type, GtkStateType state_type, - const gchar *detail, gint x, gint y, gint width, gint height) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_vline)(gtk2_widget->style, gtk2_white_pixmap, state_type, - NULL, gtk2_widget, detail, y, y + height, x); - (*fp_gtk_paint_vline)(gtk2_widget->style, gtk2_black_pixmap, state_type, - NULL, gtk2_widget, detail, y, y + height, x); -} - -static void gtk_paint_background(WidgetType widget_type, GtkStateType state_type, - gint x, gint y, gint width, gint height) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_style_apply_default_background)(gtk2_widget->style, - gtk2_white_pixmap, TRUE, state_type, NULL, x, y, width, height); - (*fp_gtk_style_apply_default_background)(gtk2_widget->style, - gtk2_black_pixmap, TRUE, state_type, NULL, x, y, width, height); -} - -static GdkPixbuf *gtk2_get_stock_icon(gint widget_type, const gchar *stock_id, - GtkIconSize size, GtkTextDirection direction, const char *detail) -{ - init_containers(); - gtk2_widget = gtk2_get_widget((widget_type < 0) ? IMAGE : widget_type); - gtk2_widget->state = GTK_STATE_NORMAL; - (*fp_gtk_widget_set_direction)(gtk2_widget, direction); - return (*fp_gtk_widget_render_icon)(gtk2_widget, stock_id, size, detail); -} - -static jboolean gtk2_get_pixbuf_data(JNIEnv *env, GdkPixbuf* pixbuf, - jmethodID icon_upcall_method, jobject this) { - if (!pixbuf) { - return JNI_FALSE; - } - guchar *pixbuf_data = (*fp_gdk_pixbuf_get_pixels)(pixbuf); - if (pixbuf_data) { - int row_stride = (*fp_gdk_pixbuf_get_rowstride)(pixbuf); - int width = (*fp_gdk_pixbuf_get_width)(pixbuf); - int height = (*fp_gdk_pixbuf_get_height)(pixbuf); - int bps = (*fp_gdk_pixbuf_get_bits_per_sample)(pixbuf); - int channels = (*fp_gdk_pixbuf_get_n_channels)(pixbuf); - gboolean alpha = (*fp_gdk_pixbuf_get_has_alpha)(pixbuf); - - jbyteArray data = (*env)->NewByteArray(env, (row_stride * height)); - JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE); - - (*env)->SetByteArrayRegion(env, data, 0, (row_stride * height), - (jbyte *)pixbuf_data); - (*fp_g_object_unref)(pixbuf); - - /* Call the callback method to create the image on the Java side. */ - (*env)->CallVoidMethod(env, this, icon_upcall_method, data, - width, height, row_stride, bps, channels, alpha); - return JNI_TRUE; - } - return JNI_FALSE; -} - -static jboolean gtk2_get_file_icon_data(JNIEnv *env, const char *filename, - GError **error, jmethodID icon_upcall_method, jobject this) { - GdkPixbuf* pixbuf = fp_gdk_pixbuf_new_from_file(filename, error); - return gtk2_get_pixbuf_data(env, pixbuf, icon_upcall_method, this); -} - -static jboolean gtk2_get_icon_data(JNIEnv *env, gint widget_type, - const gchar *stock_id, GtkIconSize size, - GtkTextDirection direction, const char *detail, - jmethodID icon_upcall_method, jobject this) { - GdkPixbuf* pixbuf = gtk2_get_stock_icon(widget_type, stock_id, size, - direction, detail); - return gtk2_get_pixbuf_data(env, pixbuf, icon_upcall_method, this); -} - -/*************************************************/ -static gint gtk2_get_xthickness(JNIEnv *env, WidgetType widget_type) -{ - init_containers(); - - gtk2_widget = gtk2_get_widget(widget_type); - GtkStyle* style = gtk2_widget->style; - return style->xthickness; -} - -static gint gtk2_get_ythickness(JNIEnv *env, WidgetType widget_type) -{ - init_containers(); - - gtk2_widget = gtk2_get_widget(widget_type); - GtkStyle* style = gtk2_widget->style; - return style->ythickness; -} - -/*************************************************/ -static guint8 recode_color(guint16 channel) -{ - return (guint8)(channel>>8); -} - -static gint gtk2_get_color_for_state(JNIEnv *env, WidgetType widget_type, - GtkStateType state_type, ColorType color_type) -{ - gint result = 0; - GdkColor *color = NULL; - - init_containers(); - - gtk2_widget = gtk2_get_widget(widget_type); - GtkStyle* style = gtk2_widget->style; - - switch (color_type) - { - case FOREGROUND: - color = &(style->fg[state_type]); - break; - case BACKGROUND: - color = &(style->bg[state_type]); - break; - case TEXT_FOREGROUND: - color = &(style->text[state_type]); - break; - case TEXT_BACKGROUND: - color = &(style->base[state_type]); - break; - case LIGHT: - color = &(style->light[state_type]); - break; - case DARK: - color = &(style->dark[state_type]); - break; - case MID: - color = &(style->mid[state_type]); - break; - case FOCUS: - case BLACK: - color = &(style->black); - break; - case WHITE: - color = &(style->white); - break; - } - - if (color) - result = recode_color(color->red) << 16 | - recode_color(color->green) << 8 | - recode_color(color->blue); - - return result; -} - -/*************************************************/ -static jobject create_Boolean(JNIEnv *env, jboolean boolean_value); -static jobject create_Integer(JNIEnv *env, jint int_value); -static jobject create_Long(JNIEnv *env, jlong long_value); -static jobject create_Float(JNIEnv *env, jfloat float_value); -static jobject create_Double(JNIEnv *env, jdouble double_value); -static jobject create_Character(JNIEnv *env, jchar char_value); -static jobject create_Insets(JNIEnv *env, GtkBorder *border); - -static jobject gtk2_get_class_value(JNIEnv *env, WidgetType widget_type, - const char* key) -{ - init_containers(); - - gtk2_widget = gtk2_get_widget(widget_type); - - GValue value; - value.g_type = 0; - - GParamSpec* param = (*fp_gtk_widget_class_find_style_property)( - ((GTypeInstance*)gtk2_widget)->g_class, key); - if( param ) - { - (*fp_g_value_init)( &value, param->value_type ); - (*fp_gtk_widget_style_get_property)(gtk2_widget, key, &value); - - if( (*fp_g_type_is_a)( param->value_type, G_TYPE_BOOLEAN )) - { - gboolean val = (*fp_g_value_get_boolean)(&value); - return create_Boolean(env, (jboolean)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_CHAR )) - { - gchar val = (*fp_g_value_get_char)(&value); - return create_Character(env, (jchar)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_UCHAR )) - { - guchar val = (*fp_g_value_get_uchar)(&value); - return create_Character(env, (jchar)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_INT )) - { - gint val = (*fp_g_value_get_int)(&value); - return create_Integer(env, (jint)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_UINT )) - { - guint val = (*fp_g_value_get_uint)(&value); - return create_Integer(env, (jint)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_LONG )) - { - glong val = (*fp_g_value_get_long)(&value); - return create_Long(env, (jlong)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_ULONG )) - { - gulong val = (*fp_g_value_get_ulong)(&value); - return create_Long(env, (jlong)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_INT64 )) - { - gint64 val = (*fp_g_value_get_int64)(&value); - return create_Long(env, (jlong)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_UINT64 )) - { - guint64 val = (*fp_g_value_get_uint64)(&value); - return create_Long(env, (jlong)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_FLOAT )) - { - gfloat val = (*fp_g_value_get_float)(&value); - return create_Float(env, (jfloat)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_DOUBLE )) - { - gdouble val = (*fp_g_value_get_double)(&value); - return create_Double(env, (jdouble)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_ENUM )) - { - gint val = (*fp_g_value_get_enum)(&value); - return create_Integer(env, (jint)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_FLAGS )) - { - guint val = (*fp_g_value_get_flags)(&value); - return create_Integer(env, (jint)val); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_STRING )) - { - const gchar* val = (*fp_g_value_get_string)(&value); - - /* We suppose that all values come in C locale and - * utf-8 representation of a string is the same as - * the string itself. If this isn't so we should - * use g_convert. - */ - return (*env)->NewStringUTF(env, val); - } - else if( (*fp_g_type_is_a)( param->value_type, GTK_TYPE_BORDER )) - { - GtkBorder *border = (GtkBorder*)(*fp_g_value_get_boxed)(&value); - return border ? create_Insets(env, border) : NULL; - } - - /* TODO: Other types are not supported yet.*/ -/* else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_PARAM )) - { - GParamSpec* val = (*fp_g_value_get_param)(&value); - printf( "Param: %p\n", val ); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_BOXED )) - { - gpointer* val = (*fp_g_value_get_boxed)(&value); - printf( "Boxed: %p\n", val ); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_POINTER )) - { - gpointer* val = (*fp_g_value_get_pointer)(&value); - printf( "Pointer: %p\n", val ); - } - else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_OBJECT )) - { - GObject* val = (GObject*)(*fp_g_value_get_object)(&value); - printf( "Object: %p\n", val ); - }*/ - } - - return NULL; -} - -static void gtk2_set_range_value(WidgetType widget_type, jdouble value, - jdouble min, jdouble max, jdouble visible) -{ - GtkAdjustment *adj; - - gtk2_widget = gtk2_get_widget(widget_type); - - adj = (*fp_gtk_range_get_adjustment)((GtkRange *)gtk2_widget); - adj->value = (gdouble)value; - adj->lower = (gdouble)min; - adj->upper = (gdouble)max; - adj->page_size = (gdouble)visible; -} - -/*************************************************/ -static jobject create_Object(JNIEnv *env, jmethodID *cid, - const char* class_name, - const char* signature, - jvalue* value) -{ - jclass class; - jobject result; - - class = (*env)->FindClass(env, class_name); - if( class == NULL ) - return NULL; /* can't find/load the class, exception thrown */ - - if( *cid == NULL) - { - *cid = (*env)->GetMethodID(env, class, "", signature); - if( *cid == NULL ) - { - (*env)->DeleteLocalRef(env, class); - return NULL; /* can't find/get the method, exception thrown */ - } - } - - result = (*env)->NewObjectA(env, class, *cid, value); - - (*env)->DeleteLocalRef(env, class); - return result; -} - -jobject create_Boolean(JNIEnv *env, jboolean boolean_value) -{ - static jmethodID cid = NULL; - jvalue value; - - value.z = boolean_value; - - return create_Object(env, &cid, "java/lang/Boolean", "(Z)V", &value); -} - -jobject create_Integer(JNIEnv *env, jint int_value) -{ - static jmethodID cid = NULL; - jvalue value; - - value.i = int_value; - - return create_Object(env, &cid, "java/lang/Integer", "(I)V", &value); -} - -jobject create_Long(JNIEnv *env, jlong long_value) -{ - static jmethodID cid = NULL; - jvalue value; - - value.j = long_value; - - return create_Object(env, &cid, "java/lang/Long", "(J)V", &value); -} - -jobject create_Float(JNIEnv *env, jfloat float_value) -{ - static jmethodID cid = NULL; - jvalue value; - - value.f = float_value; - - return create_Object(env, &cid, "java/lang/Float", "(F)V", &value); -} - -jobject create_Double(JNIEnv *env, jdouble double_value) -{ - static jmethodID cid = NULL; - jvalue value; - - value.d = double_value; - - return create_Object(env, &cid, "java/lang/Double", "(D)V", &value); -} - -jobject create_Character(JNIEnv *env, jchar char_value) -{ - static jmethodID cid = NULL; - jvalue value; - - value.c = char_value; - - return create_Object(env, &cid, "java/lang/Character", "(C)V", &value); -} - - -jobject create_Insets(JNIEnv *env, GtkBorder *border) -{ - static jmethodID cid = NULL; - jvalue values[4]; - - values[0].i = border->top; - values[1].i = border->left; - values[2].i = border->bottom; - values[3].i = border->right; - - return create_Object(env, &cid, "java/awt/Insets", "(IIII)V", values); -} - -/*********************************************/ -static jstring gtk2_get_pango_font_name(JNIEnv *env, WidgetType widget_type) -{ - init_containers(); - - gtk2_widget = gtk2_get_widget(widget_type); - jstring result = NULL; - GtkStyle* style = gtk2_widget->style; - - if (style && style->font_desc) - { - gchar* val = (*fp_pango_font_description_to_string)(style->font_desc); - result = (*env)->NewStringUTF(env, val); - (*fp_g_free)( val ); - } - - return result; -} - -/***********************************************/ -static jobject get_string_property(JNIEnv *env, GtkSettings* settings, const gchar* key) -{ - jobject result = NULL; - gchar* strval = NULL; - - (*fp_g_object_get)(settings, key, &strval, NULL); - result = (*env)->NewStringUTF(env, strval); - (*fp_g_free)(strval); - - return result; -} - -static jobject get_integer_property(JNIEnv *env, GtkSettings* settings, const gchar* key) -{ - gint intval = 0; - (*fp_g_object_get)(settings, key, &intval, NULL); - return create_Integer(env, intval); -} - -static jobject get_boolean_property(JNIEnv *env, GtkSettings* settings, const gchar* key) -{ - gint intval = 0; - (*fp_g_object_get)(settings, key, &intval, NULL); - return create_Boolean(env, intval); -} - -static jobject gtk2_get_setting(JNIEnv *env, Setting property) -{ - GtkSettings* settings = (*fp_gtk_settings_get_default)(); - - switch (property) - { - case GTK_FONT_NAME: - return get_string_property(env, settings, "gtk-font-name"); - case GTK_ICON_SIZES: - return get_string_property(env, settings, "gtk-icon-sizes"); - case GTK_CURSOR_BLINK: - return get_boolean_property(env, settings, "gtk-cursor-blink"); - case GTK_CURSOR_BLINK_TIME: - return get_integer_property(env, settings, "gtk-cursor-blink-time"); - } - - return NULL; -} - -static gboolean gtk2_get_drawable_data(JNIEnv *env, jintArray pixelArray, jint x, - jint y, jint width, jint height, jint jwidth, int dx, int dy) { - GdkPixbuf *pixbuf; - jint *ary; - - GdkWindow *root = (*fp_gdk_get_default_root_window)(); - - pixbuf = (*fp_gdk_pixbuf_get_from_drawable)(NULL, root, NULL, x, y, - 0, 0, width, height); - - if (pixbuf) { - int nchan = (*fp_gdk_pixbuf_get_n_channels)(pixbuf); - int stride = (*fp_gdk_pixbuf_get_rowstride)(pixbuf); - - if ((*fp_gdk_pixbuf_get_width)(pixbuf) == width - && (*fp_gdk_pixbuf_get_height)(pixbuf) == height - && (*fp_gdk_pixbuf_get_bits_per_sample)(pixbuf) == 8 - && (*fp_gdk_pixbuf_get_colorspace)(pixbuf) == GDK_COLORSPACE_RGB - && nchan >= 3 - ) { - guchar *p, *pix = (*fp_gdk_pixbuf_get_pixels)(pixbuf); - - ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL); - if (ary) { - jint _x, _y; - int index; - for (_y = 0; _y < height; _y++) { - for (_x = 0; _x < width; _x++) { - p = pix + (intptr_t) _y * stride + _x * nchan; - - index = (_y + dy) * jwidth + (_x + dx); - ary[index] = 0xff000000 - | (p[0] << 16) - | (p[1] << 8) - | (p[2]); - - } - } - (*env)->ReleasePrimitiveArrayCritical(env, pixelArray, ary, 0); - } - } - (*fp_g_object_unref)(pixbuf); - } - return JNI_FALSE; -} - -static GdkWindow* gtk2_get_window(void *widget) { - return ((GtkWidget*)widget)->window; -} - -void gtk2_init(GtkApi* gtk) { - gtk->version = GTK_2; - - gtk->show_uri_load = >k2_show_uri_load; - gtk->unload = >k2_unload; - gtk->flush_event_loop = &flush_gtk_event_loop; - gtk->gtk_check_version = fp_gtk_check_version; - gtk->get_setting = >k2_get_setting; - - gtk->paint_arrow = >k2_paint_arrow; - gtk->paint_box = >k2_paint_box; - gtk->paint_box_gap = >k2_paint_box_gap; - gtk->paint_expander = >k2_paint_expander; - gtk->paint_extension = >k2_paint_extension; - gtk->paint_flat_box = >k2_paint_flat_box; - gtk->paint_focus = >k2_paint_focus; - gtk->paint_handle = >k2_paint_handle; - gtk->paint_hline = >k2_paint_hline; - gtk->paint_vline = >k2_paint_vline; - gtk->paint_option = >k2_paint_option; - gtk->paint_shadow = >k2_paint_shadow; - gtk->paint_slider = >k2_paint_slider; - gtk->paint_background = >k_paint_background; - gtk->paint_check = >k2_paint_check; - gtk->set_range_value = >k2_set_range_value; - - gtk->init_painting = >k2_init_painting; - gtk->copy_image = >k2_copy_image; - - gtk->get_xthickness = >k2_get_xthickness; - gtk->get_ythickness = >k2_get_ythickness; - gtk->get_color_for_state = >k2_get_color_for_state; - gtk->get_class_value = >k2_get_class_value; - - gtk->get_pango_font_name = >k2_get_pango_font_name; - gtk->get_icon_data = >k2_get_icon_data; - gtk->get_file_icon_data = >k2_get_file_icon_data; - gtk->gdk_threads_enter = fp_gdk_threads_enter; - gtk->gdk_threads_leave = fp_gdk_threads_leave; - gtk->gtk_show_uri = fp_gtk_show_uri; - gtk->get_drawable_data = >k2_get_drawable_data; - gtk->g_free = fp_g_free; - - gtk->gtk_file_chooser_get_filename = fp_gtk_file_chooser_get_filename; - gtk->gtk_widget_hide = fp_gtk_widget_hide; - gtk->gtk_main_quit = fp_gtk_main_quit; - gtk->gtk_file_chooser_dialog_new = fp_gtk_file_chooser_dialog_new; - gtk->gtk_file_chooser_set_current_folder = - fp_gtk_file_chooser_set_current_folder; - gtk->gtk_file_chooser_set_filename = fp_gtk_file_chooser_set_filename; - gtk->gtk_file_chooser_set_current_name = - fp_gtk_file_chooser_set_current_name; - gtk->gtk_file_filter_add_custom = fp_gtk_file_filter_add_custom; - gtk->gtk_file_chooser_set_filter = fp_gtk_file_chooser_set_filter; - gtk->gtk_file_chooser_get_type = fp_gtk_file_chooser_get_type; - gtk->gtk_file_filter_new = fp_gtk_file_filter_new; - gtk->gtk_file_chooser_set_do_overwrite_confirmation = - fp_gtk_file_chooser_set_do_overwrite_confirmation; - gtk->gtk_file_chooser_set_select_multiple = - fp_gtk_file_chooser_set_select_multiple; - gtk->gtk_file_chooser_get_current_folder = - fp_gtk_file_chooser_get_current_folder; - gtk->gtk_file_chooser_get_filenames = fp_gtk_file_chooser_get_filenames; - gtk->gtk_g_slist_length = fp_gtk_g_slist_length; - gtk->g_signal_connect_data = fp_g_signal_connect_data; - gtk->gtk_widget_show = fp_gtk_widget_show; - gtk->gtk_main = fp_gtk_main; - gtk->gtk_main_level = fp_gtk_main_level; - gtk->g_path_get_dirname = fp_g_path_get_dirname; - gtk->gdk_x11_drawable_get_xid = fp_gdk_x11_drawable_get_xid; - gtk->gtk_widget_destroy = fp_gtk_widget_destroy; - gtk->gtk_window_present = fp_gtk_window_present; - gtk->gtk_window_move = fp_gtk_window_move; - gtk->gtk_window_resize = fp_gtk_window_resize; - gtk->get_window = >k2_get_window; - - gtk->g_object_unref = fp_g_object_unref; - gtk->g_list_append = fp_g_list_append; - gtk->g_list_free = fp_g_list_free; - gtk->g_list_free_full = fp_g_list_free_full; -} diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h deleted file mode 100644 index 8075d4f419fad..0000000000000 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.h +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifdef HEADLESS - #error This file should not be included in headless library -#endif - -#ifndef _GTK2_INTERFACE_H -#define _GTK2_INTERFACE_H - -#include -#include -#include -#include "gtk_interface.h" - -#define GTK_HAS_FOCUS (1 << 12) -#define GTK_HAS_DEFAULT (1 << 14) - -typedef enum -{ - GTK_WINDOW_TOPLEVEL, - GTK_WINDOW_POPUP -} GtkWindowType; - -typedef enum -{ - G_PARAM_READABLE = 1 << 0, - G_PARAM_WRITABLE = 1 << 1, - G_PARAM_CONSTRUCT = 1 << 2, - G_PARAM_CONSTRUCT_ONLY = 1 << 3, - G_PARAM_LAX_VALIDATION = 1 << 4, - G_PARAM_PRIVATE = 1 << 5 -} GParamFlags; - -/* We define all structure pointers to be void* */ -typedef void GVfs; - -typedef void GdkColormap; -typedef void GdkDrawable; -typedef void GdkGC; -typedef void GdkPixmap; - -typedef void GtkFixed; -typedef void GtkMenuItem; -typedef void GtkMenuShell; -typedef void GtkWidgetClass; -typedef void PangoFontDescription; -typedef void GtkSettings; - -/* Some real structures */ -typedef struct -{ - guint32 pixel; - guint16 red; - guint16 green; - guint16 blue; -} GdkColor; - -typedef struct { - gint fd; - gushort events; - gushort revents; -} GPollFD; - -typedef struct { - gint x; - gint y; - gint width; - gint height; -} GtkAllocation; - -typedef struct { - gint width; - gint height; -} GtkRequisition; - -typedef struct { - GtkWidgetClass *g_class; -} GTypeInstance; - -typedef struct { - gint left; - gint right; - gint top; - gint bottom; -} GtkBorder; - -/****************************************************** - * FIXME: it is more safe to include gtk headers for - * the precise type definition of GType and other - * structures. This is a place where getting rid of gtk - * headers may be dangerous. - ******************************************************/ - -typedef struct -{ - GType g_type; - - union { - gint v_int; - guint v_uint; - glong v_long; - gulong v_ulong; - gint64 v_int64; - guint64 v_uint64; - gfloat v_float; - gdouble v_double; - gpointer v_pointer; - } data[2]; -} GValue; - -typedef struct -{ - GTypeInstance g_type_instance; - - gchar *name; - GParamFlags flags; - GType value_type; - GType owner_type; -} GParamSpec; - -typedef struct { - GTypeInstance g_type_instance; - guint ref_count; - void *qdata; -} GObject; - -typedef struct { - GObject parent_instance; - guint32 flags; -} GtkObject; - -typedef struct -{ - GObject parent_instance; - - GdkColor fg[5]; - GdkColor bg[5]; - GdkColor light[5]; - GdkColor dark[5]; - GdkColor mid[5]; - GdkColor text[5]; - GdkColor base[5]; - GdkColor text_aa[5]; /* Halfway between text/base */ - - GdkColor black; - GdkColor white; - PangoFontDescription *font_desc; - - gint xthickness; - gint ythickness; - - GdkGC *fg_gc[5]; - GdkGC *bg_gc[5]; - GdkGC *light_gc[5]; - GdkGC *dark_gc[5]; - GdkGC *mid_gc[5]; - GdkGC *text_gc[5]; - GdkGC *base_gc[5]; - GdkGC *text_aa_gc[5]; - GdkGC *black_gc; - GdkGC *white_gc; - - GdkPixmap *bg_pixmap[5]; -} GtkStyle; - -typedef struct _GtkWidget GtkWidget; -struct _GtkWidget -{ - GtkObject object; - guint16 private_flags; - guint8 state; - guint8 saved_state; - gchar *name; - GtkStyle *style; - GtkRequisition requisition; - GtkAllocation allocation; - GdkWindow *window; - GtkWidget *parent; -}; - -typedef struct -{ - GtkWidget widget; - - gfloat xalign; - gfloat yalign; - - guint16 xpad; - guint16 ypad; -} GtkMisc; - -typedef struct { - GtkWidget widget; - GtkWidget *focus_child; - guint border_width : 16; - guint need_resize : 1; - guint resize_mode : 2; - guint reallocate_redraws : 1; - guint has_focus_chain : 1; -} GtkContainer; - -typedef struct { - GtkContainer container; - GtkWidget *child; -} GtkBin; - -typedef struct { - GtkBin bin; - GdkWindow *event_window; - gchar *label_text; - guint activate_timeout; - guint constructed : 1; - guint in_button : 1; - guint button_down : 1; - guint relief : 2; - guint use_underline : 1; - guint use_stock : 1; - guint depressed : 1; - guint depress_on_activate : 1; - guint focus_on_click : 1; -} GtkButton; - -typedef struct { - GtkButton button; - guint active : 1; - guint draw_indicator : 1; - guint inconsistent : 1; -} GtkToggleButton; - -typedef struct _GtkAdjustment GtkAdjustment; -struct _GtkAdjustment -{ - GtkObject parent_instance; - - gdouble lower; - gdouble upper; - gdouble value; - gdouble step_increment; - gdouble page_increment; - gdouble page_size; -}; - -typedef enum -{ - GTK_UPDATE_CONTINUOUS, - GTK_UPDATE_DISCONTINUOUS, - GTK_UPDATE_DELAYED -} GtkUpdateType; - -typedef struct _GtkRange GtkRange; -struct _GtkRange -{ - GtkWidget widget; - GtkAdjustment *adjustment; - GtkUpdateType update_policy; - guint inverted : 1; - /*< protected >*/ - guint flippable : 1; - guint has_stepper_a : 1; - guint has_stepper_b : 1; - guint has_stepper_c : 1; - guint has_stepper_d : 1; - guint need_recalc : 1; - guint slider_size_fixed : 1; - gint min_slider_size; - GtkOrientation orientation; - GdkRectangle range_rect; - gint slider_start, slider_end; - gint round_digits; - /*< private >*/ - guint trough_click_forward : 1; - guint update_pending : 1; - /*GtkRangeLayout * */ void *layout; - /*GtkRangeStepTimer * */ void* timer; - gint slide_initial_slider_position; - gint slide_initial_coordinate; - guint update_timeout_id; - GdkWindow *event_window; -}; - -typedef struct _GtkProgressBar GtkProgressBar; - -typedef enum -{ - GTK_PROGRESS_CONTINUOUS, - GTK_PROGRESS_DISCRETE -} GtkProgressBarStyle; - -typedef enum -{ - GTK_PROGRESS_LEFT_TO_RIGHT, - GTK_PROGRESS_RIGHT_TO_LEFT, - GTK_PROGRESS_BOTTOM_TO_TOP, - GTK_PROGRESS_TOP_TO_BOTTOM -} GtkProgressBarOrientation; - -typedef struct _GtkProgress GtkProgress; - -struct _GtkProgress -{ - GtkWidget widget; - GtkAdjustment *adjustment; - GdkPixmap *offscreen_pixmap; - gchar *format; - gfloat x_align; - gfloat y_align; - guint show_text : 1; - guint activity_mode : 1; - guint use_text_format : 1; -}; - -struct _GtkProgressBar -{ - GtkProgress progress; - GtkProgressBarStyle bar_style; - GtkProgressBarOrientation orientation; - guint blocks; - gint in_block; - gint activity_pos; - guint activity_step; - guint activity_blocks; - gdouble pulse_fraction; - guint activity_dir : 1; - guint ellipsize : 3; -}; - -/** - * Returns : - * NULL if the GLib library is compatible with the given version, or a string - * describing the version mismatch. - * Please note that the glib_check_version() is available since 2.6, - * so you should use GLIB_CHECK_VERSION macro instead. - */ -static gchar* (*fp_glib_check_version)(guint required_major, guint required_minor, - guint required_micro); - -/** - * Returns : - * TRUE if the GLib library is compatible with the given version - */ -#define GLIB_CHECK_VERSION(major, minor, micro) \ - (fp_glib_check_version && fp_glib_check_version(major, minor, micro) == NULL) - -/** - * Returns : - * NULL if the GTK+ library is compatible with the given version, or a string - * describing the version mismatch. - */ -static gchar* (*fp_gtk_check_version)(guint required_major, guint required_minor, - guint required_micro); - -static void gtk2_init(GtkApi* gtk); - -static void (*fp_g_free)(gpointer mem); -static void (*fp_g_object_unref)(gpointer object); -static GdkWindow *(*fp_gdk_get_default_root_window) (void); - -static int (*fp_gdk_pixbuf_get_bits_per_sample)(const GdkPixbuf *pixbuf); -static guchar *(*fp_gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf); -static gboolean (*fp_gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf); -static int (*fp_gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf); -static int (*fp_gdk_pixbuf_get_n_channels)(const GdkPixbuf *pixbuf); -static int (*fp_gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf); -static int (*fp_gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf); -static GdkPixbuf *(*fp_gdk_pixbuf_new_from_file)(const char *filename, GError **error); -static GdkColorspace (*fp_gdk_pixbuf_get_colorspace)(const GdkPixbuf *pixbuf); - -static GdkPixbuf *(*fp_gdk_pixbuf_get_from_drawable)(GdkPixbuf *dest, - GdkDrawable *src, GdkColormap *cmap, int src_x, int src_y, - int dest_x, int dest_y, int width, int height); -static GdkPixbuf *(*fp_gdk_pixbuf_scale_simple)(GdkPixbuf *src, - int dest_width, int dest_heigh, GdkInterpType interp_type); - - -static void (*fp_gtk_widget_destroy)(void *widget); -static void (*fp_gtk_window_present)(GtkWindow *window); -static void (*fp_gtk_window_move)(GtkWindow *window, gint x, gint y); -static void (*fp_gtk_window_resize)(GtkWindow *window, gint width, gint height); - -/** - * Function Pointers for GtkFileChooser - */ -static gchar* (*fp_gtk_file_chooser_get_filename)(GtkFileChooser *chooser); -static void (*fp_gtk_widget_hide)(void *widget); -static void (*fp_gtk_main_quit)(void); -static void* (*fp_gtk_file_chooser_dialog_new)(const gchar *title, - GtkWindow *parent, GtkFileChooserAction action, - const gchar *first_button_text, ...); -static gboolean (*fp_gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, - const gchar *filename); -static gboolean (*fp_gtk_file_chooser_set_filename)(GtkFileChooser *chooser, - const char *filename); -static void (*fp_gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, - const gchar *name); -static void (*fp_gtk_file_filter_add_custom)(GtkFileFilter *filter, - GtkFileFilterFlags needed, GtkFileFilterFunc func, gpointer data, - GDestroyNotify notify); -static void (*fp_gtk_file_chooser_set_filter)(GtkFileChooser *chooser, - GtkFileFilter *filter); -static GType (*fp_gtk_file_chooser_get_type)(void); -static GtkFileFilter* (*fp_gtk_file_filter_new)(void); -static void (*fp_gtk_file_chooser_set_do_overwrite_confirmation)( - GtkFileChooser *chooser, gboolean do_overwrite_confirmation); -static void (*fp_gtk_file_chooser_set_select_multiple)( - GtkFileChooser *chooser, gboolean select_multiple); -static gchar* (*fp_gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser); -static GSList* (*fp_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser); -static guint (*fp_gtk_g_slist_length)(GSList *list); -static gulong (*fp_g_signal_connect_data)(gpointer instance, - const gchar *detailed_signal, GCallback c_handler, gpointer data, - GClosureNotify destroy_data, GConnectFlags connect_flags); -static void (*fp_gtk_widget_show)(void *widget); -static void (*fp_gtk_main)(void); -static guint (*fp_gtk_main_level)(void); -static gchar* (*fp_g_path_get_dirname) (const gchar *file_name); -static XID (*fp_gdk_x11_drawable_get_xid) (GdkWindow *drawable); - -static GList* (*fp_g_list_append) (GList *list, gpointer data); -static void (*fp_g_list_free) (GList *list); -static void (*fp_g_list_free_full) (GList *list, GDestroyNotify free_func); - -static gboolean (*fp_gtk_show_uri)(GdkScreen *screen, const gchar *uri, - guint32 timestamp, GError **error); - -#endif /* !_GTK2_INTERFACE_H */ diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c index c8573ed3fbfb9..fcf84948c66ee 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,10 +32,8 @@ #include "jvm_md.h" #include "gtk_interface.h" -GtkApi* gtk2_load(JNIEnv *env, const char* lib_name); GtkApi* gtk3_load(JNIEnv *env, const char* lib_name); -gboolean gtk2_check(const char* lib_name, gboolean load); gboolean gtk3_check(const char* lib_name, gboolean load); GtkApi *gtk; @@ -56,13 +54,6 @@ static GtkLib gtk_libs[] = { >k3_load, >k3_check }, - { - GTK_2, - JNI_LIB_NAME("gtk-x11-2.0"), - VERSIONED_JNI_LIB_NAME("gtk-x11-2.0", "0"), - >k2_load, - >k2_check - } }; static GtkLib** get_libs_order(GtkVersion version) { diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c index e2f4ea31d2edb..6884b939ae727 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -675,7 +675,6 @@ static gboolean isAllDataReady() { static void *pipewire_libhandle = NULL; -//glib_version_2_68 false for gtk2, as it comes from gtk3_interface.c extern gboolean glib_version_2_68; diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index 4f87e8ef4c113..640b06d0521a9 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -3362,10 +3362,10 @@ BOOL AwtComponent::IsNumPadKey(UINT vkey, BOOL extended) return FALSE; } static void -resetKbdState( BYTE kstate[256]) { - BYTE tmpState[256]; +resetKbdState( BYTE (&kstate)[AwtToolkit::KB_STATE_SIZE]) { + BYTE tmpState[AwtToolkit::KB_STATE_SIZE]; WCHAR wc[2]; - memmove(tmpState, kstate, 256 * sizeof(BYTE)); + memmove(tmpState, kstate, sizeof(kstate)); tmpState[VK_SHIFT] = 0; tmpState[VK_CONTROL] = 0; tmpState[VK_MENU] = 0; diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KdcComm.java b/src/java.security.jgss/share/classes/sun/security/krb5/KdcComm.java index 63d9998256758..688a9183304d9 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/KdcComm.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KdcComm.java @@ -62,15 +62,18 @@ public final class KdcComm { // them can also be defined in a realm, which overrides value here. /** - * max retry time for a single KDC, default Krb5.KDC_RETRY_LIMIT (3) + * max retry time for a single KDC, default Krb5.KDC_RETRY_LIMIT (3), + * Must be > 0. */ private static int defaultKdcRetryLimit; /** - * timeout requesting a ticket from KDC, in millisec, default 30 sec + * timeout requesting a ticket from KDC, in millisec, default + * Krb5.KDC_TIMEOUT (30000). Must be > 0. */ private static int defaultKdcTimeout; /** - * max UDP packet size, default unlimited (-1) + * max UDP packet size, default Krb5.KDC_DEFAULT_UDP_PREF_LIMIT (1465). + * Must be >= 0 and <= Krb5.KDC_HARD_UDP_LIMIT (32700). */ private static int defaultUdpPrefLimit; @@ -146,9 +149,9 @@ public String run() { timeout = parseTimeString(temp); temp = cfg.get("libdefaults", "max_retries"); - max_retries = parsePositiveIntString(temp); + max_retries = parseNonNegativeIntString(temp); temp = cfg.get("libdefaults", "udp_preference_limit"); - udp_pref_limit = parsePositiveIntString(temp); + udp_pref_limit = parseNonNegativeIntString(temp); } catch (Exception exc) { // ignore any exceptions; use default values if (DEBUG != null) { @@ -157,7 +160,7 @@ public String run() { exc.getMessage()); } } - defaultKdcTimeout = timeout > 0 ? timeout : 30*1000; // 30 seconds + defaultKdcTimeout = timeout > 0 ? timeout : Krb5.KDC_TIMEOUT; defaultKdcRetryLimit = max_retries > 0 ? max_retries : Krb5.KDC_RETRY_LIMIT; @@ -175,11 +178,11 @@ public String run() { /** * The instance fields */ - private String realm; + private final String realm; public KdcComm(String realm) throws KrbException { if (realm == null) { - realm = Config.getInstance().getDefaultRealm(); + realm = Config.getInstance().getDefaultRealm(); if (realm == null) { throw new KrbException(Krb5.KRB_ERR_GENERIC, "Cannot find default realm"); @@ -191,11 +194,10 @@ public KdcComm(String realm) throws KrbException { public byte[] send(KrbKdcReq req) throws IOException, KrbException { int udpPrefLimit = getRealmSpecificValue( - realm, "udp_preference_limit", defaultUdpPrefLimit); + realm, "udp_preference_limit", defaultUdpPrefLimit, false); byte[] obuf = req.encoding(); - boolean useTCP = (udpPrefLimit > 0 && - (obuf != null && obuf.length > udpPrefLimit)); + boolean useTCP = obuf != null && obuf.length > udpPrefLimit; return send(req, useTCP); } @@ -207,14 +209,6 @@ private byte[] send(KrbKdcReq req, boolean useTCP) return null; Config cfg = Config.getInstance(); - if (realm == null) { - realm = cfg.getDefaultRealm(); - if (realm == null) { - throw new KrbException(Krb5.KRB_ERR_GENERIC, - "Cannot find default realm"); - } - } - String kdcList = cfg.getKDCList(realm); if (kdcList == null) { throw new KrbException("Cannot get kdc for realm " + realm); @@ -296,9 +290,9 @@ private byte[] send(KrbKdcReq req, String tempKdc, boolean useTCP) int port = Krb5.KDC_INET_DEFAULT_PORT; int retries = getRealmSpecificValue( - realm, "max_retries", defaultKdcRetryLimit); + realm, "max_retries", defaultKdcRetryLimit, true); int timeout = getRealmSpecificValue( - realm, "kdc_timeout", defaultKdcTimeout); + realm, "kdc_timeout", defaultKdcTimeout, true); if (badPolicy == BpType.TRY_LESS && KdcAccessibility.isBad(tempKdc)) { if (retries > tryLessMaxRetries) { @@ -339,7 +333,7 @@ private byte[] send(KrbKdcReq req, String tempKdc, boolean useTCP) } } if (portStr != null) { - int tempPort = parsePositiveIntString(portStr); + int tempPort = parseNonNegativeIntString(portStr); if (tempPort > 0) port = tempPort; } @@ -444,10 +438,10 @@ private static int parseTimeString(String s) { return -1; } if (s.endsWith("s")) { - int seconds = parsePositiveIntString(s.substring(0, s.length()-1)); + int seconds = parseNonNegativeIntString(s.substring(0, s.length()-1)); return (seconds < 0) ? -1 : (seconds*1000); } else { - return parsePositiveIntString(s); + return parseNonNegativeIntString(s); } } @@ -461,9 +455,11 @@ private static int parseTimeString(String s) { * the global setting if null * @param key the key for the setting * @param defValue default value + * @param mustBePositive true if value must be >0, false if value must be >=0 * @return a value for the key */ - private int getRealmSpecificValue(String realm, String key, int defValue) { + private int getRealmSpecificValue(String realm, String key, int defValue, + boolean mustBePositive) { int v = defValue; if (realm == null) return v; @@ -475,18 +471,22 @@ private int getRealmSpecificValue(String realm, String key, int defValue) { if (key.equals("kdc_timeout")) { temp = parseTimeString(value); } else { - temp = parsePositiveIntString(value); + temp = parseNonNegativeIntString(value); } } catch (Exception exc) { // Ignored, defValue will be picked up } - if (temp > 0) v = temp; + if (mustBePositive) { + if (temp > 0) v = temp; + } else { + if (temp >= 0) v = temp; + } return v; } - private static int parsePositiveIntString(String intString) { + private static int parseNonNegativeIntString(String intString) { if (intString == null) return -1; diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java index 3cbef7e74d24b..c3cac113c4029 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java @@ -134,6 +134,7 @@ public class Krb5 { // number of retries before giving up public static final int KDC_RETRY_LIMIT = 3; + public static final int KDC_TIMEOUT = 30000; public static final int KDC_DEFAULT_UDP_PREF_LIMIT = 1465; public static final int KDC_HARD_UDP_LIMIT = 32700; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 28768f2517bed..8576d8cfb2f02 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -62,6 +62,8 @@ import static com.sun.tools.javac.code.Type.*; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.jvm.ClassFile.externalize; +import static com.sun.tools.javac.main.Option.DOE; + import com.sun.tools.javac.resources.CompilerProperties.Fragments; /** @@ -99,6 +101,7 @@ public class Types { final Name capturedName; public final Warner noWarnings; + public final boolean dumpStacktraceOnError; // public static Types instance(Context context) { @@ -120,6 +123,8 @@ protected Types(Context context) { messages = JavacMessages.instance(context); diags = JCDiagnostic.Factory.instance(context); noWarnings = new Warner(null); + Options options = Options.instance(context); + dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE); } // @@ -634,12 +639,13 @@ public boolean isConvertible(Type t, Type s) { * wraps a diagnostic that can be used to generate more details error * messages. */ - public static class FunctionDescriptorLookupError extends RuntimeException { + public static class FunctionDescriptorLookupError extends CompilerInternalException { private static final long serialVersionUID = 0; transient JCDiagnostic diagnostic; - FunctionDescriptorLookupError() { + FunctionDescriptorLookupError(boolean dumpStackTraceOnError) { + super(dumpStackTraceOnError); this.diagnostic = null; } @@ -651,12 +657,6 @@ FunctionDescriptorLookupError setMessage(JCDiagnostic diag) { public JCDiagnostic getDiagnostic() { return diagnostic; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } /** @@ -809,7 +809,7 @@ FunctionDescriptorLookupError failure(String msg, Object... args) { } FunctionDescriptorLookupError failure(JCDiagnostic diag) { - return new FunctionDescriptorLookupError().setMessage(diag); + return new FunctionDescriptorLookupError(Types.this.dumpStacktraceOnError).setMessage(diag); } } @@ -5107,41 +5107,30 @@ public RetentionPolicy getRetention(TypeSymbol sym) { // - public abstract static class SignatureGenerator { + public abstract class SignatureGenerator { - public static class InvalidSignatureException extends RuntimeException { + public class InvalidSignatureException extends CompilerInternalException { private static final long serialVersionUID = 0; private final transient Type type; - InvalidSignatureException(Type type) { + InvalidSignatureException(Type type, boolean dumpStackTraceOnError) { + super(dumpStackTraceOnError); this.type = type; } public Type type() { return type; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } - private final Types types; - protected abstract void append(char ch); protected abstract void append(byte[] ba); protected abstract void append(Name name); protected void classReference(ClassSymbol c) { /* by default: no-op */ } - protected SignatureGenerator(Types types) { - this.types = types; - } - protected void reportIllegalSignature(Type t) { - throw new InvalidSignatureException(t); + throw new InvalidSignatureException(t, Types.this.dumpStacktraceOnError); } /** @@ -5257,9 +5246,9 @@ public void assembleClassSig(Type type) { if (outer.allparams().nonEmpty()) { boolean rawOuter = c.owner.kind == MTH || // either a local class - c.name == types.names.empty; // or anonymous + c.name == Types.this.names.empty; // or anonymous assembleClassSig(rawOuter - ? types.erasure(outer) + ? Types.this.erasure(outer) : outer); append(rawOuter ? '$' : '.'); Assert.check(c.flatname.startsWith(c.owner.enclClass().flatname)); @@ -5281,7 +5270,7 @@ public void assembleParamsSig(List typarams) { for (List ts = typarams; ts.nonEmpty(); ts = ts.tail) { Type.TypeVar tvar = (Type.TypeVar) ts.head; append(tvar.tsym.name); - List bounds = types.getBounds(tvar); + List bounds = Types.this.getBounds(tvar); if ((bounds.head.tsym.flags() & INTERFACE) != 0) { append(':'); } 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 48459dd96bb22..77a7e3332f0c6 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 @@ -945,7 +945,13 @@ private MethodSymbol validateContainer(Type targetContainerType, boolean fatalError = false; // Validate that there is a (and only 1) value method - Scope scope = targetContainerType.tsym.members(); + Scope scope = null; + try { + scope = targetContainerType.tsym.members(); + } catch (CompletionFailure ex) { + chk.completionError(pos, ex); + return null; + } int nr_value_elems = 0; boolean error = false; for(Symbol elm : scope.getSymbolsByName(names.value)) { 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 89709bd3ec767..792b5ca234655 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 @@ -787,7 +787,7 @@ public void visitSwitchExpression(JCSwitchExpression tree) { private boolean exhausts(JCExpression selector, List cases) { Set patternSet = new HashSet<>(); Map> enum2Constants = new HashMap<>(); - Set booleanLiterals = new HashSet<>(); + Set booleanLiterals = new HashSet<>(Set.of(0, 1)); for (JCCase c : cases) { if (!TreeInfo.unguardedCase(c)) continue; @@ -800,7 +800,7 @@ private boolean exhausts(JCExpression selector, List cases) { } else if (l instanceof JCConstantCaseLabel constantLabel) { if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN)) { Object value = ((JCLiteral) constantLabel.expr).value; - booleanLiterals.add(value); + booleanLiterals.remove(value); } else { Symbol s = TreeInfo.symbol(constantLabel.expr); if (s != null && s.isEnum()) { @@ -817,7 +817,7 @@ private boolean exhausts(JCExpression selector, List cases) { } } - if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN) && booleanLiterals.size() == 2) { + if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN) && booleanLiterals.isEmpty()) { return true; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java index e3e7e1adea533..f5df6adddf377 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java @@ -47,6 +47,7 @@ import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; +import com.sun.tools.javac.util.CompilerInternalException; import java.io.IOException; import java.io.Writer; @@ -68,7 +69,7 @@ import java.util.function.Predicate; import static com.sun.tools.javac.code.TypeTag.*; -import java.util.Comparator; +import static com.sun.tools.javac.main.Option.DOE; /** Helper class for type parameter inference, used by the attribution phase. * @@ -97,6 +98,8 @@ public class Infer { */ private List pendingGraphs; + private final boolean dumpStacktraceOnError; + public static Infer instance(Context context) { Infer instance = context.get(inferKey); if (instance == null) @@ -119,6 +122,7 @@ protected Infer(Context context) { pendingGraphs = List.nil(); emptyContext = new InferenceContext(this, List.nil()); + dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE); } /** A value for prototypes that admit any type, including polymorphic ones. */ @@ -133,8 +137,8 @@ public static class InferenceException extends InapplicableMethodException { transient List messages = List.nil(); - InferenceException() { - super(null); + InferenceException(boolean dumpStacktrace) { + super(null, dumpStacktrace); } @Override @@ -144,7 +148,7 @@ public JCDiagnostic getDiagnostic() { } InferenceException error(JCDiagnostic diag) { - InferenceException result = new InferenceException(); + InferenceException result = new InferenceException(dumpStacktraceOnError); if (diag != null) { result.messages = result.messages.append(diag); } @@ -1342,20 +1346,15 @@ interface GraphStrategy { * A NodeNotFoundException is thrown whenever an inference strategy fails * to pick the next node to solve in the inference graph. */ - public static class NodeNotFoundException extends RuntimeException { + class NodeNotFoundException extends CompilerInternalException { private static final long serialVersionUID = 0; transient InferenceGraph graph; - public NodeNotFoundException(InferenceGraph graph) { + public NodeNotFoundException(InferenceGraph graph, boolean dumpStacktraceOnError) { + super(dumpStacktraceOnError); this.graph = graph; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } /** * Pick the next node (leaf) to solve in the graph @@ -1375,7 +1374,7 @@ abstract class LeafSolver implements GraphStrategy { public Node pickNode(InferenceGraph g) { if (g.nodes.isEmpty()) { //should not happen - throw new NodeNotFoundException(g); + throw new NodeNotFoundException(g, Infer.this.dumpStacktraceOnError); } return g.nodes.get(0); } @@ -1450,7 +1449,7 @@ public Node pickNode(final InferenceGraph g) { } if (bestPath == noPath) { //no path leads there - throw new NodeNotFoundException(g); + throw new NodeNotFoundException(g, Infer.this.dumpStacktraceOnError); } return bestPath.fst.head; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 7f1ad8211cb84..2c3d79c0ab6da 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -1796,7 +1796,7 @@ private class L2MSignatureGenerator extends Types.SignatureGenerator { boolean allowIllegalSignatures; L2MSignatureGenerator(boolean allowIllegalSignatures) { - super(types); + types.super(); this.allowIllegalSignatures = allowIllegalSignatures; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 7c7db6de0863b..c5fd2177d49a7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -62,6 +62,7 @@ import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; + import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT; import com.sun.tools.javac.tree.JCTree.JCSwitchExpression; @@ -2629,7 +2630,7 @@ private class LowerSignatureGenerator extends Types.SignatureGenerator { StringBuilder sb = new StringBuilder(); LowerSignatureGenerator() { - super(types); + types.super(); } @Override @@ -3329,6 +3330,9 @@ List boxArgs(List parameters, List _args, Type /** Expand a boxing or unboxing conversion if needed. */ @SuppressWarnings("unchecked") // XXX unchecked T boxIfNeeded(T tree, Type type) { + Assert.check(!type.hasTag(VOID)); + if (type.hasTag(NONE)) + return tree; boolean havePrimitive = tree.type.isPrimitive(); if (havePrimitive == type.isPrimitive()) return tree; @@ -3845,6 +3849,9 @@ public void visitLambda(JCLambda tree) { Type prevRestype = currentRestype; try { currentRestype = types.erasure(tree.getDescriptorType(types)).getReturnType(); + // represent void results as NO_TYPE, to avoid unnecessary boxing in boxIfNeeded + if (currentRestype.hasTag(VOID)) + currentRestype = Type.noType; tree.body = tree.getBodyKind() == BodyKind.EXPRESSION ? translate((JCExpression) tree.body, currentRestype) : translate(tree.body); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index 643bb22c85358..c71b6d341dc12 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -79,6 +79,7 @@ import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.comp.Resolve.MethodResolutionPhase.*; +import static com.sun.tools.javac.main.Option.DOE; import static com.sun.tools.javac.tree.JCTree.Tag.*; import static com.sun.tools.javac.util.Iterators.createCompoundIterator; @@ -112,6 +113,7 @@ public class Resolve { private final boolean allowYieldStatement; final EnumSet verboseResolutionMode; final boolean dumpMethodReferenceSearchResults; + final boolean dumpStacktraceOnError; WriteableScope polymorphicSignatureScope; @@ -149,6 +151,7 @@ protected Resolve(Context context) { allowModules = Feature.MODULES.allowedInSource(source); allowRecords = Feature.RECORDS.allowedInSource(source); dumpMethodReferenceSearchResults = options.isSet("debug.dumpMethodReferenceSearchResults"); + dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE); } /** error symbols, which are returned when resolution fails @@ -584,7 +587,7 @@ Type rawInstantiate(Env env, ForAll pmt = (ForAll) mt; if (typeargtypes.length() != pmt.tvars.length()) // not enough args - throw new InapplicableMethodException(diags.fragment(Fragments.WrongNumberTypeArgs(Integer.toString(pmt.tvars.length())))); + throw new InapplicableMethodException(diags.fragment(Fragments.WrongNumberTypeArgs(Integer.toString(pmt.tvars.length()))), dumpStacktraceOnError); // Check type arguments are within bounds List formals = pmt.tvars; List actuals = typeargtypes; @@ -593,7 +596,7 @@ Type rawInstantiate(Env env, pmt.tvars, typeargtypes); for (; bounds.nonEmpty(); bounds = bounds.tail) { if (!types.isSubtypeUnchecked(actuals.head, bounds.head, warn)) { - throw new InapplicableMethodException(diags.fragment(Fragments.ExplicitParamDoNotConformToBounds(actuals.head, bounds))); + throw new InapplicableMethodException(diags.fragment(Fragments.ExplicitParamDoNotConformToBounds(actuals.head, bounds)), dumpStacktraceOnError); } } formals = formals.tail; @@ -830,7 +833,7 @@ protected void reportMC(DiagnosticPosition pos, MethodCheckDiag diag, InferenceC String key = inferDiag ? diag.inferKey : diag.basicKey; throw inferDiag ? infer.error(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)) : - methodCheckFailure.setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); + getMethodCheckFailure().setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); } /** @@ -842,7 +845,7 @@ class SharedInapplicableMethodException extends InapplicableMethodException { private static final long serialVersionUID = 0; SharedInapplicableMethodException() { - super(null); + super(null, Resolve.this.dumpStacktraceOnError); } SharedInapplicableMethodException setMessage(JCDiagnostic details) { @@ -851,12 +854,15 @@ SharedInapplicableMethodException setMessage(JCDiagnostic details) { } } - SharedInapplicableMethodException methodCheckFailure = new SharedInapplicableMethodException(); + private SharedInapplicableMethodException methodCheckFailure; public MethodCheck mostSpecificCheck(List actuals) { return nilMethodCheck; } + private SharedInapplicableMethodException getMethodCheckFailure() { + return methodCheckFailure == null ? methodCheckFailure = new SharedInapplicableMethodException() : methodCheckFailure; + } } /** @@ -1036,7 +1042,7 @@ public boolean compatible(Type found, Type req, Warner warn) { } public void report(DiagnosticPosition pos, JCDiagnostic details) { - throw new InapplicableMethodException(details); + throw new InapplicableMethodException(details, Resolve.this.dumpStacktraceOnError); } public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) { @@ -1392,24 +1398,19 @@ public MethodCheck mostSpecificCheck(List actuals) { } } - public static class InapplicableMethodException extends RuntimeException { + public static class InapplicableMethodException extends CompilerInternalException { private static final long serialVersionUID = 0; transient JCDiagnostic diagnostic; - InapplicableMethodException(JCDiagnostic diag) { + InapplicableMethodException(JCDiagnostic diag, boolean dumpStackTraceOnError) { + super(dumpStackTraceOnError); this.diagnostic = diag; } public JCDiagnostic getDiagnostic() { return diagnostic; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } /* *************************************************************************** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 2d522d01641f5..f1f4e73b5a63d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -79,6 +79,7 @@ import com.sun.tools.javac.code.Type; import static com.sun.tools.javac.code.TypeTag.BOT; import static com.sun.tools.javac.code.TypeTag.VOID; + import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.tree.JCTree; @@ -786,7 +787,7 @@ private class PrimitiveGenerator extends Types.SignatureGenerator { StringBuilder sb = new StringBuilder(); PrimitiveGenerator() { - super(types); + types.super(); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java index af6f4b67faeb5..bfdb9775e1170 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java @@ -100,7 +100,7 @@ public class PoolWriter { public PoolWriter(Types types, Names names) { this.types = types; this.names = names; - this.signatureGen = new SharedSignatureGenerator(types); + this.signatureGen = new SharedSignatureGenerator(); this.pool = new WriteablePoolHelper(); } @@ -278,8 +278,8 @@ class SharedSignatureGenerator extends Types.SignatureGenerator { */ ByteBuffer sigbuf = new ByteBuffer(); - SharedSignatureGenerator(Types types) { - super(types); + SharedSignatureGenerator() { + types.super(); } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java index f804174c711fd..941f5c4c40ec4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java @@ -184,10 +184,9 @@ byte[] compileJavaFileByName(String name) { * @param parent the class loader to be used as the parent loader * @param mainClassName the fully-qualified name of the application class to load * @return class loader object able to find and load the desired class - * @throws ClassNotFoundException if the class cannot be located * @throws Fault if a modular application class is in the unnamed package */ - ClassLoader newClassLoaderFor(ClassLoader parent, String mainClassName) throws ClassNotFoundException, Fault { + ClassLoader newClassLoaderFor(ClassLoader parent, String mainClassName) throws Fault { var moduleInfoBytes = inMemoryClasses.get("module-info"); if (moduleInfoBytes == null) { // Trivial case: no compiled module descriptor available, no extra module layer required diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java index 4ae6b841542a3..8a8226b3dba1b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java @@ -198,8 +198,9 @@ private Class execute(MemoryContext context, String[] mainArgs) // 1. Find a main method in the first class and if there is one - invoke it Class firstClass; String firstClassName = program.qualifiedTypeNames().getFirst(); + ClassLoader loader = context.newClassLoaderFor(parentLoader, firstClassName); + Thread.currentThread().setContextClassLoader(loader); try { - ClassLoader loader = context.newClassLoaderFor(parentLoader, firstClassName); firstClass = Class.forName(firstClassName, false, loader); } catch (ClassNotFoundException e) { throw new Fault(Errors.CantFindClass(firstClassName)); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties index dfbeb0fd0b715..33063b6558d3a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties @@ -2305,13 +2305,14 @@ compiler.err.this.as.identifier=Ab Release 8 ist "this" nur als Parametername f compiler.err.receiver.parameter.not.applicable.constructor.toplevel.class=receiver-Parameter nicht für Konstruktor der obersten Klasse anwendbar -# TODO 308: make a better error message +# 0: fragment, 1: symbol, 2: annotated-type +compiler.err.type.annotation.inadmissible={0} wurde hier nicht erwartet\n(Um einen qualifizierten Typ zu annotieren, schreiben Sie {1}.{2}) + # 0: annotation -compiler.err.cant.type.annotate.scoping.1=Scoping-Konstrukt kann nicht mit type-use-Annotation versehen werden: {0} +compiler.misc.type.annotation.1=Typannotation {0} ist -# TODO 308: make a better error message # 0: list of annotation -compiler.err.cant.type.annotate.scoping=Scoping-Konstrukt kann nicht mit type-use-Annotationen versehen werden: {0} +compiler.misc.type.annotation=Typannotationen {0} sind # 0: type, 1: type compiler.err.incorrect.receiver.name=Der Empfängername stimmt nicht mit dem einschließenden Klassentyp überein\nErforderlich: {0}\nErmittelt: {1} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties index 4d98e4cabccba..818b5fb70668b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties @@ -2305,13 +2305,14 @@ compiler.err.this.as.identifier=リリース8から''this''は受信タイプの compiler.err.receiver.parameter.not.applicable.constructor.toplevel.class=受取り側パラメータは最上位レベル・クラスのコンストラクタに適用できません -# TODO 308: make a better error message +# 0: fragment, 1: symbol, 2: annotated-type +compiler.err.type.annotation.inadmissible=ここでは{0}は予期されていません\n(修飾されたタイプに注釈を付けるには、{1}.{2}と記述します) + # 0: annotation -compiler.err.cant.type.annotate.scoping.1=スコープ・コンストラクトを型使用注釈で注釈付けすることはできません: {0} +compiler.misc.type.annotation.1=タイプ注釈{0}は -# TODO 308: make a better error message # 0: list of annotation -compiler.err.cant.type.annotate.scoping=スコープ・コンストラクトを型使用注釈で注釈付けすることはできません: {0} +compiler.misc.type.annotation=タイプ注釈{0}は # 0: type, 1: type compiler.err.incorrect.receiver.name=受取り側の名前が、包含するクラス・タイプと一致しません\n必須: {0}\n検出: {1} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties index 81452f23a0d55..54298cb23b912 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties @@ -93,7 +93,7 @@ compiler.err.abstract.cant.be.instantiated={0}是抽象的; 无法实例化 compiler.err.abstract.meth.cant.have.body=抽象方法不能有主体 # 0: kind name, 1: symbol -compiler.err.already.annotated={0} {1}已进行注释 +compiler.err.already.annotated={0} {1} 已进行批注 # 0: kind name, 1: symbol, 2: kind name, 3: symbol compiler.err.already.defined=已在{2} {3}中定义了{0} {1} @@ -642,7 +642,7 @@ compiler.err.malformed.fp.lit=浮点文字的格式错误 compiler.err.method.does.not.override.superclass=方法不会覆盖或实现超类型的方法 -compiler.err.static.methods.cannot.be.annotated.with.override=不能使用 @Override 注释静态方法 +compiler.err.static.methods.cannot.be.annotated.with.override=不能使用 @Override 对静态方法进行批注 compiler.err.missing.meth.body.or.decl.abstract=缺少方法主体, 或声明抽象 @@ -1638,7 +1638,7 @@ compiler.warn.unchecked.varargs.non.reifiable.type=参数化 vararg 类型{0}的 # 0: symbol compiler.warn.varargs.unsafe.use.varargs.param=Varargs 方法可能导致来自不可具体化 varargs 参数 {0} 的堆污染 -compiler.warn.missing.deprecated.annotation=未使用 @Deprecated 对已过时的项目进行注释 +compiler.warn.missing.deprecated.annotation=未使用 @Deprecated 对已过时的项目进行批注 # 0: kind name compiler.warn.deprecated.annotation.has.no.effect=@Deprecated 批注对此 {0} 声明没有任何效果 @@ -2305,13 +2305,14 @@ compiler.err.this.as.identifier=从发行版 8 开始,''this'' 只能作为接 compiler.err.receiver.parameter.not.applicable.constructor.toplevel.class=接收方参数不适用于顶层类的构造器 -# TODO 308: make a better error message +# 0: fragment, 1: symbol, 2: annotated-type +compiler.err.type.annotation.inadmissible={0} 不应出现在此处\n(要对限定类型进行批注,请编写 {1}.{2}) + # 0: annotation -compiler.err.cant.type.annotate.scoping.1=无法使用 type-use 批注 {0} 来批注确定作用域结构 +compiler.misc.type.annotation.1=类型批注 {0} 为 -# TODO 308: make a better error message # 0: list of annotation -compiler.err.cant.type.annotate.scoping=无法使用 type-use 批注 {0} 来批注确定作用域结构 +compiler.misc.type.annotation=类型批注 {0} 为 # 0: type, 1: type compiler.err.incorrect.receiver.name=接收方名称与封闭类类型不匹配\n需要: {0}\n找到: {1} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_zh_CN.properties index ee666cc02c11d..9382ac9e348e4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_zh_CN.properties @@ -120,7 +120,7 @@ launcher.err.cant.read.file=读取源文件 {0} 时出错:{1} launcher.err.no.value.for.option=没有为选项 {0} 指定值 # 0: string -launcher.err.invalid.value.for.source=--source 选项的值无效:{0}\n +launcher.err.invalid.value.for.source=--source 选项的值无效:{0} launcher.err.unnamed.pkg.not.allowed.named.modules=命名模块中不允许未命名程序包 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/CompilerInternalException.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/CompilerInternalException.java new file mode 100644 index 0000000000000..3d3c45368c2fa --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/CompilerInternalException.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.util; + +/** The super class of all compiler internal exceptions + * + *

      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 CompilerInternalException extends RuntimeException { + private static final long serialVersionUID = 0; + + @SuppressWarnings("this-escape") + public CompilerInternalException(boolean dumpStackTraceOnError) { + /* by default the stacktrace wont be filled, meaning that method CompilerInternalException::fillInStackTrace + * will always be invoked, if we do want to dump the stacktrace then we will invoke super::fillInStackTrace + * there is a bit of a dance here that could be fixed once flexible constructor bodies exits the preview + * state + */ + if (dumpStackTraceOnError) { + super.fillInStackTrace(); + } + } + + @Override + public Throwable fillInStackTrace() { + return this; + } +} diff --git a/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt b/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt index 8fab1f56765cd..fd1bd11f6b9c3 100644 --- a/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt @@ -707,6 +707,7 @@ innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$ClassEntryIm innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$AbstractNamedEntry outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName AbstractNamedEntry flags 408 innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$AbstractRefsEntry outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName AbstractRefsEntry flags 408 innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$AbstractRefEntry outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName AbstractRefEntry flags 408 +-method name phiMix descriptor (I)I class name jdk/internal/classfile/impl/AbstractPoolEntry$AbstractDynamicConstantPoolEntry header extends jdk/internal/classfile/impl/AbstractPoolEntry nestHost jdk/internal/classfile/impl/AbstractPoolEntry sealed true permittedSubclasses jdk/internal/classfile/impl/AbstractPoolEntry$InvokeDynamicEntryImpl,jdk/internal/classfile/impl/AbstractPoolEntry$ConstantDynamicEntryImpl flags 421 @@ -799,6 +800,10 @@ innerclass innerClass jdk/internal/classfile/impl/BoundAttribute$BoundSyntheticA innerclass innerClass jdk/internal/classfile/impl/BoundAttribute$BoundStackMapTableAttribute outerClass jdk/internal/classfile/impl/BoundAttribute innerClassName BoundStackMapTableAttribute flags 19 method name standardAttribute descriptor (Ljava/lang/classfile/constantpool/Utf8Entry;)Ljava/lang/classfile/AttributeMapper; flags 9 signature (Ljava/lang/classfile/constantpool/Utf8Entry;)Ljava/lang/classfile/AttributeMapper<*>; +class name jdk/internal/classfile/impl/BufferedCodeBuilder +header extends java/lang/Object implements jdk/internal/classfile/impl/TerminalCodeBuilder nestMembers jdk/internal/classfile/impl/BufferedCodeBuilder$Model flags 31 +innerclass innerClass jdk/internal/classfile/impl/BufferedCodeBuilder$Model outerClass jdk/internal/classfile/impl/BufferedCodeBuilder innerClassName Model flags 11 + class name jdk/internal/classfile/impl/ClassPrinterImpl header extends java/lang/Object nestMembers jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$LeafNodeImpl flags 31 innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$LeafNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName LeafNodeImpl flags 19 @@ -914,6 +919,16 @@ innerclass innerClass java/lang/classfile/ClassFileTransform$ResolvedTransform o innerclass innerClass java/lang/classfile/Signature$ThrowableSig outerClass java/lang/classfile/Signature innerClassName ThrowableSig flags 609 innerclass innerClass java/lang/invoke/MethodHandles$Lookup outerClass java/lang/invoke/MethodHandles innerClassName Lookup flags 19 +class name jdk/internal/classfile/impl/DirectCodeBuilder +header extends jdk/internal/classfile/impl/AbstractDirectBuilder implements jdk/internal/classfile/impl/TerminalCodeBuilder flags 31 signature Ljdk/internal/classfile/impl/AbstractDirectBuilder;Ljdk/internal/classfile/impl/TerminalCodeBuilder; +innerclass innerClass java/lang/classfile/ClassFile$ShortJumpsOption outerClass java/lang/classfile/ClassFile innerClassName ShortJumpsOption flags 4019 +innerclass innerClass jdk/internal/classfile/impl/AbstractPseudoInstruction$ExceptionCatchImpl outerClass jdk/internal/classfile/impl/AbstractPseudoInstruction innerClassName ExceptionCatchImpl flags 19 +innerclass innerClass java/lang/classfile/ClassFile$DeadLabelsOption outerClass java/lang/classfile/ClassFile innerClassName DeadLabelsOption flags 4019 +innerclass innerClass java/lang/classfile/ClassFile$DebugElementsOption outerClass java/lang/classfile/ClassFile innerClassName DebugElementsOption flags 4019 + +class name jdk/internal/classfile/impl/LabelContext +header extends java/lang/Object sealed true permittedSubclasses jdk/internal/classfile/impl/TerminalCodeBuilder,jdk/internal/classfile/impl/CodeImpl flags 601 + class name jdk/internal/classfile/impl/SignaturesImpl header extends java/lang/Object nestMembers jdk/internal/classfile/impl/SignaturesImpl$MethodSignatureImpl,jdk/internal/classfile/impl/SignaturesImpl$ClassSignatureImpl,jdk/internal/classfile/impl/SignaturesImpl$TypeParamImpl,jdk/internal/classfile/impl/SignaturesImpl$TypeArgImpl,jdk/internal/classfile/impl/SignaturesImpl$UnboundedTypeArgImpl,jdk/internal/classfile/impl/SignaturesImpl$ClassTypeSigImpl,jdk/internal/classfile/impl/SignaturesImpl$ArrayTypeSigImpl,jdk/internal/classfile/impl/SignaturesImpl$TypeVarSigImpl,jdk/internal/classfile/impl/SignaturesImpl$BaseTypeSigImpl flags 31 innerclass innerClass java/lang/classfile/Signature$ClassTypeSig outerClass java/lang/classfile/Signature innerClassName ClassTypeSig flags 609 @@ -1017,6 +1032,12 @@ method name hashCode descriptor ()I flags 1 class name jdk/internal/classfile/impl/TemporaryConstantPool method name entryByIndex descriptor (ILjava/lang/Class;)Ljava/lang/classfile/constantpool/PoolEntry; flags 1 signature (ILjava/lang/Class;)TT; +class name jdk/internal/classfile/impl/TerminalCodeBuilder +header extends java/lang/Object implements java/lang/classfile/CodeBuilder,jdk/internal/classfile/impl/LabelContext sealed true permittedSubclasses jdk/internal/classfile/impl/DirectCodeBuilder,jdk/internal/classfile/impl/BufferedCodeBuilder flags 601 +method name curTopLocal descriptor ()I flags 401 + +-class name jdk/internal/classfile/impl/TransformingCodeBuilder + class name jdk/internal/classfile/impl/UnboundAttribute header extends jdk/internal/classfile/impl/AbstractElement implements java/lang/classfile/Attribute nestMembers jdk/internal/classfile/impl/UnboundAttribute$EmptyBootstrapAttribute,jdk/internal/classfile/impl/UnboundAttribute$AdHocAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleAttribute,jdk/internal/classfile/impl/UnboundAttribute$TypePathComponentImpl,jdk/internal/classfile/impl/UnboundAttribute$UnboundTypeAnnotation,jdk/internal/classfile/impl/UnboundAttribute$UnboundRecordComponentInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleRequiresInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleProvideInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleOpenInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleHashInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleExportInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundMethodParameterInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTypeInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundLineNumberInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundInnerClassInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundCharacterRangeInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTypeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLineNumberTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCharacterRangeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceDebugExtensionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCompilationIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestHostAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestMembersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundPermittedSubclassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleResolutionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModulePackagesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleHashesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleMainClassAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleTargetAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundMethodParametersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundEnclosingMethodAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRecordAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundInnerClassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundStackMapTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceFileAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundAnnotationDefaultAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundExceptionsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSignatureAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSyntheticAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundDeprecatedAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundConstantValueAttribute sealed true permittedSubclasses jdk/internal/classfile/impl/UnboundAttribute$UnboundConstantValueAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundDeprecatedAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSyntheticAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSignatureAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundExceptionsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundAnnotationDefaultAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceFileAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundStackMapTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundInnerClassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRecordAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundEnclosingMethodAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundMethodParametersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleTargetAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleMainClassAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleHashesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModulePackagesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleResolutionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundPermittedSubclassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestMembersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestHostAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCompilationIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceDebugExtensionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCharacterRangeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLineNumberTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTypeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleAttribute,jdk/internal/classfile/impl/UnboundAttribute$AdHocAttribute,jdk/internal/classfile/impl/UnboundAttribute$EmptyBootstrapAttribute flags 421 signature ;>Ljdk/internal/classfile/impl/AbstractElement;Ljava/lang/classfile/Attribute; innerclass innerClass jdk/internal/classfile/impl/UnboundAttribute$EmptyBootstrapAttribute outerClass jdk/internal/classfile/impl/UnboundAttribute innerClassName EmptyBootstrapAttribute flags 19 diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index 837a3a3dba1c8..d528f1b848584 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -561,47 +561,73 @@ static class P11RSAPrivateKeyInternal extends P11PrivateKey { static P11RSAPrivateKeyInternal of(Session session, long keyID, String algorithm, int keyLength, CK_ATTRIBUTE[] attrs, boolean keySensitive) { - if (keySensitive) { - return new P11RSAPrivateKeyInternal(session, keyID, algorithm, + P11RSAPrivateKeyInternal p11Key = null; + if (!keySensitive) { + // Key is not sensitive: try to interpret as CRT or non-CRT. + p11Key = asCRT(session, keyID, algorithm, keyLength, attrs); + if (p11Key == null) { + p11Key = asNonCRT(session, keyID, algorithm, keyLength, + attrs); + } + } + if (p11Key == null) { + // Key is sensitive or there was a failure while querying its + // attributes: handle as opaque. + p11Key = new P11RSAPrivateKeyInternal(session, keyID, algorithm, keyLength, attrs); - } else { - CK_ATTRIBUTE[] rsaAttrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_MODULUS), - new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT), - new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), - new CK_ATTRIBUTE(CKA_PRIME_1), - new CK_ATTRIBUTE(CKA_PRIME_2), - new CK_ATTRIBUTE(CKA_EXPONENT_1), - new CK_ATTRIBUTE(CKA_EXPONENT_2), - new CK_ATTRIBUTE(CKA_COEFFICIENT), - }; - boolean isCRT = true; - Session tempSession = null; - try { - tempSession = session.token.getOpSession(); - session.token.p11.C_GetAttributeValue(tempSession.id(), - keyID, rsaAttrs); - for (CK_ATTRIBUTE attr : rsaAttrs) { - isCRT &= (attr.pValue instanceof byte[]); - if (!isCRT) break; + } + return p11Key; + } + + private static CK_ATTRIBUTE[] tryFetchAttributes(Session session, + long keyID, long... attrTypes) { + int i = 0; + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[attrTypes.length]; + for (long attrType : attrTypes) { + attrs[i++] = new CK_ATTRIBUTE(attrType); + } + try { + session.token.p11.C_GetAttributeValue(session.id(), keyID, + attrs); + for (CK_ATTRIBUTE attr : attrs) { + if (!(attr.pValue instanceof byte[])) { + return null; } - } catch (PKCS11Exception e) { - // ignore, assume not available - isCRT = false; - } finally { - session.token.releaseSession(tempSession); - } - BigInteger n = rsaAttrs[0].getBigInteger(); - BigInteger d = rsaAttrs[1].getBigInteger(); - if (isCRT) { - return new P11RSAPrivateKey(session, keyID, algorithm, - keyLength, attrs, n, d, - Arrays.copyOfRange(rsaAttrs, 2, rsaAttrs.length)); - } else { - return new P11RSAPrivateNonCRTKey(session, keyID, - algorithm, keyLength, attrs, n, d); } + return attrs; + } catch (PKCS11Exception ignored) { + // ignore, assume not available + return null; + } + } + + private static P11RSAPrivateKeyInternal asCRT(Session session, + long keyID, String algorithm, int keyLength, + CK_ATTRIBUTE[] attrs) { + CK_ATTRIBUTE[] rsaCRTAttrs = tryFetchAttributes(session, keyID, + CKA_MODULUS, CKA_PRIVATE_EXPONENT, CKA_PUBLIC_EXPONENT, + CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, + CKA_COEFFICIENT); + if (rsaCRTAttrs == null) { + return null; + } + return new P11RSAPrivateKey(session, keyID, algorithm, keyLength, + attrs, rsaCRTAttrs[0].getBigInteger(), + rsaCRTAttrs[1].getBigInteger(), + Arrays.copyOfRange(rsaCRTAttrs, 2, rsaCRTAttrs.length)); + } + + private static P11RSAPrivateKeyInternal asNonCRT(Session session, + long keyID, String algorithm, int keyLength, + CK_ATTRIBUTE[] attrs) { + CK_ATTRIBUTE[] rsaNonCRTAttrs = tryFetchAttributes(session, keyID, + CKA_MODULUS, CKA_PRIVATE_EXPONENT); + if (rsaNonCRTAttrs == null) { + return null; } + return new P11RSAPrivateNonCRTKey(session, keyID, algorithm, + keyLength, attrs, rsaNonCRTAttrs[0].getBigInteger(), + rsaNonCRTAttrs[1].getBigInteger()); } protected transient BigInteger n; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GCCause.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GCCause.java index 4320f61bf36a9..b301f77439067 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GCCause.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GCCause.java @@ -48,8 +48,6 @@ public enum GCCause { _metadata_GC_threshold ("Metadata GC Threshold"), _metadata_GC_clear_soft_refs ("Metadata GC Clear Soft References"), - _adaptive_size_policy ("Ergonomics"), - _g1_inc_collection_pause ("G1 Evacuation Pause"), _g1_compaction_pause ("G1 Compaction Pause"), _g1_humongous_allocation ("G1 Humongous Allocation"), diff --git a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap index 5993acf701f51..001ce5b5cac4d 100644 --- a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap +++ b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap @@ -404,8 +404,6 @@ alias sun.gc.lastCause // 1.5.0 b39 hotspot.gc.last_cause // 1.4.2_02 // sun.gc.policy -alias sun.gc.policy.avgBaseFootprint // 1.5.0 b39 - hotspot.gc.policy.avg_base_footprint // 1.5.0 b21 alias sun.gc.policy.avgMajorIntervalTime // 1.5.0 b39 hotspot.gc.policy.avg_major_interval // 1.5.0 b21 alias sun.gc.policy.avgMajorPauseTime // 1.5.0 b39 diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java index 609e01a8db154..c0d6c4cdb84ee 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java @@ -35,6 +35,8 @@ /** * Represents the AArch64 architecture. + * + * The value returned by {@code Architecture#getName} for an instance of this class is {@code "aarch64"}. */ public class AArch64 extends Architecture { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java index 83401fed62033..f0ca7a2fc2359 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java @@ -40,6 +40,8 @@ /** * Represents the AMD64 architecture. + * + * The value returned by {@code Architecture#getName} for an instance of this class is {@code "AMD64"}. */ public class AMD64 extends Architecture { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/code/Architecture.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/code/Architecture.java index f14855cd6b995..64b0600674fd6 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/code/Architecture.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/code/Architecture.java @@ -107,7 +107,7 @@ protected Architecture(String name, PlatformKind wordKind, ByteOrder byteOrder, /** * Converts this architecture to a string. * - * @return the string representation of this architecture + * @return a lowercase version of {@linkplain #getName name} */ @Override public final String toString() { @@ -126,9 +126,14 @@ public PlatformKind getWordKind() { return wordKind; } - /** - * Gets the name of this architecture. - */ + /// Gets the name of this architecture. The value returned for + /// each architecture is shown in the table below. + /// + /// | Name | Receiver type | + /// |-----------|-----------------------------| + /// | "aarch64" | [jdk.vm.ci.aarch64.AArch64] | + /// | "AMD64" | [jdk.vm.ci.amd64.AMD64] | + /// | "riscv64" | [jdk.vm.ci.riscv64.RISCV64] | public String getName() { return name; } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java index 14646fd027521..593697545266d 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java @@ -289,6 +289,12 @@ HotSpotResolvedJavaType lookupType(ClassLoader classLoader, String name) throws native HotSpotResolvedJavaType lookupJClass(long jclass); + /** + * Gets the {@code jobject} value wrapped by {@code peerObject}. + * Must not be called if {@link Services#IS_IN_NATIVE_IMAGE} is {@code false}. + */ + native long getJObjectValue(HotSpotObjectConstantImpl peerObject); + /** * Resolves the entry at index {@code cpi} in {@code constantPool} to an interned String object. * diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledCodeStream.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledCodeStream.java index 87c6ecde07b6e..ab7a57f2759d4 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledCodeStream.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledCodeStream.java @@ -563,6 +563,7 @@ private String codeDesc() { writeInt("entryBCI", nmethod.entryBCI); writeLong("compileState", nmethod.compileState); writeBoolean("hasUnsafeAccess", nmethod.hasUnsafeAccess); + writeBoolean("hasScopedAccess", nmethod.hasScopedAccess()); writeInt("id", nmethod.id); } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java index c364af7ad410c..b1be3dee25cd8 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,4 +100,22 @@ public String toString() { public String getInstallationFailureMessage() { return installationFailureMessage; } + + /** + * Determines if {@code methods} contains at least one entry for which {@code HotSpotResolvedJavaMethod.isScoped()} returns true. + */ + public boolean hasScopedAccess() { + if (methods == null) { + return false; + } + for (ResolvedJavaMethod method : methods) { + if (method instanceof HotSpotResolvedJavaMethod hotSpotMethod) { + if (hotSpotMethod.isScoped()) { + return true; + } + } + } + return false; + } + } 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 3dcf855ff230f..3fd92f8dee5df 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 @@ -925,23 +925,23 @@ JavaType lookupTypeInternal(String name, HotSpotResolvedObjectType accessingType } /** - * Gets the {@code jobject} value wrapped by {@code peerObject}. The returned "naked" value is - * only valid as long as {@code peerObject} is valid. Note that the latter may be shorter than - * the lifetime of {@code peerObject}. As such, this method should only be used to pass an - * object parameter across a JNI call from the JVMCI shared library to HotSpot. This method must - * only be called from within the JVMCI shared library. + * Gets the {@code jobject} value wrapped by {@code peerObject}. The returned value is + * a JNI local reference whose lifetime is scoped by the nearest Java caller (from + * HotSpot's perspective). You can use {@code PushLocalFrame} and {@code PopLocalFrame} to + * shorten the lifetime of the reference. The current thread's state must be + * {@code _thread_in_native}. A call from the JVMCI shared library (e.g. libgraal) is in such + * a state. * - * @param peerObject a reference to an object in the peer runtime - * @return the {@code jobject} value wrapped by {@code peerObject} + * @param peerObject a reference to an object in the HotSpot heap + * @return the {@code jobject} value unpacked from {@code peerObject} * @throws IllegalArgumentException if the current runtime is not the JVMCI shared library or - * {@code peerObject} is not a peer object reference + * {@code peerObject} is not a HotSpot heap object reference + * @throws IllegalStateException if not called from within the JVMCI shared library + * or if there is no Java caller frame on the stack + * (i.e., JavaThread::has_last_Java_frame returns false) */ public long getJObjectValue(HotSpotObjectConstant peerObject) { - if (peerObject instanceof IndirectHotSpotObjectConstantImpl) { - IndirectHotSpotObjectConstantImpl remote = (IndirectHotSpotObjectConstantImpl) peerObject; - return remote.getHandle(); - } - throw new IllegalArgumentException("Cannot get jobject value for " + peerObject + " (" + peerObject.getClass().getName() + ")"); + return compilerToVm.getJObjectValue((HotSpotObjectConstantImpl)peerObject); } @Override diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java index 4c9dc509ce1dc..4fbd479602437 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java @@ -170,7 +170,7 @@ public int hashCode() { * @return flags of this method */ private int getFlags() { - return UNSAFE.getShort(getMethodPointer() + config().methodFlagsOffset); + return UNSAFE.getInt(getMethodPointer() + config().methodFlagsOffset); } /** @@ -179,7 +179,7 @@ private int getFlags() { * @return flags of this method's ConstMethod */ private int getConstMethodFlags() { - return UNSAFE.getChar(getConstMethod() + config().constMethodFlagsOffset); + return UNSAFE.getInt(getConstMethod() + config().constMethodFlagsOffset); } @Override @@ -324,6 +324,17 @@ public boolean hasReservedStackAccess() { return (getConstMethodFlags() & config().constMethodFlagsReservedStackAccess) != 0; } + /** + * Returns true if this method has a + * {@code jdk.internal.misc.ScopedMemoryAccess.Scoped} annotation. + * + * @return true if Scoped annotation present, false otherwise + */ + @Override + public boolean isScoped() { + return (getConstMethodFlags() & config().constMethodFlagsIsScoped) != 0; + } + /** * Sets flags on {@code method} indicating that it should never be inlined or compiled by the * VM. diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java index 57f9473c90209..954e1f6b20173 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java @@ -194,6 +194,7 @@ long prototypeMarkWord() { final int constMethodFlagsReservedStackAccess = getConstant("ConstMethodFlags::_misc_reserved_stack_access", Integer.class); final int constMethodFlagsCallerSensitive = getConstant("ConstMethodFlags::_misc_caller_sensitive", Integer.class); final int constMethodFlagsIntrinsicCandidate = getConstant("ConstMethodFlags::_misc_intrinsic_candidate", Integer.class); + final int constMethodFlagsIsScoped = getConstant("ConstMethodFlags::_misc_is_scoped", Integer.class); final int constMethodHasLineNumberTable = getConstant("ConstMethodFlags::_misc_has_linenumber_table", Integer.class); final int constMethodHasLocalVariableTable = getConstant("ConstMethodFlags::_misc_has_localvariable_table", Integer.class); final int constMethodHasMethodAnnotations = getConstant("ConstMethodFlags::_misc_has_method_annotations", Integer.class); diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java index 41eccc9d1c65d..fa42dc26ac8da 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java @@ -463,6 +463,16 @@ default boolean isJavaLangObjectInit() { return getDeclaringClass().isJavaLangObject() && getName().equals(""); } + /** + * Returns true if this method has a + * {@code jdk.internal.misc.ScopedMemoryAccess.Scoped} annotation. + * + * @return true if Scoped annotation present, false otherwise. + */ + default boolean isScoped() { + throw new UnsupportedOperationException(); + } + /** * Gets a speculation log that can be used when compiling this method to make new speculations * and query previously failed speculations. The implementation may return a new diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/riscv64/RISCV64.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/riscv64/RISCV64.java index c06ba315a149c..0a28703001503 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/riscv64/RISCV64.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/riscv64/RISCV64.java @@ -35,6 +35,8 @@ /** * Represents the RISCV64 architecture. + * + * The value returned by {@code Architecture#getName} for an instance of this class is {@code "riscv64"}. */ public class RISCV64 extends Architecture { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java index 4e929130b667f..84672926cf81a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,19 +31,21 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleTypeVisitor14; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; import static jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo.Kind.LINK_TYPE_PARAMS; import static jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS; @@ -107,7 +109,7 @@ protected Content getSummaryLink(Element member) { } String signature = utils.flatSignature((ExecutableElement) member, typeElement); if (signature.length() > 2) { - content.add(new HtmlTree(TagName.WBR)); + content.add(new HtmlTree(HtmlTag.WBR)); } content.add(signature); @@ -119,7 +121,7 @@ protected Content getSummaryLink(Element member) { protected void addSummaryLink(HtmlLinkInfo.Kind context, TypeElement te, Element member, Content target) { ExecutableElement ee = (ExecutableElement)member; - Content memberLink = writer.getDocLink(context, te, ee, name(ee), HtmlStyle.memberNameLink); + Content memberLink = writer.getDocLink(context, te, ee, name(ee), HtmlStyles.memberNameLink); var code = HtmlTree.CODE(memberLink); addParameters(ee, code); target.add(code); @@ -130,6 +132,24 @@ protected void addInheritedSummaryLink(TypeElement te, Element member, Content t target.add(writer.getDocLink(PLAIN, te, member, name(member))); } + /** + * Adds the generic type parameters. + * + * @param member the member to add the generic type parameters for + * @param target the content to which the generic type parameters will be added + */ + protected void addTypeParameters(ExecutableElement member, Content target) { + Content typeParameters = getTypeParameters(member); + target.add(typeParameters); + // Add explicit line break between method type parameters and + // return type in member summary table to avoid random wrapping. + if (typeParameters.charCount() > 10) { + target.add(new HtmlTree(HtmlTag.BR)); + } else { + target.add(Entity.NO_BREAK_SPACE); + } + } + /** * Add the parameter for the executable member. * @@ -213,7 +233,7 @@ protected void addParameters(ExecutableElement member, Content target) { Content params = getParameters(member, false); if (params.charCount() > 2) { // only add for non-empty parameters - target.add(new HtmlTree(TagName.WBR)); + target.add(new HtmlTree(HtmlTag.WBR)); } target.add(params); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java index caeb6c35e6d66..e2b81bb39822d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java @@ -44,15 +44,16 @@ import com.sun.source.doctree.DocTree; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.Resources; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.Utils; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.ANNOTATION_TYPE_MEMBER; import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL; @@ -450,7 +451,7 @@ protected abstract void addInheritedSummaryLink(TypeElement typeElement, */ protected void addModifiersAndType(Element member, TypeMirror type, Content target) { - var code = new HtmlTree(TagName.CODE); + var code = new HtmlTree(HtmlTag.CODE); addModifiers(member, code); if (type == null) { code.add(switch (member.getKind()) { @@ -466,16 +467,8 @@ protected void addModifiersAndType(Element member, TypeMirror type, ? ((ExecutableElement)member).getTypeParameters() : null; if (list != null && !list.isEmpty()) { - Content typeParameters = ((AbstractExecutableMemberWriter) this) - .getTypeParameters((ExecutableElement)member); - code.add(typeParameters); - // Add explicit line break between method type parameters and - // return type in member summary table to avoid random wrapping. - if (typeParameters.charCount() > 10) { - code.add(new HtmlTree(TagName.BR)); - } else { - code.add(Entity.NO_BREAK_SPACE); - } + ((AbstractExecutableMemberWriter) this) + .addTypeParameters((ExecutableElement)member, code); } code.add( writer.getLink(new HtmlLinkInfo(configuration, @@ -526,7 +519,7 @@ protected void addDeprecatedInfo(Element member, Content target) { var t = configuration.tagletManager.getTaglet(DocTree.Kind.DEPRECATED); Content output = t.getAllBlockTagOutput(member, writer.getTagletWriterInstance(false)); if (!output.isEmpty()) { - target.add(HtmlTree.DIV(HtmlStyle.deprecationBlock, output)); + target.add(HtmlTree.DIV(HtmlStyles.deprecationBlock, output)); } } @@ -578,9 +571,9 @@ protected void addUseInfo(List members, Content heading, Cont return; } boolean printedUseTableHeader = false; - var useTable = new Table(HtmlStyle.summaryTable) + var useTable = new Table(HtmlStyles.summaryTable) .setCaption(heading) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colSecond, HtmlStyles.colLast); for (Element element : members) { TypeElement te = (typeElement == null) ? utils.getEnclosingTypeElement(element) @@ -596,7 +589,7 @@ protected void addUseInfo(List members, Content heading, Cont && !utils.isConstructor(element) && !utils.isTypeElement(element)) { - var name = HtmlTree.SPAN(HtmlStyle.typeNameLabel); + var name = HtmlTree.SPAN(HtmlStyles.typeNameLabel); name.add(name(te) + "."); typeContent.add(name); } @@ -677,7 +670,7 @@ public Content getInheritedSummaryHeader(TypeElement tElement) { * @return the inherited summary links */ public Content getInheritedSummaryLinks() { - return new HtmlTree(TagName.CODE); + return new HtmlTree(HtmlTag.CODE); } /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractOverviewIndexWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractOverviewIndexWriter.java index 2d7fb6e9b11a9..c466a2fcb5aa7 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractOverviewIndexWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractOverviewIndexWriter.java @@ -25,14 +25,15 @@ package jdk.javadoc.internal.doclets.formats.html; -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.RawHtml; /** * Abstract class to generate the top-level "overview" files. @@ -135,8 +136,8 @@ protected void addConfigurationTitle(Content target) { if (!doctitle.isEmpty()) { var title = RawHtml.of(doctitle); var heading = HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, title); - var div = HtmlTree.DIV(HtmlStyle.header, heading); + HtmlStyles.title, title); + var div = HtmlTree.DIV(HtmlStyles.header, heading); target.add(div); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java index d0483d5d33c16..594132743eb0f 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java @@ -25,18 +25,20 @@ package jdk.javadoc.internal.doclets.formats.html; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; -import jdk.javadoc.internal.doclets.toolkit.util.ClassTree.Hierarchy; -import jdk.javadoc.internal.doclets.toolkit.util.DocPath; - -import javax.lang.model.element.TypeElement; import java.util.Collection; import java.util.SortedSet; import java.util.TreeSet; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree.Hierarchy; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; + /** * Abstract class to print the class hierarchy page for all the Classes. This @@ -77,10 +79,10 @@ protected AbstractTreeWriter(HtmlConfiguration configuration, protected void addLevelInfo(TypeElement parent, Collection collection, Hierarchy hierarchy, Content content) { if (!collection.isEmpty()) { - var ul = new HtmlTree(TagName.UL); + var ul = new HtmlTree(HtmlTag.UL); for (TypeElement local : collection) { - var li = new HtmlTree(TagName.LI); - li.setStyle(HtmlStyle.circle); + var li = new HtmlTree(HtmlTag.LI); + li.setStyle(HtmlStyles.circle); addPartialInfo(local, li); addExtendsImplements(parent, local, li); addLevelInfo(local, hierarchy.subtypes(local), hierarchy, li); // Recurse @@ -104,7 +106,7 @@ protected void addTree(Hierarchy hierarchy, String heading, Content content) { Content headingContent = contents.getContent(heading); var sectionHeading = HtmlTree.HEADING_TITLE(Headings.CONTENT_HEADING, headingContent); - var section = HtmlTree.SECTION(HtmlStyle.hierarchy, sectionHeading); + var section = HtmlTree.SECTION(HtmlStyles.hierarchy, sectionHeading); addLevelInfo(!utils.isPlainInterface(firstTypeElement) ? firstTypeElement : null, roots, hierarchy, section); content.add(section); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java index 5aa9f349a5137..68ee6d98857e5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesIndexWriter.java @@ -33,14 +33,16 @@ import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DeprecatedTree; -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; + import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.Utils.ElementFlag; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; /** * Generate the file with list of all the classes in this run. @@ -79,9 +81,9 @@ public void buildPage() throws DocFileIOException { * @param target the content to which the links will be added */ protected void addContents(Content target) { - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setHeader(new TableHeader(contents.classLabel, contents.descriptionLabel)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast) + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast) .setId(HtmlIds.ALL_CLASSES_TABLE) .setDefaultTab(contents.allClassesAndInterfacesLabel) .addTab(contents.interfaces, utils::isPlainInterface) @@ -96,8 +98,8 @@ protected void addContents(Content target) { } Content titleContent = contents.allClassesAndInterfacesLabel; var pHeading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, titleContent); - var headerDiv = HtmlTree.DIV(HtmlStyle.header, pHeading); + HtmlStyles.title, titleContent); + var headerDiv = HtmlTree.DIV(HtmlStyles.header, pHeading); target.add(headerDiv); if (!table.isEmpty()) { target.add(table); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllPackagesIndexWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllPackagesIndexWriter.java index 5581ec03e2a23..2c9539d3c30dc 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllPackagesIndexWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllPackagesIndexWriter.java @@ -29,12 +29,13 @@ import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generate the file with list of all the packages in this run. @@ -60,8 +61,8 @@ public void buildPage() throws DocFileIOException { addPackages(mainContent); Content titleContent = contents.allPackagesLabel; var pHeading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, titleContent); - var headerDiv = HtmlTree.DIV(HtmlStyle.header, pHeading); + HtmlStyles.title, titleContent); + var headerDiv = HtmlTree.DIV(HtmlStyles.header, pHeading); HtmlTree body = getBody(getWindowTitle(label)); body.add(new BodyContents() .setHeader(getHeader(PageMode.ALL_PACKAGES)) @@ -77,10 +78,10 @@ public void buildPage() throws DocFileIOException { * @param target the content to which the links will be added */ protected void addPackages(Content target) { - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(Text.of(contents.packageSummaryLabel.toString())) .setHeader(new TableHeader(contents.packageLabel, contents.descriptionLabel)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); for (PackageElement pkg : configuration.packages) { if (!(options.noDeprecated() && utils.isDeprecated(pkg))) { Content packageLinkContent = getPackageLink(pkg, getLocalizedPackageName(pkg)); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeMemberWriter.java index 8f8585ecc6205..f1f6311023115 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeMemberWriter.java @@ -32,13 +32,14 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; -import jdk.javadoc.internal.doclets.formats.html.markup.Comment; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.BaseOptions; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Comment; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** @@ -96,7 +97,7 @@ protected void buildAnnotationTypeMember(Content target) { for (Element member : members) { currentMember = member; Content annotationContent = getAnnotationHeaderContent(currentMember); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); buildAnnotationTypeMemberChildren(div); annotationContent.add(div); memberList.add(writer.getMemberListItem(annotationContent)); @@ -181,7 +182,7 @@ public Content getMemberSummaryHeader(Content content) { @Override public void buildSummary(Content summariesList, Content content) { - writer.addSummary(HtmlStyle.memberSummary, + writer.addSummary(HtmlStyles.memberSummary, switch (kind) { case ANNOTATION_TYPE_MEMBER_REQUIRED -> HtmlIds.ANNOTATION_TYPE_REQUIRED_ELEMENT_SUMMARY; case ANNOTATION_TYPE_MEMBER_OPTIONAL -> HtmlIds.ANNOTATION_TYPE_OPTIONAL_ELEMENT_SUMMARY; @@ -209,7 +210,7 @@ protected Content getAnnotationHeaderContent(Element member) { var heading = HtmlTree.HEADING(Headings.TypeDeclaration.MEMBER_HEADING, Text.of(name(member))); content.add(heading); - return HtmlTree.SECTION(HtmlStyle.detail, content) + return HtmlTree.SECTION(HtmlStyles.detail, content) .setId(htmlIds.forMember((ExecutableElement) member).getFirst()); } @@ -238,7 +239,7 @@ protected void addTags(Element member, Content annotationContent) { protected Content getAnnotationDetails(Content annotationDetailsHeader, Content annotationDetails) { Content c = new ContentBuilder(annotationDetailsHeader, annotationDetails); - return getMember(HtmlTree.SECTION(HtmlStyle.memberDetails, c)); + return getMember(HtmlTree.SECTION(HtmlStyles.memberDetails, c)); } @Override @@ -278,10 +279,10 @@ public TableHeader getSummaryTableHeader(Element member) { @Override protected Table createSummaryTable() { - return new Table(HtmlStyle.summaryTable) + return new Table(HtmlStyles.summaryTable) .setCaption(getCaption()) .setHeader(getSummaryTableHeader(typeElement)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colSecond, HtmlStyles.colLast); } @Override @@ -292,7 +293,7 @@ public void addInheritedSummaryLabel(TypeElement typeElement, Content content) { protected void addSummaryLink(HtmlLinkInfo.Kind context, TypeElement typeElement, Element member, Content content) { Content memberLink = writer.getDocLink(context, utils.getEnclosingTypeElement(member), member, - name(member), HtmlStyle.memberNameLink); + name(member), HtmlStyles.memberNameLink); var code = HtmlTree.CODE(memberLink); content.add(code); } @@ -331,7 +332,7 @@ protected void addDefaultValueInfo(Element member, Content annotationContent) { ExecutableElement ee = (ExecutableElement) member; AnnotationValue value = ee.getDefaultValue(); if (value != null) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(contents.default_)); dl.add(HtmlTree.DD(HtmlTree.CODE(Text.of(value.toString())))); annotationContent.add(dl); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java index e949b4ddc6831..ac953dccb55df 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java @@ -38,11 +38,8 @@ import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; import jdk.javadoc.internal.doclets.toolkit.util.ClassUseMapper; @@ -50,6 +47,10 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; /** @@ -142,7 +143,7 @@ public ClassUseWriter(HtmlConfiguration configuration, methodSubWriter = new MethodWriter(this); constrSubWriter = new ConstructorWriter(this); - constrSubWriter.setFoundNonPubConstructor(true); + constrSubWriter.setShowConstructorModifiers(true); fieldSubWriter = new FieldWriter(this); classSubWriter = new NestedClassWriter(this); } @@ -244,10 +245,10 @@ protected void addPackageList(Content content) { "doclet.ClassUse_Packages.that.use.0", getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, typeElement))); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(caption) .setHeader(getPackageTableHeader()) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); for (PackageElement pkg : pkgSet) { addPackageUse(pkg, table); } @@ -270,10 +271,10 @@ protected void addPackageAnnotationList(Content content) { getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, typeElement))); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(caption) .setHeader(getPackageTableHeader()) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); for (PackageElement pkg : pkgToPackageAnnotations) { Content summary = new ContentBuilder(); addSummaryComment(pkg, summary); @@ -288,9 +289,9 @@ protected void addPackageAnnotationList(Content content) { * @param content the content to which the class elements will be added */ protected void addClassList(Content content) { - var ul = HtmlTree.UL(HtmlStyle.blockList); + var ul = HtmlTree.UL(HtmlStyles.blockList); for (PackageElement pkg : pkgSet) { - var section = HtmlTree.SECTION(HtmlStyle.detail) + var section = HtmlTree.SECTION(HtmlStyles.detail) .setId(htmlIds.forPackage(pkg)); Content link = contents.getContent("doclet.ClassUse_Uses.of.0.in.1", getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, @@ -301,7 +302,7 @@ protected void addClassList(Content content) { addClassUse(pkg, section); ul.add(HtmlTree.LI(section)); } - var li = HtmlTree.SECTION(HtmlStyle.classUses, ul); + var li = HtmlTree.SECTION(HtmlStyles.classUses, ul); content.add(li); } @@ -422,11 +423,11 @@ protected HtmlTree getClassUseHeader() { HtmlTree body = getBody(getWindowTitle(title)); ContentBuilder headingContent = new ContentBuilder(); headingContent.add(contents.getContent("doclet.ClassUse_Title", cltype)); - headingContent.add(new HtmlTree(TagName.BR)); + headingContent.add(new HtmlTree(HtmlTag.BR)); headingContent.add(clname); var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, headingContent); - var div = HtmlTree.DIV(HtmlStyle.header, heading); + HtmlStyles.title, headingContent); + var div = HtmlTree.DIV(HtmlStyles.header, heading); bodyContents.setHeader(getHeader(PageMode.USE, typeElement)).addMainContent(div); return body; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java index 7e0cf74c4f331..8218e5128be3b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java @@ -37,6 +37,7 @@ import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleElementVisitor8; @@ -45,12 +46,7 @@ import com.sun.source.doctree.DocTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.CommentUtils; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.PropertyUtils; @@ -58,6 +54,12 @@ import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generate the Class Information Page. @@ -154,7 +156,7 @@ protected void buildClassTree(Content classContent) { * @param target the content to which the documentation will be added */ protected void buildClassInfo(Content target) { - Content c = HtmlTree.DIV(HtmlStyle.horizontalScroll); + var c = new ContentBuilder(); buildParamInfo(c); buildSuperInterfacesInfo(c); buildImplementedInterfacesInfo(c); @@ -163,11 +165,13 @@ protected void buildClassInfo(Content target) { buildInterfaceUsageInfo(c); buildNestedClassInfo(c); buildFunctionalInterfaceInfo(c); - buildClassSignature(c); - buildDeprecationInfo(c); - buildClassDescription(c); - buildClassTagInfo(c); - + c.add(new HtmlTree(HtmlTag.HR)); + var div = HtmlTree.DIV(HtmlStyles.horizontalScroll); + buildClassSignature(div); + buildDeprecationInfo(div); + buildClassDescription(div); + buildClassTagInfo(div); + c.add(div); target.add(getClassInfo(c)); } @@ -430,13 +434,10 @@ private void setRecordDocumentation(TypeElement elem) { protected Content getHeader(String header) { HtmlTree body = getBody(getWindowTitle(utils.getSimpleName(typeElement))); - var div = HtmlTree.DIV(HtmlStyle.header); - HtmlLinkInfo linkInfo = new HtmlLinkInfo(configuration, - HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS, typeElement) - .linkToSelf(false); // Let's not link to ourselves in the header + var div = HtmlTree.DIV(HtmlStyles.header); var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, Text.of(header)); - heading.add(getTypeParameterLinks(linkInfo)); + HtmlStyles.title, Text.of(header)); + heading.add(getTypeParameters()); div.add(heading); bodyContents.setHeader(getHeader(PageMode.CLASS, typeElement)) .addMainContent(MarkerComments.START_OF_CLASS_DATA) @@ -444,6 +445,35 @@ protected Content getHeader(String header) { return body; } + // Renders type parameters for the class heading, creating id attributes + // if @param block tags are missing in doc comment. + private Content getTypeParameters() { + var content = new ContentBuilder(); + var typeParams = typeElement.getTypeParameters(); + if (!typeParams.isEmpty()) { + // Generate id attributes if @param tags are missing for type parameters. + // Note that this does not handle the case where some but not all @param tags are missing. + var needsId = !utils.hasBlockTag(typeElement, DocTree.Kind.PARAM); + var linkInfo = new HtmlLinkInfo(configuration, + HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS, typeElement) + .linkToSelf(false); // Let's not link to ourselves in the header + content.add("<"); + var first = true; + for (TypeParameterElement t : typeParams) { + if (!first) { + content.add(",").add(new HtmlTree(HtmlTag.WBR)); + } + var typeParamLink = getLink(linkInfo.forType(t.asType())); + content.add(needsId + ? HtmlTree.SPAN_ID(htmlIds.forTypeParam(t.getSimpleName().toString(), typeElement), typeParamLink) + : typeParamLink); + first = false; + } + content.add(">"); + } + return content; + } + protected Content getClassContentHeader() { return getContentHeader(); } @@ -463,7 +493,7 @@ protected void printDocument(Content content) throws DocFileIOException { } protected Content getClassInfo(Content classInfo) { - return getMember(HtmlIds.CLASS_DESCRIPTION, HtmlStyle.classDescription, classInfo); + return getMember(HtmlIds.CLASS_DESCRIPTION, HtmlStyles.classDescription, classInfo); } @Override @@ -472,7 +502,6 @@ public TypeElement getCurrentPageElement() { } protected void addClassSignature(Content classInfo) { - classInfo.add(new HtmlTree(TagName.HR)); classInfo.add(new Signatures.TypeSignature(typeElement, this) .toContent()); } @@ -514,7 +543,7 @@ private Content getClassInheritanceTreeContent(TypeMirror type) { HtmlTree classTree = null; do { sup = utils.getFirstVisibleSuperClass(type); - var entry = HtmlTree.DIV(HtmlStyle.inheritance, getClassHelperContent(type)); + var entry = HtmlTree.DIV(HtmlStyles.inheritance, getClassHelperContent(type)); if (classTree != null) entry.add(classTree); classTree = entry; @@ -564,7 +593,7 @@ protected void addParamInfo(Content target) { var t = configuration.tagletManager.getTaglet(DocTree.Kind.PARAM); Content paramInfo = t.getAllBlockTagOutput(typeElement, getTagletWriterInstance(false)); if (!paramInfo.isEmpty()) { - target.add(HtmlTree.DL(HtmlStyle.notes, paramInfo)); + target.add(HtmlTree.DL(HtmlStyles.notes, paramInfo)); } } } @@ -578,7 +607,7 @@ protected void addSubClassInfo(Content target) { } Set subclasses = classTree.hierarchy(typeElement).subtypes(typeElement); if (!subclasses.isEmpty()) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(contents.subclassesLabel)); dl.add(HtmlTree.DD(getClassLinks(HtmlLinkInfo.Kind.PLAIN, subclasses))); target.add(dl); @@ -590,7 +619,7 @@ protected void addSubInterfacesInfo(Content target) { if (utils.isPlainInterface(typeElement)) { Set subInterfaces = classTree.hierarchy(typeElement).allSubtypes(typeElement); if (!subInterfaces.isEmpty()) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(contents.subinterfacesLabel)); dl.add(HtmlTree.DD(getClassLinks(HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, subInterfaces))); target.add(dl); @@ -609,7 +638,7 @@ protected void addInterfaceUsageInfo(Content target) { } Set implcl = classTree.implementingClasses(typeElement); if (!implcl.isEmpty()) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(contents.implementingClassesLabel)); dl.add(HtmlTree.DD(getClassLinks(HtmlLinkInfo.Kind.PLAIN, implcl))); target.add(dl); @@ -620,7 +649,7 @@ protected void addImplementedInterfacesInfo(Content target) { SortedSet interfaces = new TreeSet<>(comparators.typeMirrorClassUseComparator()); interfaces.addAll(utils.getAllInterfaces(typeElement)); if (utils.isClass(typeElement) && !interfaces.isEmpty()) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(contents.allImplementedInterfacesLabel)); dl.add(HtmlTree.DD(getClassLinks(HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, interfaces))); target.add(dl); @@ -633,7 +662,7 @@ protected void addSuperInterfacesInfo(Content target) { interfaces.addAll(utils.getAllInterfaces(typeElement)); if (utils.isPlainInterface(typeElement) && !interfaces.isEmpty()) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(contents.allSuperinterfacesLabel)); dl.add(HtmlTree.DD(getClassLinks(HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS, interfaces))); target.add(dl); @@ -647,7 +676,7 @@ protected void addNestedClassInfo(final Content target) { new SimpleElementVisitor8() { @Override public Void visitType(TypeElement e, Void p) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(utils.isPlainInterface(e) ? contents.enclosingInterfaceLabel : contents.enclosingClassLabel)); @@ -660,9 +689,9 @@ public Void visitType(TypeElement e, Void p) { protected void addFunctionalInterfaceInfo (Content target) { if (utils.isFunctionalInterface(typeElement)) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(HtmlTree.DT(contents.functionalInterface)); - var dd = new HtmlTree(TagName.DD); + var dd = new HtmlTree(HtmlTag.DD); dd.add(contents.functionalInterfaceMessage); dl.add(dd); target.add(dl); @@ -672,8 +701,8 @@ protected void addFunctionalInterfaceInfo (Content target) { protected void addClassDeprecationInfo(Content classInfo) { List deprs = utils.getDeprecatedTrees(typeElement); if (utils.isDeprecated(typeElement)) { - var deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(typeElement)); - var div = HtmlTree.DIV(HtmlStyle.deprecationBlock, deprLabel); + var deprLabel = HtmlTree.SPAN(HtmlStyles.deprecatedLabel, getDeprecatedPhrase(typeElement)); + var div = HtmlTree.DIV(HtmlStyles.deprecationBlock, deprLabel); if (!deprs.isEmpty()) { CommentHelper ch = utils.getCommentHelper(typeElement); DocTree dt = deprs.get(0); @@ -726,7 +755,7 @@ public TypeElement getTypeElement() { } protected Content getMemberDetails(Content content) { - var section = HtmlTree.SECTION(HtmlStyle.details, content); + var section = HtmlTree.SECTION(HtmlStyles.details, content); // The following id is required by the Navigation bar if (utils.isAnnotationInterface(typeElement)) { section.setId(HtmlIds.ANNOTATION_TYPE_ELEMENT_DETAIL); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstantsSummaryWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstantsSummaryWriter.java index 5bddac0622c4f..cdae0e8d9115e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstantsSummaryWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstantsSummaryWriter.java @@ -39,20 +39,21 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** @@ -328,14 +329,14 @@ Content getHeader() { bodyContents.setHeader(getHeader(PageMode.CONSTANT_VALUES)); Content titleContent = contents.constantsSummaryTitle; var pHeading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, titleContent); - var div = HtmlTree.DIV(HtmlStyle.header, pHeading); + HtmlStyles.title, titleContent); + var div = HtmlTree.DIV(HtmlStyles.header, pHeading); bodyContents.addMainContent(div); return body; } Content getContentsHeader() { - return HtmlTree.UL(HtmlStyle.contentsList); + return HtmlTree.UL(HtmlStyles.contentsList); } void addLinkToTableOfContents(String abbrevPackageName) { @@ -361,14 +362,14 @@ void addPackageGroup(String abbrevPackageName, Content toContent) { var heading = HtmlTree.HEADING_TITLE( Headings.ConstantsSummary.PACKAGE_HEADING, headingContent); - summarySection = HtmlTree.SECTION(HtmlStyle.constantsSummary, heading) + summarySection = HtmlTree.SECTION(HtmlStyles.constantsSummary, heading) .setId(anchorName); toContent.add(summarySection); } Content getClassConstantHeader() { - return HtmlTree.UL(HtmlStyle.blockList); + return HtmlTree.UL(HtmlStyles.blockList); } void addClassConstant(Content fromClassConstant) { @@ -394,10 +395,10 @@ void addConstantMembers(TypeElement typeElement, Collection fie } caption.add(classLink); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(caption) .setHeader(constantsTableHeader) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colSecond, HtmlStyles.colLast); for (VariableElement field : fields) { table.addRow(getTypeColumn(field), getNameColumn(field), getValue(field)); @@ -413,7 +414,7 @@ void addConstantMembers(TypeElement typeElement, Collection fie */ private Content getTypeColumn(VariableElement member) { Content typeContent = new ContentBuilder(); - var code = new HtmlTree(TagName.CODE) + var code = new HtmlTree(HtmlTag.CODE) .setId(htmlIds.forMember(currentTypeElement, member)); for (Modifier mod : member.getModifiers()) { code.add(Text.of(mod.toString())) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java index 1c4af2faf9782..11975404a5564 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java @@ -31,16 +31,18 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.BaseOptions; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** @@ -53,7 +55,17 @@ public class ConstructorWriter extends AbstractExecutableMemberWriter { */ private ExecutableElement currentConstructor; - private boolean foundNonPubConstructor = false; + /** + * If any constructors are non-public, then we want the modifiers shown in the summary. + * This implies we need a three-column summary. + */ + private boolean showConstructorModifiers = false; + + /** + * Whether any constructors have type parameters. + * This implies we need a three column summary. + */ + private boolean hasTypeParamsConstructor = false; /** * Construct a new member writer for constructors. @@ -65,11 +77,7 @@ public ConstructorWriter(ClassWriter writer) { // the following must be done before the summary table is generated var constructors = getVisibleMembers(VisibleMemberTable.Kind.CONSTRUCTORS); - for (Element constructor : constructors) { - if (utils.isProtected(constructor) || utils.isPrivate(constructor)) { - setFoundNonPubConstructor(true); - } - } + analyzeConstructors(constructors); } /** @@ -94,11 +102,7 @@ public void buildDetails(Content target) { protected void buildConstructorDoc(Content target) { var constructors = getVisibleMembers(VisibleMemberTable.Kind.CONSTRUCTORS); if (!constructors.isEmpty()) { - for (Element constructor : constructors) { - if (utils.isProtected(constructor) || utils.isPrivate(constructor)) { - setFoundNonPubConstructor(true); - } - } + analyzeConstructors(constructors); Content constructorDetailsHeader = getConstructorDetailsHeader(target); Content memberList = getMemberList(); @@ -108,7 +112,7 @@ protected void buildConstructorDoc(Content target) { for (Element constructor : constructors) { currentConstructor = (ExecutableElement)constructor; Content constructorContent = getConstructorHeaderContent(currentConstructor); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); buildSignature(div); buildDeprecationInfo(div); buildPreviewInfo(div); @@ -126,6 +130,24 @@ protected void buildConstructorDoc(Content target) { } } + // Calculate "showConstructorModifiers" and "hasTypeParamsConstructor" + private void analyzeConstructors(List constructors) { + for (Element constructor : constructors) { + if (utils.isProtected(constructor) || utils.isPrivate(constructor)) { + setShowConstructorModifiers(true); + } + List list = ((ExecutableElement)constructor).getTypeParameters(); + if (list != null && !list.isEmpty()) { + hasTypeParamsConstructor = true; + } + } + } + + // Does the constructor summary need three columnns or just two? + protected boolean threeColumnSummary() { + return showConstructorModifiers || hasTypeParamsConstructor; + } + @Override protected void buildSignature(Content target) { target.add(getSignature(currentConstructor)); @@ -172,7 +194,7 @@ public Content getMemberSummaryHeader(Content content) { @Override public void buildSummary(Content summariesList, Content content) { - writer.addSummary(HtmlStyle.constructorSummary, + writer.addSummary(HtmlStyles.constructorSummary, HtmlIds.CONSTRUCTOR_SUMMARY, summariesList, content); } @@ -195,12 +217,13 @@ protected Content getConstructorHeaderContent(ExecutableElement constructor) { heading.setId(anchors.getLast()); } content.add(heading); - return HtmlTree.SECTION(HtmlStyle.detail, content) + return HtmlTree.SECTION(HtmlStyles.detail, content) .setId(anchors.getFirst()); } protected Content getSignature(ExecutableElement constructor) { return new Signatures.MemberSignature(constructor, this) + .setTypeParameters(getTypeParameters(constructor)) .setParameters(getParameters(constructor, true)) .setExceptions(getExceptions(constructor)) .setAnnotations(writer.getAnnotationInfo(constructor, true)) @@ -225,14 +248,14 @@ protected void addTags(ExecutableElement constructor, Content constructorContent protected Content getConstructorDetails(Content memberDetailsHeader, Content memberDetails) { return writer.getDetailsListItem( - HtmlTree.SECTION(HtmlStyle.constructorDetails) + HtmlTree.SECTION(HtmlStyles.constructorDetails) .setId(HtmlIds.CONSTRUCTOR_DETAIL) .add(memberDetailsHeader) .add(memberDetails)); } - protected void setFoundNonPubConstructor(boolean foundNonPubConstructor) { - this.foundNonPubConstructor = foundNonPubConstructor; + protected void setShowConstructorModifiers(boolean showConstructorModifiers) { + this.showConstructorModifiers = showConstructorModifiers; } @Override @@ -244,7 +267,7 @@ public void addSummaryLabel(Content content) { @Override public TableHeader getSummaryTableHeader(Element member) { - if (foundNonPubConstructor) { + if (threeColumnSummary()) { return new TableHeader(contents.modifierLabel, contents.constructorLabel, contents.descriptionLabel); } else { @@ -256,15 +279,15 @@ public TableHeader getSummaryTableHeader(Element member) { protected Table createSummaryTable() { List bodyRowStyles; - if (foundNonPubConstructor) { - bodyRowStyles = Arrays.asList(HtmlStyle.colFirst, HtmlStyle.colConstructorName, - HtmlStyle.colLast); + if (threeColumnSummary()) { + bodyRowStyles = Arrays.asList(HtmlStyles.colFirst, HtmlStyles.colConstructorName, + HtmlStyles.colLast); } else { - bodyRowStyles = Arrays.asList(HtmlStyle.colConstructorName, HtmlStyle.colLast); + bodyRowStyles = Arrays.asList(HtmlStyles.colConstructorName, HtmlStyles.colLast); } return new Table( - HtmlStyle.summaryTable) + HtmlStyles.summaryTable) .setCaption(contents.constructors) .setHeader(getSummaryTableHeader(typeElement)) .setColumnStyles(bodyRowStyles); @@ -276,8 +299,8 @@ public void addInheritedSummaryLabel(TypeElement typeElement, Content content) { @Override protected void addSummaryType(Element member, Content content) { - if (foundNonPubConstructor) { - var code = new HtmlTree(TagName.CODE); + if (threeColumnSummary()) { + var code = new HtmlTree(HtmlTag.CODE); if (utils.isProtected(member)) { code.add("protected "); } else if (utils.isPrivate(member)) { @@ -288,6 +311,11 @@ protected void addSummaryType(Element member, Content content) { code.add( resources.getText("doclet.Package_private")); } + ExecutableElement constructor = (ExecutableElement)member; + List list = constructor.getTypeParameters(); + if (list != null && !list.isEmpty()) { + addTypeParameters(constructor, code); + } content.add(code); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java index 3afccd55ba1a4..f098a8dc8f3a2 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Contents.java @@ -31,11 +31,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; import jdk.javadoc.internal.doclets.toolkit.Resources; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.Text; /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java index 684e198ef1b75..3f58ef90ad50a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java @@ -32,11 +32,13 @@ import com.sun.source.doctree.DeprecatedTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DeprecatedAPIListBuilder; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generate File to list all the deprecated classes and class members with the @@ -79,7 +81,7 @@ protected String getTitleKey() { protected void addContentSelectors(Content target) { List releases = builder.releases; if (releases.size() > 1) { - Content tabs = HtmlTree.DIV(HtmlStyle.checkboxes, contents.getContent( + Content tabs = HtmlTree.DIV(HtmlStyles.checkboxes, contents.getContent( "doclet.Deprecated_API_Checkbox_Label")); // Table column ids are 1-based int index = 1; @@ -125,7 +127,7 @@ protected void addComments(Element e, Content desc) { protected void addTableTabs(Table table, String headingKey) { List releases = builder.releases; if (!releases.isEmpty()) { - table.setGridStyle(HtmlStyle.threeColumnReleaseSummary); + table.setGridStyle(HtmlStyles.threeColumnReleaseSummary); } if (releases.size() > 1) { table.setDefaultTab(getTableCaption(headingKey)) @@ -170,7 +172,7 @@ protected HtmlStyle[] getColumnStyles() { if (releases.isEmpty()) { return super.getColumnStyles(); } - return new HtmlStyle[]{ HtmlStyle.colSummaryItemName, HtmlStyle.colSecond, HtmlStyle.colLast }; + return new HtmlStyle[]{ HtmlStyles.colSummaryItemName, HtmlStyles.colSecond, HtmlStyles.colLast }; } @Override diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java index cd2633d004d91..5b39b08c22b9f 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DocFilesHandler.java @@ -25,12 +25,23 @@ package jdk.javadoc.internal.doclets.formats.html; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.PackageElement; +import javax.tools.JavaFileManager.Location; + import com.sun.source.doctree.DocTree; import com.sun.source.doctree.EndElementTree; import com.sun.source.doctree.StartElementTree; import com.sun.source.util.DocTreeFactory; + +import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.toolkit.DocFileElement; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.util.DocFile; @@ -38,19 +49,9 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.Utils; -import jdk.javadoc.internal.doclint.HtmlTag; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ModuleElement; -import javax.lang.model.element.PackageElement; -import javax.tools.JavaFileManager.Location; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; - -import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; /** * A class to handle any files, including HTML files, found in the {@code doc-files} @@ -249,7 +250,7 @@ private List getLocalHeaderTags(List dtree switch (dt.getKind()) { case START_ELEMENT: StartElementTree startElem = (StartElementTree)dt; - switch (HtmlTag.get(startElem.getName())) { + switch (HtmlTag.of(startElem.getName())) { case HEAD: inHead = true; break; @@ -267,7 +268,7 @@ private List getLocalHeaderTags(List dtree break; case END_ELEMENT: EndElementTree endElem = (EndElementTree)dt; - switch (HtmlTag.get(endElem.getName())) { + switch (HtmlTag.of(endElem.getName())) { case HEAD: inHead = false; break loop; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/EnumConstantWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/EnumConstantWriter.java index d60fd355027cb..ff82b95fe4d8a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/EnumConstantWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/EnumConstantWriter.java @@ -29,12 +29,13 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.BaseOptions; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Writes enum constant documentation in HTML format. @@ -75,7 +76,7 @@ protected void buildEnumConstant(Content target) { for (Element enumConstant : enumConstants) { currentElement = (VariableElement)enumConstant; Content enumConstantContent = getEnumConstantsHeader(currentElement); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); buildSignature(div); buildDeprecationInfo(div); buildPreviewInfo(div); @@ -138,7 +139,7 @@ public Content getMemberSummaryHeader(Content content) { @Override public void buildSummary(Content summariesList, Content content) { - writer.addSummary(HtmlStyle.constantsSummary, + writer.addSummary(HtmlStyles.constantsSummary, HtmlIds.ENUM_CONSTANT_SUMMARY, summariesList, content); } @@ -156,7 +157,7 @@ protected Content getEnumConstantsHeader(VariableElement enumConstant) { var heading = HtmlTree.HEADING(Headings.TypeDeclaration.MEMBER_HEADING, Text.of(name(enumConstant))); enumConstantsContent.add(heading); - return HtmlTree.SECTION(HtmlStyle.detail, enumConstantsContent) + return HtmlTree.SECTION(HtmlStyles.detail, enumConstantsContent) .setId(htmlIds.forMember(enumConstant)); } @@ -186,7 +187,7 @@ protected void addTags(VariableElement enumConstant, Content content) { protected Content getEnumConstantsDetails(Content memberDetailsHeader, Content content) { return writer.getDetailsListItem( - HtmlTree.SECTION(HtmlStyle.constantDetails) + HtmlTree.SECTION(HtmlStyles.constantDetails) .setId(HtmlIds.ENUM_CONSTANT_DETAIL) .add(memberDetailsHeader) .add(content)); @@ -206,10 +207,10 @@ public TableHeader getSummaryTableHeader(Element member) { @Override protected Table createSummaryTable() { - return new Table(HtmlStyle.summaryTable) + return new Table(HtmlStyles.summaryTable) .setCaption(contents.getContent("doclet.Enum_Constants")) .setHeader(getSummaryTableHeader(typeElement)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); } @Override @@ -220,7 +221,7 @@ public void addInheritedSummaryLabel(TypeElement typeElement, Content content) { protected void addSummaryLink(HtmlLinkInfo.Kind context, TypeElement typeElement, Element member, Content content) { Content memberLink = writer.getDocLink(context, utils.getEnclosingTypeElement(member), member, - name(member), HtmlStyle.memberNameLink); + name(member), HtmlStyles.memberNameLink); var code = HtmlTree.CODE(memberLink); content.add(code); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java index cd66876997cd9..0115de5558fba 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ExternalSpecsWriter.java @@ -49,15 +49,16 @@ import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocFileElement; import jdk.javadoc.internal.doclets.toolkit.OverviewElement; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; @@ -103,7 +104,7 @@ public void buildPage() throws DocFileIOException { addExternalSpecs(mainContent); body.add(new BodyContents() .setHeader(getHeader(PageMode.EXTERNAL_SPECS)) - .addMainContent(HtmlTree.DIV(HtmlStyle.header, + .addMainContent(HtmlTree.DIV(HtmlStyles.header, HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, contents.getContent("doclet.External_Specifications")))) .addMainContent(mainContent) @@ -192,10 +193,10 @@ protected void addExternalSpecs(Content content) { } var hostNamesList = new ArrayList<>(hostNamesSet); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(contents.externalSpecifications) .setHeader(new TableHeader(contents.specificationLabel, contents.referencedIn)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast) + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast) .setId(HtmlIds.EXTERNAL_SPECS); if ((hostNamesList.size() + (noHost ? 1 : 0)) > 1) { for (var host : hostNamesList) { @@ -211,7 +212,7 @@ protected void addExternalSpecs(Content content) { for (List searchIndexItems : searchIndexMap.values()) { IndexItem ii = searchIndexItems.get(0); Content specName = createSpecLink(ii); - Content referencesList = HtmlTree.UL(HtmlStyle.refList, searchIndexItems, + Content referencesList = HtmlTree.UL(HtmlStyles.refList, searchIndexItems, item -> HtmlTree.LI(createLink(item))); Content references = searchIndexItems.size() < USE_DETAILS_THRESHHOLD ? referencesList diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriter.java index c72521b73b288..213078712ebf0 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriter.java @@ -32,13 +32,15 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.BaseOptions; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Writes field documentation in HTML format. @@ -85,7 +87,7 @@ protected void buildFieldDoc(Content target) { for (Element element : fields) { currentElement = (VariableElement)element; Content fieldContent = getFieldHeaderContent(currentElement); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); buildSignature(div); buildDeprecationInfo(div); buildPreviewInfo(div); @@ -147,7 +149,7 @@ public Content getMemberSummaryHeader(Content content) { @Override public void buildSummary(Content summariesList, Content content) { - writer.addSummary(HtmlStyle.fieldSummary, + writer.addSummary(HtmlStyles.fieldSummary, HtmlIds.FIELD_SUMMARY, summariesList, content); } @@ -165,7 +167,7 @@ protected Content getFieldHeaderContent(VariableElement field) { var heading = HtmlTree.HEADING(Headings.TypeDeclaration.MEMBER_HEADING, Text.of(name(field))); content.add(heading); - return HtmlTree.SECTION(HtmlStyle.detail, content) + return HtmlTree.SECTION(HtmlStyles.detail, content) .setId(htmlIds.forMember(field)); } @@ -196,7 +198,7 @@ protected void addTags(VariableElement field, Content fieldContent) { protected Content getFieldDetails(Content memberDetailsHeaderContent, Content memberContent) { return writer.getDetailsListItem( - HtmlTree.SECTION(HtmlStyle.fieldDetails) + HtmlTree.SECTION(HtmlStyles.fieldDetails) .setId(HtmlIds.FIELD_DETAIL) .add(memberDetailsHeaderContent) .add(memberContent)); @@ -217,10 +219,10 @@ public TableHeader getSummaryTableHeader(Element member) { @Override protected Table createSummaryTable() { - List bodyRowStyles = Arrays.asList(HtmlStyle.colFirst, HtmlStyle.colSecond, - HtmlStyle.colLast); + List bodyRowStyles = Arrays.asList(HtmlStyles.colFirst, HtmlStyles.colSecond, + HtmlStyles.colLast); - return new Table(HtmlStyle.summaryTable) + return new Table(HtmlStyles.summaryTable) .setCaption(contents.fields) .setHeader(getSummaryTableHeader(typeElement)) .setColumnStyles(bodyRowStyles); @@ -252,7 +254,7 @@ public void addInheritedSummaryLabel(TypeElement typeElement, Content content) { protected void addSummaryLink(HtmlLinkInfo.Kind context, TypeElement typeElement, Element member, Content content) { Content memberLink = writer.getDocLink(context, typeElement , member, name(member), - HtmlStyle.memberNameLink); + HtmlStyles.memberNameLink); var code = HtmlTree.CODE(memberLink); content.add(code); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Headings.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Headings.java index 84b2fda4da413..2dadfe3cfa67a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Headings.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Headings.java @@ -25,7 +25,7 @@ package jdk.javadoc.internal.doclets.formats.html; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; +import jdk.javadoc.internal.html.HtmlTag; /** * Aliases for HTML heading tags (H1..H6) for different kinds of pages. @@ -34,25 +34,25 @@ class Headings { /** * Standard top-level heading for the page title for all pages. */ - static final TagName PAGE_TITLE_HEADING = TagName.H1; + static final HtmlTag PAGE_TITLE_HEADING = HtmlTag.H1; /** * Standard second-level heading for sundry pages that do * not have their own page group. */ - static final TagName CONTENT_HEADING = TagName.H2; + static final HtmlTag CONTENT_HEADING = HtmlTag.H2; /** * Standard third-level heading for sundry pages that do * not have their own page group. */ - static final TagName SUB_HEADING = TagName.H3; + static final HtmlTag SUB_HEADING = HtmlTag.H3; /** * Headings for the page for a module declaration. */ static class ModuleDeclaration { - static final TagName SUMMARY_HEADING = TagName.H2; + static final HtmlTag SUMMARY_HEADING = HtmlTag.H2; } /** @@ -64,31 +64,31 @@ static class TypeDeclaration { * Heading for the different summary lists: * Field Summary, Constructor Summary, Method Summary, etc. */ - static final TagName SUMMARY_HEADING = TagName.H2; + static final HtmlTag SUMMARY_HEADING = HtmlTag.H2; /** * Subheading within a summary for the inherited elements: * inherited methods, etc */ - static final TagName INHERITED_SUMMARY_HEADING = TagName.H3; + static final HtmlTag INHERITED_SUMMARY_HEADING = HtmlTag.H3; /** * Heading for the different detail lists: * Field Details, Constructor Details, Method Details, etc. */ - static final TagName DETAILS_HEADING = TagName.H2; + static final HtmlTag DETAILS_HEADING = HtmlTag.H2; /** * Subheading with a Details list for an individual element. */ - static final TagName MEMBER_HEADING = TagName.H3; + static final HtmlTag MEMBER_HEADING = HtmlTag.H3; } /** * Headings for the Constants Summary page. */ static class ConstantsSummary { - static final TagName PACKAGE_HEADING = TagName.H2; + static final HtmlTag PACKAGE_HEADING = HtmlTag.H2; } /** @@ -98,28 +98,28 @@ static class SerializedForm { /** * Heading for the package name, preceding a list of types. */ - static final TagName PACKAGE_HEADING = TagName.H2; + static final HtmlTag PACKAGE_HEADING = HtmlTag.H2; /** * Heading for a type name within a package. */ - static final TagName CLASS_HEADING = TagName.H3; + static final HtmlTag CLASS_HEADING = HtmlTag.H3; /** * Subheading for info within a type. */ - static final TagName CLASS_SUBHEADING = TagName.H4; + static final HtmlTag CLASS_SUBHEADING = HtmlTag.H4; /** * Heading for an individual member element within a type. */ - static final TagName MEMBER_HEADING = TagName.H5; + static final HtmlTag MEMBER_HEADING = HtmlTag.H5; } /** * Headings for a type Use page. */ static class TypeUse { - static final TagName SUMMARY_HEADING = TagName.H2; + static final HtmlTag SUMMARY_HEADING = HtmlTag.H2; } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java index 1f396d49eeda0..22e778d39868f 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java @@ -27,17 +27,18 @@ import java.util.List; -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** @@ -103,13 +104,13 @@ protected void addHelpFileContents(Content content) { var mainHeading = getContent("doclet.help.main_heading"); tableOfContents.addLink(HtmlIds.TOP_OF_PAGE, mainHeading); tableOfContents.pushNestedList(); - content.add(HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, HtmlStyle.title, mainHeading)) - .add(new HtmlTree(TagName.HR)) + content.add(HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, HtmlStyles.title, mainHeading)) + .add(new HtmlTree(HtmlTag.HR)) .add(getNavigationSection()) - .add(new HtmlTree(TagName.HR)) + .add(new HtmlTree(HtmlTag.HR)) .add(getPageKindSection()) - .add(new HtmlTree(TagName.HR)) - .add(HtmlTree.SPAN(HtmlStyle.helpFootnote, + .add(new HtmlTree(HtmlTag.HR)) + .add(HtmlTree.SPAN(HtmlStyles.helpFootnote, getContent("doclet.help.footnote"))); tableOfContents.popNestedList(); } @@ -129,7 +130,7 @@ private Content getNavigationSection() { Content content = new ContentBuilder(); Content navHeading = contents.getContent("doclet.help.navigation.head"); - var navSection = HtmlTree.DIV(HtmlStyle.subTitle) + var navSection = HtmlTree.DIV(HtmlStyles.subTitle) .add(HtmlTree.HEADING(Headings.CONTENT_HEADING, navHeading).setId(HtmlIds.HELP_NAVIGATION)) .add(contents.getContent("doclet.help.navigation.intro", overviewLink)); if (options.createIndex()) { @@ -153,7 +154,7 @@ private Content getNavigationSection() { if (options.createIndex()) { section = newHelpSection(getContent("doclet.help.search.head"), PageMode.SEARCH); var searchIntro = HtmlTree.P(getContent("doclet.help.search.intro")); - var searchExamples = HtmlTree.OL(HtmlStyle.tocList); + var searchExamples = HtmlTree.OL(HtmlStyles.tocList); for (String[] example : SEARCH_EXAMPLES) { searchExamples.add(HtmlTree.LI( getContent("doclet.help.search.example", @@ -190,7 +191,7 @@ private Content getNavigationSection() { */ private Content getPageKindSection() { Content pageKindsHeading = contents.getContent("doclet.help.page_kinds.head"); - var pageKindsSection = HtmlTree.DIV(HtmlStyle.subTitle) + var pageKindsSection = HtmlTree.DIV(HtmlStyles.subTitle) .add(HtmlTree.HEADING(Headings.CONTENT_HEADING, pageKindsHeading).setId(HtmlIds.HELP_PAGES)) .add(contents.getContent("doclet.help.page_kinds.intro")); @@ -235,7 +236,7 @@ private Content getPageKindSection() { // Class/interface Content notes = new ContentBuilder( - HtmlTree.SPAN(HtmlStyle.helpNote, getContent("doclet.help.class_interface.note")), + HtmlTree.SPAN(HtmlStyles.helpNote, getContent("doclet.help.class_interface.note")), Text.of(" "), getContent("doclet.help.class_interface.anno"), Text.of(" "), @@ -254,7 +255,7 @@ private Content getPageKindSection() { getContent("doclet.help.class_interface.implementations"), getContent("doclet.help.class_interface.declaration"), getContent("doclet.help.class_interface.description"))) - .add(new HtmlTree(TagName.BR)) + .add(new HtmlTree(HtmlTag.BR)) .add(newHelpSectionList( contents.nestedClassSummary, contents.enumConstantSummary, @@ -264,7 +265,7 @@ private Content getPageKindSection() { contents.methodSummary, contents.annotateTypeRequiredMemberSummaryLabel, contents.annotateTypeOptionalMemberSummaryLabel)) - .add(new HtmlTree(TagName.BR)) + .add(new HtmlTree(HtmlTag.BR)) .add(newHelpSectionList( contents.enumConstantDetailLabel, contents.fieldDetailsLabel, @@ -412,7 +413,7 @@ private Content getContent(String key, Object arg1, Object arg2) { private HtmlTree newHelpSection(Content headingContent, HtmlId id) { tableOfContents.addLink(id, headingContent); - return HtmlTree.SECTION(HtmlStyle.helpSection, + return HtmlTree.SECTION(HtmlStyles.helpSection, HtmlTree.HEADING(Headings.SUB_HEADING, headingContent)) .setId(id); } @@ -422,7 +423,7 @@ private HtmlTree newHelpSection(Content headingContent, Navigation.PageMode pm) } private HtmlTree newHelpSectionList(Content first, Content... rest) { - var list = HtmlTree.UL(HtmlStyle.helpSectionList, HtmlTree.LI(first)); + var list = HtmlTree.UL(HtmlStyles.helpSectionList, HtmlTree.LI(first)); List.of(rest).forEach(i -> list.add(HtmlTree.LI(i))); return list; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index 2b8b6cfe058d4..235a0361b9233 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -94,20 +94,10 @@ import jdk.internal.org.commonmark.renderer.html.HtmlNodeRendererFactory; import jdk.internal.org.commonmark.renderer.html.HtmlRenderer; import jdk.internal.org.commonmark.renderer.html.HtmlWriter; - -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; import jdk.javadoc.internal.doclets.formats.html.markup.Head; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.formats.html.markup.Links; -import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; -import jdk.javadoc.internal.doclets.formats.html.markup.Script; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.markup.TextBuilder; import jdk.javadoc.internal.doclets.formats.html.taglets.Taglet; import jdk.javadoc.internal.doclets.formats.html.taglets.TagletWriter; import jdk.javadoc.internal.doclets.toolkit.DocFileElement; @@ -126,7 +116,17 @@ import jdk.javadoc.internal.doclets.toolkit.util.Utils.DeclarationPreviewLanguageFeatures; import jdk.javadoc.internal.doclets.toolkit.util.Utils.ElementFlag; import jdk.javadoc.internal.doclets.toolkit.util.Utils.PreviewSummary; -import jdk.javadoc.internal.doclint.HtmlTag; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.RawHtml; +import jdk.javadoc.internal.html.Script; +import jdk.javadoc.internal.html.Text; +import jdk.javadoc.internal.html.TextBuilder; import static com.sun.source.doctree.DocTree.Kind.COMMENT; import static com.sun.source.doctree.DocTree.Kind.START_ELEMENT; @@ -361,7 +361,7 @@ protected void addTagsInfo(Element e, Content content) { if (options.noComment()) { return; } - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); if (utils.isMethod(e)) { addMethodInfo((ExecutableElement)e, dl); } @@ -610,7 +610,7 @@ private String getHeadingText(Content c) { var contents = cb.getContents(); if (!contents.isEmpty()) { var first = contents.get(0); - if (first instanceof HtmlTree htmlTree && htmlTree.tagName.equals(TagName.H1)) { + if (first instanceof HtmlTree htmlTree && htmlTree.tag.equals(HtmlTag.H1)) { for (var c2 : htmlTree.getContents()) { if (c2 instanceof Text t) { sb.append(t.toString()); @@ -685,8 +685,8 @@ public HtmlTree getFooter() { return (bottom == null || bottom.isEmpty()) ? null : HtmlTree.FOOTER() - .add(new HtmlTree(TagName.HR)) - .add(HtmlTree.P(HtmlStyle.legalCopy, + .add(new HtmlTree(HtmlTag.HR)) + .add(HtmlTree.P(HtmlStyles.legalCopy, HtmlTree.SMALL( RawHtml.of(replaceDocRootDir(bottom))))); } @@ -917,7 +917,7 @@ public Content getTypeParameterLinks(HtmlLinkInfo linkInfo) { * @return the link */ public Content getCrossClassLink(TypeElement classElement, String refMemName, - Content label, HtmlStyle style, boolean code) { + Content label, HtmlStyle style, boolean code) { if (classElement != null) { String className = utils.getSimpleName(classElement); PackageElement packageElement = utils.containingPackage(classElement); @@ -1047,7 +1047,7 @@ public TypeElement getCurrentPageElement() { * @param content the content to which the link with be added */ public void addPreQualifiedStrongClassLink(HtmlLinkInfo.Kind context, TypeElement typeElement, Content content) { - addPreQualifiedClassLink(context, typeElement, HtmlStyle.typeNameLink, content); + addPreQualifiedClassLink(context, typeElement, HtmlStyles.typeNameLink, content); } /** @@ -1255,10 +1255,10 @@ private void addCommentTags(Element element, List tags, boole Content result = commentTagsToContent(element, tags, first, inSummary); if (!result.isEmpty()) { if (depr) { - div = HtmlTree.DIV(HtmlStyle.deprecationComment, result); + div = HtmlTree.DIV(HtmlStyles.deprecationComment, result); target.add(div); } else { - div = HtmlTree.DIV(HtmlStyle.block, result); + div = HtmlTree.DIV(HtmlStyles.block, result); target.add(div); } } @@ -1277,7 +1277,7 @@ boolean ignoreNonInlineTag(DocTree dtree, List openTags) { } if (name != null) { - HtmlTag htmlTag = HtmlTag.get(name); + HtmlTag htmlTag = HtmlTag.of(name); if (htmlTag != null) { if (htmlTag.blockType != HtmlTag.BlockType.INLINE) { return true; @@ -1944,9 +1944,9 @@ private boolean shouldRedirectRelativeLinks(Element element) { public Content invalidTagOutput(String summary, Optional detail) { messages.setContainsDiagnosticMarkers(); if (detail.isEmpty() || detail.get().isEmpty()) { - return HtmlTree.SPAN(HtmlStyle.invalidTag, Text.of(summary)); + return HtmlTree.SPAN(HtmlStyles.invalidTag, Text.of(summary)); } - return HtmlTree.DETAILS(HtmlStyle.invalidTag) + return HtmlTree.DETAILS(HtmlStyles.invalidTag) .add(HtmlTree.SUMMARY(Text.of(summary))) .add(HtmlTree.PRE(detail.get())); } @@ -2405,7 +2405,7 @@ static String getGenerator(Class clazz) { * @return an HtmlTree for the BODY tag */ public HtmlTree getBody(String title) { - var body = new HtmlTree(TagName.BODY).setStyle(getBodyStyle()); + var body = new HtmlTree(HtmlTag.BODY).setStyle(getBodyStyle()); this.winTitle = title; // Don't print windowtitle script for overview-frame, allclasses-frame @@ -2423,7 +2423,7 @@ public HtmlStyle getBodyStyle() { .replaceAll("^(Module|Package|Class)$", "$1Declaration") .replace("API", "Api"); String page = kind.substring(0, 1).toLowerCase(Locale.US) + kind.substring(1) + "Page"; - return HtmlStyle.valueOf(page); + return HtmlStyles.valueOf(page); } /** @@ -2473,16 +2473,16 @@ private List getStylesheets(Element element) throws DocFileIOException public void addPreviewSummary(Element forWhat, Content target) { if (utils.isPreviewAPI(forWhat)) { - var div = HtmlTree.DIV(HtmlStyle.block); - div.add(HtmlTree.SPAN(HtmlStyle.previewLabel, contents.previewPhrase)); + var div = HtmlTree.DIV(HtmlStyles.block); + div.add(HtmlTree.SPAN(HtmlStyles.previewLabel, contents.previewPhrase)); target.add(div); } } public void addRestrictedSummary(Element forWhat, Content target) { if (utils.isRestrictedAPI(forWhat)) { - var div = HtmlTree.DIV(HtmlStyle.block); - div.add(HtmlTree.SPAN(HtmlStyle.restrictedLabel, contents.restrictedPhrase)); + var div = HtmlTree.DIV(HtmlStyles.block); + div.add(HtmlTree.SPAN(HtmlStyles.restrictedLabel, contents.restrictedPhrase)); target.add(div); } } @@ -2490,7 +2490,7 @@ public void addRestrictedSummary(Element forWhat, Content target) { public void addPreviewInfo(Element forWhat, Content target) { if (utils.isPreviewAPI(forWhat)) { //in Java platform: - var previewDiv = HtmlTree.DIV(HtmlStyle.previewBlock); + var previewDiv = HtmlTree.DIV(HtmlStyles.previewBlock); previewDiv.setId(htmlIds.forPreviewSection(forWhat)); String name = (switch (forWhat.getKind()) { case PACKAGE, MODULE -> @@ -2506,14 +2506,14 @@ public void addPreviewInfo(Element forWhat, Content target) { : "doclet.ReflectivePreviewPlatformLeadingNote"; Content leadingNote = contents.getContent(leadingNoteKey, nameCode); - previewDiv.add(HtmlTree.SPAN(HtmlStyle.previewLabel, + previewDiv.add(HtmlTree.SPAN(HtmlStyles.previewLabel, leadingNote)); if (!isReflectivePreview) { Content note1 = contents.getContent("doclet.PreviewTrailingNote1", nameCode); - previewDiv.add(HtmlTree.DIV(HtmlStyle.previewComment, note1)); + previewDiv.add(HtmlTree.DIV(HtmlStyles.previewComment, note1)); } Content note2 = contents.getContent("doclet.PreviewTrailingNote2", nameCode); - previewDiv.add(HtmlTree.DIV(HtmlStyle.previewComment, note2)); + previewDiv.add(HtmlTree.DIV(HtmlStyles.previewComment, note2)); target.add(previewDiv); } else if (forWhat.getKind().isClass() || forWhat.getKind().isInterface()) { //in custom code: @@ -2521,12 +2521,12 @@ public void addPreviewInfo(Element forWhat, Content target) { if (!previewNotes.isEmpty()) { Name name = forWhat.getSimpleName(); var nameCode = HtmlTree.CODE(Text.of(name)); - var previewDiv = HtmlTree.DIV(HtmlStyle.previewBlock); + var previewDiv = HtmlTree.DIV(HtmlStyles.previewBlock); previewDiv.setId(htmlIds.forPreviewSection(forWhat)); Content leadingNote = contents.getContent("doclet.PreviewLeadingNote", nameCode); - previewDiv.add(HtmlTree.SPAN(HtmlStyle.previewLabel, + previewDiv.add(HtmlTree.SPAN(HtmlStyles.previewLabel, leadingNote)); - var ul = HtmlTree.UL(HtmlStyle.previewComment); + var ul = HtmlTree.UL(HtmlStyles.previewComment); for (Content note : previewNotes) { ul.add(HtmlTree.LI(note)); } @@ -2534,11 +2534,11 @@ public void addPreviewInfo(Element forWhat, Content target) { Content note1 = contents.getContent("doclet.PreviewTrailingNote1", nameCode); - previewDiv.add(HtmlTree.DIV(HtmlStyle.previewComment, note1)); + previewDiv.add(HtmlTree.DIV(HtmlStyles.previewComment, note1)); Content note2 = contents.getContent("doclet.PreviewTrailingNote2", name); - previewDiv.add(HtmlTree.DIV(HtmlStyle.previewComment, note2)); + previewDiv.add(HtmlTree.DIV(HtmlStyles.previewComment, note2)); target.add(previewDiv); } } @@ -2601,7 +2601,7 @@ private Content withPreviewFeatures(String key, String className, String feature }); return contents.getContent(key, HtmlTree.CODE(Text.of(className)), - new HtmlTree(TagName.EM).add(featureName), + new HtmlTree(HtmlTag.EM).add(featureName), featureCodes); } @@ -2640,19 +2640,19 @@ public URI resolveExternalSpecURI(URI specURI) { public void addRestrictedInfo(ExecutableElement forWhat, Content target) { if (utils.isRestrictedAPI(forWhat)) { //in Java platform: - var restrictedDiv = HtmlTree.DIV(HtmlStyle.restrictedBlock); + var restrictedDiv = HtmlTree.DIV(HtmlStyles.restrictedBlock); restrictedDiv.setId(htmlIds.forRestrictedSection(forWhat)); String name = forWhat.getSimpleName().toString(); var nameCode = HtmlTree.CODE(Text.of(name)); String leadingNoteKey = "doclet.RestrictedLeadingNote"; Content leadingNote = contents.getContent(leadingNoteKey, nameCode); - restrictedDiv.add(HtmlTree.SPAN(HtmlStyle.restrictedLabel, + restrictedDiv.add(HtmlTree.SPAN(HtmlStyles.restrictedLabel, leadingNote)); Content note1 = contents.getContent("doclet.RestrictedTrailingNote1", nameCode); - restrictedDiv.add(HtmlTree.DIV(HtmlStyle.restrictedComment, note1)); + restrictedDiv.add(HtmlTree.DIV(HtmlStyles.restrictedComment, note1)); Content note2 = contents.getContent("doclet.RestrictedTrailingNote2", nameCode); - restrictedDiv.add(HtmlTree.DIV(HtmlStyle.restrictedComment, note2)); + restrictedDiv.add(HtmlTree.DIV(HtmlStyles.restrictedComment, note2)); target.add(restrictedDiv); } } 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 48edce573d3c7..8b48c7a6f2262 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 @@ -33,6 +33,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; + import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -46,10 +47,10 @@ import javax.lang.model.type.TypeVariable; import javax.lang.model.util.SimpleTypeVisitor9; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; import jdk.javadoc.internal.doclets.toolkit.util.SummaryAPIListBuilder; import jdk.javadoc.internal.doclets.toolkit.util.Utils; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.HtmlId; /** * Centralized constants and factory methods for HTML ids. @@ -461,6 +462,22 @@ public static HtmlId forText(String text, Map counts) { return HtmlId.of(count == 0 ? base : base + "-" + count); } + /** + * Returns an id for text documenting a type parameter of a class or method. + * + * @param paramName the name of the type parameter + * @param owner the enclosing element + * + * @return the id + */ + public HtmlId forTypeParam(String paramName, Element owner) { + if (utils.isExecutableElement(owner)) { + return HtmlId.of(forMember((ExecutableElement) owner).getFirst().name() + + "-type-param-" + paramName); + } + return HtmlId.of("type-param-" + paramName); + } + /** * Returns an id for one of the kinds of section in the pages for item group summaries. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIndexBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIndexBuilder.java index 51831b233361b..fcc9640126a57 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIndexBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIndexBuilder.java @@ -30,11 +30,11 @@ import java.util.HashMap; import java.util.Map; import java.util.SortedSet; + import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.toolkit.Resources; import jdk.javadoc.internal.doclets.toolkit.util.DocFile; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; @@ -42,6 +42,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; +import jdk.javadoc.internal.html.HtmlTree; /** * Extensions to {@code IndexBuilder} to fill in remaining fields diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java index 87a842890a4e9..8e0c010dd1ae4 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkFactory.java @@ -43,17 +43,18 @@ import javax.lang.model.type.WildcardType; import javax.lang.model.util.SimpleTypeVisitor14; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; import jdk.javadoc.internal.doclets.toolkit.Resources; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.Utils; import jdk.javadoc.internal.doclets.toolkit.util.Utils.ElementFlag; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * A factory that returns a link given the information about it. @@ -161,9 +162,11 @@ public Content visitTypeVariable(TypeVariable type, HtmlLinkInfo linkInfo) { Element owner = typevariable.asElement().getEnclosingElement(); if (linkInfo.linkTypeParameters() && utils.isTypeElement(owner)) { linkInfo.setTypeElement((TypeElement) owner); - Content label = newContent(); - label.add(utils.getTypeName(type, false)); - linkInfo.label(label).skipPreview(true); + if (linkInfo.getLabel() == null || linkInfo.getLabel().isEmpty()) { + Content label = newContent(); + label.add(utils.getTypeName(type, false)); + linkInfo.label(label).skipPreview(true); + } link.add(getClassLink(linkInfo)); } else { // No need to link method type parameters. @@ -241,6 +244,11 @@ protected Content getClassLink(HtmlLinkInfo linkInfo) { boolean isTypeLink = linkInfo.getType() != null && utils.isTypeVariable(utils.getComponentType(linkInfo.getType())); title = getClassToolTip(typeElement, isTypeLink); + if (isTypeLink) { + linkInfo.fragment(m_writer.configuration.htmlIds.forTypeParam( + utils.getTypeName(utils.getComponentType(linkInfo.getType()), false), + typeElement).name()); + } } Content label = linkInfo.getClassLinkLabel(configuration); if (linkInfo.getContext() == HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_IN_LABEL) { @@ -377,14 +385,14 @@ protected Content getTypeParameterLinks(HtmlLinkInfo linkInfo) { } if (!vars.isEmpty()) { if (linkInfo.addLineBreakOpportunitiesInTypeParameters()) { - links.add(new HtmlTree(TagName.WBR)); + links.add(new HtmlTree(HtmlTag.WBR)); } links.add("<"); boolean many = false; for (TypeMirror t : vars) { if (many) { links.add(","); - links.add(new HtmlTree(TagName.WBR)); + links.add(new HtmlTree(HtmlTag.WBR)); if (linkInfo.addLineBreaksInTypeParameters()) { links.add(Text.NL); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkInfo.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkInfo.java index c2c79e850b846..0b7be8fe6562a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkInfo.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlLinkInfo.java @@ -31,11 +31,13 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.Text; /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java index e4543471a69bc..e449ecebf40a2 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java @@ -29,18 +29,18 @@ import java.util.Objects; import jdk.javadoc.internal.doclets.formats.html.markup.Head; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Script; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocFile; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Script; +import jdk.javadoc.internal.html.Text; /** * Writes a file that tries to redirect to an alternate page. @@ -92,7 +92,7 @@ public void buildPage() throws DocFileIOException { Script script = new Script("window.location.replace(") .appendStringLiteral(targetPath, '\'') .append(")"); - var metaRefresh = new HtmlTree(TagName.META) + var metaRefresh = new HtmlTree(HtmlTag.META) .put(HtmlAttr.HTTP_EQUIV, "Refresh") .put(HtmlAttr.CONTENT, "0;" + targetPath); head.addContent(script.asContent(), HtmlTree.NOSCRIPT(metaRefresh)); @@ -103,7 +103,7 @@ public void buildPage() throws DocFileIOException { bodyContent.add(HtmlTree.P(HtmlTree.A(targetPath, Text.of(targetPath)))); - var body = new HtmlTree(TagName.BODY).setStyle(HtmlStyle.indexRedirectPage); + var body = new HtmlTree(HtmlTag.BODY).setStyle(HtmlStyles.indexRedirectPage); var main = HtmlTree.MAIN(bodyContent); body.add(main); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexWriter.java index ff2761b8ecb9f..ecf1114364217 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexWriter.java @@ -36,22 +36,21 @@ import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; -import com.sun.source.doctree.DeprecatedTree; - -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generator for either a single index or split index for all @@ -129,7 +128,7 @@ public void buildPage() throws DocFileIOException { addLinksForIndexes(allFirstCharacters, mainContent); body.add(new BodyContents() .setHeader(getHeader(PageMode.INDEX)) - .addMainContent(HtmlTree.DIV(HtmlStyle.header, + .addMainContent(HtmlTree.DIV(HtmlStyles.header, HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, contents.getContent("doclet.Index")))) .addMainContent(mainContent) @@ -149,7 +148,7 @@ public void buildPage() throws DocFileIOException { protected void addContents(char ch, SortedSet items, Content content) { addHeading(ch, content); - var dl = HtmlTree.DL(HtmlStyle.index); + var dl = HtmlTree.DL(HtmlStyles.index); for (IndexItem item : items) { addDescription(item, dl); } @@ -164,7 +163,7 @@ protected void addContents(char ch, SortedSet items, Content content) */ protected void addHeading(char ch, Content content) { Content headContent = Text.of(String.valueOf(ch)); - var heading = HtmlTree.HEADING(Headings.CONTENT_HEADING, HtmlStyle.title, headContent) + var heading = HtmlTree.HEADING(Headings.CONTENT_HEADING, HtmlStyles.title, headContent) .setId(HtmlIds.forIndexChar(ch)); content.add(heading); } @@ -209,7 +208,7 @@ protected void addElementDescription(IndexItem item, Content target) { case CLASS, ENUM, RECORD, ANNOTATION_TYPE, INTERFACE -> { dt = HtmlTree.DT(getLink(new HtmlLinkInfo(configuration, - HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_IN_LABEL, (TypeElement) element).style(HtmlStyle.typeNameLink))); + HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_IN_LABEL, (TypeElement) element).style(HtmlStyles.typeNameLink))); dt.add(" - "); addClassInfo((TypeElement) element, dt); } @@ -217,7 +216,7 @@ protected void addElementDescription(IndexItem item, Content target) { case CONSTRUCTOR, METHOD, FIELD, ENUM_CONSTANT -> { TypeElement containingType = item.getContainingTypeElement(); dt = HtmlTree.DT(getDocLink(HtmlLinkInfo.Kind.PLAIN, containingType, element, - label, HtmlStyle.memberNameLink)); + label, HtmlStyles.memberNameLink)); dt.add(" - "); addMemberDesc(element, containingType, dt); } @@ -225,7 +224,7 @@ protected void addElementDescription(IndexItem item, Content target) { default -> throw new Error(); } target.add(dt); - var dd = new HtmlTree(TagName.DD); + var dd = new HtmlTree(HtmlTag.DD); if (element.getKind() == ElementKind.MODULE || element.getKind() == ElementKind.PACKAGE) { addSummaryComment(element, dd); } else { @@ -258,11 +257,11 @@ protected void addTagDescription(IndexItem item, Content target) { String itemPath = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/"; itemPath += item.getUrl(); var labelLink = HtmlTree.A(itemPath, Text.of(item.getLabel())); - var dt = HtmlTree.DT(labelLink.setStyle(HtmlStyle.searchTagLink)); + var dt = HtmlTree.DT(labelLink.setStyle(HtmlStyles.searchTagLink)); dt.add(" - "); dt.add(contents.getContent("doclet.Search_tag_in", item.getHolder())); target.add(dt); - var dd = new HtmlTree(TagName.DD); + var dd = new HtmlTree(HtmlTag.DD); if (item.getDescription().isEmpty()) { dd.add(Entity.NO_BREAK_SPACE); } else { @@ -281,8 +280,8 @@ protected void addTagDescription(IndexItem item, Content target) { * @param content the content to which the comment will be added */ protected void addComment(Element element, Content content) { - var span = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(element)); - var div = HtmlTree.DIV(HtmlStyle.deprecationBlock); + var span = HtmlTree.SPAN(HtmlStyles.deprecatedLabel, getDeprecatedPhrase(element)); + var div = HtmlTree.DIV(HtmlStyles.deprecationBlock); if (utils.isDeprecated(element)) { div.add(span); var tags = utils.getDeprecatedTrees(element); @@ -349,7 +348,7 @@ protected void addLinksForIndexes(List allFirstCharacters, Content co content.add(Entity.NO_BREAK_SPACE); } - content.add(new HtmlTree(TagName.BR)); + content.add(new HtmlTree(HtmlTag.BR)); var pageLinks = Stream.of(IndexItem.Category.values()) .flatMap(c -> mainIndex.getItems(c).stream()) .filter(i -> !(i.isElementItem() || i.isTagItem())) @@ -361,6 +360,6 @@ protected void addLinksForIndexes(List allFirstCharacters, Content co } private Content getVerticalSeparator() { - return HtmlTree.SPAN(HtmlStyle.verticalSeparator, Text.of("|")); + return HtmlTree.SPAN(HtmlStyles.verticalSeparator, Text.of("|")); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MarkerComments.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MarkerComments.java index 04888d7cc2f0f..78696cc320ea2 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MarkerComments.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MarkerComments.java @@ -25,7 +25,7 @@ package jdk.javadoc.internal.doclets.formats.html; -import jdk.javadoc.internal.doclets.formats.html.markup.Comment; +import jdk.javadoc.internal.html.Comment; /** * Marker comments to identify regions in the generated files. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java index 521616f982b52..9325bc07a6d99 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java @@ -35,16 +35,16 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.BaseOptions; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.Utils; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Writes method documentation in HTML format. @@ -107,7 +107,7 @@ protected void buildMethodDoc(Content detailsList) { for (Element method : methods) { currentMethod = (ExecutableElement)method; Content methodContent = getMethodHeader(currentMethod); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); buildSignature(div); buildDeprecationInfo(div); buildPreviewInfo(div); @@ -187,7 +187,7 @@ public Content getMemberSummaryHeader(Content target) { @Override public void buildSummary(Content summariesList, Content content) { - writer.addSummary(HtmlStyle.methodSummary, + writer.addSummary(HtmlStyles.methodSummary, HtmlIds.METHOD_SUMMARY, summariesList, content); } @@ -209,7 +209,7 @@ protected Content getMethodHeader(ExecutableElement method) { heading.setId(anchors.getLast()); } content.add(heading); - return HtmlTree.SECTION(HtmlStyle.detail, content) + return HtmlTree.SECTION(HtmlStyles.detail, content) .setId(anchors.getFirst()); } @@ -251,13 +251,13 @@ protected void addComments(TypeMirror holderType, ExecutableElement method, Cont ? utils.getSimpleName(holder) : utils.getFullyQualifiedName(holder)); var codeLink = HtmlTree.CODE(link); - var descriptionFromTypeLabel = HtmlTree.SPAN(HtmlStyle.descriptionFromTypeLabel, + var descriptionFromTypeLabel = HtmlTree.SPAN(HtmlStyles.descriptionFromTypeLabel, utils.isClass(holder) ? contents.descriptionFromClassLabel : contents.descriptionFromInterfaceLabel); descriptionFromTypeLabel.add(Entity.NO_BREAK_SPACE); descriptionFromTypeLabel.add(codeLink); - methodContent.add(HtmlTree.DIV(HtmlStyle.block, descriptionFromTypeLabel)); + methodContent.add(HtmlTree.DIV(HtmlStyles.block, descriptionFromTypeLabel)); } writer.addInlineComment(method, methodContent); } @@ -270,7 +270,7 @@ protected void addTags(ExecutableElement method, Content methodContent) { protected Content getMethodDetails(Content methodDetailsHeader, Content methodDetails) { Content c = new ContentBuilder(methodDetailsHeader, methodDetails); - return getMember(HtmlTree.SECTION(HtmlStyle.methodDetails, c) + return getMember(HtmlTree.SECTION(HtmlStyles.methodDetails, c) .setId(HtmlIds.METHOD_DETAIL)); } @@ -289,9 +289,9 @@ public TableHeader getSummaryTableHeader(Element member) { @Override protected Table createSummaryTable() { - return new Table(HtmlStyle.summaryTable) + return new Table(HtmlStyles.summaryTable) .setHeader(getSummaryTableHeader(typeElement)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast) + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colSecond, HtmlStyles.colLast) .setId(HtmlIds.METHOD_SUMMARY_TABLE) .setDefaultTab(contents.getContent("doclet.All_Methods")) .addTab(contents.getContent("doclet.Static_Methods"), utils::isStatic) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java index abe6159d1af8e..86e81b77ddd76 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java @@ -31,10 +31,11 @@ import javax.lang.model.element.ModuleElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Text; /** * Generate the module index page "index.html". @@ -78,9 +79,9 @@ protected void addIndex(Content target) { if (!groupModuleMap.keySet().isEmpty()) { TableHeader tableHeader = new TableHeader(contents.moduleLabel, contents.descriptionLabel); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setHeader(tableHeader) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast) + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast) .setId(HtmlIds.ALL_MODULES_TABLE) .setDefaultTab(contents.getContent("doclet.All_Modules")); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriter.java index d2f2206d27fd7..bd0ab2958d4e0 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriter.java @@ -41,18 +41,21 @@ import com.sun.source.doctree.DeprecatedTree; import com.sun.source.doctree.DocTree; + import jdk.javadoc.doclet.DocletEnvironment.ModuleMode; -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Class to generate file for each module contents in the right-hand frame. This will list all the @@ -191,8 +194,8 @@ protected void buildModuleDoc() throws DocletException { */ protected void buildContent() { Content moduleContent = getContentHeader(); - moduleContent.add(new HtmlTree(TagName.HR)); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + moduleContent.add(new HtmlTree(HtmlTag.HR)); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); addModuleSignature(div); buildModuleDescription(div); moduleContent.add(div); @@ -261,13 +264,13 @@ protected void buildModuleDescription(Content moduleContent) { protected Content getModuleHeader(String heading) { HtmlTree body = getBody(getWindowTitle(mdle.getQualifiedName().toString())); - var div = HtmlTree.DIV(HtmlStyle.header); + var div = HtmlTree.DIV(HtmlStyles.header); Content moduleHead = new ContentBuilder(); moduleHead.add(mdle.isOpen() && (configuration.docEnv.getModuleMode() == ModuleMode.ALL) ? contents.openModuleLabel : contents.moduleLabel); moduleHead.add(" ").add(heading); var tHeading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, moduleHead); + HtmlStyles.title, moduleHead); div.add(tHeading); bodyContents.setHeader(getHeader(PageMode.MODULE, mdle)) .addMainContent(div); @@ -279,11 +282,11 @@ protected Content getContentHeader() { } protected Content getSummariesList() { - return HtmlTree.UL(HtmlStyle.summaryList); + return HtmlTree.UL(HtmlStyles.summaryList); } protected Content getSummary(Content source) { - return HtmlTree.SECTION(HtmlStyle.summary, source); + return HtmlTree.SECTION(HtmlStyles.summary, source); } /** @@ -502,10 +505,10 @@ public void addSummaryHeader(Content startMarker, Content heading, * @return a content object */ private Table getTable2(Content caption, TableHeader tableHeader) { - return new Table(HtmlStyle.detailsTable) + return new Table(HtmlStyles.detailsTable) .setCaption(caption) .setHeader(tableHeader) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); } /** @@ -516,10 +519,10 @@ private Table getTable2(Content caption, TableHeader tableHeader) { * @return a content object */ private Table getTable3(Content caption, TableHeader tableHeader) { - return new Table(HtmlStyle.detailsTable) + return new Table(HtmlStyles.detailsTable) .setCaption(caption) .setHeader(tableHeader) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colSecond, HtmlStyles.colLast); } protected void addModulesSummary(Content summariesList) { @@ -528,7 +531,7 @@ protected void addModulesSummary(Content summariesList) { TableHeader requiresTableHeader = new TableHeader(contents.modifierLabel, contents.moduleLabel, contents.descriptionLabel); - var section = HtmlTree.SECTION(HtmlStyle.modulesSummary) + var section = HtmlTree.SECTION(HtmlStyles.modulesSummary) .setId(HtmlIds.MODULES); addSummaryHeader(MarkerComments.START_OF_MODULES_SUMMARY, contents.navModules, section); if (display(requires)) { @@ -570,7 +573,7 @@ protected void addPackagesSummary(Content summariesList) { if (display(packages) || display(indirectPackages) || display(indirectOpenPackages)) { tableOfContents.addLink(HtmlIds.PACKAGES, contents.navPackages); - var section = HtmlTree.SECTION(HtmlStyle.packagesSummary) + var section = HtmlTree.SECTION(HtmlStyles.packagesSummary) .setId(HtmlIds.PACKAGES); addSummaryHeader(MarkerComments.START_OF_PACKAGES_SUMMARY, contents.navPackages, section); if (display(packages)) { @@ -600,7 +603,7 @@ protected void addPackagesSummary(Content summariesList) { * @param li the tree to which the summary will be added */ public void addPackageSummary(HtmlTree li) { - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setId(HtmlIds.PACKAGE_SUMMARY_TABLE) .setDefaultTab(contents.getContent("doclet.All_Packages")) .addTab(contents.getContent("doclet.Exported_Packages_Summary"), this::isExported) @@ -636,20 +639,20 @@ public void addPackageSummary(HtmlTree li) { List colHeaders = new ArrayList<>(); List colStyles = new ArrayList<>(); colHeaders.add(contents.packageLabel); - colStyles.add(HtmlStyle.colFirst); + colStyles.add(HtmlStyles.colFirst); if (showExportedTo) { colHeaders.add(contents.exportedTo); - colStyles.add(HtmlStyle.colSecond); + colStyles.add(HtmlStyles.colSecond); } if (showOpenedTo) { colHeaders.add(contents.openedTo); - colStyles.add(HtmlStyle.colSecond); + colStyles.add(HtmlStyles.colSecond); } colHeaders.add(contents.descriptionLabel); - colStyles.add(HtmlStyle.colLast); + colStyles.add(HtmlStyles.colLast); table.setHeader(new TableHeader(colHeaders).styles(colStyles)) .setColumnStyles(colStyles); @@ -741,7 +744,7 @@ protected void addServicesSummary(Content summariesList) { if (haveProvides || haveUses) { tableOfContents.addLink(HtmlIds.SERVICES, contents.navServices); - var section = HtmlTree.SECTION(HtmlStyle.servicesSummary) + var section = HtmlTree.SECTION(HtmlStyles.servicesSummary) .setId(HtmlIds.SERVICES); addSummaryHeader(MarkerComments.START_OF_SERVICES_SUMMARY, contents.navServices, section); TableHeader usesProvidesTableHeader = @@ -783,7 +786,7 @@ public void addUsesList(Table table) { if (display(usesTrees)) { description = usesTrees.get(t); if (description != null && !description.isEmpty()) { - summary.add(HtmlTree.DIV(HtmlStyle.block, description)); + summary.add(HtmlTree.DIV(HtmlStyles.block, description)); } else { addSummaryComment(t, summary); } @@ -813,7 +816,7 @@ public void addProvidesList(Table table) { if (display(providesTrees)) { description = providesTrees.get(srv); if (description != null && !description.isEmpty()) { - desc.add(HtmlTree.DIV(HtmlStyle.block, description)); + desc.add(HtmlTree.DIV(HtmlStyles.block, description)); } else { addSummaryComment(srv, desc); } @@ -822,9 +825,9 @@ public void addProvidesList(Table table) { } // Only display the implementation details in the "all" mode. if (moduleMode == ModuleMode.ALL && !implSet.isEmpty()) { - desc.add(new HtmlTree(TagName.BR)); + desc.add(new HtmlTree(HtmlTag.BR)); desc.add("("); - var implSpan = HtmlTree.SPAN(HtmlStyle.implementationLabel, contents.implementation); + var implSpan = HtmlTree.SPAN(HtmlStyles.implementationLabel, contents.implementation); desc.add(implSpan); desc.add(Entity.NO_BREAK_SPACE); String sep = ""; @@ -848,8 +851,8 @@ public void addDeprecationInfo(Content div) { List deprs = utils.getDeprecatedTrees(mdle); if (utils.isDeprecated(mdle)) { CommentHelper ch = utils.getCommentHelper(mdle); - var deprDiv = HtmlTree.DIV(HtmlStyle.deprecationBlock); - var deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(mdle)); + var deprDiv = HtmlTree.DIV(HtmlStyles.deprecationBlock); + var deprPhrase = HtmlTree.SPAN(HtmlStyles.deprecatedLabel, getDeprecatedPhrase(mdle)); deprDiv.add(deprPhrase); if (!deprs.isEmpty()) { List commentTags = ch.getDescription(deprs.get(0)); @@ -864,7 +867,7 @@ public void addDeprecationInfo(Content div) { protected void addModuleDescription(Content moduleContent) { addPreviewInfo(mdle, moduleContent); if (!utils.getFullBody(mdle).isEmpty()) { - var tree = HtmlTree.SECTION(HtmlStyle.moduleDescription) + var tree = HtmlTree.SECTION(HtmlStyles.moduleDescription) .setId(HtmlIds.MODULE_DESCRIPTION); addDeprecationInfo(tree); tree.add(MarkerComments.START_OF_MODULE_DESCRIPTION); @@ -902,8 +905,8 @@ protected void printDocument(Content content) throws DocFileIOException { public void addPackageDeprecationInfo(Content li, PackageElement pkg) { if (utils.isDeprecated(pkg)) { List deprs = utils.getDeprecatedTrees(pkg); - var deprDiv = HtmlTree.DIV(HtmlStyle.deprecationBlock); - var deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(pkg)); + var deprDiv = HtmlTree.DIV(HtmlStyles.deprecationBlock); + var deprPhrase = HtmlTree.SPAN(HtmlStyles.deprecatedLabel, getDeprecatedPhrase(pkg)); deprDiv.add(deprPhrase); if (!deprs.isEmpty()) { CommentHelper ch = utils.getCommentHelper(pkg); 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 63c18084c1077..293a6453925f6 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 @@ -35,19 +35,19 @@ import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.formats.html.markup.Links; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; import jdk.javadoc.internal.doclets.toolkit.util.DocFile; import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; -import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Factory for navigation bar. @@ -326,7 +326,7 @@ private void addItemToList(Content list, Content item) { private void addActivePageLink(Content target, Content label, boolean display) { if (display) { - target.add(HtmlTree.LI(HtmlStyle.navBarCell1Rev, label)); + target.add(HtmlTree.LI(HtmlStyles.navBarCell1Rev, label)); } } @@ -409,7 +409,7 @@ protected void addBreadcrumbLinks(Element elem, List contents, boolean default -> throw new IllegalArgumentException(Objects.toString(elem)); }; if (selected) { - link.setStyle(HtmlStyle.currentSelection); + link.setStyle(HtmlStyles.currentSelection); } contents.add(link); } @@ -499,7 +499,7 @@ private void addSearch(Content target) { .put(HtmlAttr.AUTOCOMPLETE, "off"); var inputReset = HtmlTree.INPUT(HtmlAttr.InputType.RESET, HtmlIds.RESET_SEARCH) .put(HtmlAttr.VALUE, resources.getText("doclet.search_reset")); - var searchDiv = HtmlTree.DIV(HtmlStyle.navListSearch) + var searchDiv = HtmlTree.DIV(HtmlStyles.navListSearch) .add(inputText) .add(inputReset); target.add(searchDiv); @@ -516,36 +516,36 @@ public Content getContent() { } var navigationBar = HtmlTree.NAV(); - var navContent = new HtmlTree(TagName.DIV); + var navContent = new HtmlTree(HtmlTag.DIV); Content skipNavLinks = contents.getContent("doclet.Skip_navigation_links"); String toggleNavLinks = configuration.getDocResources().getText("doclet.Toggle_navigation_links"); navigationBar.add(MarkerComments.START_OF_TOP_NAVBAR); // The mobile menu button uses three empty spans to produce its animated icon - HtmlTree iconSpan = HtmlTree.SPAN(HtmlStyle.navBarToggleIcon).add(Entity.NO_BREAK_SPACE); - navContent.setStyle(HtmlStyle.navContent).add(HtmlTree.DIV(HtmlStyle.navMenuButton, - new HtmlTree(TagName.BUTTON).setId(HtmlIds.NAVBAR_TOGGLE_BUTTON) + HtmlTree iconSpan = HtmlTree.SPAN(HtmlStyles.navBarToggleIcon).add(Entity.NO_BREAK_SPACE); + navContent.setStyle(HtmlStyles.navContent).add(HtmlTree.DIV(HtmlStyles.navMenuButton, + new HtmlTree(HtmlTag.BUTTON).setId(HtmlIds.NAVBAR_TOGGLE_BUTTON) .put(HtmlAttr.ARIA_CONTROLS, HtmlIds.NAVBAR_TOP.name()) .put(HtmlAttr.ARIA_EXPANDED, String.valueOf(false)) .put(HtmlAttr.ARIA_LABEL, toggleNavLinks) .add(iconSpan) .add(iconSpan) .add(iconSpan))) - .add(HtmlTree.DIV(HtmlStyle.skipNav, + .add(HtmlTree.DIV(HtmlStyles.skipNav, links.createLink(HtmlIds.SKIP_NAVBAR_TOP, skipNavLinks, skipNavLinks.toString()))); Content aboutContent = userHeader; - var navList = new HtmlTree(TagName.UL) + var navList = new HtmlTree(HtmlTag.UL) .setId(HtmlIds.NAVBAR_TOP_FIRSTROW) - .setStyle(HtmlStyle.navList) + .setStyle(HtmlStyles.navList) .put(HtmlAttr.TITLE, rowListTitle); addMainNavLinks(navList); navContent.add(navList); - var aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, aboutContent); + var aboutDiv = HtmlTree.DIV(HtmlStyles.aboutLanguage, aboutContent); navContent.add(aboutDiv); - navigationBar.add(HtmlTree.DIV(HtmlStyle.topNav, navContent).setId(HtmlIds.NAVBAR_TOP)); + navigationBar.add(HtmlTree.DIV(HtmlStyles.topNav, navContent).setId(HtmlIds.NAVBAR_TOP)); - var subNavContent = HtmlTree.DIV(HtmlStyle.navContent); + var subNavContent = HtmlTree.DIV(HtmlStyles.navContent); List subNavLinks = new ArrayList<>(); switch (documentedPage) { case MODULE, PACKAGE, CLASS, USE, DOC_FILE, TREE -> { @@ -553,17 +553,17 @@ public Content getContent() { } } // Add the breadcrumb navigation links if present. - var breadcrumbNav = HtmlTree.OL(HtmlStyle.subNavList); + var breadcrumbNav = HtmlTree.OL(HtmlStyles.subNavList); breadcrumbNav.addAll(subNavLinks, HtmlTree::LI); subNavContent.addUnchecked(breadcrumbNav); if (options.createIndex() && documentedPage != PageMode.SEARCH) { addSearch(subNavContent); } - navigationBar.add(HtmlTree.DIV(HtmlStyle.subNav, subNavContent)); + navigationBar.add(HtmlTree.DIV(HtmlStyles.subNav, subNavContent)); navigationBar.add(MarkerComments.END_OF_TOP_NAVBAR); - navigationBar.add(HtmlTree.SPAN(HtmlStyle.skipNav) + navigationBar.add(HtmlTree.SPAN(HtmlStyles.skipNav) .addUnchecked(Text.EMPTY) .setId(HtmlIds.SKIP_NAVBAR_TOP)); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NestedClassWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NestedClassWriter.java index 4534f707c38b7..12d4dcbd6638d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NestedClassWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NestedClassWriter.java @@ -31,12 +31,14 @@ import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Writes nested class documentation in HTML format. @@ -71,7 +73,7 @@ public Content getMemberSummaryHeader(Content content) { @Override public void buildSummary(Content summariesList, Content content) { - writer.addSummary(HtmlStyle.nestedClassSummary, + writer.addSummary(HtmlStyles.nestedClassSummary, HtmlIds.NESTED_CLASS_SUMMARY, summariesList, content); } @@ -92,10 +94,10 @@ public TableHeader getSummaryTableHeader(Element member) { @Override protected Table createSummaryTable() { - List bodyRowStyles = Arrays.asList(HtmlStyle.colFirst, HtmlStyle.colSecond, - HtmlStyle.colLast); + List bodyRowStyles = Arrays.asList(HtmlStyles.colFirst, HtmlStyles.colSecond, + HtmlStyles.colLast); - return new Table(HtmlStyle.summaryTable) + return new Table(HtmlStyles.summaryTable) .setCaption(contents.getContent("doclet.Nested_Classes")) .setHeader(getSummaryTableHeader(typeElement)) .setColumnStyles(bodyRowStyles); @@ -125,7 +127,7 @@ public void addInheritedSummaryLabel(TypeElement typeElement, Content content) { protected void addSummaryLink(HtmlLinkInfo.Kind context, TypeElement typeElement, Element member, Content content) { Content memberLink = writer.getLink(new HtmlLinkInfo(configuration, context, (TypeElement)member) - .style(HtmlStyle.typeNameLink)); + .style(HtmlStyles.typeNameLink)); var code = HtmlTree.CODE(memberLink); content.add(code); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NewAPIListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NewAPIListWriter.java index f46765933cfdb..68066b0c92be5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NewAPIListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NewAPIListWriter.java @@ -32,14 +32,14 @@ import com.sun.source.doctree.SinceTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.NewAPIBuilder; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; import static com.sun.source.doctree.DocTree.Kind.SINCE; @@ -81,7 +81,7 @@ protected String getTitleKey() { protected void addContentSelectors(Content content) { List releases = builder.releases; if (releases.size() > 1) { - Content tabs = HtmlTree.DIV(HtmlStyle.checkboxes, + Content tabs = HtmlTree.DIV(HtmlStyles.checkboxes, contents.getContent("doclet.New_API_Checkbox_Label")); // Table column ids are 1-based int index = 1; @@ -96,7 +96,7 @@ protected void addContentSelectors(Content content) { @Override protected void addTableTabs(Table table, String headingKey) { - table.setGridStyle(HtmlStyle.threeColumnReleaseSummary); + table.setGridStyle(HtmlStyles.threeColumnReleaseSummary); List releases = builder.releases; if (releases.size() > 1) { table.setDefaultTab(getTableCaption(headingKey)) @@ -149,7 +149,7 @@ protected TableHeader getTableHeader(String headerKey) { @Override protected HtmlStyle[] getColumnStyles() { - return new HtmlStyle[]{ HtmlStyle.colSummaryItemName, HtmlStyle.colSecond, HtmlStyle.colLast }; + return new HtmlStyle[]{ HtmlStyles.colSummaryItemName, HtmlStyles.colSecond, HtmlStyles.colLast }; } private static String getHeading(HtmlConfiguration configuration) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java index ef7f25e7388d4..fd257fc06f48e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java @@ -31,11 +31,12 @@ import javax.lang.model.element.PackageElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.Group; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Text; /** * Generate the package index page "index.html". @@ -81,9 +82,9 @@ protected void addIndex(Content target) { = configuration.group.groupPackages(packages); if (!groupPackageMap.keySet().isEmpty()) { - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setHeader(getPackageTableHeader()) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast) + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast) .setId(HtmlIds.ALL_PACKAGES_TABLE) .setDefaultTab(contents.getContent("doclet.All_Packages")); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageTreeWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageTreeWriter.java index cba27fd64faa3..8ff3cf9779717 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageTreeWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageTreeWriter.java @@ -25,21 +25,17 @@ package jdk.javadoc.internal.doclets.formats.html; -import javax.lang.model.element.Element; import javax.lang.model.element.PackageElement; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; - -import java.util.ArrayList; -import java.util.List; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; /** @@ -100,8 +96,8 @@ public void buildPage() throws DocFileIOException { : contents.getContent("doclet.Hierarchy_For_Package", getLocalizedPackageName(packageElement)); var heading = HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, headContent); - var div = HtmlTree.DIV(HtmlStyle.header, heading); + HtmlStyles.title, headContent); + var div = HtmlTree.DIV(HtmlStyles.header, heading); mainContent.add(div); if (configuration.packages.size() > 1) { addLinkToAllPackages(mainContent); @@ -136,10 +132,10 @@ protected HtmlTree getPackageTreeHeader() { * @param target the content to which the link will be added */ protected void addLinkToAllPackages(Content target) { - var span = HtmlTree.SPAN(HtmlStyle.packageHierarchyLabel, + var span = HtmlTree.SPAN(HtmlStyles.packageHierarchyLabel, contents.packageHierarchies); target.add(span); - var ul = HtmlTree.UL(HtmlStyle.horizontal).addStyle(HtmlStyle.contentsList); + var ul = HtmlTree.UL(HtmlStyles.horizontal).addStyle(HtmlStyles.contentsList); // TODO the link should be more specific: // it should point to the "all packages" section of the overview tree ul.add(getNavLinkToOverviewTree(resources.getText("doclet.All_Packages"))); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageUseWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageUseWriter.java index 848e7069d66d7..b28b44d4cd3a6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageUseWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageUseWriter.java @@ -25,28 +25,26 @@ package jdk.javadoc.internal.doclets.formats.html; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; -import javax.lang.model.element.Element; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.ClassUseMapper; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generate package usage information. @@ -138,10 +136,10 @@ protected void addPackageList(Content content) { Content caption = contents.getContent( "doclet.ClassUse_Packages.that.use.0", getPackageLink(packageElement, getLocalizedPackageName(packageElement))); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(caption) .setHeader(getPackageTableHeader()) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); for (String pkgname: usingPackageToUsedClasses.keySet()) { PackageElement pkg = utils.elementUtils.getPackageElement(pkgname); Content packageLink = links.createLink(htmlIds.forPackage(pkg), @@ -165,19 +163,19 @@ protected void addPackageList(Content content) { protected void addClassList(Content content) { TableHeader classTableHeader = new TableHeader( contents.classLabel, contents.descriptionLabel); - var ul = HtmlTree.UL(HtmlStyle.blockList); + var ul = HtmlTree.UL(HtmlStyles.blockList); for (String packageName : usingPackageToUsedClasses.keySet()) { PackageElement usingPackage = utils.elementUtils.getPackageElement(packageName); - var section = HtmlTree.SECTION(HtmlStyle.detail) + var section = HtmlTree.SECTION(HtmlStyles.detail) .setId(htmlIds.forPackage(usingPackage)); Content caption = contents.getContent( "doclet.ClassUse_Classes.in.0.used.by.1", getPackageLink(packageElement, getLocalizedPackageName(packageElement)), getPackageLink(usingPackage, getLocalizedPackageName(usingPackage))); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(caption) .setHeader(classTableHeader) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); for (TypeElement te : usingPackageToUsedClasses.get(packageName)) { DocPath dp = pathString(te, DocPaths.CLASS_USE.resolve(docPaths.forName(te))); @@ -192,7 +190,7 @@ protected void addClassList(Content content) { section.add(table); ul.add(HtmlTree.LI(section)); } - var li = HtmlTree.SECTION(HtmlStyle.packageUses, ul); + var li = HtmlTree.SECTION(HtmlStyles.packageUses, ul); content.add(li); } @@ -206,11 +204,11 @@ private HtmlTree getBody() { HtmlTree body = getBody(getWindowTitle(title)); ContentBuilder headingContent = new ContentBuilder(); headingContent.add(contents.getContent("doclet.ClassUse_Title", packageText)); - headingContent.add(new HtmlTree(TagName.BR)); + headingContent.add(new HtmlTree(HtmlTag.BR)); headingContent.add(name); var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, headingContent); - var div = HtmlTree.DIV(HtmlStyle.header, heading); + HtmlStyles.title, headingContent); + var div = HtmlTree.DIV(HtmlStyles.header, heading); bodyContents.setHeader(getHeader(PageMode.USE, packageElement)) .addMainContent(div); return body; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriter.java index c70386c6b3666..ee022144fea57 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriter.java @@ -33,25 +33,26 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.lang.model.element.Element; import javax.lang.model.element.ModuleElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DeprecatedTree; import com.sun.source.doctree.DocTree; -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; + import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Class to generate file for each package contents in the right-hand @@ -71,7 +72,7 @@ public class PackageWriter extends HtmlDocletWriter { /** * The HTML element for the section tag being written. */ - private final HtmlTree section = HtmlTree.SECTION(HtmlStyle.packageDescription, new ContentBuilder()); + private final HtmlTree section = HtmlTree.SECTION(HtmlStyles.packageDescription, new ContentBuilder()); private final BodyContents bodyContents = new BodyContents(); @@ -127,8 +128,8 @@ protected void buildPackageDoc() throws DocletException { */ protected void buildContent() { Content packageContent = getContentHeader(); - packageContent.add(new HtmlTree(TagName.HR)); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + packageContent.add(new HtmlTree(HtmlTag.HR)); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); addPackageSignature(div); buildPackageDescription(div); buildPackageTags(div); @@ -202,14 +203,14 @@ protected void buildPackageTags(Content packageContent) { protected Content getPackageHeader() { String packageName = getLocalizedPackageName(packageElement).toString(); HtmlTree body = getBody(getWindowTitle(packageName)); - var div = HtmlTree.DIV(HtmlStyle.header); + var div = HtmlTree.DIV(HtmlStyles.header); Content packageHead = new ContentBuilder(); if (!packageElement.isUnnamed()) { packageHead.add(contents.packageLabel).add(" "); } packageHead.add(packageName); var tHeading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, packageHead); + HtmlStyles.title, packageHead); div.add(tHeading); bodyContents.setHeader(getHeader(PageMode.PACKAGE, packageElement)) .addMainContent(div); @@ -277,8 +278,8 @@ public void addDeprecationInfo(Content div) { List deprs = utils.getDeprecatedTrees(packageElement); if (utils.isDeprecated(packageElement)) { CommentHelper ch = utils.getCommentHelper(packageElement); - var deprDiv = HtmlTree.DIV(HtmlStyle.deprecationBlock); - var deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(packageElement)); + var deprDiv = HtmlTree.DIV(HtmlStyles.deprecationBlock); + var deprPhrase = HtmlTree.SPAN(HtmlStyles.deprecatedLabel, getDeprecatedPhrase(packageElement)); deprDiv.add(deprPhrase); if (!deprs.isEmpty()) { List commentTags = ch.getDescription(deprs.get(0)); @@ -291,7 +292,7 @@ public void addDeprecationInfo(Content div) { } protected Content getSummariesList() { - return HtmlTree.UL(HtmlStyle.summaryList); + return HtmlTree.UL(HtmlStyles.summaryList); } protected void addRelatedPackagesSummary(Content summaryContent) { @@ -308,9 +309,9 @@ protected void addRelatedPackagesSummary(Content summaryContent) { * @param target the content to which the links will be added */ public void addAllClassesAndInterfacesSummary(Content target) { - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setHeader(new TableHeader(contents.classLabel, contents.descriptionLabel)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast) + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast) .setId(HtmlIds.CLASS_SUMMARY) .setDefaultTab(contents.allClassesAndInterfacesLabel) .addTab(contents.interfaces, utils::isPlainInterface) @@ -347,14 +348,14 @@ protected void addRelatedPackageSummary(TableHeader tableHeader, Content summary boolean showModules) { if (!relatedPackages.isEmpty()) { tableOfContents.addLink(HtmlIds.RELATED_PACKAGE_SUMMARY, contents.relatedPackages); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setId(HtmlIds.RELATED_PACKAGE_SUMMARY) .setCaption(contents.relatedPackages) .setHeader(tableHeader); if (showModules) { - table.setColumnStyles(HtmlStyle.colPlain, HtmlStyle.colFirst, HtmlStyle.colLast); + table.setColumnStyles(HtmlStyles.colPlain, HtmlStyles.colFirst, HtmlStyles.colLast); } else { - table.setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + table.setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); } for (PackageElement pkg : relatedPackages) { @@ -423,7 +424,7 @@ protected void printDocument(Content content) throws DocFileIOException { } protected Content getPackageSummary(Content summaryContent) { - return HtmlTree.SECTION(HtmlStyle.summary, summaryContent); + return HtmlTree.SECTION(HtmlStyles.summary, summaryContent); } private boolean hasRelatedPackagesInOtherModules(List relatedPackages) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java index 2141f1e7be734..4fb0d427ff965 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java @@ -33,12 +33,14 @@ import com.sun.source.doctree.DocTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.PreviewAPIListBuilder; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generate File to list all the preview elements with the @@ -81,7 +83,7 @@ protected void addContentSelectors(Content target) { if (!jeps.isEmpty()) { int index = 1; target.add(HtmlTree.P(contents.getContent("doclet.Preview_API_Checkbox_Label"))); - Content list = HtmlTree.UL(HtmlStyle.previewFeatureList).addStyle(HtmlStyle.checkboxes); + Content list = HtmlTree.UL(HtmlStyles.previewFeatureList).addStyle(HtmlStyles.checkboxes); for (var jep : jeps) { String jepUrl = resources.getText("doclet.Preview_JEP_URL", String.valueOf(jep.number())); Content label = new ContentBuilder(Text.of(jep.number() + ": ")) @@ -106,7 +108,7 @@ protected void addComments(Element e, Content desc) { @Override protected void addTableTabs(Table table, String headingKey) { - table.setGridStyle(HtmlStyle.threeColumnSummary) + table.setGridStyle(HtmlStyles.threeColumnSummary) .setDefaultTab(getTableCaption(headingKey)) .setRenderTabs(false); for (PreviewAPIListBuilder.JEP jep : builder.getJEPs()) { @@ -131,6 +133,6 @@ protected TableHeader getTableHeader(String headerKey) { @Override protected HtmlStyle[] getColumnStyles() { - return new HtmlStyle[]{ HtmlStyle.colSummaryItemName, HtmlStyle.colSecond, HtmlStyle.colLast }; + return new HtmlStyle[]{ HtmlStyles.colSummaryItemName, HtmlStyles.colSecond, HtmlStyles.colLast }; } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java index 41a59c8a30c5c..7365b9b3b8f9a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriter.java @@ -35,14 +35,15 @@ import com.sun.source.doctree.DocCommentTree; import com.sun.source.doctree.DocTree; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.BaseOptions; import jdk.javadoc.internal.doclets.toolkit.CommentUtils; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Writes property documentation in HTML format. @@ -80,7 +81,7 @@ protected void buildPropertyDoc(Content detailsList) { for (Element property : properties) { currentProperty = (ExecutableElement)property; Content propertyContent = getPropertyHeaderContent(currentProperty); - Content div = HtmlTree.DIV(HtmlStyle.horizontalScroll); + Content div = HtmlTree.DIV(HtmlStyles.horizontalScroll); buildSignature(div); buildDeprecationInfo(div); buildPreviewInfo(div); @@ -161,7 +162,7 @@ public Content getMemberSummaryHeader(Content content) { @Override public void buildSummary(Content summariesList, Content content) { - writer.addSummary(HtmlStyle.propertySummary, + writer.addSummary(HtmlStyles.propertySummary, HtmlIds.PROPERTY_SUMMARY, summariesList, content); } @@ -179,7 +180,7 @@ protected Content getPropertyHeaderContent(ExecutableElement property) { var heading = HtmlTree.HEADING(Headings.TypeDeclaration.MEMBER_HEADING, Text.of(utils.getPropertyLabel(name(property)))); content.add(heading); - return HtmlTree.SECTION(HtmlStyle.detail, content) + return HtmlTree.SECTION(HtmlStyles.detail, content) .setId(htmlIds.forProperty(property)); } @@ -210,13 +211,13 @@ protected void addComments(ExecutableElement property, Content propertyContent) utils.isIncluded(holder) ? holder.getSimpleName() : holder.getQualifiedName()); var codeLink = HtmlTree.CODE(link); - var descriptionFromLabel = HtmlTree.SPAN(HtmlStyle.descriptionFromTypeLabel, + var descriptionFromLabel = HtmlTree.SPAN(HtmlStyles.descriptionFromTypeLabel, utils.isClass(holder) ? contents.descriptionFromClassLabel : contents.descriptionFromInterfaceLabel); descriptionFromLabel.add(Entity.NO_BREAK_SPACE); descriptionFromLabel.add(codeLink); - propertyContent.add(HtmlTree.DIV(HtmlStyle.block, descriptionFromLabel)); + propertyContent.add(HtmlTree.DIV(HtmlStyles.block, descriptionFromLabel)); } writer.addInlineComment(property, propertyContent); } @@ -229,7 +230,7 @@ protected void addTags(ExecutableElement property, Content propertyContent) { protected Content getPropertyDetails(Content memberDetailsHeader, Content memberDetails) { return writer.getDetailsListItem( - HtmlTree.SECTION(HtmlStyle.propertyDetails) + HtmlTree.SECTION(HtmlStyles.propertyDetails) .setId(HtmlIds.PROPERTY_DETAIL) .add(memberDetailsHeader) .add(memberDetails)); @@ -250,10 +251,10 @@ public TableHeader getSummaryTableHeader(Element member) { @Override protected Table createSummaryTable() { - return new Table(HtmlStyle.summaryTable) + return new Table(HtmlStyles.summaryTable) .setCaption(contents.properties) .setHeader(getSummaryTableHeader(typeElement)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colSecond, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colSecond, HtmlStyles.colLast); } @Override @@ -284,7 +285,7 @@ protected void addSummaryLink(HtmlLinkInfo.Kind context, TypeElement typeElement Content memberLink = writer.getDocLink(context, typeElement, member, Text.of(utils.getPropertyLabel(name(member))), - HtmlStyle.memberNameLink, + HtmlStyles.memberNameLink, true); var code = HtmlTree.CODE(memberLink); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/RestrictedListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/RestrictedListWriter.java index bfd062706a98f..ec629a227a328 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/RestrictedListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/RestrictedListWriter.java @@ -31,6 +31,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; import jdk.javadoc.internal.doclets.toolkit.util.RestrictedAPIListBuilder; +import jdk.javadoc.internal.html.Content; /** * Generate File to list all the restricted methods with the diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java index 8a2f0e2910939..0bd173df3d5f0 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java @@ -27,15 +27,16 @@ import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generates the search landing page for the generated API documentation. @@ -79,7 +80,7 @@ protected void addSearchFileContents(Content contentTree) { helpSection = HtmlTree.P(contents.getContent("doclet.search.help_page_info", helpLink)); } - contentTree.add(HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, HtmlStyle.title, + contentTree.add(HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, HtmlStyles.title, contents.getContent("doclet.search.main_heading"))) .add(HtmlTree.DIV(HtmlTree.INPUT(HtmlAttr.InputType.TEXT, HtmlId.of("page-search-input")) .put(HtmlAttr.PLACEHOLDER, resources.getText("doclet.search_placeholder")) @@ -88,31 +89,31 @@ protected void addSearchFileContents(Content contentTree) { .add(HtmlTree.INPUT(HtmlAttr.InputType.RESET, HtmlId.of("page-search-reset")) .put(HtmlAttr.VALUE, resources.getText("doclet.search_reset")) .put(HtmlAttr.STYLE, "margin: 6px;")) - .add(HtmlTree.DETAILS(HtmlStyle.pageSearchDetails) + .add(HtmlTree.DETAILS(HtmlStyles.pageSearchDetails) .add(HtmlTree.SUMMARY(contents.getContent("doclet.search.show_more")) .setId(HtmlId.of("page-search-expand"))))) - .add(HtmlTree.DIV(HtmlStyle.pageSearchInfo, helpSection) + .add(HtmlTree.DIV(HtmlStyles.pageSearchInfo, helpSection) .add(HtmlTree.P(contents.getContent("doclet.search.keyboard_info"))) .add(HtmlTree.P(contents.getContent("doclet.search.browser_info"))) .add(HtmlTree.SPAN(Text.of("link")) .setId(HtmlId.of("page-search-link"))) - .add(new HtmlTree(TagName.BUTTON) - .add(new HtmlTree(TagName.IMG) + .add(new HtmlTree(HtmlTag.BUTTON) + .add(new HtmlTree(HtmlTag.IMG) .put(HtmlAttr.SRC, pathToRoot.resolve(DocPaths.RESOURCE_FILES) .resolve(DocPaths.CLIPBOARD_SVG).getPath()) .put(HtmlAttr.ALT, copyUrlText)) .add(HtmlTree.SPAN(Text.of(copyText)) .put(HtmlAttr.DATA_COPIED, copiedText)) - .addStyle(HtmlStyle.copy) + .addStyle(HtmlStyles.copy) .put(HtmlAttr.ARIA_LABEL, copyUrlText) .setId(HtmlId.of("page-search-copy"))) .add(HtmlTree.P(HtmlTree.INPUT(HtmlAttr.InputType.CHECKBOX, HtmlId.of("search-redirect"))) .add(HtmlTree.LABEL("search-redirect", contents.getContent("doclet.search.redirect"))))) - .add(new HtmlTree(TagName.P) + .add(new HtmlTree(HtmlTag.P) .setId(HtmlId.of("page-search-notify")) .add(contents.getContent("doclet.search.loading"))) - .add(HtmlTree.DIV(new HtmlTree(TagName.DIV) + .add(HtmlTree.DIV(new HtmlTree(HtmlTag.DIV) .setId(HtmlId.of("result-container")) .addUnchecked(Text.EMPTY)) .setId(HtmlId.of("result-section")) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialFieldWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialFieldWriter.java index 758e0ee521491..c3d1cc582f36a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialFieldWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialFieldWriter.java @@ -35,11 +35,12 @@ import com.sun.source.doctree.SerialFieldTree; import com.sun.source.doctree.SerialTree; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.formats.html.taglets.TagletWriter; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generate serialized form for serializable fields. @@ -53,15 +54,15 @@ public SerialFieldWriter(SubWriterHolderWriter writer, TypeElement typeElement) } protected Content getSerializableFieldsHeader() { - return HtmlTree.UL(HtmlStyle.blockList); + return HtmlTree.UL(HtmlStyles.blockList); } protected Content getFieldsContentHeader() { - return new HtmlTree(TagName.LI).setStyle(HtmlStyle.blockList); + return new HtmlTree(HtmlTag.LI).setStyle(HtmlStyles.blockList); } protected Content getSerializableFields(String heading, Content source) { - var section = HtmlTree.SECTION(HtmlStyle.detail); + var section = HtmlTree.SECTION(HtmlStyles.detail); if (!source.isEmpty()) { Content headingContent = Text.of(heading); var serialHeading = HtmlTree.HEADING(Headings.SerializedForm.CLASS_SUBHEADING, headingContent); @@ -75,7 +76,7 @@ protected void addMemberHeader(TypeMirror fieldType, String fieldName, Content c Content nameContent = Text.of(fieldName); var heading = HtmlTree.HEADING(Headings.SerializedForm.MEMBER_HEADING, nameContent); content.add(heading); - var pre = new HtmlTree(TagName.PRE); + var pre = new HtmlTree(HtmlTag.PRE); Content fieldContent = writer.getLink(new HtmlLinkInfo( configuration, HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS, fieldType)); pre.add(fieldContent); @@ -122,7 +123,7 @@ protected void addMemberDescription(VariableElement field, SerialFieldTree seria Content serialFieldContent = writer.commentTagsToContent(field, description, new TagletWriter.Context(false, false)); - var div = HtmlTree.DIV(HtmlStyle.block, serialFieldContent); + var div = HtmlTree.DIV(HtmlStyles.block, serialFieldContent); content.add(div); } } @@ -136,7 +137,7 @@ protected void addMemberDescription(VariableElement field, SerialFieldTree seria protected void addMemberTags(VariableElement field, Content content) { Content tagContent = writer.getBlockTagOutput(field); if (!tagContent.isEmpty()) { - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(tagContent); content.add(dl); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialMethodWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialMethodWriter.java index 4af46f3ea0180..d9b888f2ee1b8 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialMethodWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerialMethodWriter.java @@ -28,11 +28,12 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.formats.html.taglets.TagletManager; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** @@ -46,11 +47,11 @@ public SerialMethodWriter(SubWriterHolderWriter writer, TypeElement typeElement) } protected Content getSerializableMethodsHeader() { - return HtmlTree.UL(HtmlStyle.blockList); + return HtmlTree.UL(HtmlStyles.blockList); } protected Content getMethodsContentHeader() { - return new HtmlTree(TagName.LI); + return new HtmlTree(HtmlTag.LI); } /** @@ -64,7 +65,7 @@ protected Content getMethodsContentHeader() { protected Content getSerializableMethods(String heading, Content source) { Content headingContent = Text.of(heading); var serialHeading = HtmlTree.HEADING(Headings.SerializedForm.CLASS_SUBHEADING, headingContent); - var section = HtmlTree.SECTION(HtmlStyle.detail, serialHeading); + var section = HtmlTree.SECTION(HtmlStyles.detail, serialHeading); section.add(source); return HtmlTree.LI(section); } @@ -121,7 +122,7 @@ protected void addMemberDescription(ExecutableElement member, Content methodsCon protected void addMemberTags(ExecutableElement member, Content methodsContent) { TagletManager tagletManager = configuration.tagletManager; Content tagContent = writer.getBlockTagOutput(member, tagletManager.getSerializedFormTaglets()); - var dl = HtmlTree.DL(HtmlStyle.notes); + var dl = HtmlTree.DL(HtmlStyles.notes); dl.add(tagContent); methodsContent.add(dl); if (name(member).equals("writeExternal") diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java index ffd654023d113..09fa5ed347c42 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriter.java @@ -41,18 +41,19 @@ import com.sun.source.doctree.SerialFieldTree; import com.sun.source.doctree.SerialTree; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocletException; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Generates the Serialized Form Information Page, serialized-form.html. @@ -562,8 +563,8 @@ Content getHeader(String header) { HtmlTree body = getBody(getWindowTitle(header)); Content h1Content = Text.of(header); var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, h1Content); - var div = HtmlTree.DIV(HtmlStyle.header, heading); + HtmlStyles.title, h1Content); + var div = HtmlTree.DIV(HtmlStyles.header, heading); bodyContents.setHeader(getHeader(PageMode.SERIALIZED_FORM)) .addMainContent(div); return body; @@ -575,7 +576,7 @@ Content getHeader(String header) { * @return the serialized form summaries header */ Content getSerializedSummariesHeader() { - return HtmlTree.UL(HtmlStyle.blockList); + return HtmlTree.UL(HtmlStyles.blockList); } /** @@ -584,7 +585,7 @@ Content getSerializedSummariesHeader() { * @return the package serialized form header tree */ Content getPackageSerializedHeader() { - return HtmlTree.SECTION(HtmlStyle.serializedPackageContainer); + return HtmlTree.SECTION(HtmlStyles.serializedPackageContainer); } Content getPackageHeader(PackageElement packageElement) { @@ -596,7 +597,7 @@ Content getPackageHeader(PackageElement packageElement) { } Content getClassSerializedHeader() { - return HtmlTree.UL(HtmlStyle.blockList); + return HtmlTree.UL(HtmlStyles.blockList); } /** @@ -615,7 +616,7 @@ Content getClassHeader(TypeElement typeElement) { ? getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.PLAIN, typeElement) .label(configuration.getClassName(typeElement))) : Text.of(utils.getFullyQualifiedName(typeElement)); - var section = HtmlTree.SECTION(HtmlStyle.serializedClassDetails) + var section = HtmlTree.SECTION(HtmlStyles.serializedClassDetails) .setId(htmlIds.forClass(typeElement)); Content superClassLink = typeElement.getSuperclass() != null ? getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS, @@ -640,12 +641,12 @@ Content getClassHeader(TypeElement typeElement) { signature.add(superClassLink); signature.add(" implements "); signature.add(interfaceLink); - section.add(HtmlTree.DIV(HtmlStyle.typeSignature, signature)); + section.add(HtmlTree.DIV(HtmlStyles.typeSignature, signature)); return section; } Content getSerialUIDInfoHeader() { - return HtmlTree.DL(HtmlStyle.nameValue); + return HtmlTree.DL(HtmlStyles.nameValue); } /** @@ -667,7 +668,7 @@ void addSerialUIDInfo(String header, } Content getClassContentHeader() { - return HtmlTree.UL(HtmlStyle.blockList); + return HtmlTree.UL(HtmlStyles.blockList); } /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java index f4f472de9f99a..9059efcf81438 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,12 @@ package jdk.javadoc.internal.doclets.formats.html; -import jdk.javadoc.doclet.DocletEnvironment; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.stream.Collectors; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -43,12 +41,16 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementKindVisitor14; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.stream.Collectors; + +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; import static javax.lang.model.element.Modifier.ABSTRACT; import static javax.lang.model.element.Modifier.FINAL; @@ -63,17 +65,17 @@ public class Signatures { public static Content getModuleSignature(ModuleElement mdle, ModuleWriter moduleWriter) { - var signature = HtmlTree.DIV(HtmlStyle.moduleSignature); + var signature = HtmlTree.DIV(HtmlStyles.moduleSignature); Content annotations = moduleWriter.getAnnotationInfo(mdle, true); if (!annotations.isEmpty()) { - signature.add(HtmlTree.SPAN(HtmlStyle.annotations, annotations)); + signature.add(HtmlTree.SPAN(HtmlStyles.annotations, annotations)); } DocletEnvironment docEnv = moduleWriter.configuration.docEnv; String label = mdle.isOpen() && (docEnv.getModuleMode() == DocletEnvironment.ModuleMode.ALL) ? "open module" : "module"; signature.add(label); signature.add(" "); - var nameSpan = HtmlTree.SPAN(HtmlStyle.elementName); + var nameSpan = HtmlTree.SPAN(HtmlStyles.elementName); nameSpan.add(mdle.getQualifiedName().toString()); signature.add(nameSpan); return signature; @@ -83,13 +85,13 @@ public static Content getPackageSignature(PackageElement pkg, PackageWriter pkgW if (pkg.isUnnamed()) { return Text.EMPTY; } - var signature = HtmlTree.DIV(HtmlStyle.packageSignature); + var signature = HtmlTree.DIV(HtmlStyles.packageSignature); Content annotations = pkgWriter.getAnnotationInfo(pkg, true); if (!annotations.isEmpty()) { - signature.add(HtmlTree.SPAN(HtmlStyle.annotations, annotations)); + signature.add(HtmlTree.SPAN(HtmlStyles.annotations, annotations)); } signature.add("package "); - var nameSpan = HtmlTree.SPAN(HtmlStyle.elementName); + var nameSpan = HtmlTree.SPAN(HtmlStyles.elementName); nameSpan.add(pkg.getQualifiedName().toString()); signature.add(nameSpan); return signature; @@ -122,16 +124,16 @@ public Content toContent() { Content content = new ContentBuilder(); Content annotationInfo = writer.getAnnotationInfo(typeElement, true); if (!annotationInfo.isEmpty()) { - content.add(HtmlTree.SPAN(HtmlStyle.annotations, annotationInfo)); + content.add(HtmlTree.SPAN(HtmlStyles.annotations, annotationInfo)); } - content.add(HtmlTree.SPAN(HtmlStyle.modifiers, modifiers)); + content.add(HtmlTree.SPAN(HtmlStyles.modifiers, modifiers)); - var nameSpan = HtmlTree.SPAN(HtmlStyle.elementName); + var nameSpan = HtmlTree.SPAN(HtmlStyles.elementName); Content className = Text.of(utils.getSimpleName(typeElement)); if (configuration.getOptions().linkSource()) { writer.addSrcLink(typeElement, className, nameSpan); } else { - nameSpan.addStyle(HtmlStyle.typeNameLabel).add(className); + nameSpan.addStyle(HtmlStyles.typeNameLabel).add(className); } HtmlLinkInfo linkInfo = new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.SHOW_TYPE_PARAMS_AND_BOUNDS, typeElement) @@ -144,7 +146,7 @@ public Content toContent() { content.add(getRecordComponents()); } if (!utils.isAnnotationInterface(typeElement)) { - var extendsImplements = HtmlTree.SPAN(HtmlStyle.extendsImplements); + var extendsImplements = HtmlTree.SPAN(HtmlStyles.extendsImplements); if (!utils.isPlainInterface(typeElement)) { TypeMirror superclass = utils.getFirstVisibleSuperClass(typeElement); if (superclass != null) { @@ -186,7 +188,7 @@ public Content toContent() { .filter(t -> utils.isLinkable(utils.asTypeElement(t))) .toList(); if (!linkablePermits.isEmpty()) { - var permitsSpan = HtmlTree.SPAN(HtmlStyle.permits); + var permitsSpan = HtmlTree.SPAN(HtmlStyles.permits); boolean isFirst = true; for (TypeMirror type : linkablePermits) { if (isFirst) { @@ -205,11 +207,11 @@ public Content toContent() { if (linkablePermits.size() < permits.size()) { Content c = Text.of(configuration.getDocResources().getText("doclet.not.exhaustive")); permitsSpan.add(" "); - permitsSpan.add(HtmlTree.SPAN(HtmlStyle.permitsNote, c)); + permitsSpan.add(HtmlTree.SPAN(HtmlStyles.permitsNote, c)); } content.add(permitsSpan); } - return HtmlTree.DIV(HtmlStyle.typeSignature, content); + return HtmlTree.DIV(HtmlStyles.typeSignature, content); } private Content getRecordComponents() { @@ -450,7 +452,7 @@ Content toContent() { // Annotations if (annotations != null && !annotations.isEmpty()) { - content.add(HtmlTree.SPAN(HtmlStyle.annotations, annotations)); + content.add(HtmlTree.SPAN(HtmlStyles.annotations, annotations)); lastLineSeparator = content.charCount(); } @@ -464,12 +466,12 @@ Content toContent() { // Return type if (returnType != null) { - content.add(HtmlTree.SPAN(HtmlStyle.returnType, returnType)); + content.add(HtmlTree.SPAN(HtmlStyles.returnType, returnType)); content.add(Entity.NO_BREAK_SPACE); } // Name - var nameSpan = HtmlTree.SPAN(HtmlStyle.elementName); + var nameSpan = HtmlTree.SPAN(HtmlStyles.elementName); if (memberWriter.options.linkSource()) { Content name = Text.of(memberWriter.name(element)); memberWriter.writer.addSrcLink(element, name, nameSpan); @@ -483,7 +485,7 @@ Content toContent() { appendParametersAndExceptions(content, lastLineSeparator); } - return HtmlTree.DIV(HtmlStyle.memberSignature, content); + return HtmlTree.DIV(HtmlStyles.memberSignature, content); } /** @@ -514,7 +516,7 @@ private void appendModifiers(Content target) { } if (!set.isEmpty()) { String mods = set.stream().map(Modifier::toString).collect(Collectors.joining(" ")); - target.add(HtmlTree.SPAN(HtmlStyle.modifiers, Text.of(mods))) + target.add(HtmlTree.SPAN(HtmlStyles.modifiers, Text.of(mods))) .add(Entity.NO_BREAK_SPACE); } } @@ -529,19 +531,21 @@ private void appendModifiers(Content target) { private int appendTypeParameters(Content target, int lastLineSeparator) { // Apply different wrapping strategies for type parameters // depending on the combined length of type parameters and return type. + // Note return type will be null if this is a constructor. int typeParamLength = typeParameters.charCount(); if (typeParamLength >= TYPE_PARAMS_MAX_INLINE_LENGTH) { - target.add(HtmlTree.SPAN(HtmlStyle.typeParametersLong, typeParameters)); + target.add(HtmlTree.SPAN(HtmlStyles.typeParametersLong, typeParameters)); } else { - target.add(HtmlTree.SPAN(HtmlStyle.typeParameters, typeParameters)); + target.add(HtmlTree.SPAN(HtmlStyles.typeParameters, typeParameters)); } int lineLength = target.charCount() - lastLineSeparator; int newLastLineSeparator = lastLineSeparator; + int returnTypeLength = returnType != null ? returnType.charCount() : 0; // sum below includes length of modifiers plus type params added above - if (lineLength + returnType.charCount() > RETURN_TYPE_MAX_LINE_LENGTH) { + if (lineLength + returnTypeLength > RETURN_TYPE_MAX_LINE_LENGTH) { target.add(Text.NL); newLastLineSeparator = target.charCount(); } else { @@ -565,8 +569,8 @@ private void appendParametersAndExceptions(Content target, int lastLineSeparator // empty parameters are added without packing target.add(parameters); } else { - target.add(new HtmlTree(TagName.WBR)) - .add(HtmlTree.SPAN(HtmlStyle.parameters, parameters)); + target.add(new HtmlTree(HtmlTag.WBR)) + .add(HtmlTree.SPAN(HtmlStyles.parameters, parameters)); } // Exceptions @@ -575,7 +579,7 @@ private void appendParametersAndExceptions(Content target, int lastLineSeparator target.add(Text.NL) .add(indent) .add("throws ") - .add(HtmlTree.SPAN(HtmlStyle.exceptions, exceptions)); + .add(HtmlTree.SPAN(HtmlStyles.exceptions, exceptions)); } } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java index 6f1381a70ca4a..070b38421225b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java @@ -25,8 +25,6 @@ package jdk.javadoc.internal.doclets.formats.html; -import jdk.javadoc.internal.doclets.formats.html.markup.Head; - import java.io.IOException; import java.io.LineNumberReader; import java.io.Reader; @@ -38,12 +36,9 @@ import javax.lang.model.element.TypeElement; import javax.tools.FileObject; +import jdk.javadoc.internal.doclets.formats.html.markup.Head; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.Messages; import jdk.javadoc.internal.doclets.toolkit.Resources; import jdk.javadoc.internal.doclets.toolkit.util.DocFile; @@ -52,6 +47,11 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.SimpleDocletException; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Converts Java Source Code to HTML. @@ -201,7 +201,7 @@ public void convertClass(TypeElement te, DocPath outputdir) .resolve(configuration.docPaths.forPackage(te)) .invert(); Content body = getHeader(); - var pre = new HtmlTree(TagName.PRE); + var pre = new HtmlTree(HtmlTag.PRE); try (var reader = new LineNumberReader(r)) { while ((line = reader.readLine()) != null) { addLineNo(pre, lineno); @@ -210,7 +210,7 @@ public void convertClass(TypeElement te, DocPath outputdir) } } addBlankLines(pre); - var div = HtmlTree.DIV(HtmlStyle.sourceContainer, pre); + var div = HtmlTree.DIV(HtmlStyles.sourceContainer, pre); body.add(HtmlTree.MAIN(div)); writeToFile(body, outputdir.resolve(configuration.docPaths.forClass(te)), te); } catch (IOException e) { @@ -246,7 +246,7 @@ private void writeToFile(Content body, DocPath path, TypeElement te) throws DocF * @return the header content for the HTML file */ private static Content getHeader() { - return new HtmlTree(TagName.BODY).setStyle(HtmlStyle.sourcePage); + return new HtmlTree(HtmlTag.BODY).setStyle(HtmlStyles.sourcePage); } /** @@ -256,7 +256,7 @@ private static Content getHeader() { * @param lineno The line number */ private static void addLineNo(Content pre, int lineno) { - var span = HtmlTree.SPAN(HtmlStyle.sourceLineNo); + var span = HtmlTree.SPAN(HtmlStyles.sourceLineNo); if (lineno < 10) { span.add("00" + lineno); } else if (lineno < 100) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SubWriterHolderWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SubWriterHolderWriter.java index 3926d4e9a4c13..0876a1611cdb6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SubWriterHolderWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SubWriterHolderWriter.java @@ -25,20 +25,23 @@ package jdk.javadoc.internal.doclets.formats.html; -import java.util.*; +import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DeprecatedTree; import com.sun.source.doctree.DocTree; + import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.PropertyUtils; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; /** * This abstract class exists to provide functionality needed in the @@ -117,8 +120,8 @@ protected void addIndexComment(Element member, List firstSent List deprs = utils.getDeprecatedTrees(member); Content div; if (utils.isDeprecated(member)) { - var deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(member)); - div = HtmlTree.DIV(HtmlStyle.block, deprLabel); + var deprLabel = HtmlTree.SPAN(HtmlStyles.deprecatedLabel, getDeprecatedPhrase(member)); + div = HtmlTree.DIV(HtmlStyles.block, deprLabel); if (!deprs.isEmpty()) { addSummaryDeprecatedComment(member, deprs.get(0), div); } @@ -127,8 +130,8 @@ protected void addIndexComment(Element member, List firstSent } else { Element te = member.getEnclosingElement(); if (te != null && utils.isTypeElement(te) && utils.isDeprecated(te)) { - var deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, getDeprecatedPhrase(te)); - div = HtmlTree.DIV(HtmlStyle.block, deprLabel); + var deprLabel = HtmlTree.SPAN(HtmlStyles.deprecatedLabel, getDeprecatedPhrase(te)); + div = HtmlTree.DIV(HtmlStyles.block, deprLabel); tdSummaryContent.add(div); } } @@ -200,7 +203,7 @@ public void addClassContent(Content classContent) { * @return a list to be used for the list of summaries for members of a given kind */ public Content getSummariesList() { - return HtmlTree.UL(HtmlStyle.summaryList); + return HtmlTree.UL(HtmlStyles.summaryList); } /** @@ -219,7 +222,7 @@ public Content getSummariesListItem(Content content) { * @return a list to be used for the list of details for members of a given kind */ public Content getDetailsList() { - return HtmlTree.UL(HtmlStyle.detailsList); + return HtmlTree.UL(HtmlStyles.detailsList); } /** @@ -236,7 +239,7 @@ public Content getDetailsListItem(Content content) { * {@return a list to add member items to} */ public Content getMemberList() { - return HtmlTree.UL(HtmlStyle.memberList); + return HtmlTree.UL(HtmlStyles.memberList); } /** @@ -249,7 +252,7 @@ public Content getMemberListItem(Content member) { } public Content getMemberInherited() { - return HtmlTree.DIV(HtmlStyle.inheritedList); + return HtmlTree.DIV(HtmlStyles.inheritedList); } /** @@ -281,7 +284,7 @@ public Content getMember(Content content) { * @param memberContent the content used to generate the member summary */ public Content getMemberSummary(Content memberContent) { - return HtmlTree.SECTION(HtmlStyle.summary, memberContent); + return HtmlTree.SECTION(HtmlStyles.summary, memberContent); } /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java index ffe0350678de3..8d2f621e5d455 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java @@ -31,18 +31,20 @@ import javax.lang.model.element.ModuleElement; import javax.lang.model.element.PackageElement; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.Script; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.SummaryAPIListBuilder; import jdk.javadoc.internal.doclets.toolkit.util.SummaryAPIListBuilder.SummaryElementKind; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Script; +import jdk.javadoc.internal.html.Text; /** * Base class for generating a summary page that lists elements with a common characteristic, @@ -139,8 +141,8 @@ public void buildPage() throws DocFileIOException { HtmlTree body = getHeader(getPageMode(), getTitleKey()); Content content = new ContentBuilder(); var heading = HtmlTree.HEADING_TITLE(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, getHeadContent()); - content.add(HtmlTree.DIV(HtmlStyle.header, heading)); + HtmlStyles.title, getHeadContent()); + content.add(HtmlTree.DIV(HtmlStyles.header, heading)); addContentSelectors(content); if (showContentsList()) { content.add(HtmlTree.HEADING_TITLE(Headings.CONTENT_HEADING, contents.contentsHeading)); @@ -201,7 +203,7 @@ protected boolean showContentsList() { * @return the contents list */ public Content getContentsList() { - var ul= HtmlTree.UL(HtmlStyle.contentsList); + var ul= HtmlTree.UL(HtmlStyles.contentsList); addExtraIndexLink(ul); for (SummaryElementKind kind : SummaryElementKind.values()) { if (builder.hasDocumentation(kind)) { @@ -238,7 +240,7 @@ protected void addSummaryAPI(SortedSet apiList, HtmlId id, if (apiList.size() > 0) { TableHeader tableHeader = getTableHeader(headerKey); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(getTableCaption(headingKey)) .setHeader(tableHeader) .setId(id) @@ -267,7 +269,7 @@ protected void addSummaryAPI(SortedSet apiList, HtmlId id, } } // note: singleton list - content.add(HtmlTree.UL(HtmlStyle.blockList, HtmlTree.LI(table))); + content.add(HtmlTree.UL(HtmlStyles.blockList, HtmlTree.LI(table))); } } @@ -370,7 +372,7 @@ protected TableHeader getTableHeader(String headerKey) { * @return the styles to use for table columns */ protected HtmlStyle[] getColumnStyles() { - return new HtmlStyle[]{ HtmlStyle.colSummaryItemName, HtmlStyle.colLast }; + return new HtmlStyle[]{ HtmlStyles.colSummaryItemName, HtmlStyles.colLast }; } /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SystemPropertiesWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SystemPropertiesWriter.java index 45985532ad6fb..d7aacd3ab0ff5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SystemPropertiesWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SystemPropertiesWriter.java @@ -38,15 +38,16 @@ import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.DocFileElement; import jdk.javadoc.internal.doclets.toolkit.OverviewElement; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; import static java.util.stream.Collectors.groupingBy; @@ -86,7 +87,7 @@ public void buildPage() throws DocFileIOException { addSystemProperties(mainContent); body.add(new BodyContents() .setHeader(getHeader(PageMode.SYSTEM_PROPERTIES)) - .addMainContent(HtmlTree.DIV(HtmlStyle.header, + .addMainContent(HtmlTree.DIV(HtmlStyles.header, HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, contents.getContent("doclet.systemProperties")))) .addMainContent(mainContent) @@ -106,10 +107,10 @@ public void buildPage() throws DocFileIOException { protected void addSystemProperties(Content target) { Map> searchIndexMap = groupSystemProperties(); Content separator = Text.of(", "); - var table = new Table(HtmlStyle.summaryTable) + var table = new Table(HtmlStyles.summaryTable) .setCaption(contents.systemPropertiesSummaryLabel) .setHeader(new TableHeader(contents.propertyLabel, contents.referencedIn)) - .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast); + .setColumnStyles(HtmlStyles.colFirst, HtmlStyles.colLast); for (Entry> entry : searchIndexMap.entrySet()) { Content propertyName = Text.of(entry.getKey()); List searchIndexItems = entry.getValue(); @@ -119,7 +120,7 @@ protected void addSystemProperties(Content target) { separatedReferenceLinks.add(separator); separatedReferenceLinks.add(createLink(searchIndexItems.get(i))); } - table.addRow(propertyName, HtmlTree.DIV(HtmlStyle.block, separatedReferenceLinks)); + table.addRow(propertyName, HtmlTree.DIV(HtmlStyles.block, separatedReferenceLinks)); } target.add(table); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Table.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Table.java index 339cfbf6f727f..5d09682d4920b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Table.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Table.java @@ -34,13 +34,15 @@ import java.util.Set; import java.util.function.Predicate; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * An HTML container used to display summary tables for various kinds of elements @@ -303,7 +305,7 @@ public void addRow(T item, List contents) { Content row = new ContentBuilder(); int rowIndex = bodyRows.size(); - HtmlStyle rowStyle = rowIndex % 2 == 0 ? HtmlStyle.evenRowColor : HtmlStyle.oddRowColor; + HtmlStyle rowStyle = rowIndex % 2 == 0 ? HtmlStyles.evenRowColor : HtmlStyles.oddRowColor; List tabClasses = new ArrayList<>(); if (tabs != null) { @@ -359,16 +361,16 @@ public boolean write(Writer out, String newline, boolean atNewline) throws IOExc private Content toContent() { Content main; if (id != null) { - main = new HtmlTree(TagName.DIV).setId(id); + main = new HtmlTree(HtmlTag.DIV).setId(id); } else { main = new ContentBuilder(); } // If no grid style is set use on of the default styles if (gridStyle == null) { gridStyle = switch (columnStyles.size()) { - case 2 -> HtmlStyle.twoColumnSummary; - case 3 -> HtmlStyle.threeColumnSummary; - case 4 -> HtmlStyle.fourColumnSummary; + case 2 -> HtmlStyles.twoColumnSummary; + case 3 -> HtmlStyles.threeColumnSummary; + case 4 -> HtmlStyles.fourColumnSummary; default -> throw new IllegalStateException(); }; } @@ -383,16 +385,16 @@ private Content toContent() { table.add(getTableBody()); main.add(table); } else { - var tablist = HtmlTree.DIV(HtmlStyle.tableTabs) + var tablist = HtmlTree.DIV(HtmlStyles.tableTabs) .put(HtmlAttr.ROLE, "tablist") .put(HtmlAttr.ARIA_ORIENTATION, "horizontal"); HtmlId defaultTabId = HtmlIds.forTab(id, 0); if (renderTabs) { - tablist.add(createTab(defaultTabId, HtmlStyle.activeTableTab, true, defaultTab)); + tablist.add(createTab(defaultTabId, HtmlStyles.activeTableTab, true, defaultTab)); for (var tab : tabs) { if (occurringTabs.contains(tab)) { - tablist.add(createTab(HtmlIds.forTab(id, tab.index()), HtmlStyle.tableTab, false, tab.label())); + tablist.add(createTab(HtmlIds.forTab(id, tab.index()), HtmlStyles.tableTab, false, tab.label())); } } } else { @@ -401,7 +403,7 @@ private Content toContent() { if (id == null) { throw new IllegalStateException("no id set for table"); } - var tabpanel = new HtmlTree(TagName.DIV) + var tabpanel = new HtmlTree(HtmlTag.DIV) .setId(HtmlIds.forTabPanel(id)) .put(HtmlAttr.ROLE, "tabpanel") .put(HtmlAttr.ARIA_LABELLEDBY, defaultTabId.name()); @@ -414,7 +416,7 @@ private Content toContent() { } private HtmlTree createTab(HtmlId tabId, HtmlStyle style, boolean defaultTab, Content tabLabel) { - var tab = new HtmlTree(TagName.BUTTON) + var tab = new HtmlTree(HtmlTag.BUTTON) .setId(tabId) .put(HtmlAttr.ROLE, "tab") .put(HtmlAttr.ARIA_SELECTED, defaultTab ? "true" : "false") @@ -436,6 +438,6 @@ private Content getTableBody() { } private HtmlTree getCaption(Content title) { - return HtmlTree.DIV(HtmlStyle.caption, HtmlTree.SPAN(title)); + return HtmlTree.DIV(HtmlStyles.caption, HtmlTree.SPAN(title)); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableHeader.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableHeader.java index a505797764e33..e1c42062c2190 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableHeader.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableHeader.java @@ -30,10 +30,12 @@ import java.util.Arrays; import java.util.List; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; /** * A row of header cells for an HTML table. @@ -152,10 +154,10 @@ private Content toContent() { int i = 0; for (Content cellContent : cellContents) { HtmlStyle style = (styles != null) ? styles.get(i) - : (i == 0) ? HtmlStyle.colFirst - : (i == (cellContents.size() - 1)) ? HtmlStyle.colLast - : (i == 1) ? HtmlStyle.colSecond : null; - var cell = HtmlTree.DIV(HtmlStyle.tableHeader, cellContent); + : (i == 0) ? HtmlStyles.colFirst + : (i == (cellContents.size() - 1)) ? HtmlStyles.colLast + : (i == 1) ? HtmlStyles.colSecond : null; + var cell = HtmlTree.DIV(HtmlStyles.tableHeader, cellContent); if (style != null) { cell.addStyle(style); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java index 7cfbffb6cee02..a9036d7dcd856 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TableOfContents.java @@ -24,14 +24,15 @@ */ package jdk.javadoc.internal.doclets.formats.html; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.ListBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.ListBuilder; +import jdk.javadoc.internal.html.Text; /** * A class used by various {@link HtmlDocletWriter} subclasses to build tables of contents. @@ -46,7 +47,7 @@ public class TableOfContents { */ public TableOfContents(HtmlDocletWriter writer) { this.writer = writer; - listBuilder = new ListBuilder(HtmlTree.OL(HtmlStyle.tocList)); + listBuilder = new ListBuilder(HtmlTree.OL(HtmlStyles.tocList)); } /** @@ -64,7 +65,7 @@ public TableOfContents addLink(HtmlId id, Content label) { * Adds a new nested list to add new items to. */ public void pushNestedList() { - listBuilder.pushNestedList(HtmlTree.OL(HtmlStyle.tocList)); + listBuilder.pushNestedList(HtmlTree.OL(HtmlStyles.tocList)); } /** @@ -87,23 +88,23 @@ protected Content toContent(boolean hasFilterInput) { return Text.EMPTY; } var content = HtmlTree.NAV() - .setStyle(HtmlStyle.toc) + .setStyle(HtmlStyles.toc) .put(HtmlAttr.ARIA_LABEL, writer.resources.getText("doclet.table_of_contents")); - var header = HtmlTree.DIV(HtmlStyle.tocHeader, writer.contents.contentsHeading); + var header = HtmlTree.DIV(HtmlStyles.tocHeader, writer.contents.contentsHeading); if (hasFilterInput) { header.add(Entity.NO_BREAK_SPACE) - .add(HtmlTree.INPUT(HtmlAttr.InputType.TEXT, HtmlStyle.filterInput) + .add(HtmlTree.INPUT(HtmlAttr.InputType.TEXT, HtmlStyles.filterInput) .put(HtmlAttr.PLACEHOLDER, writer.resources.getText("doclet.filter_label")) .put(HtmlAttr.ARIA_LABEL, writer.resources.getText("doclet.filter_table_of_contents")) .put(HtmlAttr.AUTOCOMPLETE, "off")) - .add(HtmlTree.INPUT(HtmlAttr.InputType.RESET, HtmlStyle.resetFilter) + .add(HtmlTree.INPUT(HtmlAttr.InputType.RESET, HtmlStyles.resetFilter) .put(HtmlAttr.VALUE, writer.resources.getText("doclet.filter_reset"))); } content.add(header); - content.add(new HtmlTree(TagName.BUTTON).addStyle(HtmlStyle.hideSidebar) + content.add(new HtmlTree(HtmlTag.BUTTON).addStyle(HtmlStyles.hideSidebar) .add(HtmlTree.SPAN(writer.contents.hideSidebar).add(Entity.NO_BREAK_SPACE)) .add(Entity.LEFT_POINTING_ANGLE)); - content.add(new HtmlTree(TagName.BUTTON).addStyle(HtmlStyle.showSidebar) + content.add(new HtmlTree(HtmlTag.BUTTON).addStyle(HtmlStyles.showSidebar) .add(Entity.RIGHT_POINTING_ANGLE) .add(HtmlTree.SPAN(Entity.NO_BREAK_SPACE).add(writer.contents.showSidebar))); return content.add(listBuilder); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TreeWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TreeWriter.java index c5e0329e9e684..8af3b7016cecb 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TreeWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TreeWriter.java @@ -29,15 +29,16 @@ import javax.lang.model.element.PackageElement; -import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; +import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; /** * Generate Class Hierarchy page for all the Classes in this run. Use @@ -78,8 +79,8 @@ public void buildPage() throws DocFileIOException { HtmlTree body = getBody(); Content headContent = contents.hierarchyForAllPackages; var heading = HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, - HtmlStyle.title, headContent); - var div = HtmlTree.DIV(HtmlStyle.header, heading); + HtmlStyles.title, headContent); + var div = HtmlTree.DIV(HtmlStyles.header, heading); Content mainContent = new ContentBuilder(); mainContent.add(div); addPackageTreeLinks(mainContent); @@ -105,10 +106,10 @@ protected void addPackageTreeLinks(Content content) { return; } if (!classesOnly) { - var span = HtmlTree.SPAN(HtmlStyle.packageHierarchyLabel, + var span = HtmlTree.SPAN(HtmlStyles.packageHierarchyLabel, contents.packageHierarchies); content.add(span); - var ul = HtmlTree.UL(HtmlStyle.horizontal).addStyle(HtmlStyle.contentsList); + var ul = HtmlTree.UL(HtmlStyles.horizontal).addStyle(HtmlStyles.contentsList); int i = 0; for (PackageElement pkg : packages) { // If the package name length is 0 or if -nodeprecated option diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/BodyContents.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/BodyContents.java index 71bf1f265cfb2..d7c865b8350fd 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/BodyContents.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/BodyContents.java @@ -25,14 +25,17 @@ package jdk.javadoc.internal.doclets.formats.html.markup; -import jdk.javadoc.internal.doclets.formats.html.Content; - import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; + /** * Content for the {@code } element. * @@ -96,7 +99,7 @@ private Content toContent() { return new ContentBuilder() .add(header) - .add(HtmlTree.DIV(HtmlStyle.mainGrid) + .add(HtmlTree.DIV(HtmlStyles.mainGrid) .add(side == null ? Text.EMPTY : side) .add(HtmlTree.MAIN() .add(mainContents) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java index c4a223a94c73b..3d5b1a494ba2c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java @@ -34,10 +34,15 @@ import java.util.List; import java.util.Locale; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.html.Comment; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Script; /** * An HTML {@code } element. @@ -267,7 +272,7 @@ public boolean write(Writer out, String newline, boolean atNewline) throws IOExc * @return the HTML */ private Content toContent() { - var head = new HtmlTree(TagName.HEAD); + var head = new HtmlTree(HtmlTag.HEAD); head.add(getGeneratedBy(showTimestamp, generatedDate)); head.add(HtmlTree.TITLE(title)); @@ -295,7 +300,7 @@ private Content toContent() { } if (canonicalLink != null) { - var link = new HtmlTree(TagName.LINK); + var link = new HtmlTree(HtmlTag.LINK); link.put(HtmlAttr.REL, "canonical"); link.put(HtmlAttr.HREF, canonicalLink.getPath()); head.add(link); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java deleted file mode 100644 index 1d0d54a05252c..0000000000000 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.javadoc.internal.doclets.formats.html.markup; - -import jdk.javadoc.internal.doclets.toolkit.util.Utils; - -/** - * Enum representing HTML tag attributes. - */ -public enum HtmlAttr { - ALT, - ARIA_CONTROLS("aria-controls"), - ARIA_EXPANDED("aria-expanded"), - ARIA_LABEL("aria-label"), - ARIA_LABELLEDBY("aria-labelledby"), - ARIA_ORIENTATION("aria-orientation"), - ARIA_SELECTED("aria-selected"), - AUTOCOMPLETE, - CHECKED, - CLASS, - CLEAR, - COLS, - CONTENT, - DATA_COPIED("data-copied"), // custom HTML5 data attribute - DISABLED, - FOR, - HREF, - HTTP_EQUIV("http-equiv"), - ID, - LANG, - NAME, - ONCLICK, - ONKEYDOWN, - ONLOAD, - PLACEHOLDER, - REL, - ROLE, - ROWS, - SCOPE, - SCROLLING, - SRC, - STYLE, - SUMMARY, - TABINDEX, - TARGET, - TITLE, - TYPE, - VALUE, - WIDTH; - - private final String value; - - public enum Role { - - BANNER, - CONTENTINFO, - MAIN, - NAVIGATION, - REGION; - - private final String role; - - Role() { - role = Utils.toLowerCase(name()); - } - - public String toString() { - return role; - } - } - - public enum InputType { - - CHECKBOX, - RESET, - TEXT; - - private final String type; - - InputType() { - type = Utils.toLowerCase(name()); - } - - public String toString() { - return type; - } - } - - HtmlAttr() { - this.value = Utils.toLowerCase(name()); - } - - HtmlAttr(String name) { - this.value = name; - } - - public String toString() { - return value; - } -} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocument.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocument.java index dcfed8707dd17..051cbfd00423b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocument.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocument.java @@ -29,9 +29,11 @@ import java.io.StringWriter; import java.io.Writer; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.DocFile; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.DocType; +import jdk.javadoc.internal.html.HtmlTag; /** * Class for generating an HTML document for javadoc output. @@ -43,7 +45,7 @@ public class HtmlDocument { /** * Constructs an HTML document. * - * @param html the {@link TagName#HTML HTML} element of the document + * @param html the {@link HtmlTag#HTML HTML} element of the document */ public HtmlDocument(Content html) { docContent = html; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java similarity index 99% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java index 35a52b0c05dd1..35240fa69dabc 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java @@ -28,6 +28,8 @@ import java.util.Locale; import java.util.regex.Pattern; +import jdk.javadoc.internal.html.HtmlStyle; + /** * Enum representing HTML styles, with associated entries in the stylesheet files. * @@ -49,7 +51,7 @@ * * @see WhatWG: {@code class} attribute */ -public enum HtmlStyle { +public enum HtmlStyles implements HtmlStyle { // // @@ -1098,13 +1100,13 @@ public enum HtmlStyle { private final String cssName; - HtmlStyle() { + HtmlStyles() { cssName = Pattern.compile("\\p{Upper}") .matcher(toString()) .replaceAll(mr -> "-" + mr.group().toLowerCase(Locale.US)); } - HtmlStyle(String cssName) { + HtmlStyles(String cssName) { this.cssName = cssName; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java index a0bd0b3dd044a..0af2913565465 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Links.java @@ -25,9 +25,14 @@ package jdk.javadoc.internal.doclets.formats.html.markup; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Factory for HTML A elements: links (with a {@code href} attribute). @@ -197,7 +202,7 @@ public HtmlTree createLink(DocLink link, Content label, HtmlStyle style, } if (isExternal) { // Use addStyle as external links might have an explicit style set above as well. - l.addStyle(HtmlStyle.externalLink); + l.addStyle(HtmlStyles.externalLink); } return l; } @@ -211,6 +216,6 @@ public HtmlTree createLink(DocLink link, Content label, HtmlStyle style, */ public HtmlTree createExternalLink(DocLink link, Content label) { return HtmlTree.A(link.relativizeAgainst(file).toString(), label) - .setStyle(HtmlStyle.externalLink); + .setStyle(HtmlStyles.externalLink); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/TagName.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/TagName.java deleted file mode 100644 index d849c2f1798ba..0000000000000 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/TagName.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.javadoc.internal.doclets.formats.html.markup; - -import java.util.Locale; - -import jdk.javadoc.internal.doclets.toolkit.util.Utils; - -/** - * Enum representing the names for HTML elements. - * - * @see WhatWG: Tag Name - * @see HTML 5.1: Tag Name - */ -public enum TagName { - A(true), - ASIDE, - BUTTON(true), - BLOCKQUOTE, - BODY, - BR(true), - CAPTION, - CODE(true), - DD, - DETAILS, - DIV, - DL, - DT, - EM(true), - FOOTER, - FORM, - H1, - H2, - H3, - H4, - H5, - H6, - HEAD, - HEADER, - HR, - HTML, - I(true), - IMG(true), - INPUT(true), - LABEL(true), - LI, - LISTING, - LINK(true), - MAIN, - MENU, - META, - NAV, - NOSCRIPT(true), - OL, - P, - PRE, - SCRIPT(true), - SECTION, - SMALL(true), - SPAN(true), - STRONG(true), - SUB(true), - SUMMARY, - SUP(true), - TABLE, - TBODY, - THEAD, - TD, - TH, - TITLE, - TR, - UL, - WBR(true); - - public final String value; - public final boolean phrasingContent; - - static TagName of(String s) { - return valueOf(s.toUpperCase(Locale.ROOT)); - } - - TagName() { - this(false); - } - - TagName(boolean phrasingContent) { - this.value = Utils.toLowerCase(name()); - this.phrasingContent = phrasingContent; - } - - public String toString() { - return value; - } - -} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/package-info.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/package-info.java index aa279972f8c6a..efad5fc0fe925 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/package-info.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/package-info.java @@ -27,7 +27,7 @@ * This package contains classes that create and write HTML markup tags. * *

      The primary low level classes are - * {@link jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree HtmlTree} + * {@link jdk.javadoc.internal.html.HtmlTree HtmlTree} * and other subtypes of {@code Content}. In addition, there are mid-level builders * like {@link jdk.javadoc.internal.doclets.formats.html.TableHeader TableHeader} * and {@link jdk.javadoc.internal.doclets.formats.html.Table Table} 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 ef09e6df90fd7..71ef847670822 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 @@ -232,6 +232,20 @@ document.addEventListener("readystatechange", (e) => { }); document.addEventListener("DOMContentLoaded", function(e) { setTopMargin(); + // Reset animation for type parameter target highlight + document.querySelectorAll("a").forEach((link) => { + link.addEventListener("click", (e) => { + const href = e.currentTarget.getAttribute("href"); + if (href && href.startsWith("#") && href.indexOf("type-param-") > -1) { + const target = document.getElementById(decodeURI(href.substring(1))); + if (target) { + target.style.animation = "none"; + void target.offsetHeight; + target.style.removeProperty("animation"); + } + } + }) + }); // Make sure current element is visible in breadcrumb navigation on small displays const subnav = document.querySelector("ol.sub-nav-list"); if (subnav && subnav.lastElementChild) { @@ -286,7 +300,7 @@ document.addEventListener("DOMContentLoaded", function(e) { }); var expanded = false; var windowWidth; - function collapse() { + function collapse(e) { if (expanded) { mainnav.removeAttribute("style"); if (toc) { @@ -336,7 +350,7 @@ document.addEventListener("DOMContentLoaded", function(e) { document.querySelectorAll("h1, h2, h3, h4, h5, h6") .forEach((hdr, idx) => { // Create anchor links for headers with an associated id attribute - var id = hdr.getAttribute("id") || hdr.parentElement.getAttribute("id") + var id = hdr.parentElement.getAttribute("id") || hdr.getAttribute("id") || (hdr.querySelector("a") && hdr.querySelector("a").getAttribute("id")); if (id) { var template = document.createElement('template'); 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 4e37588f6cda5..1130f14dc35b4 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 @@ -63,7 +63,7 @@ --search-input-text-color: #000000; --search-input-placeholder-color: #909090; /* Highlight color for active search tag target */ - --search-tag-highlight-color: #ffff00; + --search-tag-highlight-color: #ffff66; /* Adjustments for icon and active background colors of copy-to-clipboard buttons */ --copy-icon-brightness: 100%; --copy-button-background-color-active: rgba(168, 168, 176, 0.3); @@ -307,7 +307,7 @@ ol.sub-nav-list a.current-selection { */ .title { color:var(--title-color); - margin:10px 0; + margin:10px 0 12px 0; } .sub-title { margin:5px 0 0 0; @@ -988,6 +988,22 @@ input::placeholder { .search-tag-result:target { background-color:var(--search-tag-highlight-color); } +dd > span:target, +h1 > span:target { + animation: 2.4s ease-out highlight; +} +section.class-description dd > span:target, +section.class-description h1 > span:target { + scroll-margin-top: 20em; +} +@keyframes highlight { + from { + background-color: var(--search-tag-highlight-color); + } + 60% { + background-color: var(--search-tag-highlight-color); + } +} details.page-search-details { display: inline-block; } @@ -1040,7 +1056,7 @@ span#page-search-link { z-index: 5; } .inherited-list { - margin: 10px 0 10px 0; + margin: 10px 0; } .horizontal-scroll { overflow: auto hidden; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/BaseTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/BaseTaglet.java index bda31883f35de..7b0a4329ee1c7 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/BaseTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/BaseTaglet.java @@ -33,10 +33,10 @@ import jdk.javadoc.doclet.Taglet.Location; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.Messages; import jdk.javadoc.internal.doclets.toolkit.Resources; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; /** * A base class that implements the {@link Taglet} interface. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DeprecatedTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DeprecatedTaglet.java index d586b697bd771..fa30589e59d3d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DeprecatedTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DeprecatedTaglet.java @@ -35,11 +35,11 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; /** * A taglet that represents the {@code @deprecated} tag. @@ -61,7 +61,7 @@ public Content getAllBlockTagOutput(Element element, TagletWriter tagletWriter) List deprs = utils.getDeprecatedTrees(element); if (utils.isTypeElement(element)) { if (utils.isDeprecated(element)) { - result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel, + result.add(HtmlTree.SPAN(HtmlStyles.deprecatedLabel, htmlWriter.getDeprecatedPhrase(element))); if (!deprs.isEmpty()) { List commentTrees = ch.getDescription(deprs.get(0)); @@ -72,18 +72,18 @@ public Content getAllBlockTagOutput(Element element, TagletWriter tagletWriter) } } else { if (utils.isDeprecated(element)) { - result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel, + result.add(HtmlTree.SPAN(HtmlStyles.deprecatedLabel, htmlWriter.getDeprecatedPhrase(element))); if (!deprs.isEmpty()) { List bodyTrees = ch.getBody(deprs.get(0)); Content body = tagletWriter.commentTagsToOutput(element, null, bodyTrees, false); if (!body.isEmpty()) - result.add(HtmlTree.DIV(HtmlStyle.deprecationComment, body)); + result.add(HtmlTree.DIV(HtmlStyles.deprecationComment, body)); } } else { Element ee = utils.getEnclosingTypeElement(element); if (utils.isDeprecated(ee)) { - result.add(HtmlTree.SPAN(HtmlStyle.deprecatedLabel, + result.add(HtmlTree.SPAN(HtmlStyles.deprecatedLabel, htmlWriter.getDeprecatedPhrase(ee))); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DocRootTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DocRootTaglet.java index 94d701f7441e5..d4050f6e0ec26 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DocRootTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/DocRootTaglet.java @@ -33,8 +33,8 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.Text; /** * An inline taglet representing {@code {@docRoot}}. This taglet is diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/IndexTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/IndexTaglet.java index 4361dd5c1ef0c..6f5077bb32c1f 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/IndexTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/IndexTaglet.java @@ -35,7 +35,7 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.html.Content; /** * An inline taglet used to index a word or a phrase. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java index b7b937a5fc6bd..b05bb144f1b0b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java @@ -39,12 +39,12 @@ import jdk.javadoc.doclet.Taglet.Location; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Result; import jdk.javadoc.internal.doclets.toolkit.util.Utils; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; /** * A taglet that represents the {@code {@inheritDoc}} tag. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java index 1b02c9ea05e07..b16a1490b6303 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,12 +46,12 @@ import jdk.javadoc.internal.doclets.formats.html.ClassWriter; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; import static com.sun.source.doctree.DocTree.Kind.LINK; import static com.sun.source.doctree.DocTree.Kind.LINK_PLAIN; @@ -176,7 +176,7 @@ Content linkSeeReferenceOutput(Element holder, return htmlWriter.getPackageLink(refPackage, labelContent, refFragment); } else { // @see is not referencing an included class, module or package. Check for cross-links. - String refModuleName = ch.getReferencedModuleName(refSignature); + String refModuleName = ch.getReferencedModuleName(refSignature); DocLink elementCrossLink = (refPackage != null) ? htmlWriter.getCrossPackageLink(refPackage) : (config.extern.isModule(refModuleName)) ? htmlWriter.getCrossModuleLink(utils.elementUtils.getModuleElement(refModuleName)) @@ -190,12 +190,28 @@ Content linkSeeReferenceOutput(Element holder, if (!config.isDocLintReferenceGroupEnabled()) { reportWarning.accept( "doclet.link.see.reference_not_found", - new Object[] { refSignature}); + new Object[] {refSignature}); } return htmlWriter.invalidTagOutput(resources.getText("doclet.link.see.reference_invalid"), - Optional.of(labelContent.isEmpty() ? text: labelContent)); + Optional.of(labelContent.isEmpty() ? text : labelContent)); } } + } else if (utils.isTypeParameterElement(ref)) { + // This is a type parameter of a generic class, method or constructor + if (labelContent.isEmpty()) { + labelContent = plainOrCode(isPlain, Text.of(utils.getSimpleName(ref))); + } + if (refMem == null) { + return htmlWriter.getLink( + new HtmlLinkInfo(config, HtmlLinkInfo.Kind.LINK_TYPE_PARAMS, ref.asType()) + .label(labelContent)); + } else { + // HtmlLinkFactory does not render type parameters of generic methods as links, so instead of + // teaching it how to do it (making the code even more complex) just create the link directly. + return htmlWriter.getLink(new HtmlLinkInfo(config, HtmlLinkInfo.Kind.PLAIN, refClass) + .fragment(config.htmlIds.forTypeParam(ref.getSimpleName().toString(), refMem).name()) + .label((labelContent))); + } } else if (refFragment == null) { // Must be a class reference since refClass is not null and refFragment is null. if (labelContent.isEmpty() && refTree != null) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LiteralTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LiteralTaglet.java index 1d209427c214d..c0543026f23dd 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LiteralTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LiteralTaglet.java @@ -34,9 +34,9 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * An inline taglet used to denote literal text, possibly in monospace font. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java index 9963547acf3e7..8a3c47bbfd4b6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,13 +43,13 @@ import jdk.javadoc.internal.doclets.formats.html.Contents; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.formats.html.HtmlIds; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * A taglet that represents the {@code @param} tag. @@ -278,7 +278,9 @@ private Content paramTagOutput(Element element, ParamTree paramTag, String param body.add(" - "); List description = ch.getDescription(paramTag); body.add(htmlWriter.commentTagsToContent(element, description, context.within(paramTag))); - return HtmlTree.DD(body); + return HtmlTree.DD(paramTag.isTypeParameter() + ? HtmlTree.SPAN_ID(config.htmlIds.forTypeParam(paramName, element), body) + : body); } private record Documentation(ParamTree paramTree, ExecutableElement method) { } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java index 6ba5d897c463a..b9572afb9bc4f 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java @@ -41,11 +41,11 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.Contents; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; /** * A taglet that represents the {@code @return} and {@code {@return }} tags. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java index 9052b1a49aa9d..abfb3b526ec08 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java @@ -45,16 +45,16 @@ import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter; import jdk.javadoc.internal.doclets.formats.html.SerializedFormWriter; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.DocPath; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; public class SeeTaglet extends BaseTaglet implements InheritableTaglet { SeeTaglet(HtmlConfiguration config) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java index 110a3e1246eb3..6ce38b3b515ab 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java @@ -42,11 +42,11 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.RawHtml; /** * A custom single-argument block tag. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java index 0642b37406420..af2378c0351d1 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SnippetTaglet.java @@ -51,21 +51,21 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.Action; import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.ParseException; import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.Parser; import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.Style; import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.StyledText; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.DocletElement; import jdk.javadoc.internal.doclets.toolkit.Resources; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; import static jdk.javadoc.internal.doclets.formats.html.taglets.SnippetTaglet.Language.*; @@ -122,13 +122,13 @@ public Content getInlineTagOutput(Element holder, DocTree tag, TagletWriter tagl private Content snippetTagOutput(Element element, SnippetTree tag, StyledText content, String id, String lang) { var pathToRoot = tagletWriter.htmlWriter.pathToRoot; - var pre = new HtmlTree(TagName.PRE).setStyle(HtmlStyle.snippet); + var pre = new HtmlTree(HtmlTag.PRE).setStyle(HtmlStyles.snippet); if (id != null && !id.isBlank()) { pre.put(HtmlAttr.ID, id); } else { pre.put(HtmlAttr.ID, config.htmlIds.forSnippet(element, ids).name()); } - var code = new HtmlTree(TagName.CODE) + var code = new HtmlTree(HtmlTag.CODE) .addUnchecked(Text.EMPTY); // Make sure the element is always rendered if (lang != null && !lang.isBlank()) { code.addStyle("language-" + lang); @@ -196,16 +196,16 @@ private Content snippetTagOutput(Element element, SnippetTree tag, StyledText co String copyText = resources.getText("doclet.Copy_to_clipboard"); String copiedText = resources.getText("doclet.Copied_to_clipboard"); String copySnippetText = resources.getText("doclet.Copy_snippet_to_clipboard"); - var snippetContainer = HtmlTree.DIV(HtmlStyle.snippetContainer, - new HtmlTree(TagName.BUTTON) + var snippetContainer = HtmlTree.DIV(HtmlStyles.snippetContainer, + new HtmlTree(HtmlTag.BUTTON) .add(HtmlTree.SPAN(Text.of(copyText)) .put(HtmlAttr.DATA_COPIED, copiedText)) - .add(new HtmlTree(TagName.IMG) + .add(new HtmlTree(HtmlTag.IMG) .put(HtmlAttr.SRC, pathToRoot.resolve(DocPaths.RESOURCE_FILES) .resolve(DocPaths.CLIPBOARD_SVG).getPath()) .put(HtmlAttr.ALT, copySnippetText)) - .addStyle(HtmlStyle.copy) - .addStyle(HtmlStyle.snippetCopy) + .addStyle(HtmlStyles.copy) + .addStyle(HtmlStyles.snippetCopy) .put(HtmlAttr.ARIA_LABEL, copySnippetText) .put(HtmlAttr.ONCLICK, "copySnippet(this)")); return snippetContainer.add(pre.add(code)); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SpecTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SpecTaglet.java index e55ede7a7e1cf..b408d30b16835 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SpecTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SpecTaglet.java @@ -30,7 +30,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -42,16 +41,16 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.Contents; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.Entity; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.Content; -import jdk.javadoc.internal.doclets.formats.html.markup.TextBuilder; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.Entity; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.RawHtml; +import jdk.javadoc.internal.html.Text; +import jdk.javadoc.internal.html.TextBuilder; /** * A taglet that represents the {@code @spec} tag. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SummaryTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SummaryTaglet.java index 0961c7312d563..7912abef0c6ec 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SummaryTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SummaryTaglet.java @@ -34,7 +34,7 @@ import jdk.javadoc.doclet.Taglet.Location; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.html.Content; /** * A taglet that represents the {@code {@summary}} tag. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SystemPropertyTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SystemPropertyTaglet.java index 3dc72e561a131..4cab9d90ce0d0 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SystemPropertyTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SystemPropertyTaglet.java @@ -34,8 +34,8 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTree; /** * A taglet that represents the {@code @systemProperty} tag. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/Taglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/Taglet.java index 0236287468cb2..c1c6a56e5cbb3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/Taglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/Taglet.java @@ -26,12 +26,14 @@ package jdk.javadoc.internal.doclets.formats.html.taglets; import java.util.Set; + import javax.lang.model.element.Element; import com.sun.source.doctree.DocTree; + import jdk.javadoc.doclet.Taglet.Location; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.HtmlTree; /** * This is the taglet interface used internally within the doclet. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletWriter.java index b977951f2fa73..79b4433a58bc0 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletWriter.java @@ -43,20 +43,13 @@ import javax.lang.model.util.SimpleElementVisitor14; import com.sun.source.doctree.DocTree; - import com.sun.source.doctree.InlineTagTree; + import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter; import jdk.javadoc.internal.doclets.formats.html.HtmlIds; import jdk.javadoc.internal.doclets.formats.html.HtmlOptions; -import jdk.javadoc.internal.doclets.formats.html.IndexWriter; -import jdk.javadoc.internal.doclets.formats.html.SummaryListWriter; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlId; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.formats.html.taglets.Taglet.UnsupportedTagletOperationException; import jdk.javadoc.internal.doclets.toolkit.DocletElement; import jdk.javadoc.internal.doclets.toolkit.Resources; @@ -64,6 +57,11 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocLink; import jdk.javadoc.internal.doclets.toolkit.util.IndexItem; import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlId; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.Text; /** * Context and utility methods for taglet classes. @@ -377,7 +375,7 @@ Content createAnchorAndSearchIndex(Element element, String tagText, Content tagC result = tagContent; } else { HtmlId id = HtmlIds.forText(tagText, htmlWriter.indexAnchorTable); - result = HtmlTree.SPAN(id, HtmlStyle.searchTagResult, tagContent); + result = HtmlTree.SPAN(id, HtmlStyles.searchTagResult, tagContent); if (options.createIndex() && !tagText.isEmpty()) { String holder = getHolderName(element); IndexItem item = IndexItem.of(element, tree, tagText, holder, desc, @@ -457,7 +455,7 @@ private String getHolderName(DocletElement de) { Content tagList(List items) { // Use a different style if any list item is longer than 30 chars or contains commas. boolean hasLongLabels = items.stream().anyMatch(this::isLongOrHasComma); - var list = HtmlTree.UL(hasLongLabels ? HtmlStyle.tagListLong : HtmlStyle.tagList); + var list = HtmlTree.UL(hasLongLabels ? HtmlStyles.tagListLong : HtmlStyles.tagList); items.stream() .filter(Predicate.not(Content::isEmpty)) .forEach(item -> list.add(HtmlTree.LI(item))); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java index 131f0350e5e13..ca4a8c939abba 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java @@ -57,12 +57,12 @@ import jdk.javadoc.internal.doclets.formats.html.Contents; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo; -import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; import jdk.javadoc.internal.doclets.toolkit.util.Utils; import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.ContentBuilder; +import jdk.javadoc.internal.html.HtmlTree; /** * A taglet that processes {@link ThrowsTree}, which represents {@code @throws} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/UserTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/UserTaglet.java index bcfdd6682ed29..cc5ae21ba2965 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/UserTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/UserTaglet.java @@ -32,8 +32,8 @@ import com.sun.source.doctree.DocTree; -import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; -import jdk.javadoc.internal.doclets.formats.html.Content; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.RawHtml; /** * A taglet wrapper, allows the public taglet {@link jdk.javadoc.doclet.Taglet} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ValueTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ValueTaglet.java index 006277eeeb006..a4fddef0f117a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ValueTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ValueTaglet.java @@ -39,10 +39,10 @@ import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration; import jdk.javadoc.internal.doclets.formats.html.HtmlLinkInfo; -import jdk.javadoc.internal.doclets.formats.html.markup.Text; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; -import jdk.javadoc.internal.doclets.formats.html.Content; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; +import jdk.javadoc.internal.html.Content; +import jdk.javadoc.internal.html.Text; /** * An inline taglet representing the value tag. This tag should only be used with diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties index 4e84cdc52c77f..7ab2d3fc1dcaf 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_de.properties @@ -278,9 +278,9 @@ doclet.record_constructor_doc.param_name=Wert für die Datensatzkomponente {0} doclet.record_equals_doc.fullbody.head=Gibt an, ob ein anderes Objekt diesem gleich ("equal to") ist. Die Objekte sind gleich, wenn das andere Objekt der gleichen Klasse angehört und alle Datensatzkomponenten gleich sind. -doclet.record_equals_doc.fullbody.tail.both=Referenzkomponenten werden verglichen mit {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}; primitive Komponenten werden verglichen mit "==". +doclet.record_equals_doc.fullbody.tail.both=Referenzkomponenten werden verglichen mit {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}. Primitive Komponenten werden mit der compare-Methode aus den entsprechenden Wrapper-Klassen verglichen. -doclet.record_equals_doc.fullbody.tail.primitive=Alle Komponenten in dieser Datensatzklasse werden verglichen mit "==". +doclet.record_equals_doc.fullbody.tail.primitive=Alle Komponenten dieser Datensatzklasse werden mit der compare-Methode aus den entsprechenden Wrapper-Klassen verglichen. doclet.record_equals_doc.fullbody.tail.reference=Alle Komponenten in dieser Datensatzklasse werden verglichen mit {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties index 515df706272d8..1b00ea9fbc02c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_ja.properties @@ -278,9 +278,9 @@ doclet.record_constructor_doc.param_name={0}レコード・コンポーネント doclet.record_equals_doc.fullbody.head=他のオブジェクトがこれと"等しい"かどうかを示します。他のオブジェクトが同じクラスであり、すべてのレコード・コンポーネントが等しい場合、オブジェクトは等しくなります。 -doclet.record_equals_doc.fullbody.tail.both=参照コンポーネントは{@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}と比較され、プリミティブ・コンポーネントは'=='と比較されます。 +doclet.record_equals_doc.fullbody.tail.both=参照コンポーネントは{@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}と比較され、プリミティブ・コンポーネントは対応するラッパー・クラスのcompareメソッドで比較されます。 -doclet.record_equals_doc.fullbody.tail.primitive=このレコード・クラスのすべてのコンポーネントは'=='と比較されます。 +doclet.record_equals_doc.fullbody.tail.primitive=このレコード・クラスのすべてのコンポーネントは対応するラッパー・クラスのcompareメソッドで比較されます。 doclet.record_equals_doc.fullbody.tail.reference=このレコード・クラスのすべてのコンポーネントは{@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)}と比較されます。 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties index d01d42faa56a4..e9786173c5f1d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_zh_CN.properties @@ -278,9 +278,9 @@ doclet.record_constructor_doc.param_name={0} 记录组件的值 doclet.record_equals_doc.fullbody.head=指示某个其他对象是否“等于”此对象。如果两个对象属于同一个类,而且所有记录组件都相等,则这两个对象相等。 -doclet.record_equals_doc.fullbody.tail.both=使用 {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)} 对参考组件进行比较;使用 '==' 对基元组件进行比较 +doclet.record_equals_doc.fullbody.tail.both=使用 {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)} 对参考组件进行比较;使用 compare 方法从对应的包装类对基元组件进行比较。 -doclet.record_equals_doc.fullbody.tail.primitive=此记录类中的所有组件都使用 '==' 进行比较。 +doclet.record_equals_doc.fullbody.tail.primitive=此记录类中的所有组件都使用 compare 方法从对应的包装类进行比较。 doclet.record_equals_doc.fullbody.tail.reference=此记录类中的所有组件都使用 {@link java.util.Objects#equals(Object,Object) Objects::equals(Object,Object)} 进行比较。 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java index d7a0bd2d0a070..30aa86aea7196 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/CommentHelper.java @@ -188,6 +188,12 @@ public Element getReferencedMember(Element e) { Utils utils = configuration.utils; if (e == null) { return null; + } else if (utils.isTypeParameterElement(e)) { + // Return the enclosing member for type parameters of generic methods or constructors. + Element encl = e.getEnclosingElement(); + if (utils.isExecutableElement(encl)) { + return encl; + } } return (utils.isExecutableElement(e) || utils.isVariableElement(e)) ? e : null; } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java index d33e4874c008a..7bc5563950186 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java @@ -101,9 +101,16 @@ import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; -import jdk.javadoc.internal.doclint.HtmlTag.AttrKind; -import jdk.javadoc.internal.doclint.HtmlTag.ElemKind; -import static jdk.javadoc.internal.doclint.Messages.Group.*; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlAttr.AttrKind; +import jdk.javadoc.internal.html.HtmlTag; +import jdk.javadoc.internal.html.HtmlTag.ElemKind; + +import static jdk.javadoc.internal.doclint.Messages.Group.ACCESSIBILITY; +import static jdk.javadoc.internal.doclint.Messages.Group.HTML; +import static jdk.javadoc.internal.doclint.Messages.Group.MISSING; +import static jdk.javadoc.internal.doclint.Messages.Group.REFERENCE; +import static jdk.javadoc.internal.doclint.Messages.Group.SYNTAX; /** @@ -132,12 +139,12 @@ public enum Flag { static class TagStackItem { final DocTree tree; // typically, but not always, StartElementTree final HtmlTag tag; - final Set attrs; + final Set attrs; final Set flags; TagStackItem(DocTree tree, HtmlTag tag) { this.tree = tree; this.tag = tag; - attrs = EnumSet.noneOf(HtmlTag.Attr.class); + attrs = EnumSet.noneOf(HtmlAttr.class); flags = EnumSet.noneOf(Flag.class); } @Override @@ -399,7 +406,7 @@ void checkAllowsText(DocTree tree) { @Override @DefinedBy(Api.COMPILER_TREE) public Void visitStartElement(StartElementTree tree, Void ignore) { final Name treeName = tree.getName(); - final HtmlTag t = HtmlTag.get(treeName); + final HtmlTag t = HtmlTag.of(treeName); if (t == null) { env.messages.error(HTML, tree, "dc.tag.unknown", treeName); } else if (t.elemKind == ElemKind.HTML4) { @@ -472,7 +479,7 @@ public Void visitStartElement(StartElementTree tree, Void ignore) { } case IMG -> { - if (!top.attrs.contains(HtmlTag.Attr.ALT)) + if (!top.attrs.contains(HtmlAttr.ALT)) env.messages.error(ACCESSIBILITY, tree, "dc.no.alt.attr.for.image"); } } @@ -592,7 +599,7 @@ private int getHeadingRank(HtmlTag tag) { @Override @DefinedBy(Api.COMPILER_TREE) public Void visitEndElement(EndElementTree tree, Void ignore) { final Name treeName = tree.getName(); - final HtmlTag t = HtmlTag.get(treeName); + final HtmlTag t = HtmlTag.of(treeName); if (t == null) { env.messages.error(HTML, tree, "dc.tag.unknown", treeName); } else if (t.endKind == HtmlTag.EndKind.NONE) { @@ -605,7 +612,7 @@ public Void visitEndElement(EndElementTree tree, Void ignore) { switch (t) { case TABLE -> { if (!top.flags.contains(Flag.TABLE_IS_PRESENTATION) - && !top.attrs.contains(HtmlTag.Attr.SUMMARY) + && !top.attrs.contains(HtmlAttr.SUMMARY) && !top.flags.contains(Flag.TABLE_HAS_CAPTION)) { env.messages.error(ACCESSIBILITY, tree, "dc.no.summary.or.caption.for.table"); @@ -682,7 +689,7 @@ public Void visitAttribute(AttributeTree tree, Void ignore) { HtmlTag currTag = tagStack.peek().tag; if (currTag != null && currTag.elemKind != ElemKind.HTML4) { Name name = tree.getName(); - HtmlTag.Attr attr = currTag.getAttr(name); + HtmlAttr attr = currTag.getAttr(name); if (attr != null) { boolean first = tagStack.peek().attrs.add(attr); if (!first) @@ -758,19 +765,21 @@ public Void visitAttribute(AttributeTree tree, Void ignore) { String v = getAttrValue(tree); try { if (v == null || (!v.isEmpty() && Integer.parseInt(v) != 1)) { - env.messages.error(HTML, tree, "dc.attr.table.border.not.valid", attr); + env.messages.error(HTML, tree, "dc.attr.table.border.not.valid", + (v == null ? tree : v)); } } catch (NumberFormatException ex) { - env.messages.error(HTML, tree, "dc.attr.table.border.not.number", attr); + env.messages.error(HTML, tree, "dc.attr.table.border.not.number", v); } } else if (currTag == HtmlTag.IMG) { String v = getAttrValue(tree); try { if (v == null || (!v.isEmpty() && Integer.parseInt(v) != 0)) { - env.messages.error(HTML, tree, "dc.attr.img.border.not.valid", attr); + env.messages.error(HTML, tree, "dc.attr.img.border.not.valid", + (v == null ? tree : v)); } } catch (NumberFormatException ex) { - env.messages.error(HTML, tree, "dc.attr.img.border.not.number", attr); + env.messages.error(HTML, tree, "dc.attr.img.border.not.number", v); } } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Comment.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Comment.java similarity index 94% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Comment.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Comment.java index aa563eeded768..77b456cc7ffe8 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Comment.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Comment.java @@ -23,14 +23,12 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; import java.util.Objects; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * Class for generating a comment for HTML pages of javadoc output. */ diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Content.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Content.java similarity index 99% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Content.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Content.java index 13a2bb4c1bf8b..63142f8301138 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Content.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Content.java @@ -23,7 +23,7 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.StringWriter; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/ContentBuilder.java similarity index 96% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/ContentBuilder.java index b059b0fdb3660..358b1591dfc66 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/ContentBuilder.java @@ -23,7 +23,7 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; @@ -31,8 +31,6 @@ import java.util.List; import java.util.Objects; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * A sequence of Content nodes. */ diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/DocType.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/DocType.java similarity index 96% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/DocType.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/DocType.java index dd4b50eb76089..5e6bf502fe111 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/DocType.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/DocType.java @@ -23,7 +23,7 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; /** * Supported DOCTYPE declarations. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Entity.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Entity.java similarity index 97% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Entity.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Entity.java index 542e081a58385..fbc7e08761626 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Entity.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Entity.java @@ -23,9 +23,7 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; - -import jdk.javadoc.internal.doclets.formats.html.Content; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; 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 new file mode 100644 index 0000000000000..ccb193d6ab9ad --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.html; + +import java.util.HashMap; +import java.util.Map; +import java.util.Locale; + + +/** + * An abstraction for the type-safe representation and use of HTML attributes. + * + * @apiNote + * Attributes are used when performing simple validity checks on HTML in + * documentation comments, and when generating HTML for output. + * + * @see HtmlTree#put(HtmlAttr, String) + */ +public enum HtmlAttr { + ABBR, + ACCESSKEY(true), + ALIGN, + ALINK, + ALT, + ARIA_ACTIVEDESCENDANT(true), + ARIA_CONTROLS(true), + ARIA_DESCRIBEDBY(true), + ARIA_EXPANDED(true), + ARIA_LABEL(true), + ARIA_LABELLEDBY(true), + ARIA_LEVEL(true), + ARIA_MULTISELECTABLE(true), + ARIA_ORIENTATION(true), + ARIA_OWNS(true), + ARIA_POSINSET(true), + ARIA_READONLY(true), + ARIA_REQUIRED(true), + ARIA_SELECTED(true), + ARIA_SETSIZE(true), + ARIA_SORT(true), + AUTOCAPITALIZE(true), + AUTOCOMPLETE, + AUTOFOCUS(true), + AXIS, + BACKGROUND, + BGCOLOR, + BORDER, + CELLPADDING, + CELLSPACING, + CHAR, + CHAROFF, + CHARSET, + CHECKED, + CITE, + CLASS(true), + CLEAR, + COLOR, + COLS, + COLSPAN, + COMPACT, + CONTENT, + CONTENTEDITABLE(true), + COORDS, + CROSSORIGIN, + DATA_COPIED, // custom HTML5 data attribute + DATETIME, + DIR(true), + DISABLED, + DRAGGABLE(true), + ENTERKEYHINT(true), + FACE, + FOR, + FORM, + FRAME, + FRAMEBORDER, + HEADERS, + HEIGHT, + HIDDEN(true), + HREF, + HSPACE, + HTTP_EQUIV, + ID(true), + INERT(true), + INPUTMODE(true), + IS(true), + ITEMID(true), + ITEMPROP(true), + ITEMREF(true), + ITEMSCOPE(true), + ITEMTYPE(true), + LANG(true), + LINK, + LONGDESC, + MARGINHEIGHT, + MARGINWIDTH, + NAME, + NONCE(true), + NOSHADE, + NOWRAP, + ONCLICK, + ONKEYDOWN, + ONLOAD, + PLACEHOLDER, + POPOVER(true), + PROFILE, + REL, + REV, + REVERSED, + ROLE(true), + ROWS, + ROWSPAN, + RULES, + SCHEME, + SCOPE, + SCROLLING, + SHAPE, + SIZE, + SPACE, + SPELLCHECK(true), + SRC, + START, + STYLE(true), + SUMMARY, + TABINDEX(true), + TARGET, + TEXT, + TITLE(true), + TRANSLATE(true), + TYPE, + VALIGN, + VALUE, + VERSION, + VLINK, + VSPACE, + WIDTH, + WRITINGSUGGESTIONS(true); + + /** + * The "external" name of this attribute. + */ + private final String name; + + /** + * Whether this is a global attribute, that can be used with all HTML tags. + */ + private final boolean isGlobal; + + /** + * An abstraction for the type-safe representation and use of ARIA roles. + * + * @see HtmlTree#setRole(Role) + */ + public enum Role { + + BANNER, + CONTENTINFO, + MAIN, + NAVIGATION, + REGION; + + private final String role; + + Role() { + role = name().toLowerCase(Locale.ROOT); + } + + public String toString() { + return role; + } + } + + /** + * An abstraction for the type-safe representation and use of "input" types. + * + * @see HtmlTree#INPUT(InputType, HtmlId) + * @see HtmlTree#INPUT(InputType, HtmlStyle) + */ + public enum InputType { + + CHECKBOX, + RESET, + TEXT; + + private final String type; + + InputType() { + type = name().toLowerCase(Locale.ROOT); + } + + public String toString() { + return type; + } + } + + /** + * An abstraction for the kind of an attribute in the context of an HTML tag. + * + * @see HtmlTag#attrs(AttrKind,HtmlAttr...) + */ + public enum AttrKind { + OK, + INVALID, + OBSOLETE, + HTML4 + } + + HtmlAttr() { + this(false); + } + + HtmlAttr(boolean flag) { + name = name().toLowerCase(Locale.ROOT).replace("_", "-"); + isGlobal = flag; + } + + /** + * {@return the "external" name of this attribute} + * The external name is the name of the enum member in lower case with {@code _} replaced by {@code -}. + */ + public String getName() { + return name; + } + + /** + * {@return whether this attribute is a global attribute, that may appear on all tags} + */ + public boolean isGlobal() { + return isGlobal; + } + + // FIXME: this is used in doclint Checker, when generating messages + @Override + public String toString() { + return name; + } + + private static final Map index = new HashMap<>(); + static { + for (HtmlAttr t : values()) { + index.put(t.getName(), t); + } + } + + /** + * {@return the attribute with the given name, or {@code null} if there is no known attribute} + * + * @param name the name + */ + public static HtmlAttr of(CharSequence name) { + return index.get(name.toString().toLowerCase(Locale.ROOT)); + } +} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlId.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlId.java similarity index 96% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlId.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlId.java index 1e0db271ef544..a6af14382848b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlId.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlId.java @@ -23,7 +23,7 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; /** * A type-safe wrapper around a {@code String}, for use as an "id" diff --git a/src/java.base/share/classes/java/lang/classfile/WritableElement.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlStyle.java similarity index 52% rename from src/java.base/share/classes/java/lang/classfile/WritableElement.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlStyle.java index 9a4db4e370f4b..9b77d5de8b49f 100644 --- a/src/java.base/share/classes/java/lang/classfile/WritableElement.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlStyle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,32 +22,21 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.classfile; -import java.lang.classfile.constantpool.ConstantPoolBuilder; -import java.lang.classfile.constantpool.PoolEntry; -import jdk.internal.classfile.impl.DirectFieldBuilder; -import jdk.internal.classfile.impl.DirectMethodBuilder; -import jdk.internal.javac.PreviewFeature; +package jdk.javadoc.internal.html; /** - * A classfile element that can encode itself as a stream of bytes in the - * encoding expected by the classfile format. + * An abstraction for the type-safe representation and use of CSS class names. * - * @param the type of the entity + * @apiNote + * Despite the name, implementations of this interface provide values for the HTML + * {@code class} attribute, and not the HTML {@code style} attribute. + * This is to avoid confusion with the widespread use of the word "class" in the Java ecosystem, + * and the potential for clashes with methods called {@code setClass} instead of {@code setStyle}. * - * @sealedGraph - * @since 22 + * @see HtmlTree#addStyle(HtmlStyle) + * @see HtmlTree#setStyle(HtmlStyle) */ -@PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) -public sealed interface WritableElement extends ClassFileElement - permits Annotation, AnnotationElement, AnnotationValue, Attribute, - PoolEntry, BootstrapMethodEntry, FieldModel, MethodModel, - ConstantPoolBuilder, DirectFieldBuilder, DirectMethodBuilder { - /** - * Writes the element to the specified writer - * - * @param buf the writer - */ - void writeTo(BufWriter buf); +public interface HtmlStyle { + String cssName(); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/HtmlTag.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTag.java similarity index 72% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/HtmlTag.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTag.java index fa9d3ea578dd4..7b198a066315a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/HtmlTag.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTag.java @@ -23,18 +23,18 @@ * questions. */ -package jdk.javadoc.internal.doclint; +package jdk.javadoc.internal.html; +import java.io.Serial; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; import javax.lang.model.element.Name; -import com.sun.tools.javac.util.StringUtils; - -import static jdk.javadoc.internal.doclint.HtmlTag.Attr.*; +import static jdk.javadoc.internal.html.HtmlAttr.*; /** * Enum representing HTML tags. @@ -42,10 +42,6 @@ * The intent of this class is to embody the semantics of the current HTML standard, * to the extent supported/used by javadoc. * - * This class is derivative of {@link jdk.javadoc.internal.doclets.formats.html.markup.TagName}. - * Eventually, these two should be merged back together, and possibly made - * public. - * * @see HTML Living Standard * @see HTML 5 Specification * @see HTML 4.01 Specification @@ -88,6 +84,9 @@ public enum HtmlTag { BR(BlockType.INLINE, EndKind.NONE, attrs(AttrKind.HTML4, CLEAR)), + BUTTON(BlockType.INLINE, EndKind.REQUIRED, + attrs(AttrKind.OK, FORM, NAME, TYPE, VALUE)), + CAPTION(BlockType.TABLE_ITEM, EndKind.REQUIRED, EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT), attrs(AttrKind.HTML4, ALIGN)), @@ -117,7 +116,10 @@ public boolean accepts(HtmlTag t) { DEL(BlockType.INLINE, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST), - attrs(AttrKind.OK, Attr.CITE, Attr.DATETIME)), + attrs(AttrKind.OK, HtmlAttr.CITE, HtmlAttr.DATETIME)), + + DETAILS(BlockType.BLOCK, EndKind.REQUIRED, + EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), DFN(BlockType.INLINE, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), @@ -149,12 +151,10 @@ public boolean accepts(HtmlTag t) { EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)) { @Override public boolean accepts(HtmlTag t) { - switch (t) { - case HEADER: case FOOTER: case MAIN: - return false; - default: - return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); - } + return switch (t) { + case HEADER, FOOTER, MAIN -> false; + default -> (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); + }; } }, @@ -186,12 +186,10 @@ public boolean accepts(HtmlTag t) { EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)) { @Override public boolean accepts(HtmlTag t) { - switch (t) { - case HEADER: case FOOTER: case MAIN: - return false; - default: - return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); - } + return switch (t) { + case HEADER, FOOTER, MAIN -> false; + default -> (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); + }; } }, @@ -209,19 +207,25 @@ public boolean accepts(HtmlTag t) { attrs(AttrKind.OK, SRC, ALT, HEIGHT, WIDTH, CROSSORIGIN), attrs(AttrKind.HTML4, NAME, ALIGN, HSPACE, VSPACE, BORDER)), + INPUT(BlockType.INLINE, EndKind.NONE, + attrs(AttrKind.OK, NAME, TYPE, VALUE)), + INS(BlockType.INLINE, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST), - attrs(AttrKind.OK, Attr.CITE, Attr.DATETIME)), + attrs(AttrKind.OK, HtmlAttr.CITE, HtmlAttr.DATETIME)), KBD(BlockType.INLINE, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), + LABEL(BlockType.INLINE, EndKind.REQUIRED), + LI(BlockType.LIST_ITEM, EndKind.OPTIONAL, EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), attrs(AttrKind.OK, VALUE), attrs(AttrKind.HTML4, TYPE)), - LINK(BlockType.OTHER, EndKind.NONE), + LINK(BlockType.INLINE, EndKind.NONE, + attrs(AttrKind.OK, REL)), MAIN(BlockType.OTHER, EndKind.REQUIRED), @@ -262,12 +266,10 @@ public boolean accepts(HtmlTag t) { attrs(AttrKind.HTML4, WIDTH)) { @Override public boolean accepts(HtmlTag t) { - switch (t) { - case IMG: case BIG: case SMALL: case SUB: case SUP: - return false; - default: - return (t.blockType == BlockType.INLINE); - } + return switch (t) { + case IMG, BIG, SMALL, SUB, SUP -> false; + default -> (t.blockType == BlockType.INLINE); + }; } }, @@ -280,8 +282,8 @@ public boolean accepts(HtmlTag t) { SAMP(BlockType.INLINE, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), - SCRIPT(BlockType.OTHER, EndKind.REQUIRED, - attrs(AttrKind.OK, SRC)), + SCRIPT(BlockType.INLINE, EndKind.REQUIRED, + attrs(AttrKind.OK, SRC, TYPE)), SECTION(BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), @@ -303,25 +305,22 @@ public boolean accepts(HtmlTag t) { SUB(BlockType.INLINE, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), + SUMMARY(BlockType.BLOCK, EndKind.REQUIRED), + SUP(BlockType.INLINE, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), TABLE(BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT), attrs(AttrKind.OK, BORDER), - attrs(AttrKind.HTML4, SUMMARY, CELLPADDING, CELLSPACING, - Attr.FRAME, RULES, WIDTH, ALIGN, BGCOLOR)) { + attrs(AttrKind.HTML4, HtmlAttr.SUMMARY, CELLPADDING, CELLSPACING, + HtmlAttr.FRAME, RULES, WIDTH, ALIGN, BGCOLOR)) { @Override public boolean accepts(HtmlTag t) { - switch (t) { - case CAPTION: - case COLGROUP: - case THEAD: case TBODY: case TFOOT: - case TR: // HTML 3.2 - return true; - default: - return false; - } + return switch (t) { + case CAPTION, COLGROUP, THEAD, TBODY, TFOOT, TR -> true; + default -> false; + }; } }, @@ -337,7 +336,7 @@ public boolean accepts(HtmlTag t) { TD(BlockType.TABLE_ITEM, EndKind.OPTIONAL, EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS), - attrs(AttrKind.HTML4, AXIS, Attr.ABBR, SCOPE, ALIGN, VALIGN, CHAR, CHAROFF, + attrs(AttrKind.HTML4, AXIS, HtmlAttr.ABBR, SCOPE, ALIGN, VALIGN, CHAR, CHAROFF, WIDTH, BGCOLOR, HEIGHT, NOWRAP)), TEMPLATE(BlockType.BLOCK, EndKind.REQUIRED, @@ -353,7 +352,7 @@ public boolean accepts(HtmlTag t) { TH(BlockType.TABLE_ITEM, EndKind.OPTIONAL, EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), - attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, Attr.ABBR), + attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, HtmlAttr.ABBR), attrs(AttrKind.HTML4, WIDTH, BGCOLOR, HEIGHT, NOWRAP, AXIS, ALIGN, CHAR, CHAROFF, VALIGN)), THEAD(BlockType.TABLE_ITEM, EndKind.REQUIRED, @@ -408,6 +407,7 @@ public enum ElemKind { /** * Enum representing the type of HTML element. */ + // See JDK-8337586 for suggestions public enum BlockType { BLOCK, INLINE, @@ -432,150 +432,13 @@ public enum Flag { NO_NEST } - public enum Attr { - ABBR, - ACCESSKEY(true), - ALIGN, - ALINK, - ALT, - ARIA_ACTIVEDESCENDANT(true), - ARIA_CONTROLS(true), - ARIA_DESCRIBEDBY(true), - ARIA_EXPANDED(true), - ARIA_LABEL(true), - ARIA_LABELLEDBY(true), - ARIA_LEVEL(true), - ARIA_MULTISELECTABLE(true), - ARIA_OWNS(true), - ARIA_POSINSET(true), - ARIA_READONLY(true), - ARIA_REQUIRED(true), - ARIA_SELECTED(true), - ARIA_SETSIZE(true), - ARIA_SORT(true), - AUTOCAPITALIZE(true), - AUTOFOCUS(true), - AXIS, - BACKGROUND, - BGCOLOR, - BORDER, - CELLPADDING, - CELLSPACING, - CHAR, - CHAROFF, - CHARSET, - CITE, - CLASS(true), - CLEAR, - COLOR, - COLSPAN, - COMPACT, - CONTENTEDITABLE(true), - COORDS, - CROSSORIGIN, - DATETIME, - DIR(true), - DRAGGABLE(true), - ENTERKEYHINT(true), - FACE, - FRAME, - FRAMEBORDER, - HEADERS, - HEIGHT, - HIDDEN(true), - HREF, - HSPACE, - ID(true), - INERT(true), - INPUTMODE(true), - IS(true), - ITEMID(true), - ITEMPROP(true), - ITEMREF(true), - ITEMSCOPE(true), - ITEMTYPE(true), - LANG(true), - LINK, - LONGDESC, - MARGINHEIGHT, - MARGINWIDTH, - NAME, - NONCE(true), - NOSHADE, - NOWRAP, - POPOVER(true), - PROFILE, - REV, - REVERSED, - ROLE(true), - ROWSPAN, - RULES, - SCHEME, - SCOPE, - SCROLLING, - SHAPE, - SIZE, - SPACE, - SPELLCHECK(true), - SRC, - START, - STYLE(true), - SUMMARY, - TABINDEX(true), - TARGET, - TEXT, - TITLE(true), - TRANSLATE(true), - TYPE, - VALIGN, - VALUE, - VERSION, - VLINK, - VSPACE, - WIDTH, - WRITINGSUGGESTIONS(true); - - private final String name; - private final boolean isGlobal; - - Attr() { - this(false); - } - - Attr(boolean flag) { - name = StringUtils.toLowerCase(name().replace("_", "-")); - isGlobal = flag; - } - - public boolean isGlobal() { - return isGlobal; - } - - public String getText() { - return name; - } - - static final Map index = new HashMap<>(); - static { - for (Attr t: values()) { - index.put(t.getText(), t); - } - } - } - - public enum AttrKind { - OK, - INVALID, - OBSOLETE, - HTML4 - } - // This class exists to avoid warnings from using parameterized vararg type // Map in signature of HtmlTag constructor. - private static class AttrMap extends EnumMap { + private static class AttrMap extends EnumMap { + @Serial private static final long serialVersionUID = 0; AttrMap() { - super(Attr.class); + super(HtmlAttr.class); } } @@ -584,7 +447,7 @@ private static class AttrMap extends EnumMap { public final BlockType blockType; public final EndKind endKind; public final Set flags; - private final Map attrs; + private final Map attrs; HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps) { this(ElemKind.OK, blockType, endKind, Set.of(), attrMaps); @@ -603,8 +466,8 @@ private static class AttrMap extends EnumMap { this.blockType = blockType; this.endKind = endKind; this.flags = flags; - this.attrs = new EnumMap<>(Attr.class); - for (Map m: attrMaps) + this.attrs = new EnumMap<>(HtmlAttr.class); + for (Map m: attrMaps) this.attrs.putAll(m); } @@ -615,20 +478,18 @@ public boolean accepts(HtmlTag t) { return (t.blockType == BlockType.BLOCK); } else if (flags.contains(Flag.ACCEPTS_INLINE)) { return (t.blockType == BlockType.INLINE); - } else - switch (blockType) { - case BLOCK: - case INLINE: - return (t.blockType == BlockType.INLINE); - case OTHER: + } else { + // any combination which could otherwise arrive here + // ought to have been handled in an overriding method + return switch (blockType) { + case BLOCK, INLINE -> (t.blockType == BlockType.INLINE); + case OTHER -> // OTHER tags are invalid in doc comments, and will be // reported separately, so silently accept/ignore any content - return true; - default: - // any combination which could otherwise arrive here - // ought to have been handled in an overriding method - throw new AssertionError(this + ":" + t); - } + true; + default -> throw new AssertionError(this + ":" + t); + }; + } } public boolean acceptsText() { @@ -637,16 +498,16 @@ public boolean acceptsText() { return accepts(B); } - public String getText() { - return StringUtils.toLowerCase(name()); + public String getName() { + return name().toLowerCase(Locale.ROOT).replace("_", "-"); } - public Attr getAttr(Name attrName) { - return Attr.index.get(StringUtils.toLowerCase(attrName.toString())); + public HtmlAttr getAttr(Name attrName) { + return HtmlAttr.of(attrName); } public AttrKind getAttrKind(Name attrName) { - Attr attr = getAttr(attrName); + HtmlAttr attr = getAttr(attrName); if (attr == null) { return AttrKind.INVALID; } @@ -655,20 +516,20 @@ public AttrKind getAttrKind(Name attrName) { attrs.getOrDefault(attr, AttrKind.INVALID); } - private static AttrMap attrs(AttrKind k, Attr... attrs) { + private static AttrMap attrs(AttrKind k, HtmlAttr... attrs) { AttrMap map = new AttrMap(); - for (Attr a: attrs) map.put(a, k); + for (HtmlAttr a : attrs) map.put(a, k); return map; } private static final Map index = new HashMap<>(); static { for (HtmlTag t: values()) { - index.put(t.getText(), t); + index.put(t.getName(), t); } } - public static HtmlTag get(Name tagName) { - return index.get(StringUtils.toLowerCase(tagName.toString())); + public static HtmlTag of(CharSequence tagName) { + return index.get(tagName.toString().toLowerCase(Locale.ROOT)); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java similarity index 90% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java index 57adb96632612..f60537511746d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java @@ -23,7 +23,7 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; @@ -38,9 +38,6 @@ import java.util.Objects; import java.util.function.Function; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr.Role; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * A tree node representing an HTML element, containing the name of the element, * a collection of attributes, and content. @@ -63,10 +60,10 @@ public class HtmlTree extends Content { /** - * The name of the HTML element. + * The tag for the HTML element. * This value is never {@code null}. */ - public final TagName tagName; + public final HtmlTag tag; /** * The attributes for the HTML element. @@ -84,10 +81,10 @@ public class HtmlTree extends Content { * Creates an {@code HTMLTree} object representing an HTML element * with the given name. * - * @param tagName the name + * @param tag the name */ - public HtmlTree(TagName tagName) { - this.tagName = Objects.requireNonNull(tagName); + public HtmlTree(HtmlTag tag) { + this.tag = Objects.requireNonNull(tag); } /** @@ -131,7 +128,7 @@ public HtmlTree setTitle(Content body) { * @param role the role * @return this object */ - public HtmlTree setRole(Role role) { + public HtmlTree setRole(HtmlAttr.Role role) { return put(HtmlAttr.ROLE, role.toString()); } @@ -347,7 +344,7 @@ public static String encodeURL(String url) { * @return the element */ public static HtmlTree A(String ref, Content body) { - return new HtmlTree(TagName.A) + return new HtmlTree(HtmlTag.A) .put(HtmlAttr.HREF, encodeURL(ref)) .add(body); } @@ -363,7 +360,7 @@ public static HtmlTree A(String ref, Content body) { * @return the element */ public static HtmlTree A(URI ref, Content body) { - return new HtmlTree(TagName.A) + return new HtmlTree(HtmlTag.A) .put(HtmlAttr.HREF, ref.toASCIIString()) .add(body); } @@ -375,7 +372,7 @@ public static HtmlTree A(URI ref, Content body) { * @return the element */ public static HtmlTree CAPTION(Content body) { - return new HtmlTree(TagName.CAPTION) + return new HtmlTree(HtmlTag.CAPTION) .add(body); } @@ -386,7 +383,7 @@ public static HtmlTree CAPTION(Content body) { * @return the element */ public static HtmlTree CODE(Content body) { - return new HtmlTree(TagName.CODE) + return new HtmlTree(HtmlTag.CODE) .add(body); } @@ -397,7 +394,7 @@ public static HtmlTree CODE(Content body) { * @return the element */ public static HtmlTree DD(Content body) { - return new HtmlTree(TagName.DD) + return new HtmlTree(HtmlTag.DD) .add(body); } @@ -407,7 +404,7 @@ public static HtmlTree DD(Content body) { * @return the element */ public static HtmlTree DETAILS() { - return new HtmlTree(TagName.DETAILS); + return new HtmlTree(HtmlTag.DETAILS); } /** @@ -416,7 +413,7 @@ public static HtmlTree DETAILS() { * @return the element */ public static HtmlTree DETAILS(HtmlStyle style) { - return new HtmlTree(TagName.DETAILS) + return new HtmlTree(HtmlTag.DETAILS) .setStyle(style); } @@ -427,7 +424,7 @@ public static HtmlTree DETAILS(HtmlStyle style) { * @return the element */ public static HtmlTree DL(HtmlStyle style) { - return new HtmlTree(TagName.DL) + return new HtmlTree(HtmlTag.DL) .setStyle(style); } @@ -439,7 +436,7 @@ public static HtmlTree DL(HtmlStyle style) { * @return the element */ public static HtmlTree DL(HtmlStyle style, Content body) { - return new HtmlTree(TagName.DL) + return new HtmlTree(HtmlTag.DL) .setStyle(style) .add(body); } @@ -451,7 +448,7 @@ public static HtmlTree DL(HtmlStyle style, Content body) { * @return the element */ public static HtmlTree DIV(HtmlStyle style) { - return new HtmlTree(TagName.DIV) + return new HtmlTree(HtmlTag.DIV) .setStyle(style); } @@ -463,7 +460,7 @@ public static HtmlTree DIV(HtmlStyle style) { * @return the element */ public static HtmlTree DIV(HtmlStyle style, Content body) { - return new HtmlTree(TagName.DIV) + return new HtmlTree(HtmlTag.DIV) .setStyle(style) .add(body); } @@ -475,7 +472,7 @@ public static HtmlTree DIV(HtmlStyle style, Content body) { * @return the element */ public static HtmlTree DIV(Content body) { - return new HtmlTree(TagName.DIV) + return new HtmlTree(HtmlTag.DIV) .add(body); } @@ -486,7 +483,7 @@ public static HtmlTree DIV(Content body) { * @return the element */ public static HtmlTree DT(Content body) { - return new HtmlTree(TagName.DT) + return new HtmlTree(HtmlTag.DT) .add(body); } @@ -497,8 +494,8 @@ public static HtmlTree DT(Content body) { * @return the element */ public static HtmlTree FOOTER() { - return new HtmlTree(TagName.FOOTER) - .setRole(Role.CONTENTINFO); + return new HtmlTree(HtmlTag.FOOTER) + .setRole(HtmlAttr.Role.CONTENTINFO); } /** @@ -508,8 +505,8 @@ public static HtmlTree FOOTER() { * @return the element */ public static HtmlTree HEADER() { - return new HtmlTree(TagName.HEADER) - .setRole(Role.BANNER); + return new HtmlTree(HtmlTag.HEADER) + .setRole(HtmlAttr.Role.BANNER); } /** @@ -519,7 +516,7 @@ public static HtmlTree HEADER() { * @param body the content * @return the element */ - public static HtmlTree HEADING(TagName headingTag, Content body) { + public static HtmlTree HEADING(HtmlTag headingTag, Content body) { return new HtmlTree(checkHeading(headingTag)) .add(body); } @@ -532,7 +529,7 @@ public static HtmlTree HEADING(TagName headingTag, Content body) { * @param body the content * @return the element */ - public static HtmlTree HEADING(TagName headingTag, HtmlStyle style, Content body) { + public static HtmlTree HEADING(HtmlTag headingTag, HtmlStyle style, Content body) { return new HtmlTree(checkHeading(headingTag)) .setStyle(style) .add(body); @@ -547,7 +544,7 @@ public static HtmlTree HEADING(TagName headingTag, HtmlStyle style, Content body * @param body the content * @return the element */ - public static HtmlTree HEADING_TITLE(TagName headingTag, + public static HtmlTree HEADING_TITLE(HtmlTag headingTag, HtmlStyle style, Content body) { return new HtmlTree(checkHeading(headingTag)) .setTitle(body) @@ -563,13 +560,13 @@ public static HtmlTree HEADING_TITLE(TagName headingTag, * @param body the content * @return the element */ - public static HtmlTree HEADING_TITLE(TagName headingTag, Content body) { + public static HtmlTree HEADING_TITLE(HtmlTag headingTag, Content body) { return new HtmlTree(checkHeading(headingTag)) .setTitle(body) .add(body); } - private static TagName checkHeading(TagName headingTag) { + private static HtmlTag checkHeading(HtmlTag headingTag) { return switch (headingTag) { case H1, H2, H3, H4, H5, H6 -> headingTag; default -> throw new IllegalArgumentException(headingTag.toString()); @@ -586,7 +583,7 @@ private static TagName checkHeading(TagName headingTag) { * @return the {@code HTML} element */ public static HtmlTree HTML(String lang, Content head, Content body) { - return new HtmlTree(TagName.HTML) + return new HtmlTree(HtmlTag.HTML) .put(HtmlAttr.LANG, lang) .add(head) .add(body); @@ -601,7 +598,7 @@ public static HtmlTree HTML(String lang, Content head, Content body) { * @return the element */ public static HtmlTree INPUT(HtmlAttr.InputType type, HtmlId id) { - return new HtmlTree(TagName.INPUT) + return new HtmlTree(HtmlTag.INPUT) .put(HtmlAttr.TYPE, type.toString()) .setId(id) .put(HtmlAttr.DISABLED, ""); @@ -615,7 +612,7 @@ public static HtmlTree INPUT(HtmlAttr.InputType type, HtmlId id) { * @return the element */ public static HtmlTree INPUT(HtmlAttr.InputType type, HtmlStyle style) { - return new HtmlTree(TagName.INPUT) + return new HtmlTree(HtmlTag.INPUT) .put(HtmlAttr.TYPE, type.toString()) .setStyle(style) .put(HtmlAttr.DISABLED, ""); @@ -628,7 +625,7 @@ public static HtmlTree INPUT(HtmlAttr.InputType type, HtmlStyle style) { * @return the element */ public static HtmlTree LABEL(String forLabel, Content body) { - return new HtmlTree(TagName.LABEL) + return new HtmlTree(HtmlTag.LABEL) .put(HtmlAttr.FOR, forLabel) .add(body); } @@ -640,7 +637,7 @@ public static HtmlTree LABEL(String forLabel, Content body) { * @return the element */ public static HtmlTree LI(Content body) { - return new HtmlTree(TagName.LI) + return new HtmlTree(HtmlTag.LI) .add(body); } @@ -666,7 +663,7 @@ public static HtmlTree LI(HtmlStyle style, Content body) { * @return the element */ public static HtmlTree LINK(String rel, String type, String href, String title) { - return new HtmlTree(TagName.LINK) + return new HtmlTree(HtmlTag.LINK) .put(HtmlAttr.REL, rel) .put(HtmlAttr.TYPE, type) .put(HtmlAttr.HREF, href) @@ -680,8 +677,8 @@ public static HtmlTree LINK(String rel, String type, String href, String title) * @return the element */ public static HtmlTree MAIN() { - return new HtmlTree(TagName.MAIN) - .setRole(Role.MAIN); + return new HtmlTree(HtmlTag.MAIN) + .setRole(HtmlAttr.Role.MAIN); } /** @@ -691,8 +688,8 @@ public static HtmlTree MAIN() { * @return the element */ public static HtmlTree MAIN(Content body) { - return new HtmlTree(TagName.MAIN) - .setRole(Role.MAIN) + return new HtmlTree(HtmlTag.MAIN) + .setRole(HtmlAttr.Role.MAIN) .add(body); } @@ -705,7 +702,7 @@ public static HtmlTree MAIN(Content body) { * @return the element */ public static HtmlTree META(String httpEquiv, String content, String charset) { - return new HtmlTree(TagName.META) + return new HtmlTree(HtmlTag.META) .put(HtmlAttr.HTTP_EQUIV, httpEquiv) .put(HtmlAttr.CONTENT, content + "; charset=" + charset); } @@ -718,7 +715,7 @@ public static HtmlTree META(String httpEquiv, String content, String charset) { * @return the element */ public static HtmlTree META(String name, String content) { - return new HtmlTree(TagName.META) + return new HtmlTree(HtmlTag.META) .put(HtmlAttr.NAME, name) .put(HtmlAttr.CONTENT, content); } @@ -730,8 +727,8 @@ public static HtmlTree META(String name, String content) { * @return the element */ public static HtmlTree NAV() { - return new HtmlTree(TagName.NAV) - .setRole(Role.NAVIGATION); + return new HtmlTree(HtmlTag.NAV) + .setRole(HtmlAttr.Role.NAVIGATION); } /** @@ -741,7 +738,7 @@ public static HtmlTree NAV() { * @return the element */ public static HtmlTree NOSCRIPT(Content body) { - return new HtmlTree(TagName.NOSCRIPT) + return new HtmlTree(HtmlTag.NOSCRIPT) .add(body); } @@ -752,7 +749,7 @@ public static HtmlTree NOSCRIPT(Content body) { * @return the element */ public static HtmlTree OL(HtmlStyle style) { - return new HtmlTree(TagName.OL) + return new HtmlTree(HtmlTag.OL) .setStyle(style); } @@ -763,7 +760,7 @@ public static HtmlTree OL(HtmlStyle style) { * @return the element */ public static HtmlTree P(Content body) { - return new HtmlTree(TagName.P) + return new HtmlTree(HtmlTag.P) .add(body); } @@ -786,7 +783,7 @@ public static HtmlTree P(HtmlStyle style, Content body) { * @return the element */ public static HtmlTree PRE(Content body) { - return new HtmlTree(TagName.PRE).add(body); + return new HtmlTree(HtmlTag.PRE).add(body); } /** @@ -797,7 +794,7 @@ public static HtmlTree PRE(Content body) { * @return the element */ public static HtmlTree SCRIPT(String src) { - return new HtmlTree(TagName.SCRIPT) + return new HtmlTree(HtmlTag.SCRIPT) .put(HtmlAttr.TYPE, "text/javascript") .put(HtmlAttr.SRC, src); @@ -810,7 +807,7 @@ public static HtmlTree SCRIPT(String src) { * @return the element */ public static HtmlTree SECTION(HtmlStyle style) { - return new HtmlTree(TagName.SECTION) + return new HtmlTree(HtmlTag.SECTION) .setStyle(style); } @@ -822,7 +819,7 @@ public static HtmlTree SECTION(HtmlStyle style) { * @return the element */ public static HtmlTree SECTION(HtmlStyle style, Content body) { - return new HtmlTree(TagName.SECTION) + return new HtmlTree(HtmlTag.SECTION) .setStyle(style) .add(body); } @@ -834,7 +831,7 @@ public static HtmlTree SECTION(HtmlStyle style, Content body) { * @return the element */ public static HtmlTree SMALL(Content body) { - return new HtmlTree(TagName.SMALL) + return new HtmlTree(HtmlTag.SMALL) .add(body); } @@ -845,7 +842,7 @@ public static HtmlTree SMALL(Content body) { * @return the element */ public static HtmlTree SPAN(Content body) { - return new HtmlTree(TagName.SPAN) + return new HtmlTree(HtmlTag.SPAN) .add(body); } @@ -856,7 +853,7 @@ public static HtmlTree SPAN(Content body) { * @return the element */ public static HtmlTree SPAN(HtmlStyle styleClass) { - return new HtmlTree(TagName.SPAN) + return new HtmlTree(HtmlTag.SPAN) .setStyle(styleClass); } @@ -880,7 +877,7 @@ public static HtmlTree SPAN(HtmlStyle styleClass, Content body) { * @return the element */ public static HtmlTree SPAN_ID(HtmlId id, Content body) { - return new HtmlTree(TagName.SPAN) + return new HtmlTree(HtmlTag.SPAN) .setId(id) .add(body); } @@ -894,7 +891,7 @@ public static HtmlTree SPAN_ID(HtmlId id, Content body) { * @return the element */ public static HtmlTree SPAN(HtmlId id, HtmlStyle style, Content body) { - return new HtmlTree(TagName.SPAN) + return new HtmlTree(HtmlTag.SPAN) .setId(id) .setStyle(style) .add(body); @@ -907,7 +904,7 @@ public static HtmlTree SPAN(HtmlId id, HtmlStyle style, Content body) { * @return the element */ public static HtmlTree SUMMARY(Content body) { - return new HtmlTree(TagName.SUMMARY) + return new HtmlTree(HtmlTag.SUMMARY) .add(body); } @@ -918,7 +915,7 @@ public static HtmlTree SUMMARY(Content body) { * @return the element */ public static HtmlTree SUP(Content body) { - return new HtmlTree(TagName.SUP) + return new HtmlTree(HtmlTag.SUP) .add(body); } @@ -930,7 +927,7 @@ public static HtmlTree SUP(Content body) { * @return the element */ public static HtmlTree TD(HtmlStyle style, Content body) { - return new HtmlTree(TagName.TD) + return new HtmlTree(HtmlTag.TD) .setStyle(style) .add(body); } @@ -944,7 +941,7 @@ public static HtmlTree TD(HtmlStyle style, Content body) { * @return the element */ public static HtmlTree TH(HtmlStyle style, String scope, Content body) { - return new HtmlTree(TagName.TH) + return new HtmlTree(HtmlTag.TH) .setStyle(style) .put(HtmlAttr.SCOPE, scope) .add(body); @@ -958,7 +955,7 @@ public static HtmlTree TH(HtmlStyle style, String scope, Content body) { * @return the element */ public static HtmlTree TH(String scope, Content body) { - return new HtmlTree(TagName.TH) + return new HtmlTree(HtmlTag.TH) .put(HtmlAttr.SCOPE, scope) .add(body); } @@ -970,7 +967,7 @@ public static HtmlTree TH(String scope, Content body) { * @return the element */ public static HtmlTree TITLE(String body) { - return new HtmlTree(TagName.TITLE) + return new HtmlTree(HtmlTag.TITLE) .add(body); } @@ -981,7 +978,7 @@ public static HtmlTree TITLE(String body) { * @return the element */ public static HtmlTree UL(HtmlStyle style) { - return new HtmlTree(TagName.UL) + return new HtmlTree(HtmlTag.UL) .setStyle(style); } @@ -994,7 +991,7 @@ public static HtmlTree UL(HtmlStyle style) { * @return the element */ public static HtmlTree UL(HtmlStyle style, Content first, Content... more) { - var ul = new HtmlTree(TagName.UL) + var ul = new HtmlTree(HtmlTag.UL) .setStyle(style); ul.add(first); for (Content c : more) { @@ -1013,7 +1010,7 @@ public static HtmlTree UL(HtmlStyle style, Content first, Content... more) { * @return the element */ public static HtmlTree UL(HtmlStyle style, Collection items, Function mapper) { - return new HtmlTree(TagName.UL) + return new HtmlTree(HtmlTag.UL) .setStyle(style) .addAll(items, mapper); } @@ -1025,7 +1022,7 @@ public boolean isEmpty() { @Override public boolean isPhrasingContent() { - return tagName.phrasingContent; + return tag.blockType == HtmlTag.BlockType.INLINE; } /** @@ -1069,7 +1066,7 @@ public boolean isDiscardable() { return !isVoid() && !hasContent() && !hasAttr(HtmlAttr.ID) - && tagName != TagName.SCRIPT; + && tag != HtmlTag.SCRIPT; } /** @@ -1080,22 +1077,22 @@ public boolean isDiscardable() { * @see Phrasing Content */ public boolean isInline() { - return switch (tagName) { + return switch (tag) { case A, BUTTON, BR, CODE, EM, I, IMG, LABEL, SMALL, SPAN, STRONG, SUB, SUP, WBR -> true; default -> false; }; } /** - * Returns whether or not this is a void element. + * Returns whether this is a void element. * - * @return whether or not this is a void element + * @return whether this is a void element * * @see Void Elements */ public boolean isVoid() { - return switch (tagName) { - case BR, HR, IMG, INPUT, LINK, META, WBR -> true; + return switch (tag) { + case BR, COL, FRAME, HR, IMG, INPUT, LINK, META, WBR -> true; default -> false; }; } @@ -1106,14 +1103,14 @@ public boolean write(Writer out, String newline, boolean atNewline) throws IOExc if (!isInline && !atNewline) { out.write(newline); } - String tagString = tagName.toString(); + String tagString = tag.getName(); out.write("<"); out.write(tagString); for (var attr : attrs.entrySet()) { var key = attr.getKey(); var value = attr.getValue(); out.write(" "); - out.write(key.toString()); + out.write(key.getName()); if (!value.isEmpty()) { out.write("=\""); out.write(value.replace("\"", """)); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ListBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/ListBuilder.java similarity index 97% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ListBuilder.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/ListBuilder.java index 603e571789564..05c9f8388dbc6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ListBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/ListBuilder.java @@ -23,7 +23,7 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; @@ -32,8 +32,6 @@ import java.util.NoSuchElementException; import java.util.Objects; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * A utility class for building nested HTML lists. This class is implemented as a * stack of nested list/item pairs where list items are added to the inner-most diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/RawHtml.java similarity index 96% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/RawHtml.java index a71ea7831da4a..1c9f60f69e866 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/RawHtml.java @@ -23,15 +23,13 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; import java.util.regex.Matcher; import java.util.regex.Pattern; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * Class for generating raw HTML content to be added to HTML pages of javadoc output. */ @@ -147,8 +145,8 @@ public boolean isPhrasingContent() { Matcher m = tag.matcher(rawHtmlContent); while (m.find()) { try { - var tn = TagName.of(m.group("tag")); - if (!tn.phrasingContent) { + var tn = HtmlTag.of(m.group("tag")); + if (tn.blockType != HtmlTag.BlockType.INLINE) { return false; } } catch (IllegalArgumentException e) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Script.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Script.java similarity index 97% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Script.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Script.java index f194aa5527a53..a5fc15215092c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Script.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Script.java @@ -23,13 +23,11 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * A builder for HTML script elements. */ @@ -100,7 +98,7 @@ public Script appendStringLiteral(CharSequence text, char quoteChar) { */ public Content asContent() { ScriptContent scriptContent = new ScriptContent(sb); - var script = new HtmlTree(TagName.SCRIPT) { + var script = new HtmlTree(HtmlTag.SCRIPT) { @Override public HtmlTree add(Content c) { if (c != scriptContent) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Text.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Text.java similarity index 97% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Text.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Text.java index e05ca73001af7..78bb5cb89e66c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Text.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/Text.java @@ -23,13 +23,11 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * Class for containing immutable string content for HTML tags of javadoc output. * Newlines are always represented by {@code \n}. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/TextBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/TextBuilder.java similarity index 96% rename from src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/TextBuilder.java rename to src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/TextBuilder.java index 6c4d66024c58a..f27e5922043f6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/TextBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/TextBuilder.java @@ -23,13 +23,11 @@ * questions. */ -package jdk.javadoc.internal.doclets.formats.html.markup; +package jdk.javadoc.internal.html; import java.io.IOException; import java.io.Writer; -import jdk.javadoc.internal.doclets.formats.html.Content; - /** * Class for generating string content for HTML tags of javadoc output. * The content is mutable to the extent that additional content may be added. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/package-info.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/package-info.java index 4f5601b96003d..dffb34f1e9306 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/package-info.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/package-info.java @@ -68,9 +68,9 @@ * library in the * {@link jdk.javadoc.internal.doclets.formats.html.markup formats.html.markup} package, * to create trees (or acyclic graphs) of - * {@linkplain jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree HTML tree nodes}. + * {@linkplain jdk.javadoc.internal.html.HtmlTree HTML tree nodes}. * Apart from using a common format-neutral supertype, - * {@link jdk.javadoc.internal.doclets.formats.html.Content}, the {@code markup} library + * {@link jdk.javadoc.internal.html.Content}, the {@code markup} library * is mostly independent of the rest of the javadoc software stack. * *

      Toolkit diff --git a/src/jdk.jcmd/share/man/jcmd.1 b/src/jdk.jcmd/share/man/jcmd.1 index ec87ee3ae47a9..a639dab16e144 100644 --- a/src/jdk.jcmd/share/man/jcmd.1 +++ b/src/jdk.jcmd/share/man/jcmd.1 @@ -878,7 +878,7 @@ The following \f[I]options\f[R] must be specified using either \f[V]-H\f[R]: (Optional) Human readable format (BOOLEAN, false) .IP \[bu] 2 \f[V]-F\f[R]: (Optional) File path (STRING, -\[dq]vm_memory_map_.txt\[dq]) +\[dq]vm_memory_map_.txt\[dq]) .RE .TP \f[V]System.map\f[R] [\f[I]options\f[R]] (Linux only) diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java index 2483e99e49ad9..764f93bd54aeb 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,12 @@ package com.sun.tools.javap; +import java.lang.reflect.AccessFlag; import java.net.URI; import java.text.DateFormat; import java.util.Collection; import java.util.Date; +import java.util.EnumSet; import java.util.List; import java.util.Set; @@ -149,7 +151,7 @@ public boolean write(ClassModel cm) { indent(-1); } - writeModifiers(getClassModifiers(cm.flags().flagsMask())); + writeModifiers(getClassModifiers(cm.flags())); if ((classModel.flags().flagsMask() & ACC_MODULE) != 0) { var attr = classModel.findAttribute(Attributes.module()); @@ -210,7 +212,7 @@ public boolean write(ClassModel cm) { println("minor version: " + classModel.minorVersion()); println("major version: " + classModel.majorVersion()); writeList(String.format("flags: (0x%04x) ", cm.flags().flagsMask()), - getClassFlags(cm.flags().flagsMask()), "\n"); + getClassFlags(cm.flags()), "\n"); print("this_class: #");print(() -> classModel.thisClass().index()); tab(); print(() -> "// " + classModel.thisClass().asInternalName()); @@ -416,7 +418,7 @@ protected void writeField(FieldModel f) { if (!options.checkAccess(f.flags().flagsMask())) return; - var flags = AccessFlags.ofField(f.flags().flagsMask()); + var flags = f.flags(); writeModifiers(flagsReportUnknown(flags).stream().filter(fl -> fl.sourceModifier()) .map(fl -> Modifier.toString(fl.mask())).toList()); print(() -> sigPrinter.print( @@ -794,9 +796,16 @@ else switch (c) { } } - private Set getClassModifiers(int mask) { - return getModifiers(flagsReportUnknown(AccessFlags.ofClass((mask & ACC_INTERFACE) != 0 - ? mask & ~ACC_ABSTRACT : mask))); + private Set getClassModifiers(AccessFlags flags) { + var flagSet = flagsReportUnknown(flags); + Set set; + if (flagSet.contains(AccessFlag.INTERFACE)) { + set = EnumSet.copyOf(flagSet); + set.remove(AccessFlag.ABSTRACT); + } else { + set = flagSet; + } + return getModifiers(set); } private static Set getModifiers(Set flags) { @@ -806,16 +815,8 @@ private static Set getModifiers(Set flags) return s; } - private Set getClassFlags(int mask) { - return getFlags(mask, flagsReportUnknown(AccessFlags.ofClass(mask))); - } - - private Set getMethodFlags(int mask) { - return getFlags(mask, flagsReportUnknown(AccessFlags.ofMethod(mask))); - } - - private Set getFieldFlags(int mask) { - return getFlags(mask, flagsReportUnknown(AccessFlags.ofField(mask))); + private Set getClassFlags(AccessFlags flags) { + return getFlags(flags.flagsMask(), flagsReportUnknown(flags)); } private static Set getFlags(int mask, Set flags) { diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c index f91f16104ca35..cf2de8061195b 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,8 +93,8 @@ static jrawMonitorID callbackBlock; * not blocking might mean that a return would continue execution of * some java thread in the middle of VM_DEATH, this seems troubled. * - * WARNING: No not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK - * block, this will mess up the count. + * WARNING: Do not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK + * block. This will mess up the active_callbacks count. */ #define BEGIN_CALLBACK() \ @@ -1709,6 +1709,18 @@ eventHandler_unlock(void) debugMonitorExit(handlerLock); } +void +callback_lock(void) +{ + debugMonitorEnter(callbackLock); +} + +void +callback_unlock(void) +{ + debugMonitorExit(callbackLock); +} + /***** handler creation *****/ HandlerNode * diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h index c4a7aa0c4412b..096b1ed795ac6 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,6 +78,9 @@ void eventHandler_waitForActiveCallbacks(); void eventHandler_lock(void); void eventHandler_unlock(void); +void callback_lock(void); +void callback_unlock(void); + jboolean eventHandler_synthesizeUnloadEvent(char *signature, JNIEnv *env); jclass getMethodClass(jvmtiEnv *jvmti_env, jmethodID method); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c index e1b4d2f3ab6d6..4b32430ad7be0 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -719,7 +719,9 @@ invoker_completeInvokeRequest(jthread thread) exc = NULL; id = 0; - eventHandler_lock(); /* for proper lock order */ + callback_lock(); /* for proper lock order in threadControl getLocks() */ + eventHandler_lock(); /* for proper lock order in threadControl getLocks() */ + stepControl_lock(); /* for proper lock order in threadControl getLocks() */ debugMonitorEnter(invokerLock); request = threadControl_getInvokeRequest(thread); @@ -772,7 +774,7 @@ invoker_completeInvokeRequest(jthread thread) * We cannot delete saved exception or return value references * since otherwise a deleted handle would escape when writing * the response to the stream. Instead, we clean those refs up - * after writing the respone. + * after writing the response. */ deleteGlobalArgumentRefs(env, request); @@ -790,7 +792,9 @@ invoker_completeInvokeRequest(jthread thread) * Give up the lock before I/O operation */ debugMonitorExit(invokerLock); + stepControl_unlock(); eventHandler_unlock(); + callback_unlock(); if (!detached) { outStream_initReply(&out, id); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c index 0611e8fc2138c..aeec8175b1738 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -805,7 +805,8 @@ stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth, LOG_STEP(("stepControl_beginStep: thread=%p,size=%d,depth=%d", thread, size, depth)); - eventHandler_lock(); /* for proper lock order */ + callback_lock(); /* for proper lock order in threadControl getLocks() */ + eventHandler_lock(); /* for proper lock order in threadControl getLocks() */ stepControl_lock(); step = threadControl_getStepRequest(thread); @@ -852,6 +853,7 @@ stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth, stepControl_unlock(); eventHandler_unlock(); + callback_unlock(); return error; } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c index 00c34844c986a..220e004097945 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c @@ -642,26 +642,31 @@ getLocks(void) * thread) needs to be grabbed here. This allows thread control * code to safely suspend and resume the application threads * while ensuring they don't hold a critical lock. + * + * stepControl_beginStep() grabs the eventHandler lock and stepControl lock + * before eventually ending up here, so we need to maintain that order here. + * Similarly, invoker_completeInvokeRequest() grabs the eventHandler lock + * and invoker lock. */ - + callback_lock(); eventHandler_lock(); + stepControl_lock(); invoker_lock(); eventHelper_lock(); - stepControl_lock(); - commonRef_lock(); debugMonitorEnter(threadLock); - + commonRef_lock(); } static void releaseLocks(void) { - debugMonitorExit(threadLock); commonRef_unlock(); - stepControl_unlock(); + debugMonitorExit(threadLock); eventHelper_unlock(); invoker_unlock(); + stepControl_unlock(); eventHandler_unlock(); + callback_unlock(); } void diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/Control.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/Control.java index 16f827a2df528..05c75a0e48e9b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Control.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Control.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,15 +108,18 @@ public void setValue(String value) { // VM events requires no access control context try { delegate.setValue(value); + lastValue = delegate.getValue(); } catch (Throwable t) { Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\" for " + getClass()); + lastValue = null; } } else { - AccessController.doPrivileged(new PrivilegedAction() { + lastValue = AccessController.doPrivileged(new PrivilegedAction() { @Override - public Void run() { + public String run() { try { delegate.setValue(value); + return delegate.getValue(); } catch (Throwable t) { // Prevent malicious user to propagate exception callback in the wrong context Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\" for " + getClass()); @@ -125,7 +128,6 @@ public Void run() { } }, context); } - lastValue = value; } 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 71569c30273be..f086bbf884701 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -299,7 +299,7 @@ private static Control defineEnabled(PlatformEventType type) { } private static Control defineThreshold(PlatformEventType type) { - String def = type.getAnnotationValue(Threshold.class, "0 ns"); + String def = type.getAnnotationValue(Threshold.class, ThresholdSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THRESHOLD, Threshold.NAME, def, Collections.emptyList())); return new Control(new ThresholdSetting(type), def); } @@ -311,13 +311,13 @@ private static Control defineStackTrace(PlatformEventType type) { } private static Control defineCutoff(PlatformEventType type) { - String def = type.getAnnotationValue(Cutoff.class, Cutoff.INFINITY); + String def = type.getAnnotationValue(Cutoff.class, CutoffSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_CUTOFF, Cutoff.NAME, def, Collections.emptyList())); return new Control(new CutoffSetting(type), def); } private static Control defineThrottle(PlatformEventType type) { - String def = type.getAnnotationValue(Throttle.class, Throttle.DEFAULT); + String def = type.getAnnotationValue(Throttle.class, ThrottleSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THROTTLE, Throttle.NAME, def, Collections.emptyList())); return new Control(new ThrottleSetting(type), def); } @@ -330,7 +330,7 @@ private static Control defineLevel(PlatformEventType type) { } private static Control definePeriod(PlatformEventType type) { - String def = type.getAnnotationValue(Period.class, "everyChunk"); + String def = type.getAnnotationValue(Period.class, PeriodSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_PERIOD, PeriodSetting.NAME, def, Collections.emptyList())); return new Control(new PeriodSetting(type), def); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java index a614516d295bc..c14ce6025ce9b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ import java.util.Set; import java.util.StringJoiner; import jdk.jfr.internal.util.SpellChecker; +import jdk.jfr.internal.util.TimespanUnit; final class ArgumentParser { private final Map options = new HashMap<>(); @@ -302,16 +303,11 @@ private Object parseNanotime(String name, String text) { } throw new IllegalArgumentException("Integer parsing error nanotime value: unit required"); } - return switch(unit) { - case "ns" -> time; - case "us" -> time * 1000; - case "ms" -> time * 1000 * 1000; - case "s" -> time * 1000 * 1000 * 1000; - case "m" -> time * 60 * 1000 * 1000 * 1000; - case "h" -> time * 60 * 60* 1000 * 1000 * 1000; - case "d" -> time * 24 * 60 * 60 * 1000 * 1000 * 1000; - default -> throw new IllegalArgumentException("Integer parsing error nanotime value: illegal unit"); - }; + TimespanUnit tu = TimespanUnit.fromText(unit); + if (tu == null) { + throw new IllegalArgumentException("Integer parsing error nanotime value: illegal unit"); + } + return tu.toNanos(time); } int indexOfUnit(String text) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java index 76e834fe6eb3c..281a0c5794967 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,9 @@ package jdk.jfr.internal.jfc.model; import java.util.StringJoiner; +import jdk.jfr.internal.util.TimespanUnit; public final class Utilities { - private static final String[] UNITS = new String[] { - "ns", "us", "ns", "ms", "s", "m", "h", "d" // order matters - }; static XmlElement instantiate(Class type) { try { @@ -104,9 +102,9 @@ static void checkValid(String value, Object... valid) { static String parseTimespan(String s) { StringBuilder sb = new StringBuilder(); try { - for (String unit : UNITS) { - if (s.endsWith(unit)) { - return parseForUnit(s, unit); + for (TimespanUnit timespan : TimespanUnit.values()) { + if (s.endsWith(timespan.text)) { + return parseForUnit(s, timespan.text); } } Long.parseLong(s); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanSetting.java new file mode 100644 index 0000000000000..72ffcac72c926 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanSetting.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.internal.settings; + +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.internal.PlatformEventType; + +abstract class BooleanSetting extends JDKSettingControl { + private final PlatformEventType eventType; + private final String defaultValue; + private String value; + + public BooleanSetting(PlatformEventType eventType, String defaultValue) { + this.eventType = Objects.requireNonNull(eventType); + this.defaultValue = defaultValue; + this.value = defaultValue; + if (parse(defaultValue) == null) { + throw new InternalError("Only 'true' or 'false' is allowed with class BooleanSetting"); + } + } + + protected abstract void apply(PlatformEventType eventType, boolean value); + + @Override + public String combine(Set values) { + if (values.contains("true")) { + return "true"; + } + if (values.contains("false")) { + return "false"; + } + return defaultValue; + } + + @Override + public void setValue(String value) { + Boolean b = parse(value); + if (b != null) { + apply(eventType, b.booleanValue()); + this.value = value; + } + } + + @Override + public String getValue() { + return value; + } + + private static Boolean parse(String value) { + if ("true".equals(value)) { + return Boolean.TRUE; + } + if ("false".equals(value)) { + return Boolean.FALSE; + } + return null; + } +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java index 553336b59e242..68950d71b63d7 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package jdk.jfr.internal.settings; +import static jdk.jfr.internal.util.ValueParser.MISSING; + import java.util.Objects; import java.util.Set; @@ -43,8 +45,8 @@ @Name(Type.SETTINGS_PREFIX + "Cutoff") @Timespan public final class CutoffSetting extends JDKSettingControl { - - private String value = "0 ns"; + public static final String DEFAULT_VALUE = ValueParser.INFINITY; + private String value = DEFAULT_VALUE; private final PlatformEventType eventType; public CutoffSetting(PlatformEventType eventType) { @@ -54,22 +56,24 @@ public CutoffSetting(PlatformEventType eventType) { @Override public String combine(Set values) { long max = 0; - String text = "0 ns"; + String text = null; for (String value : values) { - long l = ValueParser.parseTimespanWithInfinity(value); - if (l > max) { + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING && nanos > max) { text = value; - max = l; + max = nanos; } } - return text; + return Objects.requireNonNullElse(text, DEFAULT_VALUE); } @Override public void setValue(String value) { - long l = ValueParser.parseTimespanWithInfinity(value); - this.value = value; - eventType.setCutoff(l); + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + eventType.setCutoff(nanos); + this.value = value; + } } @Override @@ -81,10 +85,6 @@ public static long parseValueSafe(String value) { if (value == null) { return 0L; } - try { - return ValueParser.parseTimespanWithInfinity(value); - } catch (NumberFormatException nfe) { - return 0L; - } + return ValueParser.parseTimespanWithInfinity(value, 0L); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java index 77a57a9b75b0d..e81bce484f3ea 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,6 @@ package jdk.jfr.internal.settings; -import java.util.Objects; -import java.util.Set; - import jdk.jfr.Description; import jdk.jfr.BooleanFlag; import jdk.jfr.Label; @@ -41,33 +38,18 @@ @Description("Record event") @Name(Type.SETTINGS_PREFIX + "Enabled") @BooleanFlag -public final class EnabledSetting extends JDKSettingControl { - private final BooleanValue booleanValue; - private final PlatformEventType eventType; - +public final class EnabledSetting extends BooleanSetting { public EnabledSetting(PlatformEventType eventType, String defaultValue) { - this.booleanValue = BooleanValue.valueOf(defaultValue); - this.eventType = Objects.requireNonNull(eventType); - } - - @Override - public String combine(Set values) { - return booleanValue.union(values); + super(eventType, defaultValue); } @Override - public void setValue(String value) { - booleanValue.setValue(value); - eventType.setEnabled(booleanValue.getBoolean()); + protected void apply(PlatformEventType eventType, boolean value) { + eventType.setEnabled(value); if (eventType.isEnabled() && !eventType.isJVM()) { if (!eventType.isInstrumented()) { eventType.markForInstrumentation(true); } } } - - @Override - public String getValue() { - return booleanValue.getValue(); - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java index b6aa7500d4b68..9531f75c9257a 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java @@ -47,30 +47,29 @@ public final class LevelSetting extends JDKSettingControl { public LevelSetting(PlatformEventType eventType, String[] levels) { this.eventType = Objects.requireNonNull(eventType); this.levels = Arrays.asList(Objects.requireNonNull(levels)); + this.value = levels[0]; } @Override public String combine(Set values) { - int maxIndex = 0; + int maxIndex = 0; // index 0 contains the default value for (String value : values) { - maxIndex = Math.max(maxIndex, indexOf(value)); + maxIndex = Math.max(maxIndex, levels.indexOf(value)); } return levels.get(maxIndex); } @Override public void setValue(String value) { - this.value = value; - this.eventType.setLevel(indexOf(value)); + int index = levels.indexOf(value); + if (index != -1) { + this.eventType.setLevel(index); + this.value = value; + } } @Override public String getValue() { return value; } - - private int indexOf(String value) { - int index = levels.indexOf(value); - return index < 0 ? 0 : index; - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java index 05b5a08a3bb71..22fef2691aa91 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ import jdk.jfr.internal.PlatformEventType; import jdk.jfr.internal.Type; import jdk.jfr.internal.util.ValueParser; +import static jdk.jfr.internal.util.ValueParser.MISSING; @MetadataDefinition @Label("Period") @@ -46,6 +47,7 @@ public final class PeriodSetting extends JDKSettingControl { public static final String EVERY_CHUNK = "everyChunk"; public static final String BEGIN_CHUNK = "beginChunk"; public static final String END_CHUNK = "endChunk"; + public static final String DEFAULT_VALUE = EVERY_CHUNK; public static final String NAME = "period"; private final PlatformEventType eventType; private String value = EVERY_CHUNK; @@ -56,7 +58,6 @@ public PeriodSetting(PlatformEventType eventType) { @Override public String combine(Set values) { - boolean beginChunk = false; boolean endChunk = false; Long min = null; @@ -74,15 +75,11 @@ public String combine(Set values) { endChunk = true; break; default: - long l = ValueParser.parseTimespanWithInfinity(value); - // Always accept first specified value - if (min == null) { - text = value; - min = l; - } else { - if (l < min) { + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + if (min == null || nanos < min) { text = value; - min = l; + min = nanos; } } } @@ -97,7 +94,7 @@ public String combine(Set values) { if (!beginChunk && endChunk) { return END_CHUNK; } - return EVERY_CHUNK; // also default + return DEFAULT_VALUE; // "everyChunk" is default } @Override @@ -113,7 +110,10 @@ public void setValue(String value) { eventType.setPeriod(0, false, true); break; default: - long nanos = ValueParser.parseTimespanWithInfinity(value); + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos == MISSING) { + return; + } if (nanos == 0 || nanos == Long.MAX_VALUE) { eventType.setPeriod(nanos, false, false); } else { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java index cfd85a0f240ca..45489ebc691c8 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,8 @@ package jdk.jfr.internal.settings; -import java.util.Objects; -import java.util.Set; - -import jdk.jfr.Description; import jdk.jfr.BooleanFlag; +import jdk.jfr.Description; import jdk.jfr.Label; import jdk.jfr.MetadataDefinition; import jdk.jfr.Name; @@ -41,30 +38,16 @@ @Name(Type.SETTINGS_PREFIX + "StackTrace") @Description("Record stack traces") @BooleanFlag -public final class StackTraceSetting extends JDKSettingControl { - private static final long typeId = Type.getTypeId(StackTraceSetting.class); - private final BooleanValue booleanValue; - private final PlatformEventType eventType; +public final class StackTraceSetting extends BooleanSetting { + private static final long typeId = Type.getTypeId(StackTraceSetting.class); public StackTraceSetting(PlatformEventType eventType, String defaultValue) { - this.booleanValue = BooleanValue.valueOf(defaultValue); - this.eventType = Objects.requireNonNull(eventType); - } - - @Override - public String combine(Set values) { - return booleanValue.union(values); - } - - @Override - public void setValue(String value) { - booleanValue.setValue(value); - eventType.setStackTraceEnabled(booleanValue.getBoolean()); + super(eventType, defaultValue); } @Override - public String getValue() { - return booleanValue.getValue(); + protected void apply(PlatformEventType eventType, boolean value) { + eventType.setStackTraceEnabled(value); } public static boolean isType(long typeId) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java index 1a7138b11cd93..8870417f9dd35 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package jdk.jfr.internal.settings; +import static jdk.jfr.internal.util.ValueParser.MISSING; + import java.util.Objects; import java.util.Set; @@ -36,14 +38,16 @@ import jdk.jfr.internal.PlatformEventType; import jdk.jfr.internal.Type; import jdk.jfr.internal.util.ValueParser; + @MetadataDefinition @Label("Threshold") @Name(Type.SETTINGS_PREFIX + "Threshold") @Description("Record event with duration above or equal to threshold") @Timespan public final class ThresholdSetting extends JDKSettingControl { + public static final String DEFAULT_VALUE = "0 ns"; private static final long typeId = Type.getTypeId(ThresholdSetting.class); - private String value = "0 ns"; + private String value = DEFAULT_VALUE; private final PlatformEventType eventType; public ThresholdSetting(PlatformEventType eventType) { @@ -55,26 +59,24 @@ public String combine(Set values) { Long min = null; String text = null; for (String value : values) { - long l = ValueParser.parseTimespanWithInfinity(value); - // always accept first value - if (min == null) { - min = l; - text = value; - } else { - if (l < min) { + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + if (min == null || nanos < min) { text = value; - min = l; + min = nanos; } } } - return text == null ? "0 ns" : text; + return Objects.requireNonNullElse(text, DEFAULT_VALUE); } @Override public void setValue(String value) { - long l = ValueParser.parseTimespanWithInfinity(value); - this.value = value; - eventType.setThreshold(l); + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + eventType.setThreshold(nanos); + this.value = value; + } } @Override diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java index 457502963891f..c18031d514f98 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Datadog, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,6 +26,9 @@ 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; @@ -34,17 +37,20 @@ import jdk.jfr.MetadataDefinition; import jdk.jfr.Name; import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Throttle; import jdk.jfr.internal.Type; +import jdk.jfr.internal.util.Rate; +import jdk.jfr.internal.util.TimespanUnit; +import jdk.jfr.internal.util.Utils; @MetadataDefinition -@Label("Event Emission Throttle") +@Label("Throttle") @Description("Throttles the emission rate for an event") @Name(Type.SETTINGS_PREFIX + "Throttle") public final class ThrottleSetting extends JDKSettingControl { - static final String OFF_TEXT = "off"; - private static final long OFF = -2; - private String value = "0/s"; + public static final String DEFAULT_VALUE = Throttle.DEFAULT; private final PlatformEventType eventType; + private String value = DEFAULT_VALUE; public ThrottleSetting(PlatformEventType eventType) { this.eventType = Objects.requireNonNull(eventType); @@ -52,75 +58,51 @@ public ThrottleSetting(PlatformEventType eventType) { @Override public String combine(Set values) { - long max = OFF; - String text = "off"; + Rate max = null; + String text = null; for (String value : values) { - long l = parseValueSafe(value); - if (l > max) { - text = value; - max = l; + Rate rate = Rate.of(value); + if (rate != null) { + if (max == null || rate.isHigher(max)) { + text = value; + max = rate; + } } } - return text; + // "off" is default + return Objects.requireNonNullElse(text, DEFAULT_VALUE); } - private static long parseValueSafe(String s) { - long value = 0L; - try { - value = parseThrottleValue(s); - } catch (NumberFormatException nfe) { + @Override + public void setValue(String value) { + if ("off".equals(value)) { + eventType.setThrottle(-2, 1000); + this.value = value; + return; } - return value; - } - @Override - public void setValue(String s) { - long size = 0; - long millis = 1000; - try { - size = parseThrottleValue(s); - millis = parseThrottleTimeUnit(s); - this.value = s; - } catch (NumberFormatException nfe) { + Rate rate = Rate.of(value); + if (rate != null) { + long millis = 1000; + long samples = rate.amount(); + TimespanUnit unit = rate.unit(); + // if unit is more than 1 s, set millis + if (unit.nanos > SECONDS.nanos) { + millis = unit.nanos / MILLISECONDS.nanos; + } + // if unit is less than 1 s, scale samples + if (unit.nanos < SECONDS.nanos) { + long perSecond = SECONDS.nanos / unit.nanos; + samples *= Utils.multiplyOverflow(samples, perSecond, Long.MAX_VALUE); + } + eventType.setThrottle(samples, millis); + this.value = value; } - eventType.setThrottle(size, millis); } @Override public String getValue() { return value; } - - private static long parseThrottleValue(String s) { - if (s.equals(OFF_TEXT)) { - return OFF; - } - String parsedValue = parseThrottleString(s, true); - long normalizedValue = 0; - try { - normalizedValue = ThrottleUnit.normalizeValueAsMillis(Long.parseLong(parsedValue), s); - } catch (NumberFormatException nfe) { - throwThrottleNumberFormatException(s); - } - return normalizedValue; - } - - private static long parseThrottleTimeUnit(String s) { - return ThrottleUnit.asMillis(s); - } - - // Expected input format is "x/y" where x is a non-negative long - // and y is a time unit. Split the string at the delimiter. - static String parseThrottleString(String s, boolean value) { - String[] split = s.split("/"); - if (split.length != 2) { - throwThrottleNumberFormatException(s); - } - return value ? split[0].trim() : split[1].trim(); - } - - private static void throwThrottleNumberFormatException(String s) { - throw new NumberFormatException("'" + s + "' is not valid. Should be a non-negative numeric value followed by a delimiter. i.e. '/', and then followed by a unit e.g. 100/s."); - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleUnit.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleUnit.java deleted file mode 100644 index ec2fafc7656ae..0000000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleUnit.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Datadog, Inc. All rights reserved. - * 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.jfr.internal.settings; - -import java.util.concurrent.TimeUnit; - -enum ThrottleUnit { - NANOSECONDS("ns", TimeUnit.SECONDS.toNanos(1), TimeUnit.SECONDS.toMillis(1)), - MICROSECONDS("us", TimeUnit.SECONDS.toNanos(1) / 1000, TimeUnit.SECONDS.toMillis(1)), - MILLISECONDS("ms", TimeUnit.SECONDS.toMillis(1), TimeUnit.SECONDS.toMillis(1)), - SECONDS("s", 1, TimeUnit.SECONDS.toMillis(1)), - MINUTES("m", 1, TimeUnit.MINUTES.toMillis(1)), - HOUR("h", 1, TimeUnit.HOURS.toMillis(1)), - DAY("d", 1, TimeUnit.DAYS.toMillis(1)); - - private final String text; - private final long factor; - private final long millis; - - ThrottleUnit(String t, long factor, long millis) { - this.text = t; - this.factor = factor; - this.millis = millis; - } - - private static ThrottleUnit parse(String s) { - if (s.equals(ThrottleSetting.OFF_TEXT)) { - return MILLISECONDS; - } - return unit(ThrottleSetting.parseThrottleString(s, false)); - } - - private static ThrottleUnit unit(String s) { - if (s.endsWith("ns") || s.endsWith("us") || s.endsWith("ms")) { - return value(s.substring(s.length() - 2)); - } - if (s.endsWith("s") || s.endsWith("m") || s.endsWith("h") || s.endsWith("d")) { - return value(s.substring(s.length() - 1)); - } - throw new NumberFormatException("'" + s + "' is not a valid time unit."); - } - - private static ThrottleUnit value(String s) { - for (ThrottleUnit t : values()) { - if (t.text.equals(s)) { - return t; - } - } - throw new NumberFormatException("'" + s + "' is not a valid time unit."); - } - - static long asMillis(String s) { - return parse(s).millis; - } - - static long normalizeValueAsMillis(long value, String s) { - return value * parse(s).factor; - } - } \ No newline at end of file diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanValue.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java similarity index 50% rename from src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanValue.java rename to src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java index d8f46d76787cd..f32436a5e0feb 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanValue.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,53 +22,37 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package jdk.jfr.internal.util; -package jdk.jfr.internal.settings; +public record Rate(long amount, TimespanUnit unit) { -import java.util.Set; - -/** - * Helper class for settings that use boolean numbers - * - */ -final class BooleanValue { - private String value = "false"; - private boolean booleanValue; - - private BooleanValue(boolean b) { - booleanValue = b; - value = b ? "true" : "false"; - } - - public String union(Set values) { - for (String v : values) { - if ("true".equals(v)) { - return "true"; + public static Rate of(String text) { + String[] splitted = text.split("/"); + if (splitted.length != 2) { + return null; + } + String value = splitted[0].strip(); + String unit = splitted[1].strip(); + TimespanUnit tu = TimespanUnit.fromText(unit); + if (unit == null) { + return null; + } + try { + long v = Long.parseLong(value); + if (v >= 0) { + return new Rate(v, tu); } + } catch (NumberFormatException nfe) { + // Ignore } - return "false"; - } - - public void setValue(String value) { - this.value = value; - this.booleanValue = Boolean.valueOf(value); + return null; } - public final String getValue() { - return this.value; + public boolean isHigher(Rate that) { + return this.inNanos() > that.inNanos(); } - public boolean getBoolean() { - return booleanValue; - } - - public static BooleanValue valueOf(String defaultValue) { - if ("true".equals(defaultValue)) { - return new BooleanValue(true); - } - if ("false".equals(defaultValue)) { - return new BooleanValue(false); - } - throw new InternalError("Unknown default value for settings '" + defaultValue + "'"); + private double inNanos() { + return (double) amount / unit.nanos; } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanUnit.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanUnit.java new file mode 100644 index 0000000000000..0fdde833bebd3 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanUnit.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.internal.util; + +import java.util.concurrent.TimeUnit; + +public enum TimespanUnit { + NANOSECONDS ("ns", TimeUnit.NANOSECONDS, 1000), + MICROSECONDS("us", TimeUnit.MICROSECONDS, 1000), + MILLISECONDS("ms", TimeUnit.MILLISECONDS, 1000), + SECONDS ("s", TimeUnit.SECONDS, 60), + MINUTES ("m", TimeUnit.MINUTES, 60), + HOURS ("h", TimeUnit.HOURS, 24), + DAYS ("d", TimeUnit.DAYS, 7); + public final String text; + public final long nanos; + public final int size; + private final TimeUnit timeUnit; + TimespanUnit(String text, TimeUnit tu, int size) { + this.text = text; + this.nanos = tu.toNanos(1); + this.size = size; + this.timeUnit = tu; + } + + public long toNanos(long value) { + return timeUnit.toNanos(value); + } + + public static TimespanUnit fromText(String text) { + for (TimespanUnit tu : values()) { + // Case-sensitive by design + if (tu.text.equals(text)) { + return tu; + } + } + return null; + } +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java index ae83727096a2a..b5ae906c71cb1 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java @@ -430,4 +430,12 @@ public static boolean isJDKClass(Class type) { // but only if it is safe and there is a mechanism to register event // classes in other modules besides jdk.jfr and java.base. } + + public static long multiplyOverflow(long a, long b, long defaultValue) { + try { + return Math.multiplyExact(a, b); + } catch (ArithmeticException ae) { + return defaultValue; + } + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java index 8fd47dbf5b3c7..53a11101944fa 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,23 +40,6 @@ import jdk.jfr.consumer.RecordedMethod; public final class ValueFormatter { - private static enum TimespanUnit { - NANOSECONDS("ns", 1000), - MICROSECONDS("us", 1000), - MILLISECONDS("ms", 1000), - SECONDS("s", 60), MINUTES("m", 60), - HOURS("h", 24), - DAYS("d", 7); - - private final String text; - private final long amount; - - TimespanUnit(String unit, long amount) { - this.text = unit; - this.amount = amount; - } - } - private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss"); private static final Duration MICRO_SECOND = Duration.ofNanos(1_000); private static final Duration SECOND = Duration.ofSeconds(1); @@ -82,7 +65,7 @@ public static String formatTimespan(Duration dValue, String separation) { TimespanUnit result = TimespanUnit.NANOSECONDS; for (TimespanUnit unit : TimespanUnit.values()) { result = unit; - long amount = unit.amount; + long amount = unit.size; if (result == TimespanUnit.DAYS || value < amount || value % amount != 0) { break; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java index 730c83daaae67..cf112d3beb543 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java @@ -32,7 +32,16 @@ import static java.util.concurrent.TimeUnit.SECONDS; public final class ValueParser { - private static final String INFINITY = "infinity"; + public static final String INFINITY = "infinity"; + public static final long MISSING = Long.MIN_VALUE; + + public static long parseTimespanWithInfinity(String s, long defaultValue) { + try { + return parseTimespanWithInfinity(s); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } public static long parseTimespanWithInfinity(String s) { if (INFINITY.equals(s)) { @@ -42,26 +51,12 @@ public static long parseTimespanWithInfinity(String s) { } public static long parseTimespan(String s) { - if (s.endsWith("ns")) { - return Long.parseLong(s.substring(0, s.length() - 2).trim()); - } - if (s.endsWith("us")) { - return MICROSECONDS.toNanos(Long.parseLong(s.substring(0, s.length() - 2).trim())); - } - if (s.endsWith("ms")) { - return MILLISECONDS.toNanos(Long.parseLong(s.substring(0, s.length() - 2).trim())); - } - if (s.endsWith("s")) { - return SECONDS.toNanos(Long.parseLong(s.substring(0, s.length() - 1).trim())); - } - if (s.endsWith("m")) { - return MINUTES.toNanos(Long.parseLong(s.substring(0, s.length() - 1).trim())); - } - if (s.endsWith("h")) { - return HOURS.toNanos(Long.parseLong(s.substring(0, s.length() - 1).trim())); - } - if (s.endsWith("d")) { - return DAYS.toNanos(Long.parseLong(s.substring(0, s.length() - 1).trim())); + for (TimespanUnit unit : TimespanUnit.values()) { + String text = unit.text; + if (s.endsWith(text)) { + long value = Long.parseLong(s.substring(0, s.length() - text.length()).strip()); + return unit.toNanos(value); + } } try { diff --git a/src/jdk.jlink/share/man/jlink.1 b/src/jdk.jlink/share/man/jlink.1 index 9f4bf38ffa580..5cadc153a376b 100644 --- a/src/jdk.jlink/share/man/jlink.1 +++ b/src/jdk.jlink/share/man/jlink.1 @@ -44,8 +44,8 @@ into a custom runtime image .SH SYNOPSIS .PP \f[V]jlink\f[R] [\f[I]options\f[R]] \f[V]--module-path\f[R] -\f[I]modulepath\f[R] \f[V]--add-modules\f[R] \f[I]module\f[R] [, -\f[I]module\f[R]...] +\f[I]modulepath\f[R] \f[V]--add-modules\f[R] +\f[I]module\f[R][,\f[I]module\f[R]...] .TP \f[I]options\f[R] Command-line options separated by spaces. @@ -69,7 +69,7 @@ transitive dependences, to create a custom runtime image. Developers are responsible for updating their custom runtime images. .SH JLINK OPTIONS .TP -\f[V]--add-modules\f[R] \f[I]mod\f[R] [\f[V],\f[R] \f[I]mod\f[R]...] +\f[V]--add-modules\f[R] \f[I]mod\f[R][\f[V],\f[R]\f[I]mod\f[R]...] Adds the named modules, \f[I]mod\f[R], to the default set of root modules. The default set of root modules is empty. @@ -110,7 +110,7 @@ Specifies the launcher command name for the module or the command name for the module and main class (the module and the main class names are separated by a slash (\f[V]/\f[R])). .TP -\f[V]--limit-modules\f[R] \f[I]mod\f[R] [\f[V],\f[R] \f[I]mod\f[R]...] +\f[V]--limit-modules\f[R] \f[I]mod\f[R][\f[V],\f[R]\f[I]mod\f[R]...] Limits the universe of observable modules to those in the transitive closure of the named modules, \f[V]mod\f[R], plus the main module, if any, plus any further modules specified in the \f[V]--add-modules\f[R] diff --git a/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java b/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java index 518a61d6969a7..fc259b8bc7ca2 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java @@ -246,7 +246,7 @@ private static class TDSignatureGenerator extends Types.SignatureGenerator { StringBuilder sb = new StringBuilder(); TDSignatureGenerator(Types types) { - super(types); + types.super(); } @Override diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipConstants.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipConstants.java index 5c249e3dc400e..e1f5954af8a15 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipConstants.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -130,7 +130,6 @@ class ZipConstants { static final int ZIP64_ENDHDR = 56; // ZIP64 end header size static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size static final int ZIP64_EXTHDR = 24; // EXT header size - static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID static final int ZIP64_MINVAL32 = 0xFFFF; static final long ZIP64_MINVAL = 0xFFFFFFFFL; diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java index 5e5b665790f96..20ed86eae4f1b 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java @@ -1710,7 +1710,7 @@ private void checkExtraFields( byte[] cen, int cenPos, long size, long csize, private void checkZip64ExtraFieldValues(byte[] cen, int off, int blockSize, long csize, long size, long locoff, int diskNo) throws ZipException { - // if ZIP64_EXTID blocksize == 0, which may occur with some older + // if EXTID_ZIP64 blocksize == 0, which may occur with some older // versions of Apache Ant and Commons Compress, validate csize and size // to make sure neither field == ZIP64_MAGICVAL if (blockSize == 0) { @@ -1718,7 +1718,7 @@ private void checkZip64ExtraFieldValues(byte[] cen, int off, int blockSize, long locoff == ZIP64_MINVAL || diskNo == ZIP64_MINVAL32) { zerror("Invalid CEN header (invalid zip64 extra data field size)"); } - // Only validate the ZIP64_EXTID data if the block size > 0 + // Only validate the EXTID_ZIP64 data if the block size > 0 return; } // Validate the Zip64 Extended Information Extra Field (0x0001) diff --git a/test/hotspot/gtest/classfile/test_placeholders.cpp b/test/hotspot/gtest/classfile/test_placeholders.cpp index 4179a1fc695da..8cd9536271e23 100644 --- a/test/hotspot/gtest/classfile/test_placeholders.cpp +++ b/test/hotspot/gtest/classfile/test_placeholders.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ TEST_VM(PlaceholderTable, supername) { { MutexLocker ml(THREAD, SystemDictionary_lock); - PlaceholderTable::classloadAction super_action = PlaceholderTable::LOAD_SUPER; + PlaceholderTable::classloadAction super_action = PlaceholderTable::DETECT_CIRCULARITY; PlaceholderTable::classloadAction define_action = PlaceholderTable::DEFINE_CLASS; // DefineClass A and D @@ -71,7 +71,7 @@ TEST_VM(PlaceholderTable, supername) { // Another thread comes in and finds A loading Super PlaceholderEntry* placeholder = PlaceholderTable::get_entry(A, loader_data); - SymbolHandle supername = placeholder->supername(); + SymbolHandle supername = placeholder->next_klass_name(); // Other thread is done before handle_parallel_super_load PlaceholderTable::find_and_remove(A, loader_data, super_action, THREAD); @@ -86,7 +86,7 @@ TEST_VM(PlaceholderTable, supername) { // Refcount should be 3: one in table for class A, one in table for class D // and one locally with SymbolHandle keeping it alive placeholder = PlaceholderTable::get_entry(A, loader_data); - supername = placeholder->supername(); + supername = placeholder->next_klass_name(); EXPECT_EQ(super->refcount(), 3) << "super class name refcount should be 3"; // Second thread's done too diff --git a/test/hotspot/gtest/utilities/test_utf8.cpp b/test/hotspot/gtest/utilities/test_utf8.cpp index ffd8121075be8..80f6671207bbd 100644 --- a/test/hotspot/gtest/utilities/test_utf8.cpp +++ b/test/hotspot/gtest/utilities/test_utf8.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,8 @@ */ #include "precompiled.hpp" +#include "nmt/memflags.hpp" +#include "runtime/os.hpp" #include "utilities/utf8.hpp" #include "unittest.hpp" @@ -101,5 +103,105 @@ TEST_VM(utf8, jbyte_length) { UNICODE::as_utf8(str, 19, res, i); EXPECT_TRUE(test_stamp(res + i, sizeof(res) - i)); } +} + +TEST_VM(utf8, truncation) { + + // Test that truncation removes partial encodings as expected. + + const char orig_bytes[] = { 'A', 'B', 'C', 'D', 'E', '\0' }; + const int orig_length = sizeof(orig_bytes)/sizeof(char); + ASSERT_TRUE(UTF8::is_legal_utf8((const unsigned char*)orig_bytes, orig_length - 1, false)); + const char* orig_str = &orig_bytes[0]; + ASSERT_EQ((int)strlen(orig_str), orig_length - 1); + + unsigned char* temp_bytes; + const char* temp_str; + char* utf8; + int n_utf8; // Number of bytes in the encoding + + // Test 1: a valid UTF8 "ascii" ending string should be returned as-is + + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * orig_length, mtTest); + strcpy((char*)temp_bytes, orig_str); + temp_str = (const char*) temp_bytes; + UTF8::truncate_to_legal_utf8(temp_bytes, orig_length); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be unchanged"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be unchanged"; + os::free(temp_bytes); + + // Test 2: a UTF8 sequence that "ends" with a 2-byte encoding + // drops the 2-byte encoding + + jchar two_byte_char[] = { 0x00D1 }; // N with tilde + n_utf8 = 2; + utf8 = (char*) os::malloc(sizeof(char) * (n_utf8 + 1), mtTest); // plus NUL + UNICODE::convert_to_utf8(two_byte_char, 1, utf8); + int utf8_len = (int)strlen(utf8); + ASSERT_EQ(utf8_len, n_utf8) << "setup error"; + + // Now drop zero or one byte from the end and check it truncates as expected + for (int drop = 0; drop < n_utf8; drop++) { + int temp_len = orig_length + utf8_len - drop; + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * temp_len, mtTest); + temp_str = (const char*) temp_bytes; + strcpy((char*)temp_bytes, orig_str); + strncat((char*)temp_bytes, utf8, utf8_len - drop); + ASSERT_EQ((int)strlen(temp_str), temp_len - 1) << "setup error"; + UTF8::truncate_to_legal_utf8(temp_bytes, temp_len); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be truncated to original length"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be truncated to original"; + os::free(temp_bytes); + } + os::free(utf8); + + // Test 3: a UTF8 sequence that "ends" with a 3-byte encoding + // drops the 3-byte encoding + n_utf8 = 3; + jchar three_byte_char[] = { 0x0800 }; + utf8 = (char*) os::malloc(sizeof(char) * (n_utf8 + 1), mtTest); // plus NUL + UNICODE::convert_to_utf8(three_byte_char, 1, utf8); + utf8_len = (int)strlen(utf8); + ASSERT_EQ(utf8_len, n_utf8) << "setup error"; + + // Now drop zero, to two bytes from the end and check it truncates as expected + for (int drop = 0; drop < n_utf8; drop++) { + int temp_len = orig_length + utf8_len - drop; + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * temp_len, mtTest); + temp_str = (const char*) temp_bytes; + strcpy((char*)temp_bytes, orig_str); + strncat((char*)temp_bytes, utf8, utf8_len - drop); + ASSERT_EQ((int)strlen(temp_str), temp_len - 1) << "setup error"; + UTF8::truncate_to_legal_utf8(temp_bytes, temp_len); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be truncated to original length"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be truncated to original"; + os::free(temp_bytes); + } + os::free(utf8); + + // Test 4: a UTF8 sequence that "ends" with a 6-byte encoding + // drops the 6-byte encoding + n_utf8 = 6; + jchar six_byte_char[] = { 0xD801, 0xDC37 }; // U+10437 as its UTF-16 surrogate pairs + utf8 = (char*) os::malloc(sizeof(char) * (n_utf8 + 1), mtTest); // plus NUL + UNICODE::convert_to_utf8(six_byte_char, 2, utf8); + utf8_len = (int)strlen(utf8); + ASSERT_EQ(utf8_len, n_utf8) << "setup error"; + + // Now drop zero to five bytes from the end and check it truncates as expected + for (int drop = 0; drop < n_utf8; drop++) { + int temp_len = orig_length + utf8_len - drop; + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * temp_len, mtTest); + temp_str = (const char*) temp_bytes; + strcpy((char*)temp_bytes, orig_str); + strncat((char*)temp_bytes, utf8, utf8_len - drop); + ASSERT_EQ((int)strlen(temp_str), temp_len - 1) << "setup error"; + UTF8::truncate_to_legal_utf8(temp_bytes, temp_len); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be truncated to original length"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be truncated to original"; + os::free(temp_bytes); + } + os::free(utf8); + } diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 7e7970a4c6caf..2414f3d090a18 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -59,8 +59,16 @@ compiler/codecache/CheckLargePages.java 8332654 linux-x64 compiler/vectorapi/reshape/TestVectorReinterpret.java 8320897 aix-ppc64,linux-ppc64le compiler/vectorapi/VectorLogicalOpIdentityTest.java 8302459 linux-x64,windows-x64 +compiler/vectorapi/VectorRebracket128Test.java#ZSinglegen 8330538 generic-all +compiler/vectorapi/VectorRebracket128Test.java#ZGenerational 8330538 generic-all compiler/jvmci/TestUncaughtErrorInCompileMethod.java 8309073 generic-all +compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DataPatchTest.java 8331704 linux-riscv64 +compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/MaxOopMapStackOffsetTest.java 8331704 linux-riscv64 +compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/NativeCallTest.java 8331704 linux-riscv64 +compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java 8331704 linux-riscv64 +compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleCodeInstallationTest.java 8331704 linux-riscv64 +compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/VirtualObjectDebugInfoTest.java 8331704 linux-riscv64 compiler/floatingpoint/TestSubnormalFloat.java 8317810 generic-i586 compiler/floatingpoint/TestSubnormalDouble.java 8317810 generic-i586 @@ -109,8 +117,10 @@ runtime/ErrorHandling/CreateCoredumpOnCrash.java 8267433 macosx-x64 runtime/StackGuardPages/TestStackGuardPagesNative.java 8303612 linux-all runtime/ErrorHandling/TestDwarf.java#checkDecoder 8305489 linux-all runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le +runtime/Thread/TestAlwaysPreTouchStacks.java 8335167 macosx-aarch64 runtime/cds/appcds/customLoader/HelloCustom_JFR.java 8241075 linux-all,windows-x64 + applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJcmd.java 8278102 linux-all diff --git a/test/hotspot/jtreg/compiler/c2/TestCastX2P.java b/test/hotspot/jtreg/compiler/c2/TestCastX2P.java new file mode 100644 index 0000000000000..6591692f0cc61 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestCastX2P.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.c2; + +import jdk.internal.misc.Unsafe; +import jdk.test.lib.Asserts; + +/** + * @test TestCastX2P + * @summary AArch64: remove extra register copy when converting from long to pointer. + * @bug 8336245 + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run main/othervm -XX:-TieredCompilation compiler.c2.TestCastX2P + */ + +public class TestCastX2P { + + public static final int LEN = 2040; + + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + public static long lseed = 0xbeef; + public static int iseed = 0xbeef; + public static short sseed = (short) (0xef); + public static byte bseed = (byte) (0xe); + + public static long off1 = 16; + public static long off2 = 32; + public static long off3 = 64; + + public static class TestLong { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putLong(address+i, lseed); + } + } + + UNSAFE.putLong(address + off1 + 1030, lseed); + UNSAFE.putLong(address + 1023, lseed); + UNSAFE.putLong(address + off2 + 1001, lseed); + } + } + + public static class TestLongIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putLong(address+i, lseed); + } + } + + UNSAFE.putLong(address + off1, lseed); + UNSAFE.putLong(address + off1 + off2, lseed); + UNSAFE.putLong(address + off3, lseed); + } + } + + public static class TestInt { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putInt(address+i, iseed); + } + } + + UNSAFE.putInt(address + off1 + 274, iseed); + UNSAFE.putInt(address + 278, iseed); + UNSAFE.putInt(address + off2 + 282, iseed); + } + } + + public static class TestIntIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putInt(address+i, iseed); + } + } + + UNSAFE.putInt(address + off1, iseed); + UNSAFE.putInt(address + off1 + off2, iseed); + UNSAFE.putInt(address + off3, iseed); + } + } + + public static class TestShort { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putShort(address+i, sseed); + } + } + + UNSAFE.putShort(address + off1 + 257, sseed); + UNSAFE.putShort(address + 277, sseed); + UNSAFE.putShort(address + off2 + 283, sseed); + } + } + + public static class TestShortIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putShort(address+i, sseed); + } + } + + UNSAFE.putShort(address + off1, sseed); + UNSAFE.putShort(address + off1 + off2, sseed); + UNSAFE.putShort(address + off3, sseed); + } + } + + public static class TestByte { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putByte(address+i, bseed); + } + } + + UNSAFE.putByte(address + off1 + 257, bseed); + UNSAFE.putByte(address + 277, bseed); + UNSAFE.putByte(address + off2 + 283, bseed); + } + } + + public static class TestByteIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putByte(address+i, bseed); + } + } + + UNSAFE.putByte(address + off1, bseed); + UNSAFE.putByte(address + off1 + off2, bseed); + UNSAFE.putByte(address + off3, bseed); + } + } + + static void test() { + TestLong t1 = new TestLong(); + Asserts.assertEquals(UNSAFE.getLong(t1.address + off1 + 1030), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t1.address + 1023), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t1.address + off2 + 1001), lseed, "put long failed!"); + + TestLongIndirect t2 = new TestLongIndirect(); + Asserts.assertEquals(UNSAFE.getLong(t2.address + off1), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t2.address + off1 + off2), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t2.address + off3), lseed, "put long failed!"); + + TestInt t3 = new TestInt(); + Asserts.assertEquals(UNSAFE.getInt(t3.address + off1 + 274), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t3.address + 278), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t3.address + off2 + 282), iseed, "put int failed!"); + + TestIntIndirect t4 = new TestIntIndirect(); + Asserts.assertEquals(UNSAFE.getInt(t4.address + off1), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t4.address + off1 + off2), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t4.address + off3), iseed, "put int failed!"); + + TestShort t5 = new TestShort(); + Asserts.assertEquals(UNSAFE.getShort(t5.address + off1 + 257), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t5.address + 277), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t5.address + off2 + 283), sseed, "put short failed!"); + + TestShortIndirect t6 = new TestShortIndirect(); + Asserts.assertEquals(UNSAFE.getShort(t6.address + off1), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t6.address + off1 + off2), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t6.address + off3), sseed, "put short failed!"); + + TestByte t7 = new TestByte(); + Asserts.assertEquals(UNSAFE.getByte(t7.address + off1 + 257), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t7.address + 277), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t7.address + off2 + 283), bseed, "put byte failed!"); + + TestByteIndirect t8 = new TestByteIndirect(); + Asserts.assertEquals(UNSAFE.getByte(t8.address + off1), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t8.address + off1 + off2), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t8.address + off3), bseed, "put byte failed!"); + } + + public static void main(String[] strArr) { + test(); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java index 10bc237f2be0f..23b9321fc35c1 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java @@ -39,8 +39,7 @@ * and in {G1, * Serial, * Parallel, - * Shenandoah, - * ShenandoahIU} + * Shenandoah} */ @@ -95,13 +94,6 @@ public void runtest(String classname, String testType) throws Throwable { procArgs[argcount - 3] = "-XX:+UnlockExperimentalVMOptions"; procArgs[argcount - 2] = "-XX:+UseShenandoahGC"; break; - case "ShenandoahIU": - argcount = 11; - procArgs = new String[argcount]; - procArgs[argcount - 4] = "-XX:+UnlockExperimentalVMOptions"; - procArgs[argcount - 3] = "-XX:+UseShenandoahGC"; - procArgs[argcount - 2] = "-XX:ShenandoahGCMode=iu"; - break; default: throw new RuntimeException("unexpected test type " + testType); } @@ -286,7 +278,6 @@ private void checkstore(OutputAnalyzer output, String testType, boolean useCompr }; break; case "Shenandoah": - case "ShenandoahIU": // Shenandoah generates normal object graphs for // volatile stores matches = new String[] { @@ -358,7 +349,6 @@ private void checkcas(OutputAnalyzer output, String testType, boolean useCompres }; break; case "Shenandoah": - case "ShenandoahIU": // For volatile CAS, Shenanodoah generates normal // graphs with a shenandoah-specific cmpxchg matches = new String[] { @@ -445,7 +435,6 @@ private void checkcae(OutputAnalyzer output, String testType, boolean useCompres }; break; case "Shenandoah": - case "ShenandoahIU": // For volatile CAS, Shenanodoah generates normal // graphs with a shenandoah-specific cmpxchg matches = new String[] { @@ -512,7 +501,6 @@ private void checkgas(OutputAnalyzer output, String testType, boolean useCompres }; break; case "Shenandoah": - case "ShenandoahIU": matches = new String[] { "membar_release \\(elided\\)", useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq", diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java index 9678c37a384d6..60c9cc3713e68 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java @@ -71,33 +71,6 @@ * @run driver compiler.c2.aarch64.TestVolatilesShenandoah * TestUnsafeVolatileGAA Shenandoah * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestVolatileLoad ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestVolatileStore ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileLoad ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileStore ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileCAS ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileWeakCAS ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileCAE ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileGAS ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileGAA ShenandoahIU - * */ package compiler.c2.aarch64; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java index 41402036aea0e..e232895257a48 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java @@ -30,7 +30,7 @@ /* * @test - * @bug 8324655 8329797 + * @bug 8324655 8329797 8331090 * @key randomness * @summary Test that if expressions are properly folded into min/max nodes * @requires os.arch != "riscv64" @@ -505,7 +505,27 @@ public void checkTestMinLongVector(Object[] vals) { } } - @Run(test = { "testMinI1", "testMinI2", "testMaxI1", "testMaxI2", "testMinI1E", "testMinI2E", "testMaxI1E", "testMaxI2E" }) + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MIN_I, "1" }) + public int testMinIConst(int a) { + if (a > 65535) { + a = 65535; + } + + return a; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MIN_L, "1" }) + public long testMinLConst(long a) { + if (a > 65535) { + a = 65535; + } + + return a; + } + + @Run(test = { "testMinI1", "testMinI2", "testMaxI1", "testMaxI2", "testMinI1E", "testMinI2E", "testMaxI1E", "testMaxI2E", "testMinIConst" }) public void runTestIntegers() { testIntegers(10, 20); testIntegers(20, 10); @@ -526,9 +546,12 @@ public void testIntegers(int a, int b) { Asserts.assertEQ(a >= b ? b : a, testMinI2E(a, b)); Asserts.assertEQ(a >= b ? a : b, testMaxI1E(a, b)); Asserts.assertEQ(a <= b ? b : a, testMaxI2E(a, b)); + + Asserts.assertEQ(a > 65535 ? 65535 : a, testMinIConst(a)); + Asserts.assertEQ(b > 65535 ? 65535 : b, testMinIConst(b)); } - @Run(test = { "testMinL1", "testMinL2", "testMaxL1", "testMaxL2", "testMinL1E", "testMinL2E", "testMaxL1E", "testMaxL2E" }) + @Run(test = { "testMinL1", "testMinL2", "testMaxL1", "testMaxL2", "testMinL1E", "testMinL2E", "testMaxL1E", "testMaxL2E", "testMinLConst" }) public void runTestLongs() { testLongs(10, 20); testLongs(20, 10); @@ -551,5 +574,8 @@ public void testLongs(long a, long b) { Asserts.assertEQ(a >= b ? b : a, testMinL2E(a, b)); Asserts.assertEQ(a >= b ? a : b, testMaxL1E(a, b)); Asserts.assertEQ(a <= b ? b : a, testMaxL2E(a, b)); + + Asserts.assertEQ(a > 65535L ? 65535L : a, testMinLConst(a)); + Asserts.assertEQ(b > 65535L ? 65535L : b, testMinLConst(b)); } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceRefersToTests.java b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceRefersToTests.java new file mode 100644 index 0000000000000..7cf7c72c8fd89 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceRefersToTests.java @@ -0,0 +1,156 @@ +/* + * 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. + */ +package compiler.c2.irTests.gc; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; +import jdk.test.whitebox.gc.GC; + +import java.lang.ref.*; +import java.util.*; + +/* + * @test + * @bug 8256999 + * @summary Test that Reference.refersTo intrinsics are properly handled + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @requires vm.compiler2.enabled + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.gc.ReferenceRefersToTests + */ +public class ReferenceRefersToTests { + + private static String[] args(String... add) { + List args = new ArrayList<>(); + + // Use PerMethodTrapLimit=0 to compile all branches in the intrinsics. + args.add("-XX:PerMethodTrapLimit=0"); + + // Forcefully inline all methods to reach the intrinsic code. + args.add("-XX:CompileCommand=inline,compiler.c2.irTests.gc.ReferenceRefersToTests::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.Reference::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.PhantomReference::*"); + + // Mix in test config code. + args.addAll(Arrays.asList(add)); + + return args.toArray(new String[0]); + } + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + + int idx = 0; + if (GC.isSelectedErgonomically() && GC.Serial.isSupported()) { + // Serial does not have any barriers in refersTo. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseSerialGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Parallel.isSupported()) { + // Parallel does not have any barriers in refersTo. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseParallelGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.G1.isSupported()) { + // G1 nominally needs keep-alive barriers for Reference loads, + // but should not have them for refersTo. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseG1GC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Shenandoah.isSupported()) { + // Shenandoah nominally needs keep-alive barriers for Reference loads, + // but should not have them for refersTo. We only care to check that + // SATB barrier is not emitted. Shenandoah would also emit LRB barrier, + // which would false-negative the test. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:ShenandoahGCMode=passive", + "-XX:+ShenandoahSATBBarrier", + "-XX:+UseShenandoahGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Z.isSupported()) { + // ZGC does not emit barriers in IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseZGC" + ))); + } + framework.start(); + } + + static final Object REF = new Object(); + + static final SoftReference SR = new SoftReference<>(REF); + static final WeakReference WR = new WeakReference<>(REF); + static final PhantomReference PR = new PhantomReference<>(REF, null); + + // Verify that we are left with a single load of Reference.referent and no stores. + // This serves as a signal that no GC barriers are emitted in IR. + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean soft_null() { + return SR.refersTo(null); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean soft_ref() { + return SR.refersTo(REF); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean weak_null() { + return WR.refersTo(null); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean weak_ref() { + return WR.refersTo(REF); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean phantom_null() { + return PR.refersTo(null); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean phantom_ref() { + return PR.refersTo(REF); + } + +} diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestFindInstMemRecursion.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestFindInstMemRecursion.java new file mode 100644 index 0000000000000..90a5ff92cd3ac --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestFindInstMemRecursion.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8324345 + * @summary Ensure that ConnectionGraph::find_inst_mem does not cause a stack + * overflow. + * + * @run main/othervm -Xcomp -XX:CompileThreshold=10 -XX:-TieredCompilation + * -XX:CompileCommand=CompileOnly,javax.swing.plaf.basic.BasicLookAndFeel::initComponentDefaults + * -XX:CompileCommand=MemLimit,*.*,0 + * compiler.escapeAnalysis.TestFindInstMemRecursion + * + */ + +package compiler.escapeAnalysis; + +import javax.swing.*; +import javax.swing.plaf.metal.*; + +public class TestFindInstMemRecursion { + public static void main(String[] args) throws Exception { + LookAndFeel lookAndFeel = new MetalLookAndFeel(); + for (int i = 0; i < 20; ++i) { + UIManager.setLookAndFeel(lookAndFeel); + } + } +} diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java index c7d8d2cf83064..e77ac8dc4f998 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java @@ -200,84 +200,4 @@ public void testStackLong() { testLongOnStack(compiler); testLongInLocal(compiler); } - - public static Class objectOnStack() { - return SimpleDebugInfoTest.class; - } - - private void testObjectOnStack(DebugInfoCompiler compiler) { - test(compiler, getMethod("objectOnStack"), 2, JavaKind.Object); - } - - public static Class objectInLocal() { - Class local = SimpleDebugInfoTest.class; - return local; - } - - private void testObjectInLocal(DebugInfoCompiler compiler) { - test(compiler, getMethod("objectInLocal"), 3, JavaKind.Object); - } - - @Test - public void testConstObject() { - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - values[0] = constantReflection.asJavaClass(type); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testRegObject() { - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - Register reg = asm.emitLoadPointer((HotSpotConstant) constantReflection.asJavaClass(type)); - values[0] = reg.asValue(asm.getValueKind(JavaKind.Object)); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testStackObject() { - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - Register reg = asm.emitLoadPointer((HotSpotConstant) constantReflection.asJavaClass(type)); - values[0] = asm.emitPointerToStack(reg); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testRegNarrowObject() { - Assume.assumeTrue(config.useCompressedOops); - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - HotSpotConstant wide = (HotSpotConstant) constantReflection.asJavaClass(type); - Register reg = asm.emitLoadPointer((HotSpotConstant) wide.compress()); - values[0] = reg.asValue(asm.narrowOopKind); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testStackNarrowObject() { - Assume.assumeTrue(config.useCompressedOops); - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - HotSpotConstant wide = (HotSpotConstant) constantReflection.asJavaClass(type); - Register reg = asm.emitLoadPointer((HotSpotConstant) wide.compress()); - values[0] = asm.emitNarrowPointerToStack(reg); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } } diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java index 00973fd78c29d..98d7d168ba718 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java @@ -475,6 +475,24 @@ public void isJavaLangObjectInitTest() throws NoSuchMethodException { } } + @Test + public void isScopedTest() throws NoSuchMethodException, ClassNotFoundException { + // Must use reflection as ScopedMemoryAccess$Scoped is package-private + Class scopedAnnotationClass = Class.forName("jdk.internal.misc.ScopedMemoryAccess$Scoped").asSubclass(Annotation.class); + boolean scopedMethodFound = false; + for (Map.Entry e : methods.entrySet()) { + ResolvedJavaMethod m = e.getValue(); + Method key = e.getKey(); + boolean expect = key.isAnnotationPresent(scopedAnnotationClass); + boolean actual = m.isScoped(); + assertEquals(m.toString(), expect, actual); + if (expect) { + scopedMethodFound = true; + } + } + assertTrue("At least one scoped method must be present", scopedMethodFound); + } + abstract static class UnlinkedType { abstract void abstractMethod(); diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TypeUniverse.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TypeUniverse.java index 2420a133b63f9..13003095ee6a2 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TypeUniverse.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TypeUniverse.java @@ -51,6 +51,7 @@ import org.junit.Test; import jdk.internal.misc.Unsafe; +import jdk.internal.misc.ScopedMemoryAccess; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.MetaAccessProvider; @@ -115,7 +116,7 @@ protected class ProtectedInnerClass { byte[][].class, short[][].class, char[][].class, int[][].class, float[][].class, long[][].class, double[][].class, Object[][].class, Class[][].class, List[][].class, ClassLoader.class, String.class, Serializable.class, Cloneable.class, Test.class, TestMetaAccessProvider.class, List.class, Collection.class, Map.class, Queue.class, HashMap.class, LinkedHashMap.class, IdentityHashMap.class, AbstractCollection.class, AbstractList.class, ArrayList.class, InnerClass.class, InnerStaticClass.class, - InnerStaticFinalClass.class, PrivateInnerClass.class, ProtectedInnerClass.class}; + InnerStaticFinalClass.class, PrivateInnerClass.class, ProtectedInnerClass.class, ScopedMemoryAccess.class}; for (Class c : initialClasses) { addClass(c); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md index 3f6e0d4163faa..55d9fcbaecead 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md @@ -157,7 +157,16 @@ The framework allows the use of additional compiler control annotations for help - [@ForceCompile](./ForceCompile.java) - [@ForceCompileClassInitializer](./ForceCompileClassInitializer.java) -### 2.5 Framework Debug and Stress Flags +### 2.5 IR Tests with Privileged Classes +To run tests in a privileged mode (e.g. when using `@Stable`, `@Contended`, `@ReservedStackAccess` etc.), one need to add the test classes to the boot classpath. This can easily be achieved by calling `TestFramework.addTestClassesToBootClassPath()` on the test framework object: +``` +TestFramework testFramework = new TestFramework(); +testFramework + .addTestClassesToBootClassPath() + .start(); +``` + +### 2.6 Framework Debug and Stress Flags The framework provides various stress and debug flags. They should mainly be used as JTreg VM and/or Javaoptions (apart from `VerifyIR`). The following (property) flags are supported: - `-DVerifyIR=false`: Explicitly disable IR verification. This is useful, for example, if some scenarios use VM flags that let `@IR` annotation rules fail and the user does not want to provide separate IR rules or add flag preconditions to the already existing IR rules. diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 67fadbc4eac31..d477aa44763fa 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -172,6 +172,7 @@ public class TestFramework { private Set scenarioIndices; private List flags; private int defaultWarmup = -1; + private boolean testClassesOnBootClassPath; /* * Public interface methods @@ -323,6 +324,15 @@ public TestFramework addScenarios(Scenario... scenarios) { return this; } + /** + * Add test classes to boot classpath. This adds all classes found on path {@link jdk.test.lib.Utils#TEST_CLASSES} + * to the boot classpath with "-Xbootclasspath/a". This is useful when trying to run tests in a privileged mode. + */ + public TestFramework addTestClassesToBootClassPath() { + this.testClassesOnBootClassPath = true; + return this; + } + /** * Start the testing of the implicitly (by {@link #TestFramework()}) or explicitly (by {@link #TestFramework(Class)}) * set test class. @@ -744,7 +754,8 @@ private boolean onlyWhitelistedJTregVMAndJavaOptsFlags() { } private void runTestVM(List additionalFlags) { - TestVMProcess testVMProcess = new TestVMProcess(additionalFlags, testClass, helperClasses, defaultWarmup); + TestVMProcess testVMProcess = new TestVMProcess(additionalFlags, testClass, helperClasses, defaultWarmup, + testClassesOnBootClassPath); if (shouldVerifyIR) { try { TestClassParser testClassParser = new TestClassParser(testClass); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java index 04f8096d96917..8c168b73260cf 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java @@ -34,6 +34,7 @@ import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; +import java.io.File; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -63,11 +64,12 @@ public class TestVMProcess { private OutputAnalyzer oa; private String irEncoding; - public TestVMProcess(List additionalFlags, Class testClass, Set> helperClasses, int defaultWarmup) { + public TestVMProcess(List additionalFlags, Class testClass, Set> helperClasses, int defaultWarmup, + boolean testClassesOnBootClassPath) { this.cmds = new ArrayList<>(); TestFrameworkSocket socket = new TestFrameworkSocket(); try (socket) { - prepareTestVMFlags(additionalFlags, socket, testClass, helperClasses, defaultWarmup); + prepareTestVMFlags(additionalFlags, socket, testClass, helperClasses, defaultWarmup, testClassesOnBootClassPath); start(); } processSocketOutput(socket); @@ -91,11 +93,16 @@ public static String getLastTestVMOutput() { } private void prepareTestVMFlags(List additionalFlags, TestFrameworkSocket socket, Class testClass, - Set> helperClasses, int defaultWarmup) { + Set> helperClasses, int defaultWarmup, boolean testClassesOnBootClassPath) { // Set java.library.path so JNI tests which rely on jtreg nativepath setting work cmds.add("-Djava.library.path=" + Utils.TEST_NATIVE_PATH); // Need White Box access in test VM. - cmds.add("-Xbootclasspath/a:."); + String bootClassPath = "-Xbootclasspath/a:."; + if (testClassesOnBootClassPath) { + // Add test classes themselves to boot classpath to make them privileged. + bootClassPath += File.pathSeparator + Utils.TEST_CLASSES; + } + cmds.add(bootClassPath); cmds.add("-XX:+UnlockDiagnosticVMOptions"); cmds.add("-XX:+WhiteBoxAPI"); // Ignore CompileCommand flags which have an impact on the profiling information. diff --git a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java index c2689d883721b..2dbf46fd04025 100644 --- a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java +++ b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java @@ -77,16 +77,17 @@ private static void test(String include, String exclude) throws Exception { // Should see final report // Looks like this: - // total NA RA result #nodes limit time type #rc thread method - // 2149912 0 1986272 ok - - 0.101 c1 1 0x000000015180a600 jdk/internal/org/objectweb/asm/Frame::execute((IILjdk/internal/org/objectweb/asm/Symbol;Ljdk/internal/org/objectweb/asm/SymbolTable;)V) oa.shouldMatch("total.*method"); + // total Others RA HA NA result #nodes limit time type #rc thread method + // 523648 32728 490920 0 0 ok - - 0.250 c1 1 0x00007f4ec00d4ac0 java/lang/Class::descriptorString(()Ljava/lang/String;) // or - // 537784 98184 208536 ok 267 - 0.096 c2 1 0x0000000153019c00 jdk/internal/classfile/impl/BufWriterImpl::writeU1((I)V) 4521912 0 1986272 ok - - 0.101 c1 1 0x000000015180a600 jdk/internal/org/objectweb/asm/Frame::execute((IILjdk/internal/org/objectweb/asm/Symbol;Ljdk/internal/org/objectweb/asm/SymbolTable;)V) oa.shouldMatch("total.*method"); - oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +(\\d+|-) +.*" + expectedNameIncl + ".*"); + // 1898600 853176 750872 0 294552 ok 934 - 1.501 c2 1 0x00007f4ec00d3330 java/lang/String::replace((CC)Ljava/lang/String;) + oa.shouldMatch("total.*method"); + oa.shouldMatch("\\d+ +(\\d+ +){4}ok +(\\d+|-) +.*" + expectedNameIncl + ".*"); // In debug builds, we have a default memory limit enabled. That implies MemStat. Therefore we // expect to see all methods, not just the one we specified on the command line. if (Platform.isDebugBuild()) { - oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +(\\d+|-) +.*" + expectedNameExcl + ".*"); + oa.shouldMatch("\\d+ +(\\d+ +){4}ok +(\\d+|-) +.*" + expectedNameExcl + ".*"); } else { oa.shouldNotMatch(".*" + expectedNameExcl + ".*"); } diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestEmptyLoopDeadCast.java b/test/hotspot/jtreg/compiler/rangechecks/TestEmptyLoopDeadCast.java new file mode 100644 index 0000000000000..105f297c8cbb5 --- /dev/null +++ b/test/hotspot/jtreg/compiler/rangechecks/TestEmptyLoopDeadCast.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, Red Hat and/or its affiliates. All rights reserved. + * 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 8335393 + * @summary C2: assert(!had_error) failed: bad dominance + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:-UseLoopPredicate + * -XX:LoopMaxUnroll=0 TestEmptyLoopDeadCast + */ + +public class TestEmptyLoopDeadCast { + public static void main(String[] args) { + int[] array = new int[100]; + for (int i = 0; i < 20_000; i++) { + test1Helper(1, 101, array); + test1(0, array); + test2Helper(0, -101, array); + test2(0, array); + } + } + + private static int test1(int start, int[] array) { + return test1Helper(start, 0, array); + } + + private static int test1Helper(int start, int stop, int[] array) { + if (array == null) { + } + int v = 0; + for (int i = start; i < stop; i++) { + v = array[i - 1]; + } + return v; + } + + private static int test2(int start, int[] array) { + return test2Helper(start, -1, array); + } + + private static int test2Helper(int start, int stop, int[] array) { + if (array == null) { + } + int v = 0; + for (int i = start-1; i > stop; i--) { + v = array[-1 - i]; + } + return v; + } + +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java index e2ad716d1fa95..b885abb632a44 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java @@ -36,7 +36,7 @@ * @library /test/lib * @requires os.arch == "aarch64" * @modules jdk.incubator.vector - * @run testng/othervm -XX:UseSVE=0 -XX:-TieredCompilation -XX:CompileThreshold=100 compiler.vectorapi.VectorReplicateLongSpecialImmTest + * @run testng/othervm -XX:UseSVE=0 -XX:-TieredCompilation -XX:CompileThreshold=100 -XX:+IgnoreUnrecognizedVMOptions -XX:CompileCommand=MemLimit,*.*,0 compiler.vectorapi.VectorReplicateLongSpecialImmTest */ public class VectorReplicateLongSpecialImmTest { diff --git a/test/hotspot/jtreg/compiler/vectorization/TestFloat16VectorConvChain.java b/test/hotspot/jtreg/compiler/vectorization/TestFloat16VectorConvChain.java index 9a2b9d6b8487a..384cc411d3eb8 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestFloat16VectorConvChain.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestFloat16VectorConvChain.java @@ -24,6 +24,8 @@ /** * @test * @summary Test Float16 vector conversion chain. +* @requires (vm.cpu.features ~= ".*avx512vl.*" | vm.cpu.features ~= ".*f16c.*") | os.arch == "aarch64" +* | (os.arch == "riscv64" & vm.cpu.features ~= ".*zfh.*") * @library /test/lib / * @run driver compiler.vectorization.TestFloat16VectorConvChain */ diff --git a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java index 88b838b4bde43..6498a39491101 100644 --- a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java +++ b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java @@ -120,51 +120,6 @@ * TestAllocHumongousFragment */ -/* - * @test id=iu-aggressive - * @summary Make sure Shenandoah can recover from humongous allocation fragmentation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocHumongousFragment - */ - -/* - * @test id=iu - * @summary Make sure Shenandoah can recover from humongous allocation fragmentation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocHumongousFragment - */ - /* * @test id=g1 * @summary Make sure G1 can recover from humongous allocation fragmentation diff --git a/test/hotspot/jtreg/gc/TestDisableExplicitGC.java b/test/hotspot/jtreg/gc/TestDisableExplicitGC.java index 9a0b61900dde7..08fda78baaa1b 100644 --- a/test/hotspot/jtreg/gc/TestDisableExplicitGC.java +++ b/test/hotspot/jtreg/gc/TestDisableExplicitGC.java @@ -26,6 +26,7 @@ /* * @test TestDisableExplicitGC * @requires vm.opt.DisableExplicitGC == null + * @requires vm.compMode != "Xcomp" * @summary Verify GC behavior with DisableExplicitGC flag. * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java b/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java index e8ee2598c9ad2..48345da250a8e 100644 --- a/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java +++ b/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java @@ -51,14 +51,14 @@ private static final class OptionDescription { } } - private static final String[] defaultValid = new String[] { - "0", "1", "50", "95", "100" }; - private static final String[] defaultInvalid = new String[] { - "-10", "110", "bad" }; + private static final String[] rangeOneToHundredValid = new String[] { + "1", "50", "95", "100" }; + private static final String[] rangeOneToHundredInvalid = new String[] { + "0", "-10", "110", "bad" }; // All of the G1 product arguments that are percentages. private static final OptionDescription[] percentOptions = new OptionDescription[] { - new OptionDescription("G1ConfidencePercent", defaultValid, defaultInvalid) + new OptionDescription("G1ConfidencePercent", rangeOneToHundredValid, rangeOneToHundredInvalid) // Other percentage options are not yet validated by argument processing. }; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java index 35b0cdfa49ee2..b10e90b4055a9 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java @@ -135,56 +135,6 @@ * -XX:-UseTLAB -XX:+ShenandoahVerify * TestAllocIntArrays */ - -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestAllocIntArrays - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocIntArrays - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java index 8eebba4a30894..fa9bb7f017729 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java @@ -135,56 +135,6 @@ * -XX:-UseTLAB -XX:+ShenandoahVerify * TestAllocObjectArrays */ - -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestAllocObjectArrays - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocObjectArrays - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java index 32178555c9f6b..26dd98ed4035f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java @@ -112,52 +112,6 @@ * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact * TestAllocObjects */ - -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can withstand allocation - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestAllocObjects - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocObjects - */ - public class TestAllocObjects { static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation diff --git a/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java b/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java index f34fcd8024da5..b47a818694fd4 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java @@ -84,28 +84,6 @@ * TestDynamicSoftMaxHeapSize */ -/* - * @test id=iu-aggressive - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -Dtarget=1000 - * TestDynamicSoftMaxHeapSize - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -Dtarget=10000 - * TestDynamicSoftMaxHeapSize - */ - import java.util.Random; import jdk.test.lib.Utils; import jdk.test.lib.process.OutputAnalyzer; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java b/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java index ff1596d6833d2..1e8aaa32dd6a5 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java @@ -76,24 +76,6 @@ * TestGCThreadGroups */ -/** - * @test id=iu - * @summary Test Shenandoah GC uses concurrent/parallel threads correctly - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:ConcGCThreads=2 -XX:ParallelGCThreads=4 - * -Dtarget=1000 - * TestGCThreadGroups - * - * @run main/othervm -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:ConcGCThreads=2 -XX:ParallelGCThreads=4 - * -Dtarget=1000 - * TestGCThreadGroups -*/ - public class TestGCThreadGroups { static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation, around 1K cycles to handle diff --git a/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java b/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java index 6c54a34dae551..a57d9c7c49a9a 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java @@ -84,27 +84,6 @@ * TestHeapUncommit */ -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestHeapUncommit - * - * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestHeapUncommit - * - * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestHeapUncommit - */ - /* * @test id=default-lp * @key randomness diff --git a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java index 40a36d0a5ef23..6d8b1cb387565 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java @@ -94,38 +94,6 @@ * TestJcmdHeapDump */ -/* - * @test id=iu-aggressive - * @library /test/lib - * @modules jdk.attach/com.sun.tools.attach - * @requires vm.gc.Shenandoah - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestJcmdHeapDump - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestJcmdHeapDump - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestJcmdHeapDump - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * @library /test/lib - * @modules jdk.attach/com.sun.tools.attach - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestJcmdHeapDump - */ - import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java index 63d9fa08767d3..9d7738c25cebb 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java @@ -91,38 +91,6 @@ * TestLotsOfCycles */ -/* - * @test id=iu-aggressive - * @requires vm.gc.Shenandoah - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * -Dtarget=1000 - * TestLotsOfCycles - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * -Dtarget=1000 - * TestLotsOfCycles - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -Dtarget=1000 - * TestLotsOfCycles - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -Dtarget=10000 - * TestLotsOfCycles - */ - public class TestLotsOfCycles { static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation, around 1K cycles to handle diff --git a/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java index a0612a60a9f11..b30f7fd9ac46b 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java @@ -56,7 +56,6 @@ public static void main(String[] args) throws Exception { String[][][] modeHeuristics = new String[][][] { {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, - {{"iu"}, {"adaptive", "aggressive"}}, {{"passive"}, {"passive"}} }; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java b/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java index 3a5780069eff7..2849b58aa94ad 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java @@ -97,36 +97,6 @@ public static void main(String[] args) throws Exception { ); } - testWith("Zero interval with iu mode", - false, - "-Xlog:gc", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-XX:ShenandoahGCMode=iu", - "-XX:ShenandoahGuaranteedGCInterval=0" - ); - - testWith("Short interval with iu mode", - true, - "-Xlog:gc", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-XX:ShenandoahGCMode=iu", - "-XX:ShenandoahGuaranteedGCInterval=1000" - ); - - testWith("Long interval with iu mode", - false, - "-Xlog:gc", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-XX:ShenandoahGCMode=iu", - "-XX:ShenandoahGuaranteedGCInterval=100000" // deliberately too long - ); - testWith("Short interval with aggressive", false, "-Xlog:gc", diff --git a/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java b/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java index 8bad2fdbfa4e4..3763a7eea8768 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java @@ -35,18 +35,6 @@ * gc.shenandoah.TestReferenceRefersToShenandoah */ -/* @test id=iu - * @requires vm.gc.Shenandoah - * @library /test/lib - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm - * -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * gc.shenandoah.TestReferenceRefersToShenandoah - */ - /* @test id=satb-100 * @requires vm.gc.Shenandoah * @library /test/lib @@ -60,19 +48,6 @@ * gc.shenandoah.TestReferenceRefersToShenandoah */ -/* @test id=iu-100 - * @requires vm.gc.Shenandoah - * @library /test/lib - * @build jdk.test.whitebox.WhiteBox - * @modules java.base - * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm - * -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGarbageThreshold=100 -Xmx100m - * gc.shenandoah.TestReferenceRefersToShenandoah - */ - import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -200,10 +175,6 @@ private static void discardStrongReferences() { testObject4 = null; } - private static boolean isShenandoahIUMode() { - return "iu".equals(WB.getStringVMFlag("ShenandoahGCMode")); - } - private static void testConcurrentCollection() throws Exception { progress("setup concurrent collection test"); setup(); @@ -239,14 +210,7 @@ private static void testConcurrentCollection() throws Exception { expectCleared(testPhantom1, "testPhantom1"); expectCleared(testWeak2, "testWeak2"); expectValue(testWeak3, testObject3, "testWeak3"); - // This is true for all currently supported concurrent collectors, - // except Shenandoah+IU, which allows clearing refs even when - // accessed during concurrent marking. - if (isShenandoahIUMode()) { - expectCleared(testWeak4, "testWeak4"); - } else { - expectNotCleared(testWeak4, "testWeak4"); - } + expectNotCleared(testWeak4, "testWeak4"); progress("verify get returns expected values"); if (testWeak2.get() != null) { @@ -261,12 +225,10 @@ private static void testConcurrentCollection() throws Exception { } TestObject obj4 = testWeak4.get(); - if (!isShenandoahIUMode()) { - if (obj4 == null) { - fail("testWeak4.get() returned null"); - } else if (obj4.value != 4) { - fail("testWeak4.get().value is " + obj4.value); - } + if (obj4 == null) { + fail("testWeak4.get() returned null"); + } else if (obj4.value != 4) { + fail("testWeak4.get().value is " + obj4.value); } progress("verify queue entries"); diff --git a/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java b/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java index 1c2c2ed5ca7a9..b7ae0c1c31adb 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java @@ -36,19 +36,6 @@ * gc.shenandoah.TestReferenceShortcutCycle */ -/* @test id=iu-100 - * @requires vm.gc.Shenandoah - * @library /test/lib - * @build jdk.test.whitebox.WhiteBox - * @modules java.base - * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm - * -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGarbageThreshold=100 -Xmx100m - * gc.shenandoah.TestReferenceShortcutCycle - */ - import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java b/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java index 03f008d10c395..0bf7672e7d502 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java @@ -41,25 +41,6 @@ * TestRefprocSanity */ -/* - * @test id=iu - * @summary Test that null references/referents work fine - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestRefprocSanity - * - * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestRefprocSanity - * - * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestRefprocSanity - */ - import java.lang.ref.*; public class TestRefprocSanity { diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java b/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java index dd98585181ce2..dfa09a2f5ab2d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java @@ -73,25 +73,6 @@ * TestRegionSampling */ -/* - * @test id=iu-aggressive - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahRegionSampling - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestRegionSampling - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahRegionSampling - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestRegionSampling - * - */ - public class TestRegionSampling { static final long TARGET_MB = Long.getLong("target", 2_000); // 2 Gb allocation diff --git a/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java b/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java index 095f99395693b..4e3089d0862c6 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java @@ -138,46 +138,6 @@ * TestResizeTLAB */ -/* - * @test id=iu-aggressive - * @key randomness - * @summary Test that Shenandoah is able to work with(out) resizeable TLABs - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahVerify - * -XX:+ResizeTLAB - * TestResizeTLAB - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahVerify - * -XX:-ResizeTLAB - * TestResizeTLAB - */ - -/* - * @test id=iu - * @key randomness - * @summary Test that Shenandoah is able to work with(out) resizeable TLABs - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * -XX:+ResizeTLAB - * TestResizeTLAB - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * -XX:-ResizeTLAB - * TestResizeTLAB - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java index 7a28548f610c3..7be388b79451f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java @@ -114,41 +114,6 @@ * TestRetainObjects */ -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can deal with retained objects - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestRetainObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestRetainObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestRetainObjects - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can deal with retained objects - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestRetainObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestRetainObjects - */ - public class TestRetainObjects { static final int COUNT = 10_000_000; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java index 1b16ba4b8d2e7..2eaead55a6aac 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java @@ -127,45 +127,6 @@ * TestSieveObjects */ -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can deal with retained objects - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestSieveObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestSieveObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestSieveObjects - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can deal with retained objects - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestSieveObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestSieveObjects - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java b/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java index 94691432f38fb..9fdb822d48c27 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java @@ -65,24 +65,6 @@ * TestStringDedup */ -/* - * @test id=iu - * @summary Test Shenandoah string deduplication implementation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * @modules java.base/java.lang:open - * java.management - * - * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:StringDeduplicationAgeThreshold=3 - * TestStringDedup - * - * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive -XX:StringDeduplicationAgeThreshold=3 - * TestStringDedup - */ - import java.lang.reflect.*; import java.util.*; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java b/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java index 0382d0e00ddea..2d928e848ce12 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java @@ -72,37 +72,6 @@ * TestStringDedupStress */ - /* - * @test id=iu - * @summary Test Shenandoah string deduplication implementation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * @modules java.base/java.lang:open - * java.management - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestStringDedupStress - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -DtargetStrings=2000000 - * TestStringDedupStress - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahOOMDuringEvacALot - * -DtargetStrings=2000000 - * TestStringDedupStress - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * -DtargetStrings=2000000 - * TestStringDedupStress - */ - import java.lang.management.*; import java.lang.reflect.*; import java.util.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java b/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java index cf663f2329cb8..62c9e16f777f5 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java @@ -75,26 +75,6 @@ * TestStringInternCleanup */ -/* - * @test id=iu - * @summary Check that Shenandoah cleans up interned strings - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestStringInternCleanup - * - * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahVerify - * TestStringInternCleanup - * - * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestStringInternCleanup - */ - public class TestStringInternCleanup { static final int COUNT = 1_000_000; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java b/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java index 312ab964f5ee1..82bc74daddc4f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java @@ -58,33 +58,6 @@ * TestVerifyJCStress */ -/* - * @test id=iu - * @summary Tests that we pass at least one jcstress-like test with all verification turned on - * @requires vm.gc.Shenandoah - * @modules java.base/jdk.internal.misc - * java.management - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestVerifyJCStress - */ - -/* - * @test id=iu-c1 - * @summary Tests that we pass at least one jcstress-like test with all verification turned on - * @requires vm.gc.Shenandoah - * @modules java.base/jdk.internal.misc - * java.management - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify -XX:TieredStopAtLevel=1 - * TestVerifyJCStress - */ - - import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java b/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java index 6712cb49561f6..6dcdf259c6c68 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java @@ -26,8 +26,7 @@ * @test * @requires vm.gc.Shenandoah * - * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC TestWrongArrayMember - * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu TestWrongArrayMember + * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC TestWrongArrayMember */ public class TestWrongArrayMember { diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java b/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java index 8673005f96f2f..9054c1f470ed6 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java @@ -30,8 +30,6 @@ * * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xcomp -XX:CompileOnly=BarrierInInfiniteLoop::test1 * -XX:CompileOnly=BarrierInInfiniteLoop::test2 -XX:CompileOnly=BarrierInInfiniteLoop::test3 -XX:CompileCommand=quiet BarrierInInfiniteLoop - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -Xcomp -XX:CompileOnly=BarrierInInfiniteLoop::test1 - * -XX:CompileOnly=BarrierInInfiniteLoop::test2 -XX:CompileOnly=BarrierInInfiniteLoop::test3 -XX:CompileCommand=quiet BarrierInInfiniteLoop * */ diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java index 96586b27f65cc..73743aadedc3c 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java @@ -87,23 +87,6 @@ * TestChurnNotifications */ -/* - * @test id=iu - * @summary Check that MX notifications are reported for all cycles - * @library /test/lib / - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -Dprecise=false - * TestChurnNotifications - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -Dprecise=false - * TestChurnNotifications - */ - import java.util.*; import java.util.concurrent.atomic.*; import javax.management.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java index 0919a21d370e3..2dd9aba4c6210 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java @@ -83,21 +83,6 @@ * TestPauseNotifications */ -/* - * @test id=iu - * @summary Check that MX notifications are reported for all cycles - * @library /test/lib / - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestPauseNotifications - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestPauseNotifications - */ - import java.util.*; import java.util.concurrent.atomic.*; import javax.management.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java index 9d17e91608952..1a3d07bf80d06 100644 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java @@ -124,7 +124,6 @@ public static void main(String[] args) throws Exception { String[][][] modeHeuristics = new String[][][] { {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, - {{"iu"}, {"adaptive", "aggressive"}}, {{"passive"}, {"passive"}} }; diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java b/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java index 7fe5d56f7de99..4effff7ebd556 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java @@ -125,23 +125,5 @@ public static void main(String[] args) throws Exception { output.shouldNotContain(p); } } - - { - OutputAnalyzer output = ProcessTools.executeLimitedTestJava( - "-Xmx128m", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-Xlog:gc", - "-XX:+ExplicitGCInvokesConcurrent", - "-XX:ShenandoahGCMode=iu", - TestExplicitGC.class.getName(), - "test"); - for (String p : full) { - output.shouldNotContain(p); - } - for (String p : concNormal) { - output.shouldContain(p); - } - } } } diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java b/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java index 802038363b5fa..5422a86a496e2 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java @@ -45,7 +45,6 @@ enum Mode { public static void main(String[] args) throws Exception { testWith("-XX:ShenandoahGCMode=satb", Mode.PRODUCT); - testWith("-XX:ShenandoahGCMode=iu", Mode.EXPERIMENTAL); testWith("-XX:ShenandoahGCMode=passive", Mode.DIAGNOSTIC); } diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java b/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java index 094e62f53f35a..fe29a38c422c6 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java @@ -50,7 +50,7 @@ public class TestSelectiveBarrierFlags { public static void main(String[] args) throws Exception { String[][] opts = { new String[] { "ShenandoahLoadRefBarrier" }, - new String[] { "ShenandoahSATBBarrier", "ShenandoahIUBarrier" }, + new String[] { "ShenandoahSATBBarrier" }, new String[] { "ShenandoahCASBarrier" }, new String[] { "ShenandoahCloneBarrier" }, new String[] { "ShenandoahStackWatermarkBarrier" } diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java index aa6b79356498c..9bf1e74585618 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java @@ -44,21 +44,12 @@ public static void main(String[] args) throws Exception { "ShenandoahCloneBarrier", "ShenandoahStackWatermarkBarrier", }; - String[] iu = { - "ShenandoahLoadRefBarrier", - "ShenandoahIUBarrier", - "ShenandoahCASBarrier", - "ShenandoahCloneBarrier", - "ShenandoahStackWatermarkBarrier", - }; shouldFailAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=static", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=compact", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); - shouldFailAll("-XX:ShenandoahGCMode=iu", iu); shouldPassAll("-XX:ShenandoahGCMode=passive", concurrent); - shouldPassAll("-XX:ShenandoahGCMode=passive", iu); } private static void shouldFailAll(String h, String[] barriers) throws Exception { diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java index 486860728aba9..ea64d3ee71260 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java @@ -23,7 +23,7 @@ */ /* @test - * @summary Test that disabling wrong barriers fails early + * @summary Test that SATB barrier may be enabled for all modes * @requires vm.gc.Shenandoah * @library /test/lib * @run driver TestWrongBarrierEnable @@ -38,19 +38,13 @@ public class TestWrongBarrierEnable { public static void main(String[] args) throws Exception { String[] concurrent = { - "ShenandoahIUBarrier", - }; - String[] iu = { "ShenandoahSATBBarrier", }; - - shouldFailAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); - shouldFailAll("-XX:ShenandoahGCHeuristics=static", concurrent); - shouldFailAll("-XX:ShenandoahGCHeuristics=compact", concurrent); - shouldFailAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); - shouldFailAll("-XX:ShenandoahGCMode=iu", iu); + shouldPassAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); + shouldPassAll("-XX:ShenandoahGCHeuristics=static", concurrent); + shouldPassAll("-XX:ShenandoahGCHeuristics=compact", concurrent); + shouldPassAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); shouldPassAll("-XX:ShenandoahGCMode=passive", concurrent); - shouldPassAll("-XX:ShenandoahGCMode=passive", iu); } private static void shouldFailAll(String h, String[] barriers) throws Exception { diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java index 6e98f99b54823..501605cd0f59a 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java @@ -99,47 +99,6 @@ * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ -/* - * @test id=iu-aggressive - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient - * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - -/* - * @test id=iu - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient - * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - /* * @test id=passive-deopt-nmethod * @key stress @@ -221,53 +180,6 @@ * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ -/* - * @test id=iu-aggressive-deopt-nmethod - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false - * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * -XX:+ShenandoahOOMDuringEvacALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * -XX:+ShenandoahAllocFailureALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - -/* - * @test id=iu-deopt-nmethod - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false - * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * -XX:+ShenandoahVerify - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - - public class TestGCBasherWithShenandoah { public static void main(String[] args) throws IOException { TestGCBasher.main(args); diff --git a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java index f1e34bb1b0b66..7b9794ec4db26 100644 --- a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java @@ -102,45 +102,6 @@ * gc.stress.gcold.TestGCOld 50 1 20 10 10000 */ -/* - * @test id=iu-aggressive - * @key stress randomness - * @library / /test/lib - * @requires vm.gc.Shenandoah - * @summary Stress the GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - */ - -/* - * @test id=iu - * @key stress randomness - * @library / /test/lib - * @requires vm.gc.Shenandoah - * @summary Stress the GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm/timeout=600 -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - */ - public class TestGCOldWithShenandoah { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java b/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java index 1b12e22b62f86..f1b743bfc1523 100644 --- a/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java @@ -40,20 +40,6 @@ * -XX:+UseShenandoahGC * gc.stress.systemgc.TestSystemGCWithShenandoah 270 */ - -/* - * @test id=iu - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @summary Stress the Shenandoah GC full GC by allocating objects of different lifetimes concurrently with System.gc(). - * - * @run main/othervm/timeout=300 -Xlog:gc*=info -Xmx512m -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * gc.stress.systemgc.TestSystemGCWithShenandoah 270 - * - */ public class TestSystemGCWithShenandoah { public static void main(String[] args) throws Exception { TestSystemGC.main(args); diff --git a/test/hotspot/jtreg/runtime/ClassInitErrors/TestOutOfMemoryDuringInit.java b/test/hotspot/jtreg/runtime/ClassInitErrors/TestOutOfMemoryDuringInit.java index 1001ede544741..2f8d0fee72171 100644 --- a/test/hotspot/jtreg/runtime/ClassInitErrors/TestOutOfMemoryDuringInit.java +++ b/test/hotspot/jtreg/runtime/ClassInitErrors/TestOutOfMemoryDuringInit.java @@ -44,6 +44,10 @@ static class Nested { static void forceInit() { } static { while (theList != null) { + // Use the minimal allocation size to push heap occupation to + // the limit, ensuring there is not enough memory to create the + // ExceptionInInitializerError that the VM tries to create when + // the clinit throws the OOM. theList.add(new Object()); } } diff --git a/test/hotspot/jtreg/runtime/NMT/TotalMallocMmapDiffTest.java b/test/hotspot/jtreg/runtime/NMT/TotalMallocMmapDiffTest.java index 3ba94c6a31a86..cd89bded57827 100644 --- a/test/hotspot/jtreg/runtime/NMT/TotalMallocMmapDiffTest.java +++ b/test/hotspot/jtreg/runtime/NMT/TotalMallocMmapDiffTest.java @@ -32,7 +32,7 @@ * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary -Xms32m -Xmx32m TotalMallocMmapDiffTest + * @run main/othervm -Xbootclasspath/a:. -XX:TieredStopAtLevel=1 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary -Xms32m -Xmx32m TotalMallocMmapDiffTest * */ @@ -43,9 +43,10 @@ public class TotalMallocMmapDiffTest { private static final WhiteBox wb = WhiteBox.getWhiteBox(); private static final long ALLOCATE_SIZE = 250 * 1024 * 1024; // 250MB - private static final double FUDGE_FACTOR = 0.2; - private static final double UPPER_BOUND = ALLOCATE_SIZE * (1 + FUDGE_FACTOR); - private static final double LOWER_BOUND = ALLOCATE_SIZE * (1 - FUDGE_FACTOR); + private static final double FUDGE_FACTOR_UPPER = 0.3; + private static final double FUDGE_FACTOR_LOWER = 0.2; + private static final double UPPER_BOUND = ALLOCATE_SIZE * (1 + FUDGE_FACTOR_UPPER); + private static final double LOWER_BOUND = ALLOCATE_SIZE * (1 - FUDGE_FACTOR_LOWER); public static void main(String[] args) throws Exception { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java index ad7b8e97b81a2..eedefaa1ba16f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -190,6 +190,10 @@ protected static OutputAnalyzer test(String fileName, long pid, PidJcmdExecutor cmdExecutor = new PidJcmdExecutor(String.valueOf(pid)); OutputAnalyzer output = cmdExecutor.execute(jcmd, true/*silent*/); + if (archiveFileName.contains("%p")) { + archiveFileName = archiveFileName.replace("%p", "%d").formatted(pid); + } + if (expectOK) { output.shouldHaveExitValue(0); checkFileExistence(archiveFileName, true); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java index a95eaf672c1dc..08ccf9b7f2af5 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,6 +87,13 @@ static void test() throws Exception { } app.stopApp(); + // Test static dump with file name containing %p + print2ln(test_count++ + " Test static dump with given file name containing %p."); + app = createLingeredApp("-cp", allJars); + pid = app.getPid(); + test("%p.jsa", pid, noBoot, EXPECT_PASS, STATIC_MESSAGES); + app.stopApp(); + // Test static dump with flags with which dumping should fail // This test will result classes.jsa in default server dir if -XX:SharedArchiveFile= not set. print2ln(test_count++ + " Test static dump with flags with which dumping should fail."); diff --git a/test/hotspot/jtreg/runtime/classFileParserBug/Bad_NCDFE_Msg.java b/test/hotspot/jtreg/runtime/classFileParserBug/Bad_NCDFE_Msg.java index 1baf5bdc8a8b2..a69a374619203 100644 --- a/test/hotspot/jtreg/runtime/classFileParserBug/Bad_NCDFE_Msg.java +++ b/test/hotspot/jtreg/runtime/classFileParserBug/Bad_NCDFE_Msg.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ * java.management * @compile C.java * @run driver Bad_NCDFE_Msg + */ import java.io.File; import jdk.test.lib.process.ProcessTools; diff --git a/test/hotspot/jtreg/runtime/stringtable/StringTableCorruptionTest.java b/test/hotspot/jtreg/runtime/stringtable/StringTableCorruptionTest.java new file mode 100644 index 0000000000000..e4d6a2e5d0f95 --- /dev/null +++ b/test/hotspot/jtreg/runtime/stringtable/StringTableCorruptionTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8333356 + * @summary Verify new error message for corrupting string table contents. + * @requires vm.flagless + * @modules java.base/java.lang:open + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run driver StringTableCorruptionTest test + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class StringTableCorruptionTest { + public static void main(String[] args) throws Exception { + if (args.length > 0) { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("--add-opens", "java.base/java.lang=ALL-UNNAMED", + "-XX:-CreateCoredumpOnCrash", "StringTableCorruptionTest"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Node hash code has changed possibly due to corruption of the contents."); + output.shouldNotHaveExitValue(0); + return; + } + + Field f = String.class.getDeclaredField("value"); + f.setAccessible(true); + f.set("s1".intern(), f.get("s2")); + for (int i = 0; i < 4_000_000; i++) { + ("s_" + i).intern(); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/ParserTest.java b/test/hotspot/jtreg/serviceability/ParserTest.java index 70d666d2ed0ab..7c304af243c2e 100644 --- a/test/hotspot/jtreg/serviceability/ParserTest.java +++ b/test/hotspot/jtreg/serviceability/ParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import java.math.BigInteger; +import jdk.test.lib.process.ProcessTools; import jdk.test.whitebox.parser.DiagnosticCommand; import jdk.test.whitebox.parser.DiagnosticCommand.DiagnosticArgumentType; import jdk.test.whitebox.WhiteBox; @@ -49,6 +50,7 @@ public ParserTest() throws Exception { testQuotes(); testMemorySize(); testSingleLetterArg(); + testFileName(); } public static void main(String... args) throws Exception { @@ -159,6 +161,33 @@ public void testSingleLetterArg() throws Exception { parse("value", "v", "flag v", ' ', args); } + public void testFileName() throws Exception { + // --- Testing options + long pid = ProcessTools.getProcessId(); + + // Test pid gets injected into %p + String name = "name"; + DiagnosticCommand arg = new DiagnosticCommand(name, + "desc", DiagnosticArgumentType.FILE, + false, null); + DiagnosticCommand[] args = {arg}; + parse(name, "file%d.txt".formatted(pid), name + "=file%p.txt", args); + + // Test custom file name with no %p + parse(name, "myFile.txt", name + "=myFile.txt", args); + + // --- Testing arguments + + // Test pid gets injected into %p + arg = new DiagnosticCommand(name, "desc", DiagnosticArgumentType.FILE, true, + false, null); + args = new DiagnosticCommand[]{arg}; + parse(name, "file%d.txt".formatted(pid), "file%p.txt", args); + + // Test custom file name with no %p + parse(name, "myFile.txt", "myFile.txt", args); + } + public void testMemorySize() throws Exception { String name = "name"; String defaultValue = "1024"; diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java index 7d4a38db47b48..02bc77263d805 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java @@ -47,9 +47,9 @@ public static void main(String args[]) throws Exception { out.shouldHaveExitValue(0); // Looks like this: - // total NA RA result #nodes time type #rc thread method - // 211488 66440 77624 ok 13 0.057 c2 2 0x00007fb49428db70 compiler/print/CompileCommandPrintMemStat$TestMain::method1(()V) + // total Others RA HA NA result #nodes limit time type #rc thread method + // 1898600 853176 750872 0 294552 ok 934 - 1.501 c2 1 0x00007f4ec00d3330 java/lang/String::replace((CC)Ljava/lang/String;) out.shouldMatch("total.*method"); - out.shouldMatch("\\d+ +\\d+ +\\d+ +\\S+ +\\d+.*java.*\\(.*\\)"); + out.shouldMatch("\\d+ +(\\d+ +){4}\\S+ +\\d+.*java.*\\(.*\\)"); } } diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java index 6bbe62044d023..7a72efd68ba6c 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -113,4 +113,16 @@ public void specifiedDefaultMapFile() throws IOException { run(new JMXExecutor(), "Compiler.perfmap " + path.toString(), path); Files.deleteIfExists(path); } + + @Test + public void logErrorsDcmdOutputStream() throws IOException { + String test_dir = System.getProperty("test.dir", "."); + Path path = Paths.get("nonexistent", test_dir); + try { + OutputAnalyzer output = new JMXExecutor().execute("Compiler.perfmap %s".formatted(path)); + output.shouldContain("Warning: Failed to create nonexistent/%s for perf map".formatted(test_dir)); + } finally { + Files.deleteIfExists(path); + } + } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java index 80ca790a11d77..420f1cedaca9d 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test - * @bug 8185164 8320515 + * @bug 8185164 8320515 8334085 * @summary Checks that a contended monitor does not show up in the list of owned monitors. * 8320515 piggy-backs on this test and injects an owned monitor with a dead object, and checks that that monitor isn't exposed to GetOwnedMonitorInfo. @@ -86,6 +86,9 @@ public static void runTest(boolean isVirtual, boolean jni) throws Exception { System.out.println("Thread doing JNI call: " + Thread.currentThread().getName()); + // Extra unmount helps to reproduce 8334085. + // Two sub-sequential thaws are needed in that scenario. + Thread.yield(); jniMonitorEnterAndLetObjectDie(); } diff --git a/test/hotspot/jtreg/serviceability/jvmti/StartPhase/AllowedFunctions/libAllowedFunctions.c b/test/hotspot/jtreg/serviceability/jvmti/StartPhase/AllowedFunctions/libAllowedFunctions.c index 4299a98d7e63b..97b5cab341eb2 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/StartPhase/AllowedFunctions/libAllowedFunctions.c +++ b/test/hotspot/jtreg/serviceability/jvmti/StartPhase/AllowedFunctions/libAllowedFunctions.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,8 @@ extern "C" { #define FAILED 2 static jint result = PASSED; +static jrawMonitorID event_mon = NULL; +static jboolean is_vm_dead = JNI_FALSE; static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); @@ -68,7 +70,7 @@ jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { static void check_jvmti_error(jvmtiEnv *jvmti, char* fname, jvmtiError err) { if (err != JVMTI_ERROR_NONE) { printf(" ## %s error: %d\n", fname, err); - exit(err); + abort(); } } @@ -317,7 +319,7 @@ VMStart(jvmtiEnv *jvmti, JNIEnv* jni) { } static void JNICALL -VMInit(jvmtiEnv *jvmti, JNIEnv* jnii, jthread thread) { +VMInit(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) { jvmtiPhase phase; printf("VMInit event\n"); @@ -329,22 +331,52 @@ VMInit(jvmtiEnv *jvmti, JNIEnv* jnii, jthread thread) { } } +static void JNICALL +VMDeath(jvmtiEnv *jvmti, JNIEnv* jni) { + jvmtiError err; + + // Block ClassPrepare events while this callback is executed. + err = (*jvmti)->RawMonitorEnter(jvmti, event_mon); + check_jvmti_error(jvmti, "VMDeath event: Failed in RawMonitorEnter", err); + + is_vm_dead = JNI_TRUE; + printf("VMDeath event\n"); + + err = (*jvmti)->RawMonitorExit(jvmti, event_mon); + check_jvmti_error(jvmti, "VMDeath event: Failed in RawMonitorExit", err); +} + static void JNICALL ClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) { static const jint EVENTS_LIMIT = 2; static jint event_no = 0; - jthread cur_thread = get_cur_thread(jvmti); jvmtiPhase phase; intptr_t exp_val = 777; intptr_t act_val; + jvmtiError err; + + // Block VMDeath event and other ClassPrepare events while this callback is executed. + // Sync with VMDeath event and check for is_vm_dead guard against JVMTI_ERROR WRONG_PHASE. + err = (*jvmti)->RawMonitorEnter(jvmti, event_mon); + check_jvmti_error(jvmti, "ClassPrepare event: Failed in RawMonitorEnter", err); + if (is_vm_dead) { + printf("\nIgnoring ClassPrepare event during the dead phase\n"); + err = (*jvmti)->RawMonitorExit(jvmti, event_mon); + check_jvmti_error(jvmti, "ClassPrepare event: Failed in RawMonitorExit", err); + return; + } get_phase(jvmti, &phase); if (phase != JVMTI_PHASE_START && phase != JVMTI_PHASE_LIVE) { printf(" ## Error: unexpected phase: %d, expected: %d or %d\n", phase, JVMTI_PHASE_START, JVMTI_PHASE_LIVE); + result = FAILED; + err = (*jvmti)->RawMonitorExit(jvmti, event_mon); + check_jvmti_error(jvmti, "ClassPrepare event: Failed in RawMonitorExit", err); return; } if (phase == JVMTI_PHASE_START && event_no < EVENTS_LIMIT) { + jthread cur_thread = get_cur_thread(jvmti); printf("\nClassPrepare event during the start phase: #%d\n", event_no); // Test the JVMTI class functions during the start phase test_class_functions(jvmti, env, thread, klass); @@ -360,6 +392,8 @@ ClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) { } event_no++; } + err = (*jvmti)->RawMonitorExit(jvmti, event_mon); + check_jvmti_error(jvmti, "ClassPrepare event: Failed in RawMonitorExit", err); } static @@ -400,6 +434,9 @@ jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { callbacks.VMInit = VMInit; callbacks.ClassPrepare = ClassPrepare; + err = (*jvmti)->CreateRawMonitor(jvmti, "Events Monitor", &event_mon); + check_jvmti_error(jvmti, "## Agent_Initialize: CreateRawMonitor", err); + err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, size); check_jvmti_error(jvmti, "## Agent_Initialize: SetEventCallbacks", err); diff --git a/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java b/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java index 8d757283a3093..8a7177ec25d5f 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java +++ b/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,7 +79,7 @@ public static void main(String args[]) throws Exception { } // this is too fragile, implementation can change at any time. - checkFrames(vThread1, false, 14); + checkFrames(vThread1, false, 13); LockSupport.unpark(vThread1); vThread1.join(); diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java index 4ee1d1a0bfcae..cc8f5638c76b8 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,29 @@ * @test id=default * @bug 8312498 * @summary Basic test for JVMTI GetThreadState with virtual threads + * @modules java.base/java.lang:+open * @library /test/lib - * @run junit/othervm/native GetThreadStateTest + * @run junit/othervm/native --enable-native-access=ALL-UNNAMED GetThreadStateTest */ /* * @test id=no-vmcontinuations * @requires vm.continuations + * @modules java.base/java.lang:+open * @library /test/lib - * @run junit/othervm/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations GetThreadStateTest + * @run junit/othervm/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations --enable-native-access=ALL-UNNAMED GetThreadStateTest */ import java.util.StringJoiner; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadPinner; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; class GetThreadStateTest { @@ -51,6 +56,11 @@ class GetThreadStateTest { static void setup() { System.loadLibrary("GetThreadStateTest"); init(); + + // need >=2 carriers for testing pinning when main thread is a virtual thread + if (Thread.currentThread().isVirtual()) { + VThreadRunner.ensureParallelism(2); + } } /** @@ -105,21 +115,29 @@ void testRunnable() throws Exception { } /** - * Test state of thread waiting to enter a monitor. + * Test state of thread waiting to enter a monitor when pinned and not pinned. */ - @Test - void testMonitorEnter() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testMonitorEnter(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); Object lock = new Object(); var thread = Thread.ofVirtual().unstarted(() -> { - started.set(true); - synchronized (lock) { } + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + synchronized (lock) { } + }); + } else { + ready.set(true); + synchronized (lock) { } + } }); try { synchronized (lock) { // start thread and wait for it to start execution thread.start(); - awaitTrue(started); + awaitTrue(ready); // thread should block on monitor enter int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; @@ -135,23 +153,31 @@ void testMonitorEnter() throws Exception { } /** - * Test state of thread waiting in Object.wait(). + * Test state of thread waiting in Object.wait() when pinned and not pinned. */ - @Test - void testObjectWait() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testObjectWait(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); Object lock = new Object(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - started.set(true); try { - lock.wait(); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } } catch (InterruptedException e) { } } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should wait int expected = JVMTI_THREAD_STATE_ALIVE | @@ -177,23 +203,33 @@ void testObjectWait() throws Exception { } /** - * Test state of thread waiting in Object.wait(millis). + * Test state of thread waiting in Object.wait(millis) when pinned and not pinned. */ - @Test - void testObjectWaitMillis() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testObjectWaitMillis(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); Object lock = new Object(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - started.set(true); - try { - lock.wait(Long.MAX_VALUE); - } catch (InterruptedException e) { } + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(Long.MAX_VALUE); + }); + } else { + ready.set(true); + lock.wait(Long.MAX_VALUE); + } + } catch (InterruptedException e) { } + } } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should wait int expected = JVMTI_THREAD_STATE_ALIVE | @@ -219,83 +255,31 @@ void testObjectWaitMillis() throws Exception { } /** - * Test state of thread parked with LockSupport.park. + * Test state of thread parked with LockSupport.park when pinned and not pinned. */ - @Test - void testPark() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testPark(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - started.set(true); - while (!done.get()) { - LockSupport.park(); - } - }); - try { - // wait for thread to start execution - awaitTrue(started); - - // thread should park - int expected = JVMTI_THREAD_STATE_ALIVE | - JVMTI_THREAD_STATE_WAITING | - JVMTI_THREAD_STATE_WAITING_INDEFINITELY | - JVMTI_THREAD_STATE_PARKED; - await(thread, expected); - } finally { - done.set(true); - LockSupport.unpark(thread); - thread.join(); - } - } - - /** - * Test state of thread parked with LockSupport.parkNanos. - */ - @Test - void testParkNanos() throws Exception { - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - var thread = Thread.ofVirtual().start(() -> { - started.set(true); - while (!done.get()) { - LockSupport.parkNanos(Long.MAX_VALUE); - } - }); - try { - // wait for thread to start execution - awaitTrue(started); - - // thread should park - int expected = JVMTI_THREAD_STATE_ALIVE | - JVMTI_THREAD_STATE_WAITING | - JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | - JVMTI_THREAD_STATE_PARKED; - await(thread, expected); - } finally { - done.set(true); - LockSupport.unpark(thread); - thread.join(); - } - } - - /** - * Test state of thread parked with LockSupport.park while holding a monitor. - */ - @Test - void testParkWhenPinned() throws Exception { - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { - started.set(true); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + while (!done.get()) { + LockSupport.park(); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.park(); } - }); + } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should park int expected = JVMTI_THREAD_STATE_ALIVE | @@ -311,23 +295,31 @@ void testParkWhenPinned() throws Exception { } /** - * Test state of thread parked with LockSupport.parkNanos while holding a monitor. + * Test state of thread parked with LockSupport.parkNanos when pinned and not pinned. */ - @Test - void testParkNanosWhenPinned() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testParkNanos(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { - started.set(true); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + while (!done.get()) { + LockSupport.parkNanos(Long.MAX_VALUE); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.parkNanos(Long.MAX_VALUE); } - }); + } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should park int expected = JVMTI_THREAD_STATE_ALIVE | diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/TestPinCaseWithCFLH/TestPinCaseWithCFLH.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/TestPinCaseWithCFLH/TestPinCaseWithCFLH.java new file mode 100644 index 0000000000000..02755a0289fc1 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/TestPinCaseWithCFLH/TestPinCaseWithCFLH.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import jdk.test.lib.thread.VThreadPinner; + +/* + * @test + * @summary javaagent + tracePinnedThreads will cause jvm crash/ run into deadlock when the virtual thread is pinned + * @library /test/lib + * @requires vm.continuations + * @requires vm.jvmti + * @modules java.base/java.lang:+open + * @compile TestPinCaseWithCFLH.java + * @build jdk.test.lib.Utils + * @run driver jdk.test.lib.util.JavaAgentBuilder + * TestPinCaseWithCFLH TestPinCaseWithCFLH.jar + * @run main/othervm/timeout=100 -Djdk.virtualThreadScheduler.maxPoolSize=1 + * -Djdk.tracePinnedThreads=full --enable-native-access=ALL-UNNAMED + * -javaagent:TestPinCaseWithCFLH.jar TestPinCaseWithCFLH + */ +public class TestPinCaseWithCFLH { + + public static class TestClassFileTransformer implements ClassFileTransformer { + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException { + return classfileBuffer; + } + } + + // Called when agent is loaded at startup + public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception { + instrumentation.addTransformer(new TestClassFileTransformer()); + } + + private static int result = 0; + + public static void main(String[] args) throws Exception{ + Thread t1 = Thread.ofVirtual().name("vthread-1").start(() -> { + VThreadPinner.runPinned(() -> { + try { + // try yield, will pin, + // javaagent + tracePinnedThreads should not lead to crash + // (because of the class `PinnedThreadPrinter`) + Thread.sleep(500); + } catch (Exception e) { + e.printStackTrace(); + } + }); + }); + t1.join(); + } + +} \ No newline at end of file diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java index 61b94a4484bc1..845e9adba019b 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,47 +28,22 @@ * @requires vm.continuations * @requires vm.jvmti * @requires vm.compMode != "Xcomp" + * @modules java.base/java.lang:+open + * @library /test/lib * @run main/othervm/native - * -Djdk.virtualThreadScheduler.parallelism=9 * -Djdk.attach.allowAttachSelf=true -XX:+EnableDynamicAgentLoading VThreadEventTest attach */ import com.sun.tools.attach.VirtualMachine; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; import java.util.List; import java.util.ArrayList; - -/* - * The test uses custom implementation of the CountDownLatch class. - * The reason is we want the state of tested thread to be predictable. - * With java.util.concurrent.CountDownLatch it is not clear what thread state is expected. - */ -class CountDownLatch { - private int count = 0; - - CountDownLatch(int count) { - this.count = count; - } - - public synchronized void countDown() { - count--; - notify(); - } - - public synchronized void await() throws InterruptedException { - while (count > 0) { - wait(1); - } - } -} +import jdk.test.lib.thread.VThreadRunner; public class VThreadEventTest { - static final int TCNT1 = 10; - static final int TCNT2 = 4; - static final int TCNT3 = 4; - static final int THREAD_CNT = TCNT1 + TCNT2 + TCNT3; + static final int PARKED_THREAD_COUNT = 4; + static final int SPINNING_THREAD_COUNT = 4; private static void log(String msg) { System.out.println(msg); } @@ -77,128 +52,96 @@ public class VThreadEventTest { private static native int threadUnmountCount(); private static volatile boolean attached; - private static boolean failed; - private static List test1Threads = new ArrayList(TCNT1); - - private static CountDownLatch ready0 = new CountDownLatch(THREAD_CNT); - private static CountDownLatch ready1 = new CountDownLatch(TCNT1); - private static CountDownLatch ready2 = new CountDownLatch(THREAD_CNT); - private static CountDownLatch mready = new CountDownLatch(1); - - private static void await(CountDownLatch dumpedLatch) { - try { - dumpedLatch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + + // called by agent when it is initialized and has enabled events + static void agentStarted() { + attached = true; } - // The test1 vthreads are kept unmounted until interrupted after agent attach. - static final Runnable test1 = () -> { - synchronized (test1Threads) { - test1Threads.add(Thread.currentThread()); + public static void main(String[] args) throws Exception { + if (Thread.currentThread().isVirtual()) { + System.out.println("Skipping test as current thread is a virtual thread"); + return; } - log("test1 vthread started"); - ready0.countDown(); - await(mready); - ready1.countDown(); // to guaranty state is not State.WAITING after await(mready) - try { - Thread.sleep(20000); // big timeout to keep unmounted until interrupted - } catch (InterruptedException ex) { - // it is expected, ignore + VThreadRunner.ensureParallelism(SPINNING_THREAD_COUNT+1); + + // start threads that park (unmount) + var threads1 = new ArrayList(); + for (int i = 0; i < PARKED_THREAD_COUNT; i++) { + var started = new AtomicBoolean(); + var thread = Thread.startVirtualThread(() -> { + started.set(true); + LockSupport.park(); + }); + + // wait for thread to start execution + park + while (!started.get()) { + Thread.sleep(10); + } + await(thread, Thread.State.WAITING); + threads1.add(thread); } - ready2.countDown(); - }; - - // The test2 vthreads are kept mounted until agent attach. - static final Runnable test2 = () -> { - log("test2 vthread started"); - ready0.countDown(); - await(mready); - while (!attached) { - // keep mounted + + // start threads that spin (stay mounted) + var threads2 = new ArrayList(); + for (int i = 0; i < SPINNING_THREAD_COUNT; i++) { + var started = new AtomicBoolean(); + var thread = Thread.startVirtualThread(() -> { + started.set(true); + while (!attached) { + Thread.onSpinWait(); + } + }); + + // wait for thread to start execution + while (!started.get()) { + Thread.sleep(10); + } + threads2.add(thread); } - ready2.countDown(); - }; - - // The test3 vthreads are kept mounted until agent attach. - static final Runnable test3 = () -> { - log("test3 vthread started"); - ready0.countDown(); - await(mready); + + // attach to the current VM + VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid())); + vm.loadAgentLibrary("VThreadEventTest"); + + // wait for agent to start while (!attached) { - // keep mounted + Thread.sleep(10); } - LockSupport.parkNanos(10_000_000L); // will cause extra mount and unmount - ready2.countDown(); - }; - public static void main(String[] args) throws Exception { - if (Runtime.getRuntime().availableProcessors() < 8) { - log("WARNING: test expects at least 8 processors."); + // unpark the threads that were parked + for (Thread thread : threads1) { + LockSupport.unpark(thread); } - try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) { - for (int i = 0; i < TCNT1; i++) { - executorService.execute(test1); - } - for (int i = 0; i < TCNT2; i++) { - executorService.execute(test2); - } - for (int i = 0; i < TCNT3; i++) { - executorService.execute(test3); - } - await(ready0); - mready.countDown(); - await(ready1); // to guarantee state is not State.TIMED_WAITING after await(mready) in test1() - // wait for test1 threads to reach TIMED_WAITING state in sleep() - for (Thread t : test1Threads) { - Thread.State state = t.getState(); - log("DBG: state: " + state); - while (state != Thread.State.TIMED_WAITING) { - Thread.sleep(10); - state = t.getState(); - log("DBG: state: " + state); - } - } - - VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid())); - vm.loadAgentLibrary("VThreadEventTest"); - Thread.sleep(200); // to allow the agent to get ready - attached = true; - for (Thread t : test1Threads) { - t.interrupt(); - } - ready2.await(); + // wait for all threads to terminate + for (Thread thread : threads1) { + thread.join(); } - // wait until all VirtualThreadEnd events have been sent - for (int sleepNo = 1; threadEndCount() < THREAD_CNT; sleepNo++) { - Thread.sleep(100); - if (sleepNo % 100 == 0) { // 10 sec period of waiting - log("main: waited seconds: " + sleepNo/10); - } + for (Thread thread : threads2) { + thread.join(); } + int threadEndCnt = threadEndCount(); int threadMountCnt = threadMountCount(); int threadUnmountCnt = threadUnmountCount(); - int threadEndExp = THREAD_CNT; - int threadMountExp = THREAD_CNT - TCNT2; - int threadUnmountExp = THREAD_CNT + TCNT3; - log("ThreadEnd cnt: " + threadEndCnt + " (expected: " + threadEndExp + ")"); - log("ThreadMount cnt: " + threadMountCnt + " (expected: " + threadMountExp + ")"); - log("ThreadUnmount cnt: " + threadUnmountCnt + " (expected: " + threadUnmountExp + ")"); + int threadCount = PARKED_THREAD_COUNT + SPINNING_THREAD_COUNT; + log("VirtualThreadEnd events: " + threadEndCnt + ", expected: " + threadCount); + log("VirtualThreadMount events: " + threadMountCnt + ", expected: " + PARKED_THREAD_COUNT); + log("VirtualThreadUnmount events: " + threadUnmountCnt + ", expected: " + threadCount); - if (threadEndCnt != threadEndExp) { - log("FAILED: unexpected count of ThreadEnd events"); + boolean failed = false; + if (threadEndCnt != threadCount) { + log("FAILED: unexpected count of VirtualThreadEnd events"); failed = true; } - if (threadMountCnt != threadMountExp) { - log("FAILED: unexpected count of ThreadMount events"); + if (threadMountCnt != PARKED_THREAD_COUNT) { + log("FAILED: unexpected count of VirtualThreadMount events"); failed = true; } - if (threadUnmountCnt != threadUnmountExp) { - log("FAILED: unexpected count of ThreadUnmount events"); + if (threadUnmountCnt != threadCount) { + log("FAILED: unexpected count of VirtualThreadUnmount events"); failed = true; } if (failed) { @@ -206,5 +149,14 @@ public static void main(String[] args) throws Exception { } } + private static void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assert state != Thread.State.TERMINATED : "Thread has terminated"; + Thread.sleep(10); + state = thread.getState(); + } + } + } diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp index afd06e66b32cc..c97a8ef3471aa 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp @@ -65,6 +65,11 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { jvmtiEventCallbacks callbacks; jvmtiCapabilities caps; jvmtiError err; + JNIEnv *env; + jsize nVMs; + jint res; + jclass clazz; + jmethodID mid; LOG("Agent_OnAttach started\n"); if (vm->GetEnv(reinterpret_cast(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) { @@ -97,6 +102,41 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadUnmount"); LOG("vthread events enabled\n"); + + // call VThreadEventTest.agentStarted to notify test that agent has started + + res = JNI_GetCreatedJavaVMs(&vm, 1, &nVMs); + if (res != JNI_OK) { + LOG("JNI_GetCreatedJavaVMs failed: %d\n", res); + return JNI_ERR; + } + + res = vm->GetEnv((void **) &env, JNI_VERSION_21); + if (res != JNI_OK) { + LOG("GetEnv failed: %d\n", res); + return JNI_ERR; + } + + clazz = env->FindClass("VThreadEventTest"); + if (clazz == NULL) { + LOG("FindClass failed\n"); + return JNI_ERR; + } + + mid = env->GetStaticMethodID(clazz, "agentStarted", "()V"); + if (mid == NULL) { + LOG("GetStaticMethodID failed\n"); + return JNI_ERR; + } + + env->CallStaticVoidMethod(clazz, mid); + if (env->ExceptionOccurred()) { + LOG("CallStaticVoidMethod failed\n"); + return JNI_ERR; + } + + LOG("Agent_OnAttach done\n"); + return JVMTI_ERROR_NONE; } diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java index 31b4d2dad357c..b414a6f4e8d42 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpheap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,10 @@ * @bug 8240989 * @summary Test clhsdb dumpheap command * @requires vm.hasSA + * @requires vm.compMode != "Xcomp" + * @comment Running this test with -Xcomp is slow and therefore tends to cause + * timeouts. As there is no known direct benefit from running the test + * with -Xcomp, we disable such testing. * @library /test/lib * @run main/othervm/timeout=240 ClhsdbDumpheap */ diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java index 9a39fb5310f35..f0056ebc79da9 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java @@ -66,7 +66,7 @@ private static void run(Class testClass) { List noAdditionalFlags = new ArrayList<>(); FlagVMProcess flagVMProcess = new FlagVMProcess(testClass, noAdditionalFlags); List testVMFlags = flagVMProcess.getTestVMFlags(); - TestVMProcess testVMProcess = new TestVMProcess(testVMFlags, testClass, null, -1); + TestVMProcess testVMProcess = new TestVMProcess(testVMFlags, testClass, null, -1, false); TestClassParser testClassParser = new TestClassParser(testClass); Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), testVMProcess.getIrEncoding()); diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPrivilegedMode.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPrivilegedMode.java new file mode 100644 index 0000000000000..347b2eb39fbfc --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPrivilegedMode.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 ir_framework.tests; + +import compiler.lib.ir_framework.*; +import compiler.lib.ir_framework.driver.irmatching.IRViolationException; + +import jdk.internal.vm.annotation.Stable; +import jdk.test.lib.Asserts; + +/* + * @test + * @requires vm.flagless + * @summary Test that IR framework successfully adds test class to boot classpath in order to run in privileged mode. + * @modules java.base/jdk.internal.vm.annotation + * @library /test/lib / + * @run driver ir_framework.tests.TestPrivilegedMode + */ + +public class TestPrivilegedMode { + static @Stable int iFld; // Treated as constant after first being set. + + public static void main(String[] args) { + try { + TestFramework.run(); + Asserts.fail("should not reach"); + } catch (IRViolationException e) { + // Without adding test class to boot classpath, we fail to replace the field load by a constant. + Asserts.assertTrue(e.getExceptionInfo().contains("Matched forbidden node")); + Asserts.assertTrue(e.getExceptionInfo().contains("LoadI")); + } + + // When adding the test class to the boot classpath, we can replace the field load by a constant. + new TestFramework().addTestClassesToBootClassPath().start(); + } + + @Test + @Arguments(setup = "setup") + @IR(failOn = IRNode.LOAD_I) + public int test() { + return iFld; + } + + @Setup + public void setup() { + iFld = 34; + } +} diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002.java index 620c50fa2ae22..9dae8c17cfc36 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,17 +82,10 @@ public static int run(String argv[], PrintStream out) { static final String DEBUGGEE_CLASS = TEST_CLASS + "a"; static final String FIRST_BREAK = DEBUGGEE_CLASS + ".main"; static final String LAST_BREAK = DEBUGGEE_CLASS + ".lastBreak"; - static final String DEBUGGEE_LOCATION1 = DEBUGGEE_CLASS + "$Nested$DeeperNested$DeepestNested:43"; - static final String DEBUGGEE_LOCATION2 = DEBUGGEE_CLASS + "$Inner$MoreInner:57"; - static final String FAILURE_PATTERN = "Unable to set"; + static final String DEBUGGEE_LOCATION1 = DEBUGGEE_CLASS + "$Nested$DeeperNested$DeepestNested:64"; + static final String DEBUGGEE_LOCATION2 = DEBUGGEE_CLASS + "$Inner$MoreInner:78"; protected void runCases() { - String[] reply; - Paragrep grep; - int count; - Vector v; - String found; - if (!checkStop(DEBUGGEE_LOCATION1)) { success = false; } @@ -101,25 +94,62 @@ protected void runCases() { success = false; } - jdb.contToExit(3); + if (!checkBreakpointHit(DEBUGGEE_LOCATION1)) { + success = false; + } + + if (!checkBreakpointHit(DEBUGGEE_LOCATION2)) { + success = false; + } + + jdb.contToExit(1); } - private boolean checkStop (String location) { + private boolean checkStop(String location) { Paragrep grep; String[] reply; String found; - boolean result = true; log.display("Trying to set breakpoint at line: " + location); reply = jdb.receiveReplyFor(JdbCommand.stop_at + location); grep = new Paragrep(reply); - found = grep.findFirst(FAILURE_PATTERN); + found = grep.findFirst("Deferring breakpoint " + location); + if (found.length() == 0) { + log.complain("jdb failed to setup deferred breakpoint at line: " + location); + return false; + } + + return true; + } + + private boolean checkBreakpointHit(String location) { + Paragrep grep; + String[] reply; + String found; + + log.display("continuing to breakpoint at line: " + location); + reply = jdb.receiveReplyFor(JdbCommand.cont); + grep = new Paragrep(reply); + + found = grep.findFirst("Unable to set deferred breakpoint"); if (found.length() > 0) { - log.complain("jdb failed to set line breakpoint at line: " + found); - result = false; + log.complain("jdb failed to set deferred breakpoint at line: " + location); + return false; + } + + found = grep.findFirst("Set deferred breakpoint " + location); + if (found.length() == 0) { + log.complain("jdb failed to set deferred breakpoint at line: " + location); + return false; + } + + found = grep.findFirst("Breakpoint hit: \"thread=main\", "); + if (found.length() == 0) { + log.complain("jdb failed to hit breakpoint at line: " + location); + return false; } - return result; + return true; } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002a.java index 9195dd2698612..d4a40689372d4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/stop_at/stop_at002/stop_at002a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,8 @@ * questions. */ +// THIS TEST IS LINE NUMBER SENSITIVE + package nsk.jdb.stop_at.stop_at002; import nsk.share.*; @@ -59,7 +61,7 @@ class Nested { class DeeperNested { class DeepestNested { public void foo(boolean input) { - flag = input; /* <-------- This is line number 43 */ + flag = input; /* <-------- This is line number 64 */ } } } @@ -73,7 +75,7 @@ public MoreInner() { content = ""; } public void foo(String input) { - content += input; /* <-------- This is line number 57 */ + content += input; /* <-------- This is line number 78 */ } } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001.java index b6ca1c84e9c4c..6e50e6998cdd5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; import nsk.share.*; import nsk.share.gc.Algorithms; import nsk.share.gc.Memory; @@ -39,6 +40,7 @@ public class from001 { private static boolean testFailed = false; + private static final int MAX_TRIES = 6; // limit attempts to receive Notification data public static void main(String[] args) { @@ -62,8 +64,8 @@ public static void main(String[] args) { log.display("null CompositeData check passed."); - // 2. Check CompositeData that doest not represnt - // MemoryNotificationInfo - IllegalArgumentException must be thrown + // 2. Check CompositeData that does not represent MemoryNotificationInfo + // throws IllegalArgumentException ObjectName mbeanObjectName = null; CompositeData cdata = null; @@ -85,12 +87,13 @@ public static void main(String[] args) { testFailed = true; } catch (IllegalArgumentException e) { - // Expected: CompositeData doest not represnt MemoryNotificationInfo + // Expected: CompositeData does not represent MemoryNotificationInfo } - log.display("check for CompositeData doest not represnt MemoryNotificationInfo passed."); + log.display("check that CompositeData does not represent MemoryNotificationInfo passed."); - // 3. Check correct CompositeData + // 3. Check correct CompositeData usage: + // First try to provoke a Notification on a MemoryPool. Object poolObject = null; try { mbeanObjectName = new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME); @@ -123,7 +126,11 @@ public static void main(String[] args) { throw new TestFailure("TEST FAILED. See log."); } - // eat memory just to emmit notification + if (poolObject == null) { + throw new TestFailure("No memory pool found to test."); + } + + // eat memory just to emit notification Stresser stresser = new Stresser(args) { @Override @@ -133,41 +140,57 @@ public boolean continueExecution() { } }; stresser.start(0);// we use timeout, not iterations - GarbageUtils.eatMemory(stresser); - - boolean messageNotRecieved = true; - while(messageNotRecieved) { + int oomCount = GarbageUtils.eatMemory(stresser); + log.display("eatMemory returns OOM count: " + oomCount); + + // Check for the message. Poll on queue to avoid waiting forver on failure. + // Notification is known to fail, very rarely, with -Xcomp where the allocations + // do not affect the monitored pool. Possibly a timing issue, where the "eatMemory" + // is done before Notification/threshold processing happens. + // The Notification is quite immediate, other than that problem. + boolean messageReceived = false; + int tries = 0; + while (!messageReceived && ++tries < MAX_TRIES) { try { - from001Listener.queue.take(); - messageNotRecieved = false; + Object r = from001Listener.queue.poll(10000, TimeUnit.MILLISECONDS); + if (r == null) { + log.display("poll for Notification data returns null..."); + continue; + } else { + messageReceived = true; + break; + } } catch (InterruptedException e) { - messageNotRecieved = true; + // ignored, continue } } + // If we got a Notification, test that the CompositeData can create a MemoryNotificationInfo + if (!messageReceived) { + throw new TestFailure("No Notification received."); + } result = MemoryNotificationInfo.from(from001Listener.data.get()); try { - ObjectName poolObjectName = new ObjectName(monitor.getName(poolObject)); - ObjectName resultObjectName = new ObjectName( - ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE + - ",name=" + result.getPoolName()); - - log.display("poolObjectName : " + poolObjectName + - " resultObjectName : " + resultObjectName); - - if (!poolObjectName.equals(resultObjectName)) { - log.complain("FAILURE 3."); - log.complain("Wrong pool name : " + resultObjectName + - ", expected : " + poolObjectName); - testFailed = true; - } + ObjectName poolObjectName = new ObjectName(monitor.getName(poolObject)); + ObjectName resultObjectName = new ObjectName( + ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE + + ",name=" + result.getPoolName()); + + log.display("poolObjectName : " + poolObjectName + + " resultObjectName : " + resultObjectName); + + if (!poolObjectName.equals(resultObjectName)) { + log.complain("FAILURE 3."); + log.complain("Wrong pool name : " + resultObjectName + + ", expected : " + poolObjectName); + testFailed = true; + } } catch (Exception e) { log.complain("Unexpected exception " + e); e.printStackTrace(log.getOutStream()); testFailed = true; } - if (testFailed) { throw new TestFailure("TEST FAILED. See log."); } @@ -183,9 +206,13 @@ class from001Listener implements NotificationListener { static SynchronousQueue queue = new SynchronousQueue(); public void handleNotification(Notification notification, Object handback) { - if (data.get() != null) + if (data.get() != null) { + System.out.println("handleNotification: ignoring"); return; - data.set((CompositeData) notification.getUserData()); + } + System.out.println("handleNotification: getting data"); + CompositeData d = (CompositeData) notification.getUserData(); + data.set(d); boolean messageNotSent = true; while(messageNotSent){ @@ -193,7 +220,7 @@ public void handleNotification(Notification notification, Object handback) { queue.put(new Object()); messageNotSent = false; } catch(InterruptedException e) { - messageNotSent = true; + // ignore, retry } } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001/TestDescription.java index 3630a3dd36d05..629672b2e9176 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryNotificationInfo/from/from001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,15 +34,20 @@ * MemoryNotificationInfo.from(CompositeData) * returns correct results: * 1. null, if CompositeData is null; - * 2. trows IllegalArgumentException, if CompositeData doest not represnt + * 2. throws IllegalArgumentException, if CompositeData does not represent * MemoryNotificationInfo; - * 3. correct MemoryNotificationInfo object, if CompositeData is correst (i.e + * 3. correct MemoryNotificationInfo object, if CompositeData is correct, i.e * all attributes of the CompositeData must have correct values: pool name, * count; init, used, committed, max (from MemoryUsage). * COMMENT * Updated according to: * 5024531 Fix MBeans design flaw that restricts to use JMX CompositeData * + * Avoid running with -Xcomp due to rare failure where the MemoryPool does not + * increase in usage and send Notification. Likely the timing changes so "eatMemory" + * completes before Notification/threshold processing. + * + * @requires vm.compMode != "Xcomp" * @library /vmTestbase * /test/lib * @run main/othervm diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 05e42b4933046..6cde711138310 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -375,7 +375,7 @@ java/awt/Modal/MultipleDialogs/MultipleDialogs3Test.java 8198665 macosx-all java/awt/Modal/MultipleDialogs/MultipleDialogs4Test.java 8198665 macosx-all java/awt/Modal/MultipleDialogs/MultipleDialogs5Test.java 8198665 macosx-all java/awt/Mouse/EnterExitEvents/DragWindowOutOfFrameTest.java 8177326 macosx-all -java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java 8005021 macosx-all +java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java 8005021,8332158 macosx-all,linux-x64 java/awt/Mouse/EnterExitEvents/FullscreenEnterEventTest.java 8051455 macosx-all java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersUnitTest_Standard.java 7124407,8302787 macosx-all,windows-all java/awt/Mouse/RemovedComponentMouseListener/RemovedComponentMouseListener.java 8157170 macosx-all @@ -473,6 +473,11 @@ java/awt/image/multiresolution/MultiResolutionJOptionPaneIconTest.java 8274106 m # This test fails on macOS 14 java/awt/Choice/SelectNewItemTest/SelectNewItemTest.java 8324782 macosx-all +# Wayland related + +java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java 8280991 linux-x64 +java/awt/FullScreen/SetFullScreenTest.java 8332155 linux-x64 + ############################################################################ # jdk_beans @@ -515,9 +520,9 @@ java/lang/management/MemoryMXBean/Pending.java 8158837 generic- java/lang/management/MemoryMXBean/PendingAllGC.sh 8158837 generic-all java/lang/management/ThreadMXBean/ThreadMXBeanStateTest.java 8247426 generic-all -sun/management/jdp/JdpDefaultsTest.java 8241865,8308807 linux-aarch64,macosx-all,aix-ppc64 -sun/management/jdp/JdpJmxRemoteDynamicPortTest.java 8241865,8308807 macosx-all,aix-ppc64 -sun/management/jdp/JdpSpecificAddressTest.java 8241865,8308807 macosx-all,aix-ppc64 +sun/management/jdp/JdpDefaultsTest.java 8308807 aix-ppc64 +sun/management/jdp/JdpJmxRemoteDynamicPortTest.java 8308807 aix-ppc64 +sun/management/jdp/JdpSpecificAddressTest.java 8308807 aix-ppc64 sun/management/jdp/JdpOffTest.java 8308807 aix-ppc64 ############################################################################ @@ -606,15 +611,13 @@ sun/security/smartcardio/TestExclusive.java 8039280 generic- sun/security/smartcardio/TestMultiplePresent.java 8039280 generic-all sun/security/smartcardio/TestPresent.java 8039280 generic-all sun/security/smartcardio/TestTransmit.java 8039280 generic-all -com/sun/crypto/provider/Cipher/DES/PerformanceTest.java 8039280 generic-all -com/sun/security/auth/callback/TextCallbackHandler/Password.java 8039280 generic-all com/sun/security/sasl/gsskerb/AuthOnly.java 8039280 generic-all com/sun/security/sasl/gsskerb/ConfSecurityLayer.java 8039280 generic-all com/sun/security/sasl/gsskerb/NoSecurityLayer.java 8039280 generic-all sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.java 8039280 generic-all sun/security/provider/PolicyParser/PrincipalExpansionError.java 8039280 generic-all -sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8316183,8333317 generic-all +sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8316183 linux-ppc64le ############################################################################ @@ -641,6 +644,7 @@ javax/sound/sampled/Clip/ClipFlushCrash.java 8308395 linux-x64 # jdk_swing javax/swing/plaf/basic/BasicTextUI/8001470/bug8001470.java 8233177 linux-all,windows-all +javax/swing/plaf/basic/BasicDirectoryModel/LoaderThreadCount.java 8333880 windows-all javax/swing/JFrame/MaximizeWindowTest.java 8321289 linux-all javax/swing/JWindow/ShapedAndTranslucentWindows/ShapedTranslucentPerPixelTranslucentGradient.java 8233582 linux-all @@ -739,12 +743,8 @@ jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows- # jdk_jfr -jdk/jfr/event/compiler/TestCodeSweeper.java 8225209 generic-all jdk/jfr/event/runtime/TestResidentSetSizeEvent.java 8309846 aix-ppc64 -jdk/jfr/startupargs/TestStartName.java 8214685 windows-x64 -jdk/jfr/startupargs/TestStartDuration.java 8214685 windows-x64 jdk/jfr/jvm/TestWaste.java 8282427 generic-all -jdk/jfr/api/consumer/recordingstream/TestOnEvent.java 8255404 linux-x64,linux-aarch64 ############################################################################ diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 6a5be3736e859..97b355fbea6c5 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -621,10 +621,8 @@ jdk_core_manual_no_input = \ jdk_security_manual_no_input = \ :jdk_security_infra \ - com/sun/crypto/provider/Cipher/DES/PerformanceTest.java \ com/sun/crypto/provider/Cipher/AEAD/GCMIncrementByte4.java \ com/sun/crypto/provider/Cipher/AEAD/GCMIncrementDirect4.java \ - com/sun/security/auth/callback/TextCallbackHandler/Password.java \ com/sun/security/sasl/gsskerb/AuthOnly.java \ com/sun/security/sasl/gsskerb/ConfSecurityLayer.java \ com/sun/security/sasl/gsskerb/NoSecurityLayer.java \ @@ -653,6 +651,7 @@ jdk_core_manual_interactive = \ jdk_security_manual_interactive = \ sun/security/tools/keytool/i18n.java \ java/security/Policy/Root/Root.java \ + com/sun/security/auth/callback/TextCallbackHandler/Password.java \ sun/security/krb5/config/native/TestDynamicStore.java # Test sets for running inside container environment diff --git a/test/jdk/com/sun/crypto/provider/AlgorithmParameters/EC/CurveGetParameterSpec.java b/test/jdk/com/sun/crypto/provider/AlgorithmParameters/EC/CurveGetParameterSpec.java new file mode 100644 index 0000000000000..ba4d64c290298 --- /dev/null +++ b/test/jdk/com/sun/crypto/provider/AlgorithmParameters/EC/CurveGetParameterSpec.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8322133 + * @summary Make sure getParameterSpec returns std name for EC AlgorithmParameters + * @modules java.base/sun.security.util + */ + +import java.security.AlgorithmParameters; +import java.security.KeyPairGenerator; +import java.security.spec.ECGenParameterSpec; + +public class CurveGetParameterSpec { + public static void main(String[] args) throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); + kpg.initialize(new ECGenParameterSpec("secp384r1")); + var k = kpg.generateKeyPair().getPublic(); + var a = AlgorithmParameters.getInstance("EC"); + a.init(k.getParams()); + String name = a.getParameterSpec(ECGenParameterSpec.class).getName(); + if (!name.equals("secp384r1")) { + throw new Exception("EC getParameterSpec does not return std name secp384r1. Instead returns: " + name); + } + } +} diff --git a/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java b/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java index 698acd083b4c9..33167cafbd90f 100644 --- a/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java +++ b/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,8 @@ * @bug 0000000 * @summary This test checks performance of various ciphers. * @author Jan Luehe - * @run main/manual PerformanceTest */ -import java.security.*; import java.security.spec.*; -import java.io.*; import javax.crypto.*; import javax.crypto.spec.*; @@ -178,14 +175,16 @@ public void runTest(byte[] data, int count) throws Exception { long start, end; cipher.init(Cipher.ENCRYPT_MODE, cipherKey, params); - start = System.currentTimeMillis(); + start = getTimeInMicroseconds(); for (int i=0; i addresses = Utils.getAddressesWithSymbolicAndNumericScopes(); diff --git a/test/jdk/com/sun/jdi/JdwpNetProps.java b/test/jdk/com/sun/jdi/JdwpNetProps.java index af69c9ca3adf2..0247e4551a3da 100644 --- a/test/jdk/com/sun/jdi/JdwpNetProps.java +++ b/test/jdk/com/sun/jdi/JdwpNetProps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,11 @@ */ public class JdwpNetProps { + // Set to true to allow testing of attach from wrong address (expected to fail). + // It's off by default as it causes test interference (see JDK-8311990). + private static boolean allowNegativeAttachTesting = + "true".equalsIgnoreCase(System.getProperty("jdk.jdi.allowNegativeTesting")); + public static void main(String[] args) throws Exception { InetAddress addrs[] = InetAddress.getAllByName("localhost"); InetAddress ipv4Address = null; @@ -171,6 +176,14 @@ public ListenTest preferIPv6Addresses(String value) { } public void run(TestResult expectedResult) throws Exception { + log("\nTest: listen at " + listenAddress + ", attaching to " + connectAddress + + ", preferIPv4Stack = " + preferIPv4Stack + + ", preferIPv6Addresses = " + preferIPv6Addresses + + ", expectedResult = " + expectedResult); + if (expectedResult == TestResult.AttachFailed && !allowNegativeAttachTesting) { + log("SKIPPED: negative attach testing is disabled"); + return; + } List options = new LinkedList<>(); if (preferIPv4Stack != null) { options.add("-Djava.net.preferIPv4Stack=" + preferIPv4Stack.toString()); diff --git a/test/jdk/com/sun/security/auth/callback/TextCallbackHandler/Password.java b/test/jdk/com/sun/security/auth/callback/TextCallbackHandler/Password.java index b34ef04355867..f9231a0b36edc 100644 --- a/test/jdk/com/sun/security/auth/callback/TextCallbackHandler/Password.java +++ b/test/jdk/com/sun/security/auth/callback/TextCallbackHandler/Password.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,11 +23,23 @@ /* * @test - * @bug 6825240 + * @bug 6825240 6829785 * @summary Password.readPassword() echos the input when System.Console is null * @run main/manual Password */ +/* + * This scenario cannot be automated because util/Password.java verifies the given input stream is + * equal to the initialSystemIn. This prevents the test from providing a custom input stream. + * + * Steps to run the test: + * 1) Compile the class using the JDK version being tested: '/javac Password.java' + * 2) Run the test using the JDK version being tested: '/java -cp . Password' + * 3) Type in the first password, it should not be visible in the console + * 4) Type in the second password, it should be visible in the console + * 5) The final output line displays the entered passwords, both should be visible + */ + import com.sun.security.auth.callback.TextCallbackHandler; import javax.security.auth.callback.*; diff --git a/test/jdk/java/awt/Checkbox/CheckboxCheckerScalingTest.java b/test/jdk/java/awt/Checkbox/CheckboxCheckerScalingTest.java new file mode 100644 index 0000000000000..5e531e84801cd --- /dev/null +++ b/test/jdk/java/awt/Checkbox/CheckboxCheckerScalingTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Checkbox; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; + +/* + * @test + * @key headful + * @bug 8233068 + * @summary Tests checkbox checker on scaling + * @requires (os.family == "linux") + * @run main CheckboxCheckerScalingTest + */ + +public class CheckboxCheckerScalingTest { + private static Frame frame; + private static Checkbox checkbox; + private static BufferedImage imageAfterChecked; + private static volatile boolean checkmarkFound = false; + + public static void main(String[] args) throws Exception { + System.setProperty("sun.java2d.uiScale", "2"); + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(() -> { + frame = new Frame("ComboBox checker scaling test"); + checkbox = new Checkbox("one"); + checkbox.setState(true); + frame.add(checkbox); + frame.pack(); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(100); + EventQueue.invokeAndWait(() -> { + Point point = checkbox.getLocationOnScreen(); + Rectangle rect = new Rectangle(point.x + 5, point.y + 7, 8, 8); + imageAfterChecked = robot.createScreenCapture(rect); + + check: { + for (int i = 0; i < imageAfterChecked.getHeight(); i++) { + for (int j = 0; j < imageAfterChecked.getWidth(); j++) { + if (Color.black.getRGB() == imageAfterChecked.getRGB(i, j)) { + checkmarkFound = true; + break check; + } + } + } + } + }); + + if (!checkmarkFound) { + throw new RuntimeException("Checkmark not scaled"); + } + System.out.println("Test Passed"); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Gtk/GtkVersionTest/GtkVersionTest.java b/test/jdk/java/awt/Gtk/GtkVersionTest/GtkVersionTest.java index d2e460d4a1637..3d530178a8afc 100644 --- a/test/jdk/java/awt/Gtk/GtkVersionTest/GtkVersionTest.java +++ b/test/jdk/java/awt/Gtk/GtkVersionTest/GtkVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,8 +45,9 @@ public static void main(String[] args) { public static void main(String[] args) throws Exception { test(null, "3"); - test("2", "2"); - test("2.2", "2"); +// GTK 2 is removed, but the test can still be useful. +// test("2", "2"); +// test("2.2", "2"); test("3", "3"); } diff --git a/test/jdk/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureGtkTest.java b/test/jdk/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureGtkTest.java index 1f6298a2a612f..155933404e8c5 100644 --- a/test/jdk/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureGtkTest.java +++ b/test/jdk/java/awt/Robot/HiDPIScreenCapture/ScreenCaptureGtkTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022, JetBrains s.r.o.. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -41,10 +41,9 @@ * @test * @key headful * @bug 8280861 - * @summary Verifies Robot screen capture capabilities with different + * @summary Verifies Robot screen capture capabilities with available * Gtk backends and presence of UI scaling * @requires os.family == "linux" - * @run main/othervm -Djdk.gtk.version=2 -Dsun.java2d.uiScale=1 ScreenCaptureGtkTest * @run main/othervm -Djdk.gtk.version=3 -Dsun.java2d.uiScale=1 ScreenCaptureGtkTest */ @@ -53,12 +52,6 @@ public class ScreenCaptureGtkTest { Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED}; public static void main(String[] args) throws Exception { - if ("2".equals(System.getProperty("jdk.gtk.version")) - && System.getenv("WAYLAND_DISPLAY") != null) { - // screen capture is not supported with gtk2 on Wayland - return; - } - final int topOffset = 50; final int leftOffset = 50; diff --git a/test/jdk/java/foreign/TestConcurrentClose.java b/test/jdk/java/foreign/TestConcurrentClose.java index 7f39ef3904023..06dc7375cdaf2 100644 --- a/test/jdk/java/foreign/TestConcurrentClose.java +++ b/test/jdk/java/foreign/TestConcurrentClose.java @@ -36,6 +36,7 @@ * -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * -XX:CompileCommand=dontinline,TestConcurrentClose$SegmentAccessor::doAccess + * -Xbatch * TestConcurrentClose */ @@ -71,7 +72,7 @@ public class TestConcurrentClose { static final int ITERATIONS = 5; static final int SEGMENT_SIZE = 10_000; - static final int MAX_EXECUTOR_WAIT_SECONDS = 20; + static final int MAX_EXECUTOR_WAIT_SECONDS = 60; static final int NUM_ACCESSORS = 50; static final AtomicLong start = new AtomicLong(); @@ -176,13 +177,8 @@ static void start(String name) { } private static void awaitCompilation() throws InterruptedException { - int retries = 0; while (WB.getMethodCompilationLevel(DO_ACCESS_METHOD, false) != C2_COMPILED_LEVEL) { - if (retries > 20) { - throw new IllegalStateException("SegmentAccessor::doAccess method not being compiled"); - } Thread.sleep(1000); - retries++; } } } diff --git a/test/jdk/java/foreign/TestStubAllocFailure.java b/test/jdk/java/foreign/TestStubAllocFailure.java index b20b3e88ea221..154b455e3f286 100644 --- a/test/jdk/java/foreign/TestStubAllocFailure.java +++ b/test/jdk/java/foreign/TestStubAllocFailure.java @@ -25,6 +25,7 @@ * @test * @library ../ /test/lib * @requires jdk.foreign.linker != "FALLBACK" + * @requires vm.compMode != "Xcomp" * @run testng/othervm/native * --enable-native-access=ALL-UNNAMED * TestStubAllocFailure diff --git a/test/jdk/java/lang/Class/ProtectionDomainRace.java b/test/jdk/java/lang/Class/ProtectionDomainRace.java new file mode 100644 index 0000000000000..52b45d7ab105f --- /dev/null +++ b/test/jdk/java/lang/Class/ProtectionDomainRace.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8334394 + * @summary ensure there is no race condition in Class::protectionDomain + * @run main/othervm ProtectionDomainRace + */ +import javax.security.auth.Subject; +import java.security.PrivilegedAction; + +/** + * Without the code fix, this test would fail with + * java.lang.AssertionError: sun.security.util.ResourcesMgr (PD) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.checkInjectedInvoker(MethodHandleImpl.java:1209) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.makeInjectedInvoker(MethodHandleImpl.java:1110) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller$1.computeValue(MethodHandleImpl.java:1117) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller$1.computeValue(MethodHandleImpl.java:1114) + * at java.base/java.lang.ClassValue.getFromHashMap(ClassValue.java:229) + * at java.base/java.lang.ClassValue.getFromBackup(ClassValue.java:211) + * at java.base/java.lang.ClassValue.get(ClassValue.java:117) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.bindCallerWithInjectedInvoker(MethodHandleImpl.java:1089) + * at java.base/java.lang.invoke.MethodHandleImpl$BindCaller.bindCaller(MethodHandleImpl.java:1077) + * at java.base/java.lang.invoke.MethodHandleImpl.bindCaller(MethodHandleImpl.java:1032) + * at java.base/java.lang.invoke.MethodHandles$Lookup.maybeBindCaller(MethodHandles.java:4149) + * at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:4133) + * at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:4077) + * at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodForConstant(MethodHandles.java:4326) + * at java.base/java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:4274) + * at java.base/java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:628) + * at java.base/sun.security.util.ResourcesMgr.getBundle(ResourcesMgr.java:54) + * at java.base/sun.security.util.ResourcesMgr.getString(ResourcesMgr.java:40) + * at java.base/javax.security.auth.Subject.doAs(Subject.java:517) + * ... + * as the Class::protectionDomain might assign different objects to the (original) allPermDomain field. + */ +public class ProtectionDomainRace { + private static volatile Throwable failed = null; + public static void main(String[] args) throws Throwable { + PrivilegedAction pa = () -> null; + Thread[] threads = new Thread[100]; + for (int i = 0; i < 100; i++) { + threads[i] = new Thread(() -> { + try { + Subject.doAs(null, pa); + } catch (Throwable t) { + failed = t; + } + }); + threads[i].start(); + } + for (int i = 0; i < 100; i++) { + threads[i].join(); + } + if (failed != null) { + throw failed; + } + } +} diff --git a/test/jdk/java/lang/Process/WaitForDuration.java b/test/jdk/java/lang/Process/WaitForDuration.java new file mode 100644 index 0000000000000..76c69e36d1357 --- /dev/null +++ b/test/jdk/java/lang/Process/WaitForDuration.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8336479 + * @summary Tests for Process.waitFor(Duration) + * @library /test/lib + * @run junit WaitForDuration + */ + +import java.io.IOException; +import java.time.Duration; +import java.util.stream.Stream; +import jdk.test.lib.process.ProcessTools; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; + +public class WaitForDuration { + static Stream durations() { + return Stream.of( + Arguments.of(Duration.ZERO, 3_600_000, false), + Arguments.of(Duration.ofSeconds(-100), 3_600_000, false), + Arguments.of(Duration.ofSeconds(100), 0, true), + Arguments.of(Duration.ofSeconds(Long.MAX_VALUE), 0, true), // nano overflow + Arguments.of(Duration.ofSeconds(Long.MIN_VALUE), 3_600_000, false) // nano underflow + ); + } + + @ParameterizedTest + @MethodSource("durations") + void testEdgeDurations(Duration d, int sleepMillis, boolean expected) + throws IOException, InterruptedException { + var child = ProcessTools.createTestJavaProcessBuilder( + WaitForDuration.class.getSimpleName(), Integer.toString(sleepMillis)) + .start(); + try { + assertEquals(expected, child.waitFor(d)); + } finally { + child.destroy(); + } + } + + @Test + void testNullDuration() throws IOException, InterruptedException { + var pb = ProcessTools.createTestJavaProcessBuilder( + WaitForDuration.class.getSimpleName(), "0"); + assertThrows(NullPointerException.class, () -> pb.start().waitFor(null)); + } + + public static void main(String... args) throws InterruptedException { + Thread.sleep(Integer.parseInt(args[0])); + } +} diff --git a/test/jdk/java/lang/Thread/virtual/CarrierThreadInfo.java b/test/jdk/java/lang/Thread/virtual/CarrierThreadInfo.java new file mode 100644 index 0000000000000..75ef0376a7e0e --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/CarrierThreadInfo.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test id=default + * @bug 8284161 8286788 + * @summary Test java.lang.management.ThreadInfo contains expected information for carrier threads + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit CarrierThreadInfo + */ + +/** + * @test id=LM_LIGHTWEIGHT + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm -XX:LockingMode=2 CarrierThreadInfo + */ + +/** + * @test id=LM_LEGACY + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm -XX:LockingMode=1 CarrierThreadInfo + */ + +/** + * @test id=LM_MONITOR + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm -XX:LockingMode=0 CarrierThreadInfo + */ + +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import jdk.test.lib.thread.VThreadScheduler; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class CarrierThreadInfo { + + /** + * Test that ThreadInfo.getLockedMonitors returns information about a lock held by + * a carrier thread. + */ + @Test + void testCarrierThreadHoldsLock() throws Exception { + Object lock = new Object(); + ThreadFactory factory = task -> Thread.ofPlatform().unstarted(() -> { + synchronized (lock) { + task.run(); + } + }); + + try (var scheduler = new CustomScheduler(factory)) { + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + Thread vthread = scheduler.forkVirtualThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + try { + awaitTrue(started); + + // carrier threads holds the lock + long carrierId = scheduler.carrier().threadId(); + ThreadInfo threadInfo = ManagementFactory.getPlatformMXBean(ThreadMXBean.class) + .getThreadInfo(new long[] { carrierId }, true, true)[0]; + boolean holdsLock = Arrays.stream(threadInfo.getLockedMonitors()) + .anyMatch(mi -> mi.getIdentityHashCode() == System.identityHashCode(lock)); + assertTrue(holdsLock, "Carrier should hold lock"); + + } finally { + done.set(true); + } + } + } + + /** + * Test that ThreadInfo.getLockedMonitors does not return information about a lock + * held by mounted virtual thread. + */ + @Test + void testVirtualThreadHoldsLock() throws Exception { + ThreadFactory factory = Executors.defaultThreadFactory(); + try (var scheduler = new CustomScheduler(factory)) { + var started = new AtomicBoolean(); + var lock = new Object(); + var done = new AtomicBoolean(); + Thread vthread = scheduler.forkVirtualThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + try { + awaitTrue(started); + + // carrier threads does not hold lock + long carrierId = scheduler.carrier().threadId(); + ThreadInfo threadInfo = ManagementFactory.getPlatformMXBean(ThreadMXBean.class) + .getThreadInfo(new long[] { carrierId }, true, true)[0]; + boolean holdsLock = Arrays.stream(threadInfo.getLockedMonitors()) + .anyMatch(mi -> mi.getIdentityHashCode() == System.identityHashCode(lock)); + assertFalse(holdsLock, "Carrier should not hold lock"); + + } finally { + done.set(true); + } + } + } + + /** + * Test that ThreadInfo.getLockOwnerId and getLockInfo return information about a + * synthetic lock that make it appear that the carrier is blocking waiting on the + * virtual thread. + */ + @Test + void testCarrierThreadWaits() throws Exception { + ThreadFactory factory = Executors.defaultThreadFactory(); + try (var scheduler = new CustomScheduler(factory)) { + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + Thread vthread = scheduler.forkVirtualThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + try { + awaitTrue(started); + + long carrierId = scheduler.carrier().threadId(); + long vthreadId = vthread.threadId(); + + ThreadInfo threadInfo = ManagementFactory.getThreadMXBean().getThreadInfo(carrierId); + assertNotNull(threadInfo); + + // carrier should be blocked waiting for lock owned by virtual thread + assertEquals(vthreadId, threadInfo.getLockOwnerId()); + + // carrier thread should be on blocked waiting on virtual thread + LockInfo lockInfo = threadInfo.getLockInfo(); + assertNotNull(lockInfo); + assertEquals(vthread.getClass().getName(), lockInfo.getClassName()); + assertEquals(System.identityHashCode(vthread), lockInfo.getIdentityHashCode()); + + } finally { + done.set(true); + } + } + } + + /** + * Custom scheduler with a single carrier thread. + */ + private static class CustomScheduler implements AutoCloseable { + private final ExecutorService pool; + private final Executor scheduler; + private final AtomicReference carrierRef = new AtomicReference<>(); + + CustomScheduler(ThreadFactory factory) { + pool = Executors.newSingleThreadExecutor(factory); + scheduler = task -> { + pool.submit(() -> { + carrierRef.set(Thread.currentThread()); + try { + task.run(); + } finally { + carrierRef.set(null); + } + }); + }; + } + + /** + * Returns the carrier thread if a virtual thread is mounted. + */ + Thread carrier() throws InterruptedException { + return carrierRef.get(); + } + + /** + * Starts a virtual thread to execute the give task. + */ + Thread forkVirtualThread(Runnable task) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(task); + thread.start(); + return thread; + } + + @Override + public void close() { + pool.close(); + } + } + + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws InterruptedException { + while (!ref.get()) { + Thread.sleep(20); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/CarrierThreadWaits.java b/test/jdk/java/lang/Thread/virtual/CarrierThreadWaits.java deleted file mode 100644 index d1d5e72204f0f..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/CarrierThreadWaits.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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 8284161 8286788 - * @summary Test that a carrier thread waits on a virtual thread - * @requires vm.continuations - * @modules java.base/java.lang:+open - * @run junit CarrierThreadWaits - */ - -/** - * @test - * @requires vm.continuations & vm.debug - * @modules java.base/java.lang:+open - * @run junit/othervm -XX:LockingMode=0 CarrierThreadWaits - */ - -import java.lang.management.LockInfo; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadInfo; -import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class CarrierThreadWaits { - - @Test - void testCarrierThreadWaiting() throws Exception { - try (ForkJoinPool pool = new ForkJoinPool(1)) { - var carrierRef = new AtomicReference(); - var vthreadRef = new AtomicReference(); - - Executor scheduler = task -> { - pool.submit(() -> { - Thread carrier = Thread.currentThread(); - carrierRef.set(carrier); - Thread vthread = vthreadRef.get(); - - System.err.format("%s run task (%s) ...%n", carrier, vthread); - task.run(); - System.err.format("%s task done (%s)%n", carrier, vthread); - }); - }; - - // start a virtual thread that spins and remains mounted until "done" - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread vthread = builder.unstarted(() -> { - started.set(true); - while (!done.get()) { - Thread.onSpinWait(); - } - }); - vthreadRef.set(vthread); - vthread.start(); - - try { - // wait for virtual thread to start - while (!started.get()) { - Thread.sleep(10); - } - - Thread carrier = carrierRef.get(); - - long carrierId = carrier.threadId(); - long vthreadId = vthread.threadId(); - - // carrier thread should be on WAITING on virtual thread - ThreadInfo ti = ManagementFactory.getThreadMXBean().getThreadInfo(carrierId); - Thread.State state = ti.getThreadState(); - LockInfo lockInfo = ti.getLockInfo(); - assertEquals(Thread.State.WAITING, state); - assertNotNull(lockInfo); - assertEquals(vthread.getClass().getName(), lockInfo.getClassName()); - assertEquals(System.identityHashCode(vthread), lockInfo.getIdentityHashCode()); - assertEquals(vthreadId, ti.getLockOwnerId()); - } finally { - done.set(true); - } - } - } - -} diff --git a/test/jdk/java/lang/Thread/virtual/CustomScheduler.java b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java index 5bde81905ecdc..4289add0a3ad0 100644 --- a/test/jdk/java/lang/Thread/virtual/CustomScheduler.java +++ b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ * @summary Test virtual threads using a custom scheduler * @requires vm.continuations * @modules java.base/java.lang:+open + * @library /test/lib * @run junit CustomScheduler */ @@ -35,9 +36,12 @@ import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; +import jdk.test.lib.thread.VThreadRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.AfterAll; @@ -65,10 +69,13 @@ static void shutdown() { */ @Test void testCustomScheduler1() throws Exception { - AtomicReference ref = new AtomicReference<>(); - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { - ref.set(scheduler(Thread.currentThread())); - }).join(); + var ref = new AtomicReference(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler1); + Thread thread = factory.newThread(() -> { + ref.set(VThreadScheduler.scheduler(Thread.currentThread())); + }); + thread.start(); + thread.join(); assertTrue(ref.get() == scheduler1); } @@ -77,17 +84,7 @@ void testCustomScheduler1() throws Exception { */ @Test void testCustomScheduler2() throws Exception { - AtomicReference ref = new AtomicReference<>(); - Thread.ofVirtual().start(() -> { - try { - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { - ref.set(scheduler(Thread.currentThread())); - }).join(); - } catch (Exception e) { - e.printStackTrace(); - } - }).join(); - assertTrue(ref.get() == scheduler1); + VThreadRunner.run(this::testCustomScheduler1); } /** @@ -96,16 +93,19 @@ void testCustomScheduler2() throws Exception { */ @Test void testCustomScheduler3() throws Exception { - AtomicReference ref = new AtomicReference<>(); - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { + var ref = new AtomicReference(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler1); + Thread thread = factory.newThread(() -> { try { Thread.ofVirtual().start(() -> { - ref.set(scheduler(Thread.currentThread())); + ref.set(VThreadScheduler.scheduler(Thread.currentThread())); }).join(); } catch (Exception e) { e.printStackTrace(); } - }).join(); + }); + thread.start(); + thread.join(); assertTrue(ref.get() == scheduler1); } @@ -115,16 +115,22 @@ void testCustomScheduler3() throws Exception { */ @Test void testCustomScheduler4() throws Exception { - AtomicReference ref = new AtomicReference<>(); - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { + var ref = new AtomicReference(); + ThreadFactory factory1 = VThreadScheduler.virtualThreadFactory(scheduler1); + ThreadFactory factory2 = VThreadScheduler.virtualThreadFactory(scheduler2); + Thread thread1 = factory1.newThread(() -> { try { - ThreadBuilders.virtualThreadBuilder(scheduler2).start(() -> { - ref.set(scheduler(Thread.currentThread())); - }).join(); + Thread thread2 = factory2.newThread(() -> { + ref.set(VThreadScheduler.scheduler(Thread.currentThread())); + }); + thread2.start(); + thread2.join(); } catch (Exception e) { e.printStackTrace(); } - }).join(); + }); + thread1.start(); + thread1.join(); assertTrue(ref.get() == scheduler2); } @@ -149,8 +155,9 @@ void testBadCarrier() { } assertTrue(exc.get() instanceof WrongThreadException); }; - - ThreadBuilders.virtualThreadBuilder(scheduler).start(LockSupport::park); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(LockSupport::park); + thread.start(); } /** @@ -162,11 +169,12 @@ void testParkWithInterruptSet() { Thread carrier = Thread.currentThread(); assumeFalse(carrier.isVirtual(), "Main thread is a virtual thread"); try { - var builder = ThreadBuilders.virtualThreadBuilder(Runnable::run); - Thread vthread = builder.start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(Runnable::run); + Thread vthread = factory.newThread(() -> { Thread.currentThread().interrupt(); Thread.yield(); }); + vthread.start(); assertTrue(vthread.isInterrupted()); assertFalse(carrier.isInterrupted()); } finally { @@ -183,10 +191,11 @@ void testTerminateWithInterruptSet() { Thread carrier = Thread.currentThread(); assumeFalse(carrier.isVirtual(), "Main thread is a virtual thread"); try { - var builder = ThreadBuilders.virtualThreadBuilder(Runnable::run); - Thread vthread = builder.start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(Runnable::run); + Thread vthread = factory.newThread(() -> { Thread.currentThread().interrupt(); }); + vthread.start(); assertTrue(vthread.isInterrupted()); assertFalse(carrier.isInterrupted()); } finally { @@ -204,11 +213,13 @@ void testRunWithInterruptSet() throws Exception { Thread.currentThread().interrupt(); task.run(); }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); try { AtomicBoolean interrupted = new AtomicBoolean(); - Thread vthread = ThreadBuilders.virtualThreadBuilder(scheduler).start(() -> { + Thread vthread = factory.newThread(() -> { interrupted.set(Thread.currentThread().isInterrupted()); }); + vthread.start(); assertFalse(vthread.isInterrupted()); } finally { Thread.interrupted(); @@ -216,18 +227,60 @@ void testRunWithInterruptSet() throws Exception { } /** - * Returns the scheduler for the given virtual thread. + * Test custom scheduler throwing OOME when starting a thread. */ - private static Executor scheduler(Thread thread) { - if (!thread.isVirtual()) - throw new IllegalArgumentException("Not a virtual thread"); - try { - Field scheduler = Class.forName("java.lang.VirtualThread") - .getDeclaredField("scheduler"); - scheduler.setAccessible(true); - return (Executor) scheduler.get(thread); - } catch (Exception e) { - throw new RuntimeException(e); + @Test + void testThreadStartOOME() throws Exception { + Executor scheduler = task -> { + System.err.println("OutOfMemoryError"); + throw new OutOfMemoryError(); + }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(() -> { }); + assertThrows(OutOfMemoryError.class, thread::start); + } + + /** + * Test custom scheduler throwing OOME when unparking a thread. + */ + @Test + void testThreadUnparkOOME() throws Exception { + try (ExecutorService executor = Executors.newFixedThreadPool(1)) { + AtomicInteger counter = new AtomicInteger(); + Executor scheduler = task -> { + switch (counter.getAndIncrement()) { + case 0 -> executor.execute(task); // Thread.start + case 1, 2 -> { // unpark attempt 1+2 + System.err.println("OutOfMemoryError"); + throw new OutOfMemoryError(); + } + default -> executor.execute(task); + } + executor.execute(task); + }; + + // start thread and wait for it to park + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + var thread = factory.newThread(LockSupport::park); + thread.start(); + await(thread, Thread.State.WAITING); + + // unpark thread, this should retry until OOME is not thrown + LockSupport.unpark(thread); + thread.join(); + } + + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); } } } diff --git a/test/jdk/java/lang/Thread/virtual/GetStackTrace.java b/test/jdk/java/lang/Thread/virtual/GetStackTrace.java deleted file mode 100644 index 86d5cf7319c3e..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/GetStackTrace.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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 Thread.getStackTrace to examine the stack trace of a virtual - * thread and its carrier - * @requires vm.continuations - * @modules java.base/java.lang:+open - * @run main GetStackTrace - */ - -import java.util.Objects; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedTransferQueue; -import java.util.stream.Stream; - -public class GetStackTrace { - - private static final Object LOCK = new Object(); - - public static void main(String[] args) throws Exception { - try (var scheduler = new Scheduler()) { - Thread vthread = scheduler.startVirtualThread(() -> { - synchronized (LOCK) { - try { - LOCK.wait(); - } catch (InterruptedException e) { } - } - }); - - try { - // wait for virtual thread to wait - while (vthread.getState() != Thread.State.WAITING) { - Thread.sleep(10); - } - - // bottom-most frame of virtual thread should be VirtualThread.run - System.out.println(vthread); - StackTraceElement[] vthreadStack = vthread.getStackTrace(); - Stream.of(vthreadStack).forEach(System.out::println); - assertEquals("run", vthreadStack[vthreadStack.length - 1].getMethodName()); - - System.out.println(); - - // top-most frame of carrier thread should be Continuation.run - // bottom-most frame of carrier thread should be Thread.run - var carrier = scheduler.thread(); - System.out.println(carrier); - StackTraceElement[] carrierStack = carrier.getStackTrace(); - Stream.of(carrierStack).forEach(System.out::println); - assertEquals("run", carrierStack[0].getMethodName()); - assertEquals("run", carrierStack[carrierStack.length - 1].getMethodName()); - } finally { - vthread.interrupt(); - } - } - } - - /** - * A scheduler with one thread. - */ - private static class Scheduler implements AutoCloseable, Executor { - private final BlockingQueue tasks = new LinkedTransferQueue<>(); - private final Thread thread; - private volatile boolean done; - - Scheduler() { - this.thread = Thread.ofPlatform().start(() -> { - try { - while (!done) { - Runnable task = tasks.take(); - task.run(); - } - } catch (InterruptedException e) { } - }); - } - - Thread thread() { - return thread; - } - - @Override - public void close() throws InterruptedException { - done = true; - thread.interrupt(); - thread.join(); - } - - @Override - public void execute(Runnable task) { - tasks.add(task); - } - - Thread startVirtualThread(Runnable task) { - return ThreadBuilders.virtualThreadBuilder(this).start(task); - } - } - - private static void assertTrue(boolean e) { - if (!e) throw new RuntimeException(); - } - - private static void assertEquals(Object x, Object y) { - if (!Objects.equals(x, y)) - throw new RuntimeException(); - } -} diff --git a/test/jdk/java/lang/Thread/virtual/GetStackTraceWhenRunnable.java b/test/jdk/java/lang/Thread/virtual/GetStackTraceWhenRunnable.java deleted file mode 100644 index 38760eb52a8f0..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/GetStackTraceWhenRunnable.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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 Thread::getStackTrace on a virtual thread that is runnable-unmounted - * @requires vm.continuations - * @run main/othervm -Djdk.virtualThreadScheduler.maxPoolSize=1 GetStackTraceWhenRunnable - */ - -import java.io.IOException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.LockSupport; - -public class GetStackTraceWhenRunnable { - - public static void main(String[] args) throws Exception { - - // start thread1 and wait for it to park - Thread thread1 = Thread.startVirtualThread(LockSupport::park); - while (thread1.getState() != Thread.State.WAITING) { - Thread.sleep(20); - } - - // start thread2 to pin the carrier thread - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - Thread thread2 = Thread.startVirtualThread(() -> { - started.set(true); - while (!done.get()) { - Thread.onSpinWait(); - } - }); - try { - // wait for thread2 to start - while (!started.get()) { - Thread.sleep(10); - } - - // unpark thread1 and check that it is "stuck" in the runnable state - // (the carrier thread is pinned, no other virtual thread can run) - LockSupport.unpark(thread1); - for (int i = 0; i < 5; i++) { - assertTrue(thread1.getState() == Thread.State.RUNNABLE); - Thread.sleep(100); - } - - // print thread1's stack trace - StackTraceElement[] stack = thread1.getStackTrace(); - assertTrue(stack.length > 0); - for (StackTraceElement e : stack) { - System.out.println(e); - } - } finally { - done.set(true); - thread2.join(); - thread1.join(); - } - } - - static void assertTrue(boolean e) { - if (!e) throw new RuntimeException(); - } -} diff --git a/test/jdk/java/lang/Thread/virtual/JfrEvents.java b/test/jdk/java/lang/Thread/virtual/JfrEvents.java index 282a8959fe879..539ac2104ed93 100644 --- a/test/jdk/java/lang/Thread/virtual/JfrEvents.java +++ b/test/jdk/java/lang/Thread/virtual/JfrEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,9 +40,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -52,15 +50,25 @@ import jdk.jfr.consumer.RecordingFile; import jdk.test.lib.thread.VThreadPinner; -import jdk.test.lib.thread.VThreadRunner.ThrowingRunnable; +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; class JfrEvents { + @BeforeAll + static void setup() { + int minParallelism = 2; + if (Thread.currentThread().isVirtual()) { + minParallelism++; + } + VThreadRunner.ensureParallelism(minParallelism); + } + /** * Test jdk.VirtualThreadStart and jdk.VirtualThreadEnd events. */ @@ -93,100 +101,42 @@ void testVirtualThreadStartAndEnd() throws Exception { } /** - * Arguments for testVirtualThreadPinned to test jdk.VirtualThreadPinned event. - * [0] label/description - * [1] the operation to park/wait - * [2] the Thread.State when parked/waiting - * [3] the action to unpark/notify the thread - */ - static Stream pinnedCases() { - Object lock = new Object(); - - // park with native frame on stack - var finish1 = new AtomicBoolean(); - var parkWhenPinned = Arguments.of( - "LockSupport.park when pinned", - (ThrowingRunnable) () -> { - VThreadPinner.runPinned(() -> { - while (!finish1.get()) { - LockSupport.park(); - } - }); - }, - Thread.State.WAITING, - (Consumer) t -> { - finish1.set(true); - LockSupport.unpark(t); - } - ); - - // timed park with native frame on stack - var finish2 = new AtomicBoolean(); - var timedParkWhenPinned = Arguments.of( - "LockSupport.parkNanos when pinned", - (ThrowingRunnable) () -> { - VThreadPinner.runPinned(() -> { - while (!finish2.get()) { - LockSupport.parkNanos(Long.MAX_VALUE); - } - }); - }, - Thread.State.TIMED_WAITING, - (Consumer) t -> { - finish2.set(true); - LockSupport.unpark(t); - } - ); - - return Stream.of(parkWhenPinned, timedParkWhenPinned); - } - - /** - * Test jdk.VirtualThreadPinned event. + * Test jdk.VirtualThreadPinned event when parking while pinned. */ @ParameterizedTest - @MethodSource("pinnedCases") - void testVirtualThreadPinned(String label, - ThrowingRunnable parker, - Thread.State expectedState, - Consumer unparker) throws Exception { - + @ValueSource(booleans = { true, false }) + void testParkWhenPinned(boolean timed) throws Exception { try (Recording recording = new Recording()) { recording.enable("jdk.VirtualThreadPinned"); - recording.start(); - try { - var exception = new AtomicReference(); - var thread = Thread.ofVirtual().start(() -> { - try { - parker.run(); - } catch (Throwable e) { - exception.set(e); + + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + var vthread = Thread.startVirtualThread(() -> { + VThreadPinner.runPinned(() -> { + started.set(true); + while (!done.get()) { + if (timed) { + LockSupport.parkNanos(Long.MAX_VALUE); + } else { + LockSupport.park(); + } } }); - try { - // wait for thread to park/wait - Thread.State state = thread.getState(); - while (state != expectedState) { - assertTrue(state != Thread.State.TERMINATED, thread.toString()); - Thread.sleep(10); - state = thread.getState(); - } - } finally { - unparker.accept(thread); - thread.join(); - assertNull(exception.get()); - } + }); + + try { + // wait for thread to start and park + awaitTrue(started); + await(vthread, timed ? Thread.State.TIMED_WAITING : Thread.State.WAITING); } finally { + done.set(true); + LockSupport.unpark(vthread); + vthread.join(); recording.stop(); } - Map events = sumEvents(recording); - System.err.println(events); - - // should have at least one pinned event - int pinnedCount = events.getOrDefault("jdk.VirtualThreadPinned", 0); - assertTrue(pinnedCount >= 1, "Expected one or more events"); + assertContainsPinnedEvent(recording, vthread); } } @@ -203,16 +153,14 @@ void testVirtualThreadSubmitFailed() throws Exception { Executor scheduler = task -> pool.execute(task); // create virtual thread that uses custom scheduler - ThreadFactory factory = ThreadBuilders.virtualThreadBuilder(scheduler).factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); // start a thread Thread thread = factory.newThread(LockSupport::park); thread.start(); // wait for thread to park - while (thread.getState() != Thread.State.WAITING) { - Thread.sleep(10); - } + await(thread, Thread.State.WAITING); // shutdown scheduler pool.shutdown(); @@ -232,14 +180,23 @@ void testVirtualThreadSubmitFailed() throws Exception { recording.stop(); } - Map events = sumEvents(recording); - System.err.println(events); - - int count = events.getOrDefault("jdk.VirtualThreadSubmitFailed", 0); - assertEquals(2, count); + List submitFailedEvents = find(recording, "jdk.VirtualThreadSubmitFailed"); + System.err.println(submitFailedEvents); + assertTrue(submitFailedEvents.size() == 2, "Expected two events"); } } + /** + * Returns the list of events in the given recording with the given name. + */ + private static List find(Recording recording, String name) throws IOException { + Path recordingFile = recordingFile(recording); + return RecordingFile.readAllEvents(recordingFile) + .stream() + .filter(e -> e.getEventType().getName().equals(name)) + .toList(); + } + /** * Read the events from the recording and return a map of event name to count. */ @@ -264,4 +221,38 @@ private static Path recordingFile(Recording recording) throws IOException { } return recordingFile; } + + /** + * Assert that a recording contains a jdk.VirtualThreadPinned event on the given thread. + */ + private void assertContainsPinnedEvent(Recording recording, Thread thread) throws IOException { + List pinnedEvents = find(recording, "jdk.VirtualThreadPinned"); + assertTrue(pinnedEvents.size() > 0, "No jdk.VirtualThreadPinned events in recording"); + System.err.println(pinnedEvents); + + long tid = thread.threadId(); + assertTrue(pinnedEvents.stream() + .anyMatch(e -> e.getThread().getJavaThreadId() == tid), + "jdk.VirtualThreadPinned for javaThreadId = " + tid + " not found"); + } + + /** + * Waits for the given boolean to be set to true. + */ + private void awaitTrue(AtomicBoolean b) throws InterruptedException { + while (!b.get()) { + Thread.sleep(10); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private static void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + Thread.sleep(10); + state = thread.getState(); + } + } } diff --git a/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java b/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java new file mode 100644 index 0000000000000..d1f2bf0c1bfea --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=default + * @summary Test virtual thread with monitor enter/exit + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import jdk.test.lib.thread.VThreadPinner; +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.api.condition.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; + +class MonitorEnterExit { + static final int MAX_ENTER_DEPTH = 256; + + @BeforeAll + static void setup() { + // need >=2 carriers for testing pinning when main thread is a virtual thread + if (Thread.currentThread().isVirtual()) { + VThreadRunner.ensureParallelism(2); + } + } + + /** + * Test monitor enter with no contention. + */ + @Test + void testEnterNoContention() throws Exception { + var lock = new Object(); + VThreadRunner.run(() -> { + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test monitor enter with contention, monitor is held by platform thread. + */ + @Test + void testEnterWhenHeldByPlatformThread() throws Exception { + testEnterWithContention(); + } + + /** + * Test monitor enter with contention, monitor is held by virtual thread. + */ + @Test + void testEnterWhenHeldByVirtualThread() throws Exception { + VThreadRunner.run(this::testEnterWithContention); + } + + /** + * Test monitor enter with contention, monitor will be held by caller thread. + */ + private void testEnterWithContention() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + entered.set(true); + } + assertFalse(Thread.holdsLock(lock)); + }); + try { + synchronized (lock) { + vthread.start(); + + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + + assertFalse(entered.get()); + } + } finally { + vthread.join(); + } + assertTrue(entered.get()); + } + + /** + * Test monitor reenter. + */ + @Test + void testReenter() throws Exception { + var lock = new Object(); + VThreadRunner.run(() -> { + testReenter(lock, 0); + assertFalse(Thread.holdsLock(lock)); + }); + } + + private void testReenter(Object lock, int depth) { + if (depth < MAX_ENTER_DEPTH) { + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + testReenter(lock, depth + 1); + assertTrue(Thread.holdsLock(lock)); + } + } + } + + /** + * Test monitor enter when pinned. + */ + @Test + void testEnterWhenPinned() throws Exception { + var lock = new Object(); + VThreadPinner.runPinned(() -> { + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test monitor reenter when pinned. + */ + @Test + void testReenterWhenPinned() throws Exception { + VThreadRunner.run(() -> { + var lock = new Object(); + synchronized (lock) { + VThreadPinner.runPinned(() -> { + assertTrue(Thread.holdsLock(lock)); + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertTrue(Thread.holdsLock(lock)); + }); + } + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test contended monitor enter when pinned. Monitor is held by platform thread. + */ + @Test + void testContendedEnterWhenPinnedHeldByPlatformThread() throws Exception { + testEnterWithContentionWhenPinned(); + } + + /** + * Test contended monitor enter when pinned. Monitor is held by virtual thread. + */ + @Test + void testContendedEnterWhenPinnedHeldByVirtualThread() throws Exception { + // need at least two carrier threads + int previousParallelism = VThreadRunner.ensureParallelism(2); + try { + VThreadRunner.run(this::testEnterWithContentionWhenPinned); + } finally { + VThreadRunner.setParallelism(previousParallelism); + } + } + + /** + * Test contended monitor enter when pinned, monitor will be held by caller thread. + */ + private void testEnterWithContentionWhenPinned() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + Thread vthread = Thread.ofVirtual().unstarted(() -> { + VThreadPinner.runPinned(() -> { + started.countDown(); + synchronized (lock) { + entered.set(true); + } + }); + }); + synchronized (lock) { + // start thread and wait for it to block + vthread.start(); + started.await(); + await(vthread, Thread.State.BLOCKED); + assertFalse(entered.get()); + } + vthread.join(); + + // check thread entered monitor + assertTrue(entered.get()); + } + + /** + * Returns a stream of elements that are ordered pairs of platform and virtual thread + * counts. 0,2,4,..16 platform threads. 2,4,6,..32 virtual threads. + */ + static Stream threadCounts() { + return IntStream.range(0, 17) + .filter(i -> i % 2 == 0) + .mapToObj(i -> i) + .flatMap(np -> IntStream.range(2, 33) + .filter(i -> i % 2 == 0) + .mapToObj(vp -> Arguments.of(np, vp))); + } + + /** + * Test mutual exclusion of monitors with platform and virtual threads. + */ + @ParameterizedTest + @MethodSource("threadCounts") + void testMutualExclusion(int nPlatformThreads, int nVirtualThreads) throws Exception { + class Counter { + int count; + synchronized void increment() { + count++; + Thread.yield(); + } + } + var counter = new Counter(); + int nThreads = nPlatformThreads + nVirtualThreads; + var threads = new Thread[nThreads]; + int index = 0; + for (int i = 0; i < nPlatformThreads; i++) { + threads[index] = Thread.ofPlatform() + .name("platform-" + index) + .unstarted(counter::increment); + index++; + } + for (int i = 0; i < nVirtualThreads; i++) { + threads[index] = Thread.ofVirtual() + .name("virtual-" + index) + .unstarted(counter::increment); + index++; + } + // start all threads + for (Thread thread : threads) { + thread.start(); + } + // wait for all threads to terminate + for (Thread thread : threads) { + thread.join(); + } + assertEquals(nThreads, counter.count); + } + + /** + * Test unblocking a virtual thread waiting to enter a monitor held by a platform thread. + */ + @RepeatedTest(20) + void testUnblockingByPlatformThread() throws Exception { + testUnblocking(); + } + + /** + * Test unblocking a virtual thread waiting to enter a monitor held by another + * virtual thread. + */ + @RepeatedTest(20) + void testUnblockingByVirtualThread() throws Exception { + VThreadRunner.run(this::testUnblocking); + } + + /** + * Test unblocking a virtual thread waiting to enter a monitor, monitor will be + * initially be held by caller thread. + */ + private void testUnblocking() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + synchronized (lock) { + entered.set(true); + } + }); + try { + synchronized (lock) { + vthread.start(); + started.await(); + + // random delay before exiting monitor + switch (ThreadLocalRandom.current().nextInt(4)) { + case 0 -> { /* no delay */} + case 1 -> Thread.onSpinWait(); + case 2 -> Thread.yield(); + case 3 -> await(vthread, Thread.State.BLOCKED); + default -> fail(); + } + + assertFalse(entered.get()); + } + } finally { + vthread.join(); + } + assertTrue(entered.get()); + } + + /** + * Test that unblocking a virtual thread waiting to enter a monitor does not consume + * the thread's parking permit. + */ + @Test + void testParkingPermitNotConsumed() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + LockSupport.unpark(Thread.currentThread()); + synchronized (lock) { } // should block + LockSupport.park(); // should not park + }); + + synchronized (lock) { + vthread.start(); + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + } + vthread.join(); + } + + /** + * Test that unblocking a virtual thread waiting to enter a monitor does not make + * available the thread's parking permit. + */ + @Test + void testParkingPermitNotOffered() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + synchronized (lock) { } // should block + LockSupport.park(); // should park + }); + + synchronized (lock) { + vthread.start(); + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + } + + try { + // wait for thread to park, it should not terminate + await(vthread, Thread.State.WAITING); + vthread.join(Duration.ofMillis(100)); + assertEquals(Thread.State.WAITING, vthread.getState()); + } finally { + LockSupport.unpark(vthread); + vthread.join(); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java index 1320cc8f15f3f..5f730dbbf0ad9 100644 --- a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java +++ b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,41 +21,78 @@ * questions. */ -/** - * @test +/* + * @test id=default * @summary Test virtual threads using Object.wait/notifyAll * @modules java.base/java.lang:+open * @library /test/lib - * @run junit MonitorWaitNotify + * @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorWaitNotify */ -import java.util.concurrent.Semaphore; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.LockSupport; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.Collectors; +import jdk.test.lib.thread.VThreadScheduler; +import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadPinner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; class MonitorWaitNotify { + @BeforeAll + static void setup() { + // need >=2 carriers for testing pinning + VThreadRunner.ensureParallelism(2); + } + /** * Test virtual thread waits, notified by platform thread. */ - @Test - void testWaitNotify1() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testWaitNotify1(boolean pinned) throws Exception { var lock = new Object(); - var ready = new Semaphore(0); + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - ready.release(); try { - lock.wait(); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } } catch (InterruptedException e) { } } }); - // thread invokes notify - ready.acquire(); + awaitTrue(ready); + + // notify, thread should block waiting to reenter synchronized (lock) { lock.notifyAll(); + await(thread, Thread.State.BLOCKED); } thread.join(); } @@ -66,15 +103,13 @@ void testWaitNotify1() throws Exception { @Test void testWaitNotify2() throws Exception { var lock = new Object(); - var ready = new Semaphore(0); - var thread = Thread.ofVirtual().start(() -> { - ready.acquireUninterruptibly(); + var thread = Thread.ofVirtual().unstarted(() -> { synchronized (lock) { lock.notifyAll(); } }); synchronized (lock) { - ready.release(); + thread.start(); lock.wait(); } thread.join(); @@ -83,89 +118,447 @@ void testWaitNotify2() throws Exception { /** * Test virtual thread waits, notified by another virtual thread. */ - @Test - void testWaitNotify3() throws Exception { - // need at least two carrier threads due to pinning - int previousParallelism = VThreadRunner.ensureParallelism(2); - try { - var lock = new Object(); - var ready = new Semaphore(0); - var thread1 = Thread.ofVirtual().start(() -> { - synchronized (lock) { - ready.release(); - try { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testWaitNotify3(boolean pinned) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var thread1 = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); lock.wait(); - } catch (InterruptedException e) { } + } + } catch (InterruptedException e) { + e.printStackTrace(); } - }); - var thread2 = Thread.ofVirtual().start(() -> { - ready.acquireUninterruptibly(); + } + }); + var thread2 = Thread.ofVirtual().start(() -> { + try { + awaitTrue(ready); + + // notify, thread should block waiting to reenter synchronized (lock) { lock.notifyAll(); + await(thread1, Thread.State.BLOCKED); } - }); - thread1.join(); - thread2.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + thread1.join(); + thread2.join(); + } + + /** + * Test notifyAll when there are no threads waiting. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testNotifyBeforeWait(int timeout) throws Exception { + var lock = new Object(); + + // no threads waiting + synchronized (lock) { + lock.notifyAll(); + } + + var ready = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + try { + synchronized (lock) { + ready.set(true); + + // thread should wait + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } + } catch (InterruptedException e) { } + }); + + try { + // wait for thread to start and wait + awaitTrue(ready); + Thread.State expectedState = timeout > 0 + ? Thread.State.TIMED_WAITING + : Thread.State.WAITING; + await(thread, expectedState); + + // poll thread state again, it should still be waiting + Thread.sleep(10); + assertEquals(thread.getState(), expectedState); } finally { - // restore - VThreadRunner.setParallelism(previousParallelism); + synchronized (lock) { + lock.notifyAll(); + } + thread.join(); } } + /** + * Test duration of timed Object.wait. + */ + @Test + void testTimedWaitDuration1() throws Exception { + var lock = new Object(); + + var durationRef = new AtomicReference(); + var thread = Thread.ofVirtual().start(() -> { + try { + synchronized (lock) { + long start = millisTime(); + lock.wait(2000); + durationRef.set(millisTime() - start); + } + } catch (InterruptedException e) { } + }); + + thread.join(); + + long duration = durationRef.get(); + checkDuration(duration, 1900, 20_000); + } /** - * Test interrupt status set when calling Object.wait. + * Test duration of timed Object.wait. This test invokes wait twice, first with a short + * timeout, the second with a longer timeout. The test scenario ensures that the + * timeout from the first wait doesn't interfere with the second wait. */ @Test - void testWaitNotify4() throws Exception { + void testTimedWaitDuration2() throws Exception { + var lock = new Object(); + + var ready = new AtomicBoolean(); + var waited = new AtomicBoolean(); + var durationRef = new AtomicReference(); + var thread = Thread.ofVirtual().start(() -> { + try { + synchronized (lock) { + ready.set(true); + lock.wait(200); + waited.set(true); + + long start = millisTime(); + lock.wait(2000); + durationRef.set(millisTime() - start); + } + } catch (InterruptedException e) { } + }); + + awaitTrue(ready); + synchronized (lock) { + // wake thread if waiting in first wait + if (!waited.get()) { + lock.notifyAll(); + } + } + + thread.join(); + + long duration = durationRef.get(); + checkDuration(duration, 1900, 20_000); + } + + /** + * Testing invoking Object.wait with interrupt status set. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testWaitWithInterruptSet(int timeout) throws Exception { VThreadRunner.run(() -> { - Thread t = Thread.currentThread(); - t.interrupt(); Object lock = new Object(); + synchronized (lock) { + Thread.currentThread().interrupt(); + if (timeout > 0) { + assertThrows(InterruptedException.class, () -> lock.wait(timeout)); + } else { + assertThrows(InterruptedException.class, lock::wait); + } + assertFalse(Thread.currentThread().isInterrupted()); + } + }); + } + + /** + * Test interrupting a virtual thread waiting in Object.wait. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testInterruptWait(int timeout) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var interruptedException = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + ready.set(true); + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) { + // check stack trace has the expected frames + Set expected = Set.of("wait0", "wait", "run"); + Set methods = Stream.of(e.getStackTrace()) + .map(StackTraceElement::getMethodName) + .collect(Collectors.toSet()); + assertTrue(methods.containsAll(expected)); + + interruptedException.set(true); + } + } + }); + + // wait for thread to start and wait + awaitTrue(ready); + await(vthread, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + + // interrupt thread, should block, then throw InterruptedException + synchronized (lock) { + vthread.interrupt(); + await(vthread, Thread.State.BLOCKED); + } + vthread.join(); + assertTrue(interruptedException.get()); + } + + /** + * Test interrupting a virtual thread blocked waiting to reenter after waiting. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testInterruptReenterAfterWait(int timeout) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var interruptedException = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + ready.set(true); + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) { + interruptedException.set(true); + } + } + }); + + // wait for thread to start and wait + awaitTrue(ready); + await(vthread, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + + // notify, thread should block waiting to reenter + synchronized (lock) { + lock.notifyAll(); + await(vthread, Thread.State.BLOCKED); + + // interrupt when blocked + vthread.interrupt(); + } + + vthread.join(); + assertFalse(interruptedException.get()); + assertTrue(vthread.isInterrupted()); + } + + /** + * Test Object.wait when the monitor entry count > 1. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testWaitWhenEnteredManyTimes(int timeout) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + synchronized (lock) { + synchronized (lock) { + try { + ready.set(true); + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) { } + } + } + } + }); + + // wait for thread to start and wait + awaitTrue(ready); + await(vthread, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + + // notify, thread should block waiting to reenter + synchronized (lock) { + lock.notifyAll(); + await(vthread, Thread.State.BLOCKED); + } + vthread.join(); + } + + /** + * Test that Object.wait does not consume the thread's parking permit. + */ + @Test + void testParkingPermitNotConsumed() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var completed = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + started.countDown(); + LockSupport.unpark(Thread.currentThread()); synchronized (lock) { try { lock.wait(); - fail(); } catch (InterruptedException e) { - // interrupt status should be cleared - assertFalse(t.isInterrupted()); + fail("wait interrupted"); } } + LockSupport.park(); // should not park + completed.set(true); }); + + // wait for thread to start and wait + started.await(); + await(vthread, Thread.State.WAITING); + + // wakeup thread + synchronized (lock) { + lock.notifyAll(); + } + + // thread should terminate + vthread.join(); + assertTrue(completed.get()); } /** - * Test interrupt when blocked in Object.wait. + * Test that Object.wait does not make available the thread's parking permit. */ @Test - void testWaitNotify5() throws Exception { - VThreadRunner.run(() -> { - Thread t = Thread.currentThread(); - scheduleInterrupt(t, 1000); - Object lock = new Object(); + void testParkingPermitNotOffered() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var readyToPark = new CountDownLatch(1); + var completed = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + started.countDown(); synchronized (lock) { try { lock.wait(); - fail(); } catch (InterruptedException e) { - // interrupt status should be cleared - assertFalse(t.isInterrupted()); + fail("wait interrupted"); } } + readyToPark.countDown(); + LockSupport.park(); // should park + completed.set(true); }); + + // wait for thread to start and wait + started.await(); + await(vthread, Thread.State.WAITING); + + // wakeup thread + synchronized (lock) { + lock.notifyAll(); + } + + // thread should park + readyToPark.await(); + await(vthread, Thread.State.WAITING); + + LockSupport.unpark(vthread); + + // thread should terminate + vthread.join(); + assertTrue(completed.get()); } /** - * Schedule a thread to be interrupted after a delay. + * Test that wait(long) throws IAE when timeout is negative. */ - private static void scheduleInterrupt(Thread thread, long delay) { - Runnable interruptTask = () -> { - try { - Thread.sleep(delay); - thread.interrupt(); - } catch (Exception e) { - e.printStackTrace(); + @Test + void testIllegalArgumentException() throws Exception { + VThreadRunner.run(() -> { + Object obj = new Object(); + synchronized (obj) { + assertThrows(IllegalArgumentException.class, () -> obj.wait(-1L)); + assertThrows(IllegalArgumentException.class, () -> obj.wait(-1000L)); + assertThrows(IllegalArgumentException.class, () -> obj.wait(Long.MIN_VALUE)); } - }; - new Thread(interruptTask).start(); + }); + } + + /** + * Test that wait throws IMSE when not owner. + */ + @Test + void testIllegalMonitorStateException() throws Exception { + VThreadRunner.run(() -> { + Object obj = new Object(); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait()); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait(0)); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait(1000)); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait(Long.MAX_VALUE)); + }); + } + + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws InterruptedException { + while (!ref.get()) { + Thread.sleep(20); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } + + /** + * Returns the current time in milliseconds. + */ + private static long millisTime() { + long now = System.nanoTime(); + return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS); + } + + /** + * Check a duration is within expected bounds. + * @param duration, in milliseconds + * @param min minimum expected duration, in milliseconds + * @param max maximum expected duration, in milliseconds + * @return the duration (now - start), in milliseconds + */ + private static void checkDuration(long duration, long min, long max) { + assertTrue(duration >= min, + "Duration " + duration + "ms, expected >= " + min + "ms"); + assertTrue(duration <= max, + "Duration " + duration + "ms, expected <= " + max + "ms"); } } diff --git a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java index 950f036458d6f..28a32fc504ab4 100644 --- a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java +++ b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java @@ -26,12 +26,14 @@ * @summary Test virtual thread park when scheduler is a fixed thread pool * @requires vm.continuations * @modules java.base/java.lang:+open + * @library /test/lib * @run main ParkWithFixedThreadPool */ import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; public class ParkWithFixedThreadPool { public static void main(String[] args) throws Exception { @@ -58,9 +60,7 @@ public void run() { } }; - ThreadFactory factory = ThreadBuilders.virtualThreadBuilder(scheduler) - .name("vthread-", 0) - .factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); for (int i = 0; i < vthreadCount; i++) { vthreads[i] = factory.newThread(target); diff --git a/test/jdk/java/lang/Thread/virtual/Reflection.java b/test/jdk/java/lang/Thread/virtual/Reflection.java index 4dd34e6c72695..f508b7949ffbf 100644 --- a/test/jdk/java/lang/Thread/virtual/Reflection.java +++ b/test/jdk/java/lang/Thread/virtual/Reflection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; import jdk.test.lib.thread.VThreadRunner; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -142,11 +143,10 @@ static void foo() { } */ @Test void testInvokeStatic6() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); Method parkMethod = Parker.class.getDeclaredMethod("park"); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var ready = new CountDownLatch(1); Thread vthread = factory.newThread(() -> { @@ -321,11 +321,10 @@ static void foo() { } */ @Test void testNewInstance6() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); Constructor ctor = Parker.class.getDeclaredConstructor(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var ready = new CountDownLatch(1); Thread vthread = factory.newThread(() -> { diff --git a/test/jdk/java/lang/Thread/virtual/StackFrames.java b/test/jdk/java/lang/Thread/virtual/StackFrames.java new file mode 100644 index 0000000000000..6ae65d031dd61 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/StackFrames.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 stack traces in exceptions, stack frames walked by the StackWalker, + * and the stack trace returned by Thread.getStackTrace + * @requires vm.continuations + * @modules java.base/java.lang:+open java.management + * @library /test/lib + * @run junit StackFrames + * @run junit/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowCarrierFrames StackFrames + */ + +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; +import static java.lang.StackWalker.Option.*; + +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class StackFrames { + + /** + * Test that the stack trace in exceptions does not include the carrier thread + * frames, except when running with -XX:+ShowCarrierFrames. + */ + @Test + void testStackTraceException() throws Exception { + VThreadRunner.run(() -> { + Exception e = new Exception(); + boolean found = Arrays.stream(e.getStackTrace()) + .map(StackTraceElement::getClassName) + .anyMatch("java.util.concurrent.ForkJoinPool"::equals); + assertTrue(found == hasJvmArgument("-XX:+ShowCarrierFrames")); + }); + } + + /** + * Test that StackWalker does not include carrier thread frames in the stream of + * stack frames. + */ + @Test + void testStackWalker() throws Exception { + VThreadRunner.run(() -> { + StackWalker walker = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE)); + boolean found = walker.walk(sf -> + sf.map(StackWalker.StackFrame::getDeclaringClass) + .anyMatch(c -> c == ForkJoinPool.class)); + assertFalse(found); + }); + } + + /** + * Test Thread.getStackTrace returns the expected bottom frame for both the carrier + * and virtual thread. + */ + @Test + void testBottomFrames() throws Exception { + try (ForkJoinPool pool = new ForkJoinPool(1)) { + var carrierRef = new AtomicReference(); + Executor scheduler = task -> { + pool.submit(() -> { + carrierRef.set(Thread.currentThread()); + task.run(); + }); + }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); + + // create virtual thread to use custom scheduler + var vthread = factory.newThread(() -> { + ready.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + + vthread.start(); + try { + awaitTrue(ready); + + // get carrier Thread + Thread carrier = carrierRef.get(); + assertTrue(carrier instanceof ForkJoinWorkerThread); + + // bottom-most frame of virtual thread should be VirtualThread.run + System.err.println(vthread); + StackTraceElement[] vthreadStack = vthread.getStackTrace(); + Stream.of(vthreadStack).forEach(e -> System.err.println(" " + e)); + StackTraceElement bottomFrame = vthreadStack[vthreadStack.length - 1]; + assertEquals("java.lang.VirtualThread.run", + bottomFrame.getClassName() + "." + bottomFrame.getMethodName()); + + // bottom-most frame of carrier thread should be Thread.run + System.err.println(carrier); + StackTraceElement[] carrierStack = carrier.getStackTrace(); + Stream.of(carrierStack).forEach(e -> System.err.println(" " + e)); + bottomFrame = carrierStack[carrierStack.length - 1]; + assertEquals("java.util.concurrent.ForkJoinWorkerThread.run", + bottomFrame.getClassName() + "." + bottomFrame.getMethodName()); + + } finally { + done.set(true); + vthread.join(); + } + } + } + + /** + * Returns true if started with the given VM option. + */ + private static boolean hasJvmArgument(String arg) { + for (String argument : ManagementFactory.getRuntimeMXBean().getInputArguments()) { + if (argument.equals(arg)) return true; + } + return false; + } + + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws InterruptedException { + while (!ref.get()) { + Thread.sleep(20); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/StackTraces.java b/test/jdk/java/lang/Thread/virtual/StackTraces.java deleted file mode 100644 index cb1877090fecd..0000000000000 --- a/test/jdk/java/lang/Thread/virtual/StackTraces.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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 stack traces in exceptions and stack frames walked by the StackWalker - * API do not include the carrier stack frames - * @requires vm.continuations - * @modules java.management - * @library /test/lib - * @run junit StackTraces - * @run junit/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowCarrierFrames StackTraces - */ - -import java.lang.management.ManagementFactory; -import java.util.Arrays; -import java.util.Set; -import java.util.concurrent.ForkJoinPool; -import static java.lang.StackWalker.Option.*; - -import jdk.test.lib.thread.VThreadRunner; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class StackTraces { - - /** - * Test that the stack trace in exceptions does not include the carrier thread - * frames, except when running with -XX:+ShowCarrierFrames. - */ - @Test - void testStackTrace() throws Exception { - VThreadRunner.run(() -> { - Exception e = new Exception(); - boolean found = Arrays.stream(e.getStackTrace()) - .map(StackTraceElement::getClassName) - .anyMatch("java.util.concurrent.ForkJoinPool"::equals); - assertTrue(found == hasJvmArgument("-XX:+ShowCarrierFrames")); - }); - } - - /** - * Test that StackWalker does not include carrier thread frames. - */ - @Test - void testStackWalker() throws Exception { - VThreadRunner.run(() -> { - StackWalker walker = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE)); - boolean found = walker.walk(sf -> - sf.map(StackWalker.StackFrame::getDeclaringClass) - .anyMatch(c -> c == ForkJoinPool.class)); - assertFalse(found); - }); - } - - private static boolean hasJvmArgument(String arg) { - for (String argument : ManagementFactory.getRuntimeMXBean().getInputArguments()) { - if (argument.equals(arg)) return true; - } - return false; - } -} diff --git a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java index b1ccb21910acf..3bb5c75a1b50d 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java +++ b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,13 +61,16 @@ import java.util.stream.Stream; import java.nio.channels.Selector; -import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadPinner; +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.*; @@ -78,9 +81,12 @@ class ThreadAPI { private static ScheduledExecutorService scheduler; @BeforeAll - static void setup() throws Exception { + static void setup() { ThreadFactory factory = Executors.defaultThreadFactory(); scheduler = Executors.newSingleThreadScheduledExecutor(factory); + + // need >=2 carriers for testing pinning + VThreadRunner.ensureParallelism(2); } @AfterAll @@ -719,14 +725,7 @@ void testJoin33() throws Exception { */ @Test void testJoin34() throws Exception { - // need at least two carrier threads due to pinning - int previousParallelism = VThreadRunner.ensureParallelism(2); - try { - VThreadRunner.run(this::testJoin33); - } finally { - // restore - VThreadRunner.setParallelism(previousParallelism); - } + VThreadRunner.run(this::testJoin33); } /** @@ -1084,11 +1083,10 @@ void testSetDaemon2() throws Exception { */ @Test void testYield1() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); var list = new CopyOnWriteArrayList(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var thread = factory.newThread(() -> { list.add("A"); var child = factory.newThread(() -> { @@ -1112,11 +1110,10 @@ void testYield1() throws Exception { */ @Test void testYield2() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); var list = new CopyOnWriteArrayList(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var thread = factory.newThread(() -> { list.add("A"); var child = factory.newThread(() -> { @@ -1708,50 +1705,59 @@ void testGetState3() throws Exception { */ @Test void testGetState4() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); AtomicBoolean completed = new AtomicBoolean(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread t1 = builder.start(() -> { - Thread t2 = builder.unstarted(LockSupport::park); - assertEquals(Thread.State.NEW, t2.getState()); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread1 = factory.newThread(() -> { + Thread thread2 = factory.newThread(LockSupport::park); + assertEquals(Thread.State.NEW, thread2.getState()); // start t2 to make it runnable - t2.start(); + thread2.start(); try { - assertEquals(Thread.State.RUNNABLE, t2.getState()); + assertEquals(Thread.State.RUNNABLE, thread2.getState()); // yield to allow t2 to run and park Thread.yield(); - assertEquals(Thread.State.WAITING, t2.getState()); + assertEquals(Thread.State.WAITING, thread2.getState()); } finally { // unpark t2 to make it runnable again - LockSupport.unpark(t2); + LockSupport.unpark(thread2); } // t2 should be runnable (not mounted) - assertEquals(Thread.State.RUNNABLE, t2.getState()); + assertEquals(Thread.State.RUNNABLE, thread2.getState()); completed.set(true); }); - t1.join(); + thread1.start(); + thread1.join(); } assertTrue(completed.get() == true); } /** - * Test Thread::getState when thread is waiting to enter a monitor. + * Test Thread::getState when thread is blocked waiting to enter a monitor. */ - @Test - void testGetState5() throws Exception { - var started = new CountDownLatch(1); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetState5(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().unstarted(() -> { - started.countDown(); - synchronized (lock) { } + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + synchronized (lock) { } + }); + } else { + ready.set(true); + synchronized (lock) { } + } }); synchronized (lock) { thread.start(); - started.await(); + awaitTrue(ready); // wait for thread to block await(thread, Thread.State.BLOCKED); @@ -1762,16 +1768,35 @@ void testGetState5() throws Exception { /** * Test Thread::getState when thread is waiting in Object.wait. */ - @Test - void testGetState6() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetState6(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - try { lock.wait(); } catch (InterruptedException e) { } + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } + } catch (InterruptedException e) { } } }); try { // wait for thread to wait + awaitTrue(ready); await(thread, Thread.State.WAITING); + + // notify, thread should block trying to reenter + synchronized (lock) { + lock.notifyAll(); + await(thread, Thread.State.BLOCKED); + } } finally { thread.interrupt(); thread.join(); @@ -1781,18 +1806,35 @@ void testGetState6() throws Exception { /** * Test Thread::getState when thread is waiting in Object.wait(millis). */ - @Test - void testGetState7() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetState7(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { try { - lock.wait(Long.MAX_VALUE); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(Long.MAX_VALUE); + }); + } else { + ready.set(true); + lock.wait(Long.MAX_VALUE); + } } catch (InterruptedException e) { } } }); try { - // wait for thread to wait + // wait for thread to timed-wait + awaitTrue(ready); await(thread, Thread.State.TIMED_WAITING); + + // notify, thread should block trying to reenter + synchronized (lock) { + lock.notifyAll(); + await(thread, Thread.State.BLOCKED); + } } finally { thread.interrupt(); thread.join(); @@ -1933,7 +1975,7 @@ void testHoldsLock2() throws Exception { * Test Thread::getStackTrace on unstarted thread. */ @Test - void testGetStackTrace1() { + void testGetStackTraceUnstarted() { var thread = Thread.ofVirtual().unstarted(() -> { }); StackTraceElement[] stack = thread.getStackTrace(); assertTrue(stack.length == 0); @@ -1943,136 +1985,224 @@ void testGetStackTrace1() { * Test Thread::getStackTrace on thread that has been started but has not run. */ @Test - void testGetStackTrace2() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + void testGetStackTraceStarted() throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); Executor scheduler = task -> { }; - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread thread = builder.start(() -> { }); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(() -> { }); + thread.start(); StackTraceElement[] stack = thread.getStackTrace(); assertTrue(stack.length == 0); } /** - * Test Thread::getStackTrace on running thread. + * Test Thread::getStackTrace on thread that is runnable-mounted. */ @Test - void testGetStackTrace3() throws Exception { - var sel = Selector.open(); - var thread = Thread.ofVirtual().start(() -> { - try { sel.select(); } catch (Exception e) { } - }); - try { - while (!contains(thread.getStackTrace(), "select")) { - assertTrue(thread.isAlive()); - Thread.sleep(20); + void testGetStackTraceRunnableMounted() throws Exception { + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); + + class Foo { + void spinUntilDone() { + ready.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } } + } + + Foo foo = new Foo(); + var thread = Thread.ofVirtual().start(foo::spinUntilDone); + try { + awaitTrue(ready); + StackTraceElement[] stack = thread.getStackTrace(); + assertTrue(contains(stack, Foo.class.getName() + ".spinUntilDone")); } finally { - sel.close(); + done.set(true); thread.join(); } } /** - * Test Thread::getStackTrace on thread waiting in Object.wait. + * Test Thread::getStackTrace on thread that is runnable-unmounted. */ @Test - void testGetStackTrace4() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); - try (ForkJoinPool pool = new ForkJoinPool(1)) { - AtomicReference ref = new AtomicReference<>(); - Executor scheduler = task -> { - pool.submit(() -> { - ref.set(Thread.currentThread()); - task.run(); - }); - }; + void testGetStackTraceRunnableUnmounted() throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread vthread = builder.start(() -> { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { } + // custom scheduler with one carrier thread + try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + // start thread1 to park + Thread thread1 = factory.newThread(LockSupport::park); + thread1.start(); + await(thread1, Thread.State.WAITING); + + // start thread2 to spin and pin the carrier thread + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + Thread thread2 = factory.newThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); } }); + thread2.start(); + awaitTrue(started); - // get carrier Thread - Thread carrier; - while ((carrier = ref.get()) == null) { - Thread.sleep(20); - } + try { + // unpark thread1, it should be "stuck" in runnable state + // (the carrier thread is pinned, no other virtual thread can run) + LockSupport.unpark(thread1); + assertEquals(Thread.State.RUNNABLE, thread1.getState()); - // wait for virtual thread to block in wait - await(vthread, Thread.State.WAITING); + // print thread1's stack trace + StackTraceElement[] stack = thread1.getStackTrace(); + assertTrue(contains(stack, "LockSupport.park")); - // get stack trace of both carrier and virtual thread - StackTraceElement[] carrierStackTrace = carrier.getStackTrace(); - StackTraceElement[] vthreadStackTrace = vthread.getStackTrace(); + } finally { + done.set(true); + } + } + } - // allow virtual thread to terminate - synchronized (lock) { - lock.notifyAll(); + /** + * Test Thread::getStackTrace on thread blocked on monitor enter. + */ + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceBlocked(boolean pinned) throws Exception { + class Foo { + void enter() { + synchronized (this) { } + } + } + Foo foo = new Foo(); + var ready = new AtomicBoolean(); + var thread = Thread.ofVirtual().unstarted(() -> { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + foo.enter(); + }); + } else { + ready.set(true); + foo.enter(); } + }); + synchronized (foo) { + thread.start(); + awaitTrue(ready); - // check carrier thread's stack trace - assertTrue(contains(carrierStackTrace, "java.util.concurrent.ForkJoinPool.runWorker")); - assertFalse(contains(carrierStackTrace, "java.lang.Object.wait")); + // wait for thread to block + await(thread, Thread.State.BLOCKED); - // check virtual thread's stack trace - assertFalse(contains(vthreadStackTrace, "java.util.concurrent.ForkJoinPool.runWorker")); - assertTrue(contains(vthreadStackTrace, "java.lang.Object.wait")); + StackTraceElement[] stack = thread.getStackTrace(); + assertTrue(contains(stack, Foo.class.getName() + ".enter")); } + thread.join(); } /** - * Test Thread::getStackTrace on parked thread. + * Test Thread::getStackTrace when thread is waiting in Object.wait. */ - @Test - void testGetStackTrace5() throws Exception { - var thread = Thread.ofVirtual().start(LockSupport::park); - await(thread, Thread.State.WAITING); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceWaiting(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } + } catch (InterruptedException e) { } + } + }); try { + // wait for thread to wait + awaitTrue(ready); + await(thread, Thread.State.WAITING); + StackTraceElement[] stack = thread.getStackTrace(); - assertTrue(contains(stack, "LockSupport.park")); + assertTrue(contains(stack, "Object.wait")); } finally { - LockSupport.unpark(thread); + thread.interrupt(); thread.join(); } } /** - * Test Thread::getStackTrace on timed-parked thread. + * Test Thread::getStackTrace when thread is waiting in timed-Object.wait. */ - @Test - void testGetStackTrace6() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceTimedWaiting(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - LockSupport.parkNanos(Long.MAX_VALUE); + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(Long.MAX_VALUE); + }); + } else { + ready.set(true); + lock.wait(Long.MAX_VALUE); + } + } catch (InterruptedException e) { } + } }); - await(thread, Thread.State.TIMED_WAITING); try { + // wait for thread to wait + awaitTrue(ready); + await(thread, Thread.State.TIMED_WAITING); + StackTraceElement[] stack = thread.getStackTrace(); - assertTrue(contains(stack, "LockSupport.parkNanos")); + assertTrue(contains(stack, "Object.wait")); } finally { - LockSupport.unpark(thread); + thread.interrupt(); thread.join(); } } /** - * Test Thread::getStackTrace on parked thread that is pinned. + * Test Thread::getStackTrace when thread in park. */ - @Test - void testGetStackTrace7() throws Exception { - AtomicBoolean done = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceParked(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + while (!done.get()) { + LockSupport.park(); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.park(); } - }); + } }); - await(thread, Thread.State.WAITING); try { + // wait for thread to park + awaitTrue(ready); + await(thread, Thread.State.WAITING); + StackTraceElement[] stack = thread.getStackTrace(); assertTrue(contains(stack, "LockSupport.park")); } finally { @@ -2083,20 +2213,33 @@ void testGetStackTrace7() throws Exception { } /** - * Test Thread::getStackTrace on timed-parked thread that is pinned. + * Test Thread::getStackTrace when thread in timed-park. */ - @Test - void testGetStackTrace8() throws Exception { - AtomicBoolean done = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceTimedPark(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { + if (pinned) { + ready.set(true); + VThreadPinner.runPinned(() -> { + while (!done.get()) { + LockSupport.parkNanos(Long.MAX_VALUE); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.parkNanos(Long.MAX_VALUE); } - }); + } }); - await(thread, Thread.State.TIMED_WAITING); try { + // wait for thread to park + awaitTrue(ready); + await(thread, Thread.State.TIMED_WAITING); + StackTraceElement[] stack = thread.getStackTrace(); assertTrue(contains(stack, "LockSupport.parkNanos")); } finally { @@ -2110,7 +2253,7 @@ void testGetStackTrace8() throws Exception { * Test Thread::getStackTrace on terminated thread. */ @Test - void testGetStackTrace9() throws Exception { + void testGetStackTraceTerminated() throws Exception { var thread = Thread.ofVirtual().start(() -> { }); thread.join(); StackTraceElement[] stack = thread.getStackTrace(); @@ -2133,7 +2276,7 @@ void testGetAllStackTraces1() throws Exception { */ @Test void testGetAllStackTraces2() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); try (ForkJoinPool pool = new ForkJoinPool(1)) { AtomicReference ref = new AtomicReference<>(); Executor scheduler = task -> { @@ -2143,14 +2286,15 @@ void testGetAllStackTraces2() throws Exception { }); }; - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread vthread = builder.start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread vthread = factory.newThread(() -> { synchronized (lock) { try { lock.wait(); } catch (Exception e) { } } }); + vthread.start(); // get carrier Thread Thread carrier; @@ -2168,8 +2312,9 @@ void testGetAllStackTraces2() throws Exception { synchronized (lock) { lock.notifyAll(); } + vthread.join(); - // get stack trace for the carrier thread + // stack trace for the carrier thread StackTraceElement[] stackTrace = map.get(carrier); assertNotNull(stackTrace); assertTrue(contains(stackTrace, "java.util.concurrent.ForkJoinPool")); @@ -2417,6 +2562,15 @@ Throwable exception() { } } + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws Exception { + while (!ref.get()) { + Thread.sleep(20); + } + } + /** * Waits for the given thread to reach a given state. */ diff --git a/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java b/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java index 1ae73b280cb13..0c5f1c3d6b435 100644 --- a/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java +++ b/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @summary Test parking when pinned and emitting the JFR VirtualThreadPinnedEvent throws - * @modules java.base/jdk.internal.event + * @modules java.base/java.lang:+open java.base/jdk.internal.event * @library /test/lib * @compile/module=java.base jdk/internal/event/VirtualThreadPinnedEvent.java * @run junit/othervm --enable-native-access=ALL-UNNAMED VirtualThreadPinnedEventThrows @@ -36,12 +36,22 @@ import java.util.concurrent.locks.LockSupport; import jdk.internal.event.VirtualThreadPinnedEvent; +import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadPinner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; import static org.junit.jupiter.api.Assertions.*; class VirtualThreadPinnedEventThrows { + @BeforeAll + static void setup() { + // need >=2 carriers for testing pinning when main thread is a virtual thread + if (Thread.currentThread().isVirtual()) { + VThreadRunner.ensureParallelism(2); + } + } + /** * Test parking when pinned and creating the VirtualThreadPinnedEvent fails with OOME. */ diff --git a/test/jdk/java/lang/Thread/virtual/stress/TimedGet.java b/test/jdk/java/lang/Thread/virtual/stress/CompletableFutureTimedGet.java similarity index 79% rename from test/jdk/java/lang/Thread/virtual/stress/TimedGet.java rename to test/jdk/java/lang/Thread/virtual/stress/CompletableFutureTimedGet.java index 60125e1ac5677..29ad90895f57e 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/TimedGet.java +++ b/test/jdk/java/lang/Thread/virtual/stress/CompletableFutureTimedGet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,18 @@ * @test * @summary Stress parking with CompletableFuture timed get * @requires vm.debug != true & vm.continuations - * @run main/othervm -Xmx1g TimedGet 100000 + * @run main/othervm -Xmx1g CompletableFutureTimedGet 100000 */ import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -public class TimedGet { +public class CompletableFutureTimedGet { static final String RESULT = "foo"; @@ -77,30 +78,27 @@ public static void main(String... args) throws InterruptedException { // wait for all threads to terminate long lastTimestamp = System.currentTimeMillis(); - int i = 0; - while (i < threadCount) { - Thread t = threads.get(i); - boolean terminated; - if (t.isAlive()) { - terminated = t.join(Duration.ofMillis(500)); - - // print trace message so the output tracks progress - long currentTime = System.currentTimeMillis(); - if ((currentTime - lastTimestamp) > 500) { - System.out.println(completed.get()); - lastTimestamp = currentTime; + boolean done; + do { + done = true; + for (Thread t : threads) { + if (!t.join(Duration.ofSeconds(1))) { + done = false; } - } else { - terminated = true; } - if (terminated) { - i++; + + // print trace message so the output tracks progress + long currentTime = System.currentTimeMillis(); + if (done || ((currentTime - lastTimestamp) > 500)) { + System.out.format("%s => completed %d of %d%n", + Instant.now(), completed.get(), threadCount); + lastTimestamp = currentTime; } - } + + } while (!done); // all tasks should have completed successfully int completedCount = completed.get(); - System.out.println(completedCount); if (completedCount != threadCount) { throw new RuntimeException("completed = " + completedCount); } diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java new file mode 100644 index 0000000000000..a5aea9ee3984c --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 Stress test Thread.getStackTrace on virtual threads that are blocking or + * blocked on monitorenter + * @requires vm.debug != true + * @modules java.base/java.lang:+open + * @library /test/lib + * @run main/othervm GetStackTraceALotWhenBlocking 500000 + */ + +/* + * @test + * @requires vm.debug == true & vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run main/othervm/timeout=300 GetStackTraceALotWhenBlocking 50000 + */ + +import java.time.Instant; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicBoolean; +import jdk.test.lib.thread.VThreadRunner; + +public class GetStackTraceALotWhenBlocking { + + public static void main(String[] args) throws Exception { + // need at least two carriers + VThreadRunner.ensureParallelism(2); + + int iterations = args.length > 0 ? Integer.parseInt(args[0]) : 100_000; + + var done = new AtomicBoolean(); + var lock = new Object(); + + Runnable task = () -> { + long count = 0L; + while (!done.get()) { + synchronized (lock) { + pause(); + } + count++; + } + System.out.format("%s %s => %d loops%n", Instant.now(), Thread.currentThread(), count); + }; + + var thread1 = Thread.ofVirtual().start(task); + var thread2 = Thread.ofVirtual().start(task); + try { + for (int i = 1; i <= iterations; i++) { + if ((i % 10_000) == 0) { + System.out.format("%s => %d of %d%n", Instant.now(), i, iterations); + } + + thread1.getStackTrace(); + pause(); + thread2.getStackTrace(); + pause(); + } + } finally { + done.set(true); + thread1.join(); + thread2.join(); + } + } + + private static void pause() { + if (ThreadLocalRandom.current().nextBoolean()) { + Thread.onSpinWait(); + } else { + Thread.yield(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALot.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenParking.java similarity index 85% rename from test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALot.java rename to test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenParking.java index c86daf1bc6fa8..d35f39a991a81 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenParking.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,30 +23,32 @@ /** * @test - * @summary Stress test asynchronous Thread.getStackTrace + * @summary Stress test asynchronous Thread.getStackTrace when parking * @requires vm.debug != true & vm.continuations * @modules java.base/java.lang:+open - * @compile GetStackTraceALot.java ../ThreadBuilders.java - * @run main GetStackTraceALot + * @library /test/lib + * @run main GetStackTraceALotWhenParking */ /** * @test * @requires vm.debug == true & vm.continuations * @modules java.base/java.lang:+open - * @compile GetStackTraceALot.java ../ThreadBuilders.java - * @run main/timeout=300 GetStackTraceALot 1000 + * @library /test/lib + * @run main/timeout=300 GetStackTraceALotWhenParking 1000 */ import java.time.Duration; +import java.time.Instant; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; -public class GetStackTraceALot { +public class GetStackTraceALotWhenParking { static class RoundRobinExecutor implements Executor, AutoCloseable { private final ExecutorService[] executors; private int next; @@ -83,7 +85,9 @@ public static void main(String[] args) throws Exception { AtomicInteger count = new AtomicInteger(); try (RoundRobinExecutor executor = new RoundRobinExecutor()) { - Thread thread = ThreadBuilders.virtualThreadBuilder(executor).start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(executor); + + Thread thread = factory.newThread(() -> { while (count.incrementAndGet() < ITERATIONS) { long start = System.nanoTime(); while ((System.nanoTime() - start) < SPIN_NANOS) { @@ -92,6 +96,7 @@ public static void main(String[] args) throws Exception { LockSupport.parkNanos(500_000); } }); + thread.start(); long start = System.nanoTime(); while (thread.isAlive()) { @@ -99,7 +104,7 @@ public static void main(String[] args) throws Exception { // printStackTrace(stackTrace); Thread.sleep(5); if ((System.nanoTime() - start) > 500_000_000) { - System.out.println(count.get()); + System.out.format("%s => %d of %d%n", Instant.now(), count.get(), ITERATIONS); start = System.nanoTime(); } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java index 35986718a38c9..d7e6c3a8de460 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java @@ -28,7 +28,7 @@ * @requires vm.debug != true * @modules java.base/java.lang:+open * @library /test/lib - * @run main/othervm GetStackTraceALotWhenPinned 500000 + * @run main/othervm --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 500000 */ /* @@ -36,7 +36,7 @@ * @requires vm.debug == true * @modules java.base/java.lang:+open * @library /test/lib - * @run main/othervm/timeout=300 GetStackTraceALotWhenPinned 200000 + * @run main/othervm/timeout=300 --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 200000 */ import java.time.Instant; @@ -79,7 +79,7 @@ public static void main(String[] args) throws Exception { }); long lastTimestamp = System.currentTimeMillis(); - for (int i = 0; i < iterations; i++) { + for (int i = 1; i <= iterations; i++) { // wait for virtual thread to arrive barrier.await(); @@ -87,8 +87,8 @@ public static void main(String[] args) throws Exception { LockSupport.unpark(thread); long currentTime = System.currentTimeMillis(); - if ((currentTime - lastTimestamp) > 500) { - System.out.format("%s %d remaining ...%n", Instant.now(), (iterations - i)); + if (i == iterations || ((currentTime - lastTimestamp) > 500)) { + System.out.format("%s => %d of %d%n", Instant.now(), i, iterations); lastTimestamp = currentTime; } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/PinALot.java b/test/jdk/java/lang/Thread/virtual/stress/PinALot.java index a420ebb330e7b..7f95847fc7665 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/PinALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/PinALot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,17 +46,17 @@ public class PinALot { public static void main(String[] args) throws Exception { - int iterations = 1_000_000; + int iterations; if (args.length > 0) { iterations = Integer.parseInt(args[0]); + } else { + iterations = 1_000_000; } - final int ITERATIONS = iterations; AtomicInteger count = new AtomicInteger(); - Thread thread = Thread.ofVirtual().start(() -> { VThreadPinner.runPinned(() -> { - while (count.incrementAndGet() < ITERATIONS) { + while (count.incrementAndGet() < iterations) { LockSupport.parkNanos(1); } }); @@ -65,12 +65,12 @@ public static void main(String[] args) throws Exception { boolean terminated; do { terminated = thread.join(Duration.ofSeconds(1)); - System.out.println(Instant.now() + " => " + count.get()); + System.out.println(Instant.now() + " => " + count.get() + " of " + iterations); } while (!terminated); int countValue = count.get(); - if (countValue != ITERATIONS) { - throw new RuntimeException("count = " + countValue); + if (countValue != iterations) { + throw new RuntimeException("Thread terminated, count=" + countValue); } } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/PingPong.java b/test/jdk/java/lang/Thread/virtual/stress/PingPong.java index 404444b99ce56..2266d192fcaa5 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/PingPong.java +++ b/test/jdk/java/lang/Thread/virtual/stress/PingPong.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,7 @@ */ import java.time.Duration; +import java.time.Instant; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.atomic.AtomicInteger; @@ -91,11 +92,12 @@ public static void main(String[] args) throws Exception { boolean terminated; do { terminated = t1.join(Duration.ofMillis(500)); - if (terminated) + if (terminated) { terminated = t2.join(Duration.ofMillis(500)); - System.out.format("%d %s%n", count1.get(), count2.get()); + } + System.out.format("%s => T1 %d of %d, T2 %d of %d%n", + Instant.now(), count1.get(), iterations, count2.get(), iterations); } while (!terminated); - } interface Exchanger { diff --git a/test/jdk/java/lang/Thread/virtual/stress/Skynet.java b/test/jdk/java/lang/Thread/virtual/stress/Skynet.java index ee45fc827b005..5b63fe84b1d89 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/Skynet.java +++ b/test/jdk/java/lang/Thread/virtual/stress/Skynet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ static void skynet(int num, long expected) { long end = System.currentTimeMillis(); System.out.format("Result: %d in %s ms%n", sum, (end-start)); if (sum != expected) - throw new AssertionError("unexpected result!"); + throw new RuntimeException("Expected " + expected); } static void skynet(Channel result, int num, int size, int div) { diff --git a/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java b/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java index 12771ab499a7d..ed1c985ba7c98 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,16 +41,16 @@ public class SleepALot { public static void main(String[] args) throws Exception { - int iterations = 1_000_000; + int iterations; if (args.length > 0) { iterations = Integer.parseInt(args[0]); + } else { + iterations = 1_000_000; } - final int ITERATIONS = iterations; AtomicInteger count = new AtomicInteger(); - Thread thread = Thread.ofVirtual().start(() -> { - while (count.incrementAndGet() < ITERATIONS) { + while (count.incrementAndGet() < iterations) { try { Thread.sleep(Duration.ofNanos(100)); } catch (InterruptedException ignore) { } @@ -60,12 +60,12 @@ public static void main(String[] args) throws Exception { boolean terminated; do { terminated = thread.join(Duration.ofSeconds(1)); - System.out.println(Instant.now() + " => " + count.get()); + System.out.println(Instant.now() + " => " + count.get() + " of " + iterations); } while (!terminated); int countValue = count.get(); - if (countValue != ITERATIONS) { - throw new RuntimeException("count = " + countValue); + if (countValue != iterations) { + throw new RuntimeException("Thread terminated, count=" + countValue); } } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java b/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java new file mode 100644 index 0000000000000..2a2fce62d62c9 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=timeout + * @summary Stress test timed-Object.wait + * @run main/othervm TimedWaitALot 200 + */ + +/* + * @test id=timeout-notify + * @summary Test timed-Object.wait where the waiting thread is awakened with Object.notify + * at around the same time that the timeout expires. + * @run main/othervm TimedWaitALot 200 true false + */ + +/* + * @test id=timeout-interrupt + * @summary Test timed-Object.wait where the waiting thread is awakened with Thread.interrupt + * at around the same time that the timeout expires. + * @run main/othervm TimedWaitALot 200 false true + */ + +/* + * @test id=timeout-notify-interrupt + * @summary Test timed-Object.wait where the waiting thread is awakened with Object.notify + * and Thread.interrupt at around the same time that the timeout expires. + * @run main/othervm TimedWaitALot 100 true true + */ + +import java.time.Instant; +import java.util.concurrent.Executors; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadLocalRandom; + +public class TimedWaitALot { + public static void main(String[] args) throws Exception { + int iterations = Integer.parseInt(args[0]); + boolean notify = args.length >= 2 && "true".equals(args[1]); + boolean interrupt = args.length >=3 && "true".equals(args[2]); + + // test all timeouts concurrently + int[] timeouts = { 10, 20, 50, 100 }; + for (int i = 1; i <= iterations; i++) { + System.out.println(Instant.now() + " => " + i + " of " + iterations); + test(notify, interrupt, timeouts); + } + } + + /** + * Start a first virtual thread to wait in Object.wait(millis). + * If {@code notify} is true, start a virtual thread to use Object.notifyAll at around + * the same time that the timeout expires. + * If {@code interrupt} is true, start virtual thread to interrupts the first virtual + * thread at around the same time as the timeout expires. + */ + static void test(boolean notify, boolean interrupt, int... timeouts) throws Exception { + try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + for (int timeout : timeouts) { + var queue = new SynchronousQueue(); + var lock = new Object(); + + // virtual thread waits with Object.wait(timeout) + executor.submit(() -> { + queue.put(Thread.currentThread()); + synchronized (lock) { + lock.wait(timeout); + } + return null; + }); + + // wait for thread to start + Thread thread = queue.take(); + + // start thread to Object.notifyAll at around time that the timeout expires + if (notify) { + if (ThreadLocalRandom.current().nextBoolean()) { + synchronized (lock) { + sleepLessThan(timeout); + lock.notifyAll(); + } + } else { + sleepLessThan(timeout); + synchronized (lock) { + lock.notifyAll(); + } + } + } + + // start thread to interrupt first thread at around time that the timeout expires + if (interrupt) { + executor.submit(() -> { + sleepLessThan(timeout); + thread.interrupt(); + return null; + }); + } + } + } + } + + /** + * Sleeps for just less than the given timeout, in millis. + */ + private static void sleepLessThan(long timeout) throws InterruptedException { + int delta = ThreadLocalRandom.current().nextInt(10); + Thread.sleep(timeout - delta); + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java b/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java index 3eabfff1c4c33..b793993a1b7d2 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @summary Stress test Thread.yield * @requires vm.debug != true - * @run main YieldALot 350000 + * @run main YieldALot 500000 */ /* @@ -35,34 +35,35 @@ */ import java.time.Duration; +import java.time.Instant; import java.util.concurrent.atomic.AtomicInteger; public class YieldALot { public static void main(String[] args) throws Exception { - int iterations = 1_000_000; + int iterations; if (args.length > 0) { iterations = Integer.parseInt(args[0]); + } else { + iterations = 1_000_000; } - final int ITERATIONS = iterations; AtomicInteger count = new AtomicInteger(); - Thread thread = Thread.ofVirtual().start(() -> { - while (count.incrementAndGet() < ITERATIONS) { + while (count.incrementAndGet() < iterations) { Thread.yield(); } }); boolean terminated; do { - terminated = thread.join(Duration.ofMillis(500)); - System.out.println(count.get()); + terminated = thread.join(Duration.ofSeconds(1)); + System.out.println(Instant.now() + " => " + count.get() + " of " + iterations); } while (!terminated); int countValue = count.get(); - if (countValue != ITERATIONS) { - throw new RuntimeException("count = " + countValue); + if (countValue != iterations) { + throw new RuntimeException("Thread terminated, count=" + countValue); } } } diff --git a/test/jdk/java/lang/constant/MethodTypeDescTest.java b/test/jdk/java/lang/constant/MethodTypeDescTest.java index fd59a9240e9e4..5f7a2fc691fc8 100644 --- a/test/jdk/java/lang/constant/MethodTypeDescTest.java +++ b/test/jdk/java/lang/constant/MethodTypeDescTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,7 @@ * questions. */ +import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; @@ -304,4 +305,9 @@ public void testOfListImmutability() { mtd.parameterList().set(1, CD_void)); assertEquals(mtd, MethodTypeDesc.of(CD_void, CD_Object, CD_int)); } + + public void testMissingClass() { + var mtd = MTD_void.insertParameterTypes(0, ClassDesc.of("does.not.exist.DoesNotExist")); + assertThrows(ReflectiveOperationException.class, () -> mtd.resolveConstantDesc(MethodHandles.publicLookup())); + } } diff --git a/test/jdk/java/lang/instrument/RetransformRecordAnnotation.java b/test/jdk/java/lang/instrument/RetransformRecordAnnotation.java deleted file mode 100644 index 90d76d1f923e7..0000000000000 --- a/test/jdk/java/lang/instrument/RetransformRecordAnnotation.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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 8315575 8328137 - * @summary test that records with invisible annotation can be retransformed - * - * @library /test/lib - * @run shell MakeJAR.sh retransformAgent - * @run main/othervm -javaagent:retransformAgent.jar -Xlog:redefine+class=trace RetransformRecordAnnotation - * @run main/othervm -javaagent:retransformAgent.jar -XX:+PreserveAllAnnotations -Xlog:redefine+class=trace RetransformRecordAnnotation - */ - -import java.io.File; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.instrument.ClassFileTransformer; -import java.nio.file.Files; -import java.security.ProtectionDomain; - -public class RetransformRecordAnnotation extends AInstrumentationTestCase { - - @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) - @Retention(RetentionPolicy.RUNTIME) - @interface RuntimeTypeAnno {} - - @Retention(RetentionPolicy.RUNTIME) - @interface RuntimeParamAnno { - String s() default "foo"; - } - - @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) - @Retention(RetentionPolicy.CLASS) - @interface ClassTypeAnno {} - - @Retention(RetentionPolicy.CLASS) - @interface ClassParamAnno { - String s() default "bar"; - } - - @RuntimeTypeAnno - @RuntimeParamAnno(s = "1") - public record VisibleAnnos(@RuntimeTypeAnno @RuntimeParamAnno(s = "2") Object o, Object other) { - } - - @ClassTypeAnno - @ClassParamAnno(s = "3") - public record InvisibleAnnos(@ClassTypeAnno @ClassParamAnno(s = "4") Object o, Object other) { - } - - @RuntimeTypeAnno - @RuntimeParamAnno(s = "5") - @ClassTypeAnno - @ClassParamAnno(s = "6") - public record MixedAnnos(@RuntimeTypeAnno @RuntimeParamAnno(s = "7") - @ClassTypeAnno @ClassParamAnno(s = "8") Object o, Object other) { - } - - public static void main (String[] args) throws Throwable { - ATestCaseScaffold test = new RetransformRecordAnnotation(); - test.beVerbose(); - test.runTest(); - } - - private Transformer transformer; - - public RetransformRecordAnnotation() throws Throwable { - super("RetransformRecordAnnotation"); - } - - private void log(Object o) { - System.out.println(String.valueOf(o)); - } - - // Retransforms target class using provided class bytes; - private void retransform(Class targetClass, byte[] classBytes) throws Throwable { - transformer.prepare(targetClass, classBytes); - fInst.retransformClasses(targetClass); - assertTrue(targetClass.getName() + " was not seen by transform()", - transformer.getSeenClassBytes() != null); - } - - protected final void doRunTest() throws Throwable { - transformer = new Transformer(); - fInst.addTransformer(transformer, true); - - { - log("Sanity: retransform to original class bytes"); - retransform(InvisibleAnnos.class, loadClassBytes(InvisibleAnnos.class)); - log(""); - } - - // The following testcases use null as new class bytes (i.e. no transform is performed). - // However, it is enough for testing purposes as the JvmtiClassFileReconstituter is still involved - // in preparation of the initial class bytes. - { - log("Test: retransform VisibleAnnos to null"); - retransform(VisibleAnnos.class, null); - log(""); - } - - { - log("Test: retransform InvisibleAnnos to null"); - retransform(InvisibleAnnos.class, null); - log(""); - } - - { - log("Test: retransform MixedAnnos to null"); - retransform(MixedAnnos.class, null); - log(""); - } - } - - private byte[] loadClassBytes(Class cls) throws Exception { - String classFileName = cls.getName() + ".class"; - File classFile = new File(System.getProperty("test.classes", "."), classFileName); - log("Reading test class from " + classFile); - byte[] classBytes = Files.readAllBytes(classFile.toPath()); - log("Read " + classBytes.length + " bytes."); - return classBytes; - } - - public class Transformer implements ClassFileTransformer { - private String targetClassName; - private byte[] seenClassBytes; - private byte[] newClassBytes; - - public Transformer() { - } - - // Prepares transformer for Instrumentation.retransformClasses. - public void prepare(Class targetClass, byte[] classBytes) { - targetClassName = targetClass.getName(); - newClassBytes = classBytes; - seenClassBytes = null; - } - - byte[] getSeenClassBytes() { - return seenClassBytes; - } - - public String toString() { - return Transformer.this.getClass().getName(); - } - - public byte[] transform(ClassLoader loader, String className, - Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { - - if (className.equals(targetClassName)) { - log(this + ".transform() sees '" + className - + "' of " + classfileBuffer.length + " bytes."); - seenClassBytes = classfileBuffer; - if (newClassBytes != null) { - log(this + ".transform() sets new classbytes for '" + className - + "' of " + newClassBytes.length + " bytes."); - } - return newClassBytes; - } - - return null; - } - } -} diff --git a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java index 652272ca8a8f4..b20e18d263edd 100644 --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestReflection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import java.lang.invoke.MethodHandleInfo; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.stream.Stream; @@ -52,15 +53,33 @@ static VarHandle handle() throws Exception { } @Test(dataProvider = "accessModesProvider", expectedExceptions = IllegalArgumentException.class) - public void methodInvocation(VarHandle.AccessMode accessMode) throws Exception { + public void methodInvocationArgumentMismatch(VarHandle.AccessMode accessMode) throws Exception { VarHandle v = handle(); - // Try a reflective invoke using a Method + // Try a reflective invoke using a Method, with no arguments Method vhm = VarHandle.class.getMethod(accessMode.methodName(), Object[].class); vhm.invoke(v, new Object[]{}); } + @Test(dataProvider = "accessModesProvider") + public void methodInvocationMatchingArguments(VarHandle.AccessMode accessMode) throws Exception { + VarHandle v = handle(); + + // Try a reflective invoke using a Method, with the minimal required arguments + + Method vhm = VarHandle.class.getMethod(accessMode.methodName(), Object[].class); + Object arg = new Object[0]; + try { + vhm.invoke(v, arg); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof UnsupportedOperationException)) { + throw new RuntimeException("expected UnsupportedOperationException but got: " + + e.getCause().getClass().getName(), e); + } + } + } + @Test(dataProvider = "accessModesProvider", expectedExceptions = UnsupportedOperationException.class) public void methodHandleInvoke(VarHandle.AccessMode accessMode) throws Throwable { VarHandle v = handle(); diff --git a/test/jdk/java/lang/management/ThreadMXBean/LockedMonitorInNative.java b/test/jdk/java/lang/management/ThreadMXBean/LockedMonitorInNative.java new file mode 100644 index 0000000000000..e1e490aa040c0 --- /dev/null +++ b/test/jdk/java/lang/management/ThreadMXBean/LockedMonitorInNative.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 ThreadMXBean.getLockedMonitors returns information about an object + * monitor lock entered with a synchronized native method or JNI MonitorEnter + * @run junit/othervm LockedMonitorInNative + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import static org.junit.jupiter.api.Assertions.*; + +public class LockedMonitorInNative { + + @BeforeAll + static void setup() throws Exception { + System.loadLibrary("LockedMonitorInNative"); + } + + /** + * Test ThreadMXBean.getLockedMonitors returns information about an object + * monitor lock entered with a synchronized native method. + */ + @Test + void testSynchronizedNative() { + Object lock = this; + runWithSynchronizedNative(() -> { + assertTrue(holdsLock(lock), "Thread does not hold lock"); + }); + } + + /** + * Test ThreadMXBean.getLockedMonitors returns information about an object + * monitor lock entered with JNI MonitorEnter. + */ + @Test + void testMonitorEnteredInNative() { + var lock = new Object(); + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(holdsLock(lock), "Thread does not hold lock"); + }); + } + + private boolean holdsLock(Object lock) { + int hc = System.identityHashCode(lock); + long tid = Thread.currentThread().threadId(); + ThreadInfo ti = ManagementFactory.getPlatformMXBean(ThreadMXBean.class) + .getThreadInfo(new long[] { tid }, true, true)[0]; + return Arrays.stream(ti.getLockedMonitors()) + .anyMatch(mi -> mi.getIdentityHashCode() == hc); + } + + /** + * Invokes the given task's run method while holding the monitor for "this". + */ + private synchronized native void runWithSynchronizedNative(Runnable task); + + /** + * Invokes the given task's run method while holding the monitor for the given + * object. The monitor is entered with JNI MonitorEnter, and exited with JNI MonitorExit. + */ + private native void runWithMonitorEnteredInNative(Object lock, Runnable task); + + /** + * Called from native methods to run the given task. + */ + private void run(Runnable task) { + task.run(); + } +} diff --git a/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java b/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java index 35fe819313a72..7830c93ac13fa 100644 --- a/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java +++ b/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,19 +41,20 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.nio.channels.Selector; import java.util.Arrays; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import java.util.stream.Collectors; +import jdk.test.lib.thread.VThreadPinner; import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -195,7 +196,7 @@ void testGetThreadInfo4(int maxDepth) { */ @Test void testGetThreadInfoCarrierThread() throws Exception { - assumeTrue(supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); try (ExecutorService pool = Executors.newFixedThreadPool(1)) { var carrierRef = new AtomicReference(); Executor scheduler = (task) -> { @@ -204,19 +205,30 @@ void testGetThreadInfoCarrierThread() throws Exception { task.run(); }); }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); // start virtual thread so carrier Thread can be captured - virtualThreadBuilder(scheduler).start(() -> { }).join(); + Thread thread = factory.newThread(() -> { }); + thread.start(); + thread.join(); Thread carrier = carrierRef.get(); assertTrue(carrier != null && !carrier.isVirtual()); try (Selector sel = Selector.open()) { - // start virtual thread that blocks in a native method - virtualThreadBuilder(scheduler).start(() -> { + String selClassName = sel.getClass().getName(); + + // start virtual thread that blocks while pinned + Thread vthread = factory.newThread(() -> { try { - sel.select(); + VThreadPinner.runPinned(sel::select); } catch (Exception e) { } }); + vthread.start(); + + // wait for virtual thread to block in select + while (!contains(vthread.getStackTrace(), selClassName)) { + Thread.sleep(20); + } // invoke getThreadInfo get and check the stack trace long tid = carrier.getId(); @@ -225,7 +237,7 @@ void testGetThreadInfoCarrierThread() throws Exception { // should only see carrier frames StackTraceElement[] stack = info.getStackTrace(); assertTrue(contains(stack, "java.util.concurrent.ThreadPoolExecutor")); - assertFalse(contains(stack, "java.nio.channels.Selector")); + assertFalse(contains(stack, selClassName)); // carrier should not be holding any monitors assertEquals(0, info.getLockedMonitors().length); @@ -351,40 +363,4 @@ private static boolean contains(StackTraceElement[] stack, String className) { .map(StackTraceElement::getClassName) .anyMatch(className::equals); } - - /** - * Returns a builder to create virtual threads that use the given scheduler. - * @throws UnsupportedOperationException if there is no support for custom schedulers - */ - private static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { - Thread.Builder.OfVirtual builder = Thread.ofVirtual(); - try { - Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); - Constructor ctor = clazz.getDeclaredConstructor(Executor.class); - ctor.setAccessible(true); - return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException re) { - throw re; - } - throw new RuntimeException(e); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Return true if custom schedulers are supported. - */ - private static boolean supportsCustomScheduler() { - try (var pool = Executors.newCachedThreadPool()) { - try { - virtualThreadBuilder(pool); - return true; - } catch (UnsupportedOperationException e) { - return false; - } - } - } } diff --git a/test/jdk/java/lang/management/ThreadMXBean/libLockedMonitorInNative.c b/test/jdk/java/lang/management/ThreadMXBean/libLockedMonitorInNative.c new file mode 100644 index 0000000000000..20975188907e6 --- /dev/null +++ b/test/jdk/java/lang/management/ThreadMXBean/libLockedMonitorInNative.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 "jni.h" + +JNIEXPORT void JNICALL +Java_LockedMonitorInNative_runWithSynchronizedNative(JNIEnv *env, jobject obj, jobject task) { + jclass clazz = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, clazz, "run", "(Ljava/lang/Runnable;)V"); + if (mid != NULL) { + (*env)->CallVoidMethod(env, obj, mid, task); + } +} + +JNIEXPORT void JNICALL +Java_LockedMonitorInNative_runWithMonitorEnteredInNative(JNIEnv *env, jobject obj, jobject lock, jobject task) { + jclass clazz = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, clazz, "run", "(Ljava/lang/Runnable;)V"); + if (mid != NULL && (*env)->MonitorEnter(env, lock) == 0) { + (*env)->CallVoidMethod(env, obj, mid, task); + (*env)->MonitorExit(env, lock); // can be called with pending exception + } +} diff --git a/test/jdk/java/math/BigInteger/BigIntegerTest.java b/test/jdk/java/math/BigInteger/BigIntegerTest.java index 2ac4750e43fa0..7da3fdac61813 100644 --- a/test/jdk/java/math/BigInteger/BigIntegerTest.java +++ b/test/jdk/java/math/BigInteger/BigIntegerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -293,8 +293,30 @@ private static void squareRootSmall() { report("squareRootSmall", failCount); } + private static void perfectSquaresLong() { + /* For every long value n in [0, 2^32) such that x == n * n, + * n - 1 <= (long) Math.sqrt(x >= 0 ? x : x + 0x1p64) <= n + * must be true. + * This property is used to implement MutableBigInteger.unsignedLongSqrt(). + */ + int failCount = 0; + + long limit = 1L << 32; + for (long n = 0; n < limit; n++) { + long x = n * n; + long s = (long) Math.sqrt(x >= 0 ? x : x + 0x1p64); + if (!(s == n || s == n - 1)) { + failCount++; + System.err.println(s + "^2 != " + x + " && (" + s + "+1)^2 != " + x); + } + } + + report("perfectSquaresLong", failCount); + } + public static void squareRoot() { squareRootSmall(); + perfectSquaresLong(); ToIntFunction f = (n) -> { int failCount = 0; diff --git a/test/jdk/java/net/ServerSocket/ClosedServerSocketTest.java b/test/jdk/java/net/ServerSocket/ClosedServerSocketTest.java new file mode 100644 index 0000000000000..81d5cc9c0d08a --- /dev/null +++ b/test/jdk/java/net/ServerSocket/ClosedServerSocketTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.SocketException; +import java.net.StandardSocketOptions; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @summary verifies that the APIs on java.net.ServerSocket throw expected exceptions + * when invoked on a closed ServerSocket + * @run junit ClosedServerSocketTest + */ +public class ClosedServerSocketTest { + + private static final InetAddress loopback = InetAddress.getLoopbackAddress(); + private static final InetSocketAddress loopbackEphemeral = new InetSocketAddress(loopback, 0); + + /** + * Verifies that various operations that specify to throw an IOException on a + * closed ServerSocket, do indeed throw it. + */ + @Test + public void testIOExceptionThrown() throws Exception { + try (final ServerSocket ss = new ServerSocket()) { + // close and then invoke the operations on the ServerSocket + ss.close(); + assertTrue(ss.isClosed(), "ServerSocket isn't closed"); + assertThrows(IOException.class, + ss::accept, + "accept() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.bind(loopbackEphemeral), + "bind() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.bind(loopbackEphemeral, 10), + "bind(SocketAddress, int) when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.getOption(StandardSocketOptions.SO_RCVBUF), + "getOption() when already closed didn't throw IOException"); + assertThrows(IOException.class, + ss::getSoTimeout, + "getSoTimeout() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> ss.setOption(StandardSocketOptions.SO_RCVBUF, 1024), + "setOption() when already closed didn't throw IOException"); + } + } + + /** + * Verifies that various operations that specify to throw a SocketOperation on a + * closed ServerSocket, do indeed throw it. + */ + @Test + public void testSocketExceptionThrown() throws Exception { + try (final ServerSocket ss = new ServerSocket()) { + // close and then invoke the operations on the ServerSocket + ss.close(); + assertTrue(ss.isClosed(), "ServerSocket isn't closed"); + assertThrowsExactly(SocketException.class, + ss::getReceiveBufferSize, + "getReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + ss::getReuseAddress, + "getReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> ss.setReceiveBufferSize(1024), + "setReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> ss.setReuseAddress(false), + "setReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> ss.setSoTimeout(1000), + "setSoTimeout() when already closed didn't throw SocketException"); + } + } + + /** + * Verifies that various operations that aren't expected to throw an exception on a + * closed ServerSocket, complete normally. + */ + @Test + public void testNoExceptionThrown() throws Exception { + try (final ServerSocket ss = new ServerSocket()) { + // close and then invoke the operations on the ServerSocket + ss.close(); + assertTrue(ss.isClosed(), "ServerSocket isn't closed"); + ss.getInetAddress(); + ss.getLocalPort(); + ss.getLocalSocketAddress(); + ss.isBound(); + ss.supportedOptions(); + } + } +} diff --git a/test/jdk/java/net/Socket/ClosedSocketTest.java b/test/jdk/java/net/Socket/ClosedSocketTest.java new file mode 100644 index 0000000000000..794f3b9cb185f --- /dev/null +++ b/test/jdk/java/net/Socket/ClosedSocketTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.StandardSocketOptions; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @summary verifies that the APIs on java.net.Socket throw expected exceptions + * when invoked on a closed socket + * @run junit ClosedSocketTest + */ +public class ClosedSocketTest { + + private static final InetAddress loopback = InetAddress.getLoopbackAddress(); + private static final InetSocketAddress loopbackEphemeral = new InetSocketAddress(loopback, 0); + + /** + * Verifies that various operations that specify to throw an IOException on a closed socket, + * do indeed throw it. + */ + @Test + public void testIOExceptionThrown() throws Exception { + try (final Socket s = new Socket()) { + // close and then invoke the operation on the socket + s.close(); + assertTrue(s.isClosed(), "socket isn't closed"); + assertThrows(IOException.class, () -> s.bind(loopbackEphemeral), + "bind() when already closed didn't throw IOException"); + // connect() will never get to the stage of attempting + // a connection against this port + final int dummyPort = 12345; + assertThrows(IOException.class, + () -> s.connect(new InetSocketAddress(loopback, dummyPort)), + "connect() when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> s.connect(new InetSocketAddress(loopback, dummyPort), 10), + "connect(SocketAddress, int) when already closed didn't throw IOException"); + assertThrows(IOException.class, + () -> s.getOption(StandardSocketOptions.SO_RCVBUF), + "getOption() when already closed didn't throw IOException"); + assertThrows(IOException.class, + s::getOutputStream, + "getOutputStream() when already closed didn't throw IOException"); + assertThrows(IOException.class, + s::shutdownInput, + "shutdownInput() when already closed didn't throw IOException"); + assertThrows(IOException.class, + s::shutdownOutput, + "shutdownOutput() when already closed didn't throw IOException"); + } + } + + /** + * Verifies that various operations that specify to throw a SocketOperation on a closed socket, + * do indeed throw it. + */ + @Test + public void testSocketExceptionThrown() throws Exception { + try (final Socket s = new Socket()) { + // close and then invoke the operations on the socket + s.close(); + assertTrue(s.isClosed(), "socket isn't closed"); + assertThrowsExactly(SocketException.class, + s::getKeepAlive, + "getKeepAlive() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getOOBInline, + "getOOBInline() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getReceiveBufferSize, + "getReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getReuseAddress, + "getReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getSendBufferSize, + "getSendBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getSoLinger, + "getSoLinger() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getSoTimeout, + "getSoTimeout() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getTcpNoDelay, + "getTcpNoDelay() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + s::getTrafficClass, + "getTrafficClass() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setKeepAlive(false), + "setKeepAlive() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setOOBInline(false), + "setOOBInline() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setOption(StandardSocketOptions.SO_RCVBUF, 1024), + "setOption() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setReceiveBufferSize(1024), + "setReceiveBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setReuseAddress(false), + "setReuseAddress() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setSendBufferSize(1024), + "setSendBufferSize() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setSoLinger(false, 0), + "setSoLinger() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setSoTimeout(1000), + "setSoTimeout() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setTcpNoDelay(false), + "setTcpNoDelay() when already closed didn't throw SocketException"); + assertThrowsExactly(SocketException.class, + () -> s.setTrafficClass(123), + "setTrafficClass() when already closed didn't throw SocketException"); + } + } + + /** + * Verifies that various operations that aren't expected to throw an exception on a + * closed socket, complete normally. + */ + @Test + public void testNoExceptionThrown() throws Exception { + try (final Socket s = new Socket()) { + // close and then invoke various operation on the socket and don't expect an exception + s.close(); + assertTrue(s.isClosed(), "socket isn't closed"); + s.getInetAddress(); + s.getLocalAddress(); + s.getLocalPort(); + s.getLocalSocketAddress(); + s.getPort(); + s.getRemoteSocketAddress(); + s.isBound(); + s.isConnected(); + s.isInputShutdown(); + s.isOutputShutdown(); + s.supportedOptions(); + } + } +} diff --git a/test/jdk/java/nio/Buffer/EqualsCompareTest.java b/test/jdk/java/nio/Buffer/EqualsCompareTest.java index baee641208411..03bd7c26a584a 100644 --- a/test/jdk/java/nio/Buffer/EqualsCompareTest.java +++ b/test/jdk/java/nio/Buffer/EqualsCompareTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -36,13 +37,21 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; +import java.nio.MappedByteBuffer; import java.nio.ShortBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.LongFunction; import java.util.stream.IntStream; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.file.StandardOpenOption.*; + /* * @test * @bug 8193085 8199773 @@ -713,4 +722,17 @@ static int[] ranges(int from, int to) { .distinct().toArray(); } } + + @Test + void testHashCode() throws IOException { + byte[] bytes = "hello world".getBytes(UTF_8); + Path path = Files.createTempFile("", ""); + Files.write(path, bytes); + try (FileChannel fc = FileChannel.open(path, READ, DELETE_ON_CLOSE)) { + MappedByteBuffer one = fc.map(FileChannel.MapMode.READ_ONLY, 0, bytes.length); + ByteBuffer two = ByteBuffer.wrap(bytes); + Assert.assertEquals(one, two); + Assert.assertEquals(one.hashCode(), two.hashCode()); + } + } } diff --git a/test/jdk/java/nio/channels/Selector/RaceUpdatesAndClose.java b/test/jdk/java/nio/channels/Selector/RaceUpdatesAndClose.java new file mode 100644 index 0000000000000..8173d3d761f71 --- /dev/null +++ b/test/jdk/java/nio/channels/Selector/RaceUpdatesAndClose.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8336339 + * @summary Race registration and selection key updates with Selector.close + * @run junit RaceUpdatesAndClose + */ + +import java.nio.channels.CancelledKeyException; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Phaser; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.RepeatedTest; + +class RaceUpdatesAndClose { + private static ExecutorService executor; + + @BeforeAll + static void setup() throws Exception { + executor = Executors.newFixedThreadPool(2); + } + + @AfterAll + static void finish() { + executor.shutdown(); + } + + /** + * Race SelectableChannel.register and Selector.close. + */ + @RepeatedTest(100) + void raceRegisterAndClose() throws Exception { + try (Selector sel = Selector.open(); + DatagramChannel dc = DatagramChannel.open()) { + + dc.configureBlocking(false); + + Phaser phaser = new Phaser(2); + + // register + var task1 = executor.submit(() -> { + phaser.arriveAndAwaitAdvance(); + try { + dc.register(sel, SelectionKey.OP_READ); + } catch (ClosedSelectorException e) { } + return null; + }); + + // close selector + var task2 = executor.submit(() -> { + phaser.arriveAndAwaitAdvance(); + sel.close(); + return null; + }); + + task1.get(); + task2.get(); + } + } + + /** + * Race SelectionKey.interestOps and Selector.close. + */ + @RepeatedTest(100) + void raceInterestOpsAndClose() throws Exception { + try (Selector sel = Selector.open(); + DatagramChannel dc = DatagramChannel.open()) { + + dc.configureBlocking(false); + SelectionKey key = dc.register(sel, SelectionKey.OP_READ); + + Phaser phaser = new Phaser(2); + + // interestOps + var task1 = executor.submit(() -> { + phaser.arriveAndAwaitAdvance(); + try { + key.interestOps(0); + } catch (CancelledKeyException e) { } + }); + + // close selector + var task2 = executor.submit(() -> { + phaser.arriveAndAwaitAdvance(); + sel.close(); + return null; + }); + + task1.get(); + task2.get(); + } + } +} diff --git a/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java b/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java index de8b48ec22d98..41edb207239e3 100644 --- a/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java +++ b/test/jdk/java/nio/channels/Selector/SelectWithConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -308,11 +308,10 @@ public void testTimeout() throws Exception { Pipe.SinkChannel sink = p.sink(); source.configureBlocking(false); source.register(sel, SelectionKey.OP_READ); - long start = System.currentTimeMillis(); + long start = millisTime(); int n = sel.select(k -> assertTrue(false), 1000L); - long duration = System.currentTimeMillis() - start; + expectDuration(start, 500, Long.MAX_VALUE); assertTrue(n == 0); - assertTrue(duration > 500, "select took " + duration + " ms"); } finally { closePipe(p); } @@ -332,11 +331,10 @@ public void testWakeupBeforeSelect() throws Exception { // select(Consumer, timeout) try (Selector sel = Selector.open()) { sel.wakeup(); - long start = System.currentTimeMillis(); + long start = millisTime(); int n = sel.select(k -> assertTrue(false), 60*1000); - long duration = System.currentTimeMillis() - start; + expectDuration(start, 0, 20_000); assertTrue(n == 0); - assertTrue(duration < 5000, "select took " + duration + " ms"); } } @@ -354,12 +352,10 @@ public void testWakeupDuringSelect() throws Exception { // select(Consumer, timeout) try (Selector sel = Selector.open()) { scheduleWakeup(sel, 1, SECONDS); - long start = System.currentTimeMillis(); + long start = millisTime(); int n = sel.select(k -> assertTrue(false), 60*1000); - long duration = System.currentTimeMillis() - start; + expectDuration(start, 0, 20_000); assertTrue(n == 0); - assertTrue(duration > 500 && duration < 10*1000, - "select took " + duration + " ms"); } } @@ -381,11 +377,10 @@ public void testInterruptBeforeSelect() throws Exception { // select(Consumer, timeout) try (Selector sel = Selector.open()) { Thread.currentThread().interrupt(); - long start = System.currentTimeMillis(); + long start = millisTime(); int n = sel.select(k -> assertTrue(false), 60*1000); - long duration = System.currentTimeMillis() - start; + expectDuration(start, 0, 20_000); assertTrue(n == 0); - assertTrue(duration < 5000, "select took " + duration + " ms"); assertTrue(Thread.currentThread().isInterrupted()); assertTrue(sel.isOpen()); } finally { @@ -762,4 +757,32 @@ static ByteBuffer messageBuffer() { throw new RuntimeException(e); } } + + /** + * Returns the current time in milliseconds. + */ + private static long millisTime() { + long now = System.nanoTime(); + return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS); + } + + /** + * Check the duration of a task. The method will fail with an + * AssertionError if the millisecond duration does not satisfy: + * + * duration >= min && duration <= max + * + * Note that the inequalities are not strict, i.e., are inclusive. + * + * @param start start time, in milliseconds + * @param min minimum expected duration, in milliseconds + * @param max maximum expected duration, in milliseconds + */ + private static void expectDuration(long start, long min, long max) { + long duration = millisTime() - start; + assertTrue(duration >= min, + "Duration " + duration + "ms, expected >= " + min + "ms"); + assertTrue(duration <= max, + "Duration " + duration + "ms, expected <= " + max + "ms"); + } } diff --git a/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java b/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java index 3e2648af7cb5f..dfd7dcdc81efe 100644 --- a/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java +++ b/test/jdk/java/security/cert/CertPathValidator/OCSP/OCSPTimeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,17 +36,17 @@ * java.base/sun.security.util * @library ../../../../../java/security/testlibrary * @build CertificateBuilder SimpleOCSPServer - * @run main/othervm OCSPTimeout 1000 true - * @run main/othervm -Dcom.sun.security.ocsp.readtimeout=5 - * OCSPTimeout 1000 true - * @run main/othervm -Dcom.sun.security.ocsp.readtimeout=1 - * OCSPTimeout 5000 false - * @run main/othervm -Dcom.sun.security.ocsp.readtimeout=1s - * OCSPTimeout 5000 false - * @run main/othervm -Dcom.sun.security.ocsp.readtimeout=1500ms - * OCSPTimeout 5000 false - * @run main/othervm -Dcom.sun.security.ocsp.readtimeout=4500ms - * OCSPTimeout 1000 true + * @run main/othervm -Djava.security.debug=certpath OCSPTimeout 1000 true + * @run main/othervm -Djava.security.debug=certpath + * -Dcom.sun.security.ocsp.readtimeout=5 OCSPTimeout 1000 true + * @run main/othervm -Djava.security.debug=certpath + * -Dcom.sun.security.ocsp.readtimeout=1 OCSPTimeout 5000 false + * @run main/othervm -Djava.security.debug=certpath + * -Dcom.sun.security.ocsp.readtimeout=1s OCSPTimeout 5000 false + * @run main/othervm -Djava.security.debug=certpath + * -Dcom.sun.security.ocsp.readtimeout=1500ms OCSPTimeout 5000 false + * @run main/othervm -Djava.security.debug=certpath + * -Dcom.sun.security.ocsp.readtimeout=4500ms OCSPTimeout 1000 true */ import java.io.*; @@ -82,62 +82,72 @@ public class OCSPTimeout { static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder static int rootOcspPort; // Port number for root OCSP - public static void main(String args[]) throws Exception { + public static void main(String[] args) throws Exception { int ocspTimeout = 15000; boolean expected = false; createPKI(); - if (args[0] != null) { - ocspTimeout = Integer.parseInt(args[0]); - } - rootOcsp.setDelay(ocspTimeout); - - expected = (args[1] != null && Boolean.parseBoolean(args[1])); - log("Test case expects to " + (expected ? "pass" : "fail")); - - // validate chain - CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); - PKIXRevocationChecker prc = - (PKIXRevocationChecker) cpv.getRevocationChecker(); - prc.setOptions(EnumSet.of(NO_FALLBACK, SOFT_FAIL)); - PKIXParameters params = - new PKIXParameters(Set.of(new TrustAnchor(rootCert, null))); - params.addCertPathChecker(prc); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - CertPath cp = cf.generateCertPath(List.of(eeCert)); - cpv.validate(cp, params); - - // unwrap soft fail exceptions and check for SocketTimeoutException - List softExc = prc.getSoftFailExceptions(); - if (expected) { - if (softExc.size() > 0) { - throw new RuntimeException("Expected to pass, found " + - softExc.size() + " soft fail exceptions"); + try { + if (args[0] != null) { + ocspTimeout = Integer.parseInt(args[0]); } - } else { - // If we expect to fail the validation then there should be a - // SocketTimeoutException - boolean found = false; - for (CertPathValidatorException softFail : softExc) { - log("CPVE: " + softFail); - Throwable cause = softFail.getCause(); - log("Cause: " + cause); - while (cause != null) { - if (cause instanceof SocketTimeoutException) { - found = true; - break; + rootOcsp.setDelay(ocspTimeout); + + expected = (args[1] != null && Boolean.parseBoolean(args[1])); + log("Test case expects to " + (expected ? "pass" : "fail")); + + // validate chain + CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); + PKIXRevocationChecker prc = + (PKIXRevocationChecker) cpv.getRevocationChecker(); + prc.setOptions(EnumSet.of(NO_FALLBACK, SOFT_FAIL)); + PKIXParameters params = + new PKIXParameters(Set.of(new TrustAnchor(rootCert, null))); + params.addCertPathChecker(prc); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + CertPath cp = cf.generateCertPath(List.of(eeCert)); + cpv.validate(cp, params); + + // unwrap soft fail exceptions and check for SocketTimeoutException + List softExc = prc.getSoftFailExceptions(); + if (expected) { + if (!softExc.isEmpty()) { + log("Expected to pass, found " + softExc.size() + + " soft fail exceptions"); + for (CertPathValidatorException cpve : softExc) { + log("Exception: " + cpve); } - cause = cause.getCause(); + throw new RuntimeException("Expected to pass, found " + + softExc.size() + " soft fail exceptions"); } - if (found) { - break; + } else { + // If we expect to fail the validation then there should be a + // SocketTimeoutException + boolean found = false; + for (CertPathValidatorException softFail : softExc) { + log("CPVE: " + softFail); + Throwable cause = softFail.getCause(); + log("Cause: " + cause); + while (cause != null) { + if (cause instanceof SocketTimeoutException) { + found = true; + break; + } + cause = cause.getCause(); + } + if (found) { + break; + } } - } - if (!found) { - throw new RuntimeException("SocketTimeoutException not thrown"); + if (!found) { + throw new RuntimeException("SocketTimeoutException not thrown"); + } } + } finally { + rootOcsp.stop(); + rootOcsp.shutdownNow(); } } diff --git a/test/jdk/java/security/testlibrary/SimpleOCSPServer.java b/test/jdk/java/security/testlibrary/SimpleOCSPServer.java index e1883edeec51f..5b9fb23c4ef90 100644 --- a/test/jdk/java/security/testlibrary/SimpleOCSPServer.java +++ b/test/jdk/java/security/testlibrary/SimpleOCSPServer.java @@ -572,8 +572,8 @@ public void setDisableContentLength(boolean isDisabled) { */ private synchronized void log(String message) { if (logEnabled || debug != null) { - System.out.println("[" + Thread.currentThread().getName() + "]: " + - message); + System.out.println("[" + Thread.currentThread().getName() + "][" + + System.currentTimeMillis() + "]: " + message); } } @@ -727,6 +727,7 @@ public void run() { // wait out the delay here before any other processing. try { if (delayMsec > 0) { + log("Delaying response for " + delayMsec + " milliseconds."); Thread.sleep(delayMsec); } } catch (InterruptedException ie) { @@ -908,6 +909,13 @@ private LocalOcspRequest parseHttpOcspPost(InputStream inStream) */ private LocalOcspRequest parseHttpOcspGet(String[] headerTokens, InputStream inStream) throws IOException, CertificateException { + // Display the whole request + StringBuilder sb = new StringBuilder("OCSP GET REQUEST\n"); + for (String hTok : headerTokens) { + sb.append(hTok).append("\n"); + } + log(sb.toString()); + // Before we process the remainder of the GET URL, we should drain // the InputStream of any other header data. We (for now) won't // use it, but will display the contents if logging is enabled. @@ -1000,6 +1008,10 @@ private LocalOcspRequest(byte[] requestBytes) throws IOException, CertificateException { Objects.requireNonNull(requestBytes, "Received null input"); + // Display the DER encoding before parsing + log("Local OCSP Request Constructor, parsing bytes:\n" + + dumpHexBytes(requestBytes)); + DerInputStream dis = new DerInputStream(requestBytes); // Parse the top-level structure, it should have no more than diff --git a/test/jdk/java/util/Locale/LocaleTest.java b/test/jdk/java/util/Locale/LocaleTest.java index c6afbc099ae75..0cf272f20a0d2 100644 --- a/test/jdk/java/util/Locale/LocaleTest.java +++ b/test/jdk/java/util/Locale/LocaleTest.java @@ -20,12 +20,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/** +/* * @test * @bug 4052404 4052440 4084688 4092475 4101316 4105828 4107014 4107953 4110613 * 4118587 4118595 4122371 4126371 4126880 4135316 4135752 4139504 4139940 4143951 * 4147315 4147317 4147552 4335196 4778440 4940539 5010672 6475525 6544471 6627549 * 6786276 7066203 7085757 8008577 8030696 8170840 8174269 8255086 8263202 8287868 + * 8337603 * @summary test Locales * @modules jdk.localedata * @run junit LocaleTest @@ -84,9 +85,13 @@ import java.util.List; import java.util.Locale; import java.util.MissingResourceException; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; - +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; public class LocaleTest { @@ -734,6 +739,32 @@ public void TestChangedISO639Codes() { } + /** + * @bug 8337603 + */ + static Stream changedISOCodes() { + var hebrew = "\u05e2\u05d1\u05e8\u05d9\u05ea"; + var yiddish = "\u05d9\u05d9\u05b4\u05d3\u05d9\u05e9"; + var indonesian = "Indonesia"; + + return Stream.of( + Arguments.of("he", hebrew), + Arguments.of("iw", hebrew), + Arguments.of("yi", yiddish), + Arguments.of("ji", yiddish), + Arguments.of("id", indonesian), + Arguments.of("in", indonesian) + ); + } + @ParameterizedTest + @MethodSource("changedISOCodes") + public void TestOldISOCodeLanguageName(String code, String expected) { + var loc = Locale.of(code); + assertEquals(expected, + loc.getDisplayName(loc), + "java.locale.useOldISOCodes=" + System.getProperty("java.locale.useOldISOCodes")); + } + /** * @bug 4092475 * I could not reproduce this bug. I'm pretty convinced it was fixed with the diff --git a/test/jdk/javax/swing/JColorChooser/Test6977726.java b/test/jdk/javax/swing/JColorChooser/Test6977726.java index a79931c93eb55..1ec72036f14d3 100644 --- a/test/jdk/javax/swing/JColorChooser/Test6977726.java +++ b/test/jdk/javax/swing/JColorChooser/Test6977726.java @@ -41,7 +41,14 @@ public static void main(String[] args) throws Exception { String instructions = """ Check that there is a panel with "Text Preview Panel" text and with title "Preview" in the JColorChooser. - Test passes if the panel is as described, test fails otherwise."""; + Test passes if the panel is as described, test fails otherwise. + + Note: "Preview" title is not applicable for GTK Look and Feel."""; + + // In case this test is run with GTK L&F, the preview panel title + // is missing due to the "ColorChooser.showPreviewPanelText" property + // which is set to "Boolean.FALSE" for GTK L&F. Test instructions are + // modified to reflect that "Preview" title is not applicable for GTK L&F. PassFailJFrame.builder() .title("Test6977726") diff --git a/test/jdk/javax/swing/JOptionPane/OptionPaneInput.java b/test/jdk/javax/swing/JOptionPane/OptionPaneInput.java new file mode 100644 index 0000000000000..998efccf1f3ce --- /dev/null +++ b/test/jdk/javax/swing/JOptionPane/OptionPaneInput.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Canvas; +import java.awt.Color; +import java.awt.Graphics2D; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 8235404 + * @summary Checks that JOptionPane doesn't block drawing strings on another component + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual OptionPaneInput + */ +public class OptionPaneInput { + private static JFrame f; + private static Canvas c; + private static JTextField t; + private static final String instructions = """ + 1. Type "test" into the message dialog. + 2. Press enter key. + 3. Press OK button. + 4. If the OptionPaneInput frame has test drawn on it, pass. Otherwise fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame testFrame = new PassFailJFrame(instructions); + + SwingUtilities.invokeAndWait(() -> createGUI()); + testFrame.awaitAndCheck(); + } + + public static void createGUI() { + f = new JFrame("OptionPaneInput"); + c = new Canvas(); + t = new JTextField(); + f.add(c); + + t.addActionListener(e -> { + String text = t.getText(); + Graphics2D g2 = (Graphics2D)(c.getGraphics()); + g2.setColor(Color.BLACK); + g2.drawString(text, 10, 10); + System.out.println("drawing "+text); + g2.dispose(); + }); + + f.setSize(300, 100); + PassFailJFrame.addTestWindow(f); + PassFailJFrame.positionTestWindow(f, PassFailJFrame.Position.HORIZONTAL); + f.setVisible(true); + + JOptionPane.showMessageDialog(f, t); + } +} diff --git a/test/jdk/javax/swing/JSplitPane/TestSplitPaneOrientationTest.java b/test/jdk/javax/swing/JSplitPane/TestSplitPaneOrientationTest.java new file mode 100644 index 0000000000000..01dfebcb5c276 --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/TestSplitPaneOrientationTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 4265389 + * @summary Verifies JSplitPane support ComponentOrientation + * @run main TestSplitPaneOrientationTest + */ + +import java.awt.ComponentOrientation; +import javax.swing.JButton; +import javax.swing.JSplitPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +public class TestSplitPaneOrientationTest { + + private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (UnsupportedLookAndFeelException ignored) { + System.out.println("Unsupported LAF: " + laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws Exception { + for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { + System.out.println("Testing LAF : " + laf.getClassName()); + + SwingUtilities.invokeAndWait(() -> { + setLookAndFeel(laf); + JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + new JButton("Left"), new JButton("Right")); + jsp.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + if (jsp.getRightComponent() instanceof JButton button) { + System.out.println(button.getText()); + if (!button.getText().equals("Left")) { + throw new RuntimeException("JSplitPane did not support ComponentOrientation"); + } + } + }); + } + } + +} + diff --git a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK.java b/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK.java index a8d460bc48e0b..906126071ff1d 100644 --- a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK.java +++ b/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ @bug 8156121 @key headful @requires os.family == "linux" - @run main/othervm -Djdk.gtk.version=2 DemandGTK @run main/othervm -Djdk.gtk.version=3 DemandGTK */ diff --git a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK2.sh b/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK2.sh deleted file mode 100644 index eddae8748a7d1..0000000000000 --- a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK2.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/ksh -p - -# -# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. -# 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 Try to force GTK2. We must bail out to GTK3 (if any) if no 2 available. -# -# @key headful -# @bug 8156128 8212903 -# @compile ProvokeGTK.java -# @requires os.family == "linux" -# @run shell/timeout=400 DemandGTK2.sh - -# -# Note that we depend on -# strace in the PATH -# /sbin/ldconfig (which may be not in PATH) -# It is true for OEL 7 and Ubuntu 14, 16 -# but may fail in future. Save tomorrow for tomorrow. -# -# Read DemandGTK2.txt how to prepare GTK2-less machine. -# - -which strace -if [ $? -ne 0 ] -then - echo "Please provide strace: \"which strace\" failed." - exit 1 -fi - -HAVE_2=`/sbin/ldconfig -v 2>/dev/null | grep libgtk-x11-2 | wc -l` -HAVE_3=`/sbin/ldconfig -v 2>/dev/null | grep libgtk-3.so | wc -l` - - -if [ "${HAVE_2}" = "0" ] -then - - if [ "${HAVE_3}" = "0" ] - then - echo "Neither GTK2 nor GTK3 found: system misconfiguration. Exit." - exit 1 - fi - echo "No GTK 2 library found: we should bail out to 3" - strace -o strace.log -fe open,openat ${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} -Djdk.gtk.version=2 ProvokeGTK - EXECRES=$? - grep 'libgtk-3.*=\ *[0-9]*$' strace.log > logg -else - echo "There is GTK 2 library: we should use it" - strace -o strace.log -fe open,openat ${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} -Djdk.gtk.version=2 ProvokeGTK - EXECRES=$? - grep 'libgtk-x11.*=\ *[0-9]*$' strace.log > logg -fi - -if [ ${EXECRES} -ne 0 ] -then - echo "java execution failed for unknown reason, see logs" - exit 2 -fi - -cat logg -if [ -s logg ] -then - echo "Success." - exit 0 -else - echo "Failed. Examine logs." - exit 3 -fi - - diff --git a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK2.txt b/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK2.txt deleted file mode 100644 index 7313e3ee4a754..0000000000000 --- a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK2.txt +++ /dev/null @@ -1,36 +0,0 @@ -How to prepare an Ubuntu machine for GTK-2-less test run. - -The test DemandGTK2.sh should work well without GTK-2 switching to version 3 -if there's no GTK-2 library available. -At the moment, it's not easy to find a system with GTK-3 and without GTK-2: -many programs still depend on version 2. -We can, however, rename GTK-2 library for a single test run and then restore -it back. - -(1) Find GTK2 library: run - /sbin/ldconfig -v 2>/dev/null | grep libgtk-x11-2 - -It will output one or two lines like -libgtk-x11-2.0.so.0 -> libgtk-x11-2.0.so.0.2400.23 -Search for the target of that symlink for instance with locate: -locate libgtk-x11-2.0.so.0.2400.23 -Finally, you'll find the libraries. On my current machine they are -/usr/lib/i386-linux-gnu/libgtk-x11-2.0.so.0.2400.23 -/usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0.2400.23 - -I'm running 64-bit JDK and need to tamper with x86_64 copy only. - -(2) Find running programs depending on this library. They probably would crash -if you rename it. Stop them for this test run. -That said, I'm afraid it would be impossible to do on a system older than Ubuntu 16.04. -On my Ubuntu 16.04 I have only hud-service using this library, and that's OK, it will restart -after a crash, if any. -To find these programs, run -lsof /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0.2400.23 - -(3) Now, -sudo mv /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0.2400.23 /usr/lib/x86_64-linux-gnu/bak.libgtk-x11-2.0.so.0.2400.23 -jtreg DemandGTK2.sh -sudo mv /usr/lib/x86_64-linux-gnu/bak.libgtk-x11-2.0.so.0.2400.23 /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0.2400.23 - -Needless to say, you should substitute your own library path and however you run jtreg. diff --git a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK3.sh b/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK3.sh index bb00493c8622f..2383cc828519b 100644 --- a/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK3.sh +++ b/test/jdk/javax/swing/LookAndFeel/8145547/DemandGTK3.sh @@ -1,7 +1,7 @@ #!/bin/ksh -p # -# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ # @test -# @summary Try to force GTK3. We must bail out to GTK2 if no 3 available. +# @summary Try to force GTK3. # # @key headful # @bug 8156128 8212903 @@ -53,11 +53,8 @@ HAVE_3=`/sbin/ldconfig -v 2>/dev/null | grep libgtk-3.so | wc -l` if [ "${HAVE_3}" = "0" ] then - - echo "No GTK 3 library found: we should bail out to 2" - strace -o strace.log -fe open,openat ${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} -Djdk.gtk.version=3 ProvokeGTK - EXECRES=$? - grep 'libgtk-x11.*=\ *[0-9]*$' strace.log > logg + echo "No GTK 3 library found, do nothing" + exit 0 else echo "There is GTK 3 library: we should use it" strace -o strace.log -fe open,openat ${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} -Djdk.gtk.version=3 ProvokeGTK diff --git a/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java new file mode 100644 index 0000000000000..318626e5110a6 --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.text.View; +import javax.swing.text.html.CSS; + +/* + * @test + * @bug 8335967 + * @summary Tests 'text-decoration: none' is respected + * @run main HTMLTextDecorationNone + */ +public final class HTMLTextDecorationNone { + private static final String HTML = """ + + + + + text-decoration: none (<a>) + + + +

      underlined

      +

      not underlined

      +

      not underlined

      +

      underlined?

      +

      underlined?

      + + + """; + + private static final boolean[] underlined = {true, false, false, true, true}; + + public static void main(String[] args) { + final JEditorPane html = new JEditorPane("text/html", HTML); + html.setEditable(false); + + final Dimension size = html.getPreferredSize(); + html.setSize(size); + + BufferedImage image = new BufferedImage(size.width, size.height, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.createGraphics(); + // Paint the editor pane to ensure all views are created + html.paint(g); + g.dispose(); + + int errorCount = 0; + String firstError = null; + + System.out.println("----- Views -----"); + final View bodyView = html.getUI() + .getRootView(html) + .getView(1) + .getView(1); + for (int i = 0; i < bodyView.getViewCount(); i++) { + View pView = bodyView.getView(i); + View contentView = getContentView(pView); + + Object decorationAttr = + contentView.getAttributes() + .getAttribute(CSS.Attribute.TEXT_DECORATION); + String decoration = decorationAttr == null + ? "none" : decorationAttr.toString(); + + System.out.println(i + ": " + decoration); + if (decoration.contains("underline") != underlined[i]) { + errorCount++; + if (firstError == null) { + firstError = "Line " + i + ": " + decoration + " vs " + + (underlined[i] ? "underline" : "none"); + } + } + } + + if (errorCount > 0) { + saveImage(image); + throw new RuntimeException(errorCount + " error(s) found, " + + "the first one: " + firstError); + } + } + + private static View getContentView(View parent) { + View view = parent.getView(0); + return view.getViewCount() > 0 + ? getContentView(view) + : view; + } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", + new File("html.png")); + } catch (IOException ignored) { } + } +} diff --git a/test/jdk/jdk/classfile/AccessFlagsTest.java b/test/jdk/jdk/classfile/AccessFlagsTest.java index 97c0d97f8f9f1..379dcddb9c86c 100644 --- a/test/jdk/jdk/classfile/AccessFlagsTest.java +++ b/test/jdk/jdk/classfile/AccessFlagsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,9 @@ * @summary Testing ClassFile AccessFlags. * @run junit AccessFlagsTest */ +import java.lang.classfile.ClassFile; +import java.lang.constant.ClassDesc; +import java.util.Arrays; import java.util.EnumSet; import java.util.Random; import java.util.Set; @@ -34,6 +37,10 @@ import java.util.function.IntFunction; import java.lang.reflect.AccessFlag; import java.lang.classfile.AccessFlags; + +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.CD_int; +import static java.lang.constant.ConstantDescs.MTD_void; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.EnumSource; @@ -45,15 +52,38 @@ class AccessFlagsTest { @EnumSource(names = { "CLASS", "METHOD", "FIELD" }) void testRandomAccessFlagsConverions(AccessFlag.Location ctx) { IntFunction intFactory = switch (ctx) { - case CLASS -> AccessFlags::ofClass; - case METHOD -> AccessFlags::ofMethod; - case FIELD -> AccessFlags::ofField; + case CLASS -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> clb.withFlags(v)); + return ClassFile.of().parse(bytes).flags(); + }; + case METHOD -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withMethod("test", MTD_void, v & ACC_STATIC, mb -> mb.withFlags(v))); + return ClassFile.of().parse(bytes).methods().getFirst().flags(); + }; + case FIELD -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withField("test", CD_int, fb -> fb.withFlags(v))); + return ClassFile.of().parse(bytes).fields().getFirst().flags(); + }; default -> null; }; Function flagsFactory = switch (ctx) { - case CLASS -> AccessFlags::ofClass; - case METHOD -> AccessFlags::ofMethod; - case FIELD -> AccessFlags::ofField; + case CLASS -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> clb.withFlags(v)); + return ClassFile.of().parse(bytes).flags(); + }; + case METHOD -> v -> { + boolean hasStatic = Arrays.stream(v).anyMatch(f -> f == AccessFlag.STATIC); + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withMethod("test", MTD_void, hasStatic ? ACC_STATIC : 0, mb -> mb.withFlags(v))); + return ClassFile.of().parse(bytes).methods().getFirst().flags(); + }; + case FIELD -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withField("test", CD_int, fb -> fb.withFlags(v))); + return ClassFile.of().parse(bytes).fields().getFirst().flags(); + }; default -> null; }; @@ -72,11 +102,11 @@ void testRandomAccessFlagsConverions(AccessFlag.Location ctx) { @Test void testInvalidFlagsUse() { - assertAll( - () -> assertThrowsForInvalidFlagsUse(AccessFlags::ofClass), - () -> assertThrowsForInvalidFlagsUse(AccessFlags::ofField), - () -> assertThrowsForInvalidFlagsUse(AccessFlags::ofMethod) - ); + ClassFile.of().build(ClassDesc.of("Test"), clb -> { + assertThrowsForInvalidFlagsUse(clb::withFlags); + clb.withMethod("test", MTD_void, ACC_STATIC, mb -> assertThrowsForInvalidFlagsUse(mb::withFlags)); + clb.withField("test", CD_int, fb -> assertThrowsForInvalidFlagsUse(fb::withFlags)); + }); } void assertThrowsForInvalidFlagsUse(Consumer factory) { diff --git a/test/jdk/jdk/classfile/AnnotationTest.java b/test/jdk/jdk/classfile/AnnotationTest.java index 0226affe7c260..4ed3b2141ad47 100644 --- a/test/jdk/jdk/classfile/AnnotationTest.java +++ b/test/jdk/jdk/classfile/AnnotationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -207,4 +207,27 @@ void testAnnosNoCPB() { assertAnno(mannos.get(0), "LAnno;", true); assertAnno(fannos.get(0), "LAnno;", true); } + + @Test + void testEquality() { + assertEquals(Annotation.of(CD_Object), Annotation.of(ClassDesc.of("java.lang.Object"))); + assertNotEquals(Annotation.of(CD_Object), Annotation.of(CD_String)); + assertEquals(Annotation.of(CD_Object, AnnotationElement.of("fly", AnnotationValue.ofInt(5))), + Annotation.of(CD_Object, AnnotationElement.ofInt("fly", 5))); + assertEquals(AnnotationElement.ofFloat("one", 1.2F), + AnnotationElement.ofFloat("one", 1.2F)); + assertEquals(AnnotationElement.ofFloat("one", 1.2F), + AnnotationElement.of("one", AnnotationValue.ofFloat(1.2F))); + assertNotEquals(AnnotationElement.ofFloat("one", 1.2F), + AnnotationElement.ofFloat("two", 1.2F)); + assertNotEquals(AnnotationElement.ofFloat("one", 1.2F), + AnnotationElement.ofFloat("one", 2.1F)); + assertNotEquals(AnnotationElement.ofFloat("one", 1.2F), + AnnotationElement.ofDouble("one", 1.2F)); + assertEquals(AnnotationValue.ofInt(23), AnnotationValue.ofInt(23)); + assertNotEquals(AnnotationValue.ofInt(23), AnnotationValue.ofInt(42)); + assertNotEquals(AnnotationValue.ofInt(23), AnnotationValue.ofLong(23)); + assertEquals(AnnotationValue.ofAnnotation(Annotation.of(CD_Object)), + AnnotationValue.ofAnnotation(Annotation.of(Object.class.describeConstable().orElseThrow()))); + } } diff --git a/test/jdk/jdk/classfile/BoundAttributeTest.java b/test/jdk/jdk/classfile/BoundAttributeTest.java index f6d2d0e9010a1..6a164bec2f9d7 100644 --- a/test/jdk/jdk/classfile/BoundAttributeTest.java +++ b/test/jdk/jdk/classfile/BoundAttributeTest.java @@ -27,6 +27,7 @@ * @summary Testing BoundAttributes * @run junit BoundAttributeTest */ +import jdk.internal.classfile.impl.BufWriterImpl; import jdk.internal.classfile.impl.DirectClassBuilder; import jdk.internal.classfile.impl.UnboundAttribute; import org.junit.jupiter.api.Assertions; @@ -34,7 +35,6 @@ import org.opentest4j.AssertionFailedError; import java.lang.classfile.Attributes; -import java.lang.classfile.BufWriter; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; @@ -84,7 +84,7 @@ void testBadEntryTypeInList() { var oneClass = cp.classEntry(oneClassString); ((DirectClassBuilder) clb).writeAttribute(new UnboundAttribute.AdHocAttribute<>(Attributes.nestMembers()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(2); b.writeIndex(oneClass); b.writeIndex(oneClassString); diff --git a/test/jdk/jdk/classfile/BuilderBlockTest.java b/test/jdk/jdk/classfile/BuilderBlockTest.java index c8e13b79f7280..30a44491e6cea 100644 --- a/test/jdk/jdk/classfile/BuilderBlockTest.java +++ b/test/jdk/jdk/classfile/BuilderBlockTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,36 +23,49 @@ /* * @test + * @bug 8337225 * @summary Testing ClassFile builder blocks. * @run junit BuilderBlockTest */ +import java.io.IOException; +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeTransform; +import java.lang.classfile.Label; +import java.lang.classfile.MethodModel; +import java.lang.classfile.MethodTransform; +import java.lang.classfile.Opcode; +import java.lang.classfile.TypeKind; +import java.lang.classfile.constantpool.StringEntry; +import java.lang.classfile.instruction.ConstantInstruction; import java.lang.constant.ClassDesc; - -import static java.lang.constant.ConstantDescs.*; - import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.AccessFlag; import java.lang.reflect.Method; +import java.net.URI; import java.nio.file.Path; -import java.nio.file.Paths; import helpers.ByteArrayClassLoader; -import java.lang.classfile.AccessFlags; -import java.lang.reflect.AccessFlag; -import java.lang.classfile.ClassFile; -import java.lang.classfile.Label; -import java.lang.classfile.Opcode; -import java.lang.classfile.TypeKind; import jdk.internal.classfile.impl.LabelImpl; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.CD_int; +import static java.lang.constant.ConstantDescs.CD_void; + +import static org.junit.jupiter.api.Assertions.*; + /** * BuilderBlockTest */ class BuilderBlockTest { - static final String testClassName = "AdaptCodeTest$TestClass"; - static final Path testClassPath = Paths.get("target/test-classes/" + testClassName + ".class"); + static final String testClassName = "BuilderBlockTest$TestClass"; + static final Path testClassPath = Path.of(URI.create(BuilderBlockTest.class.getResource(testClassName + ".class").toString())); @Test void testStartEnd() throws Exception { @@ -104,7 +117,7 @@ void testIfThenReturn() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThen(xxb -> xxb.iconst_1().ireturn()) .iconst_2() @@ -123,7 +136,7 @@ void testIfThenElseReturn() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThenElse(xxb -> xxb.iconst_1().ireturn(), xxb -> xxb.iconst_2().ireturn()))); @@ -141,7 +154,7 @@ void testIfThenBadOpcode() { ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> { xb.iload(0); xb.iload(1); @@ -161,7 +174,7 @@ void testIfThenElseImplicitBreak() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThenElse(xxb -> xxb.iconst_1().istore(2), xxb -> xxb.iconst_2().istore(2)) @@ -181,7 +194,7 @@ void testIfThenElseExplicitBreak() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThenElse(xxb -> xxb.iconst_1().istore(2).goto_(xxb.breakLabel()), xxb -> xxb.iconst_2().istore(2).goto_(xxb.breakLabel())) @@ -200,7 +213,7 @@ void testIfThenElseOpcode() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .iload(1) @@ -225,7 +238,7 @@ void testIfThenElseBadOpcode() { ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> { xb.iload(0); xb.iload(1); @@ -305,4 +318,81 @@ void testAllocateLocalIfThen() { })); }); } + + private static final CodeTransform ALLOCATE_LOCAL_EXAMINER = CodeTransform.ofStateful(() -> new CodeTransform() { + boolean foundItem = false; + + @Override + public void atStart(CodeBuilder builder) { + foundItem = false; + } + + @Override + public void accept(CodeBuilder cob, CodeElement coe) { + cob.with(coe); + if (coe instanceof ConstantInstruction.LoadConstantInstruction ldc + && ldc.constantEntry() instanceof StringEntry se + && se.utf8().equalsString("Output")) { + assertFalse(foundItem); + foundItem = true; + var i = cob.allocateLocal(TypeKind.IntType); + assertEquals(7, i, "Allocated new int slot"); + } + } + + @Override + public void atEnd(CodeBuilder builder) { + assertTrue(foundItem); + } + }); + + // Test updating local variable slot management from + // source code models in transformingCode; + // CodeBuilder.transform(CodeModel, CodeTransform) is + // not managed for now + @Test + void testAllocateLocalTransformingCodeAttribute() throws IOException { + var cf = ClassFile.of(); + var code = cf.parse(testClassPath) + .methods() + .stream() + .filter(f -> f.methodName().equalsString("work")) + .findFirst() + .orElseThrow() + .findAttribute(Attributes.code()) + .orElseThrow(); + ClassFile.of().build(ClassDesc.of("Foo"), cb -> cb + .withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), 0, mb -> mb + .transformCode(code, ALLOCATE_LOCAL_EXAMINER))); + } + + @Test + void testAllocateLocalTransformingBufferedCode() throws IOException { + var cf = ClassFile.of(); + var testClass = cf.parse(testClassPath); + ClassTransform bufferingTransform = (clb, cle) -> { + if (cle instanceof MethodModel mm && mm.methodName().equalsString("work")) { + clb.withMethodBody(mm.methodName(), mm.methodType(), mm.flags().flagsMask(), cob -> { + int d = cob.allocateLocal(TypeKind.IntType); + int e = cob.allocateLocal(TypeKind.IntType); + + assertEquals(5, d); + assertEquals(6, e); + + mm.code().ifPresent(code -> code.forEach(cob)); + }); + } + }; + cf.transformClass(testClass, bufferingTransform.andThen(ClassTransform.transformingMethods(MethodTransform.transformingCode(ALLOCATE_LOCAL_EXAMINER)))); + } + + public static class TestClass { + public void work(int a, long b, int c) { + int d = Math.addExact(a, 25); + int e = Math.multiplyExact(d, c); + System.out.println("Output"); + System.out.println(e + b); + throw new IllegalArgumentException("foo"); + } + } } diff --git a/test/jdk/jdk/classfile/BuilderTryCatchTest.java b/test/jdk/jdk/classfile/BuilderTryCatchTest.java index 666b36e11a796..192125c4bb6f2 100644 --- a/test/jdk/jdk/classfile/BuilderTryCatchTest.java +++ b/test/jdk/jdk/classfile/BuilderTryCatchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ * @run junit BuilderTryCatchTest */ -import java.lang.classfile.AccessFlags; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CompoundElement; @@ -35,6 +34,9 @@ import java.lang.classfile.TypeKind; import java.lang.classfile.instruction.BranchInstruction; import java.lang.classfile.instruction.ExceptionCatch; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -43,7 +45,6 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.AccessFlag; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -184,7 +185,7 @@ void testTryEmptyCatch() { void testEmptyTry() { byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { + ACC_PUBLIC | ACC_STATIC, mb -> { mb.withCode(xb -> { int stringSlot = xb.allocateLocal(TypeKind.ReferenceType); xb.loadConstant("S"); @@ -215,7 +216,7 @@ void testEmptyTry() { void testLocalAllocation() throws Throwable { byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { + ACC_PUBLIC | ACC_STATIC, mb -> { mb.withCode(xb -> { int stringSlot = xb.allocateLocal(TypeKind.ReferenceType); xb.loadConstant("S"); @@ -278,7 +279,7 @@ void testLocalAllocation() throws Throwable { static byte[] generateTryCatchMethod(Consumer c) { byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { + ACC_PUBLIC | ACC_STATIC, mb -> { mb.withCode(xb -> { int stringSlot = xb.allocateLocal(TypeKind.ReferenceType); xb.loadConstant("S"); diff --git a/test/jdk/jdk/classfile/CorpusTest.java b/test/jdk/jdk/classfile/CorpusTest.java index 2dfbef10d6709..631e2f8afaa1b 100644 --- a/test/jdk/jdk/classfile/CorpusTest.java +++ b/test/jdk/jdk/classfile/CorpusTest.java @@ -31,12 +31,15 @@ import helpers.ClassRecord; import helpers.ClassRecord.CompatibilityFilter; import helpers.Transforms; +import jdk.internal.classfile.impl.BufWriterImpl; +import jdk.internal.classfile.impl.Util; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; import java.io.ByteArrayInputStream; +import java.lang.classfile.attribute.CodeAttribute; import java.util.*; import static helpers.ClassRecord.assertEqualsDeep; @@ -85,7 +88,7 @@ static void splitTableAttributes(String sourceClassFile, String targetClassFile) switch (coe) { case LineNumber ln -> dcob.writeAttribute(new UnboundAttribute.AdHocAttribute<>(Attributes.lineNumberTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(1); b.writeU2(curPc); b.writeU2(ln.line()); @@ -93,16 +96,16 @@ public void writeBody(BufWriter b) { }); case LocalVariable lv -> dcob.writeAttribute(new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(1); - lv.writeTo(b); + Util.writeLocalVariable(b, lv); } }); case LocalVariableType lvt -> dcob.writeAttribute(new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTypeTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(1); - lvt.writeTo(b); + Util.writeLocalVariable(b, lvt); } }); default -> cob.with(coe); @@ -220,9 +223,11 @@ void testNullAdaptations(Path path) throws Exception { var m1 = itStack.next(); var m2 = itNoStack.next(); var text1 = m1.methodName().stringValue() + m1.methodType().stringValue() + ": " - + m1.code().map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); + + m1.code().map(CodeAttribute.class::cast) + .map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); var text2 = m2.methodName().stringValue() + m2.methodType().stringValue() + ": " - + m2.code().map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); + + m2.code().map(CodeAttribute.class::cast) + .map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); assertEquals(text1, text2); } assertFalse(itNoStack.hasNext()); diff --git a/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java b/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java index 9070f0b1d4513..5e51e585634d0 100644 --- a/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java +++ b/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java @@ -26,6 +26,7 @@ * @summary Testing ClassFile handling JSR and RET instructions. * @run junit DiscontinuedInstructionsTest */ +import java.lang.classfile.attribute.CodeAttribute; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.ArrayList; @@ -63,7 +64,7 @@ void testJsrAndRetProcessing() throws Exception { .pop() .with(DiscontinuedInstruction.RetInstruction.of(355)))); - var c = cc.parse(bytes).methods().get(0).code().get(); + var c = (CodeAttribute) cc.parse(bytes).methods().get(0).code().get(); assertEquals(356, c.maxLocals()); assertEquals(6, c.maxStack()); diff --git a/test/jdk/jdk/classfile/LDCTest.java b/test/jdk/jdk/classfile/LDCTest.java index 1e7639de4bb99..7379218840e30 100644 --- a/test/jdk/jdk/classfile/LDCTest.java +++ b/test/jdk/jdk/classfile/LDCTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,9 @@ * @run junit LDCTest */ import java.lang.constant.ClassDesc; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; @@ -38,7 +41,6 @@ import org.junit.jupiter.api.Test; import static helpers.TestConstants.MTD_VOID; import static java.lang.classfile.Opcode.*; -import static java.lang.classfile.TypeKind.VoidType; import java.lang.classfile.instruction.ConstantInstruction; class LDCTest { @@ -56,7 +58,7 @@ void testLDCisConvertedToLDCW() throws Exception { ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { ConstantPoolBuilder cpb = cb.constantPool(); for (int i = 0; i <= 256/2 + 2; i++) { // two entries per String diff --git a/test/jdk/jdk/classfile/LimitsTest.java b/test/jdk/jdk/classfile/LimitsTest.java index 442707ecd1700..9c7b8d9e72d4a 100644 --- a/test/jdk/jdk/classfile/LimitsTest.java +++ b/test/jdk/jdk/classfile/LimitsTest.java @@ -23,12 +23,11 @@ /* * @test - * @bug 8320360 8330684 8331320 8331655 8331940 8332486 8335820 + * @bug 8320360 8330684 8331320 8331655 8331940 8332486 8335820 8336833 * @summary Testing ClassFile limits. * @run junit LimitsTest */ import java.lang.classfile.Attributes; -import java.lang.classfile.BufWriter; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; @@ -44,6 +43,8 @@ import java.lang.classfile.constantpool.IntegerEntry; import java.lang.classfile.instruction.LocalVariable; import java.util.List; + +import jdk.internal.classfile.impl.BufWriterImpl; import jdk.internal.classfile.impl.DirectCodeBuilder; import jdk.internal.classfile.impl.DirectMethodBuilder; import jdk.internal.classfile.impl.LabelContext; @@ -130,7 +131,7 @@ void testInvalidLookupSwitch() { "lookupSwitchMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, mb -> ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.code()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(-1);//max stack b.writeU2(-1);//max locals b.writeInt(16); @@ -155,7 +156,7 @@ void testInvalidTableSwitch() { "tableSwitchMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, mb -> ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.code()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(-1);//max stack b.writeU2(-1);//max locals b.writeInt(16); @@ -168,6 +169,28 @@ public void writeBody(BufWriter b) { b.writeU2(0);//exception handlers b.writeU2(0);//attributes }})))).methods().get(0).code().get().elementList()); + assertThrows(IllegalArgumentException.class, () -> + ClassFile.of().parse(ClassFile.of().build(ClassDesc.of("TableSwitchClass"), cb -> cb.withMethod( + "tableSwitchMethod", MethodTypeDesc.of(ConstantDescs.CD_void), 0, mb -> + ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.code()) { + @Override + public void writeBody(BufWriterImpl b) { + b.writeU2(-1);//max stack + b.writeU2(-1);//max locals + b.writeInt(20); + b.writeU1(Opcode.NOP.bytecode()); + b.writeU1(Opcode.NOP.bytecode()); + b.writeU1(Opcode.NOP.bytecode()); + b.writeU1(Opcode.NOP.bytecode()); + b.writeU1(Opcode.TABLESWITCH.bytecode()); + b.writeU1(0); //padding + b.writeU2(0); //padding + b.writeInt(0); //default + b.writeInt(Integer.MIN_VALUE); //low + b.writeInt(Integer.MAX_VALUE - 4); //high to jump back and cause infinite loop + b.writeU2(0);//exception handlers + b.writeU2(0);//attributes + }})))).methods().get(0).code().get().elementList()); } @Test diff --git a/test/jdk/jdk/classfile/LowAdaptTest.java b/test/jdk/jdk/classfile/LowAdaptTest.java index 46d033a8676e8..d47141a885a26 100644 --- a/test/jdk/jdk/classfile/LowAdaptTest.java +++ b/test/jdk/jdk/classfile/LowAdaptTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,9 @@ * @run junit LowAdaptTest */ import java.lang.constant.ClassDesc; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicCallSiteDesc; @@ -35,15 +38,11 @@ import java.net.URI; import java.nio.file.Paths; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; -import java.lang.classfile.Opcode; -import java.lang.classfile.TypeKind; import helpers.ByteArrayClassLoader; import java.lang.classfile.attribute.SourceFileAttribute; -import jdk.internal.classfile.impl.DirectClassBuilder; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -75,10 +74,10 @@ void testAdapt() throws Exception { byte[] clazz = cc.build(ClassDesc.of(test), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.with(SourceFileAttribute.of("/some/madeup/TestClass.java")); - cl.methods().forEach(m -> ((DirectClassBuilder) cb).withMethod(m)); + cl.methods().forEach(cb::with); cb.withMethod("doit", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> { xb.invokedynamic(indy); xb.astore(1); diff --git a/test/jdk/jdk/classfile/LvtTest.java b/test/jdk/jdk/classfile/LvtTest.java index 3d7898097980f..d95a4fb7b29ac 100644 --- a/test/jdk/jdk/classfile/LvtTest.java +++ b/test/jdk/jdk/classfile/LvtTest.java @@ -35,13 +35,10 @@ import java.lang.constant.ClassDesc; import java.net.URI; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.lang.classfile.AccessFlags; import java.lang.classfile.Attributes; import java.lang.classfile.attribute.SourceFileAttribute; import java.lang.classfile.constantpool.ConstantPoolBuilder; @@ -58,11 +55,10 @@ import static helpers.TestConstants.MTD_VOID; import static helpers.TestUtil.ExpectedLvRecord; import static helpers.TestUtil.ExpectedLvtRecord; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; -import static java.lang.classfile.Opcode.*; -import static java.lang.classfile.Opcode.INVOKEVIRTUAL; -import static java.lang.classfile.TypeKind.VoidType; import static org.junit.jupiter.api.Assertions.*; class LvtTest { @@ -128,7 +124,7 @@ void testCreateLoadLVT() throws Exception { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb .withCode(c0 -> { ConstantPoolBuilder cpb = cb.constantPool(); @@ -243,7 +239,7 @@ void testCreateLoadLVTT() throws Exception { ) .withMethod("m", MethodTypeDesc.of(CD_Object, CD_Object.arrayType()), - ClassFile.ACC_PUBLIC, + ACC_PUBLIC, mb -> mb.withFlags(AccessFlag.PUBLIC) .withCode(c0 -> { ConstantPoolBuilder cpb = cb.constantPool(); diff --git a/test/jdk/jdk/classfile/OneToOneTest.java b/test/jdk/jdk/classfile/OneToOneTest.java index b20d07025e620..4e21cbebbe331 100644 --- a/test/jdk/jdk/classfile/OneToOneTest.java +++ b/test/jdk/jdk/classfile/OneToOneTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,18 +27,19 @@ * @run junit OneToOneTest */ import java.lang.constant.ClassDesc; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; import java.util.List; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; import java.lang.classfile.Instruction; import java.lang.classfile.Label; import java.lang.classfile.MethodModel; -import java.lang.classfile.TypeKind; import java.lang.classfile.attribute.SourceFileAttribute; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -74,7 +75,7 @@ void testClassWriteRead() { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.STATIC, AccessFlag.PUBLIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { Label loopTop = c0.newLabel(); Label loopEnd = c0.newLabel(); diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index 211490c3f8e75..a5109dd2a18a4 100644 --- a/test/jdk/jdk/classfile/StackMapsTest.java +++ b/test/jdk/jdk/classfile/StackMapsTest.java @@ -30,6 +30,7 @@ */ import java.lang.classfile.*; +import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.components.ClassPrinter; import java.net.URI; import java.nio.file.FileSystem; @@ -329,7 +330,7 @@ void testEmptyCounters(ClassFile.StackMapsOption option) { var cm = ClassFile.of().parse(bytes); for (var method : cm.methods()) { var name = method.methodName(); - var code = method.code().orElseThrow(); + var code = (CodeAttribute) method.code().orElseThrow(); if (name.equalsString("a")) { assertEquals(0, code.maxLocals()); // static method assertEquals(0, code.maxStack()); diff --git a/test/jdk/jdk/classfile/SwapTest.java b/test/jdk/jdk/classfile/SwapTest.java index 10c0d66d5131c..71ea6189aa61e 100644 --- a/test/jdk/jdk/classfile/SwapTest.java +++ b/test/jdk/jdk/classfile/SwapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,10 @@ * @run junit SwapTest */ -import java.lang.classfile.AccessFlags; import java.lang.classfile.ClassFile; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -38,9 +40,6 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import static java.lang.reflect.AccessFlag.PUBLIC; -import static java.lang.reflect.AccessFlag.STATIC; - class SwapTest { @Test void testTryCatchCatchAll() throws Throwable { @@ -48,7 +47,7 @@ void testTryCatchCatchAll() throws Throwable { MethodTypeDesc mtd = mt.describeConstable().get(); byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { - cb.withMethodBody("m", mtd, AccessFlags.ofMethod(PUBLIC, STATIC).flagsMask(), xb -> { + cb.withMethodBody("m", mtd, ACC_PUBLIC | ACC_STATIC, xb -> { xb.aload(0); // 0 xb.aload(1); // 1, 0 xb.swap(); // 0, 1 diff --git a/test/jdk/jdk/classfile/VerifierSelfTest.java b/test/jdk/jdk/classfile/VerifierSelfTest.java index 84a5ded161089..d0943d2eee9f5 100644 --- a/test/jdk/jdk/classfile/VerifierSelfTest.java +++ b/test/jdk/jdk/classfile/VerifierSelfTest.java @@ -47,6 +47,8 @@ import java.lang.classfile.attribute.*; import java.lang.classfile.components.ClassPrinter; import java.lang.constant.ModuleDesc; + +import jdk.internal.classfile.impl.BufWriterImpl; import jdk.internal.classfile.impl.DirectClassBuilder; import jdk.internal.classfile.impl.UnboundAttribute; import org.junit.jupiter.api.Test; @@ -103,7 +105,7 @@ void testInvalidAttrLocation() { var bytes = cc.build(ClassDesc.of("InvalidAttrLocationClass"), cb -> ((DirectClassBuilder)cb).writeAttribute(new UnboundAttribute.AdHocAttribute(Attributes.localVariableTable()) { @Override - public void writeBody(BufWriter b) { + public void writeBody(BufWriterImpl b) { b.writeU2(0); } })); diff --git a/test/jdk/jdk/classfile/WriteTest.java b/test/jdk/jdk/classfile/WriteTest.java index b4af9135d12bd..72d847c474c4b 100644 --- a/test/jdk/jdk/classfile/WriteTest.java +++ b/test/jdk/jdk/classfile/WriteTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,20 +30,16 @@ import java.lang.constant.MethodTypeDesc; import helpers.TestConstants; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassFile; -import java.lang.classfile.TypeKind; import java.lang.classfile.Label; import java.lang.classfile.attribute.SourceFileAttribute; import org.junit.jupiter.api.Test; import static helpers.TestConstants.MTD_VOID; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; -import static java.lang.classfile.Opcode.*; -import static java.lang.classfile.TypeKind.IntType; -import static java.lang.classfile.TypeKind.ReferenceType; -import static java.lang.classfile.TypeKind.VoidType; class WriteTest { @@ -61,7 +57,7 @@ void testJavapWrite() { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { Label loopTop = c0.newLabel(); Label loopEnd = c0.newLabel(); @@ -102,7 +98,7 @@ void testPrimitiveWrite() { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { Label loopTop = c0.newLabel(); Label loopEnd = c0.newLabel(); diff --git a/test/jdk/jdk/classfile/examples/ExampleGallery.java b/test/jdk/jdk/classfile/examples/ExampleGallery.java index 7de4a78ffbf86..f37f6b1b74da7 100644 --- a/test/jdk/jdk/classfile/examples/ExampleGallery.java +++ b/test/jdk/jdk/classfile/examples/ExampleGallery.java @@ -207,7 +207,7 @@ public byte[] changeFieldSig(ClassModel cm) { public byte[] changeFieldFlags(ClassModel cm) { return ClassFile.of().transformClass(cm, ClassTransform.transformingFields((fb, fe) -> { switch (fe) { - case AccessFlags a -> fb.with(AccessFlags.ofField(a.flagsMask() & ~ClassFile.ACC_PUBLIC & ~ClassFile.ACC_PROTECTED)); + case AccessFlags a -> fb.withFlags(a.flagsMask() & ~ClassFile.ACC_PUBLIC & ~ClassFile.ACC_PROTECTED); default -> fb.with(fe); } })); diff --git a/test/jdk/jdk/incubator/vector/Double128VectorTests.java b/test/jdk/jdk/incubator/vector/Double128VectorTests.java index 577b1f2691201..81c915ba7cf85 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorTests.java @@ -63,8 +63,11 @@ public class Double128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (double)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(double[] r, double rc, double[] a, static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa, - double relativeError) { + double relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2194,7 +2197,7 @@ static void ADDReduceDouble128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2240,7 +2243,7 @@ static void ADDReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double128VectorTests::ADDReduceMasked, Double128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double128VectorTests::ADDReduceMasked, Double128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double MULReduce(double[] a, int idx) { @@ -2283,7 +2286,7 @@ static void MULReduceDouble128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2329,7 +2332,7 @@ static void MULReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double128VectorTests::MULReduceMasked, Double128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double128VectorTests::MULReduceMasked, Double128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Double256VectorTests.java b/test/jdk/jdk/incubator/vector/Double256VectorTests.java index b8ecc5e6e691c..5824941d80618 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorTests.java @@ -63,8 +63,11 @@ public class Double256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (double)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(double[] r, double rc, double[] a, static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa, - double relativeError) { + double relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2194,7 +2197,7 @@ static void ADDReduceDouble256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2240,7 +2243,7 @@ static void ADDReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double256VectorTests::ADDReduceMasked, Double256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double256VectorTests::ADDReduceMasked, Double256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double MULReduce(double[] a, int idx) { @@ -2283,7 +2286,7 @@ static void MULReduceDouble256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2329,7 +2332,7 @@ static void MULReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double256VectorTests::MULReduceMasked, Double256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double256VectorTests::MULReduceMasked, Double256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Double512VectorTests.java b/test/jdk/jdk/incubator/vector/Double512VectorTests.java index 57e68cb207602..895cf243eb7e3 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorTests.java @@ -63,8 +63,11 @@ public class Double512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (double)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(double[] r, double rc, double[] a, static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa, - double relativeError) { + double relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2194,7 +2197,7 @@ static void ADDReduceDouble512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2240,7 +2243,7 @@ static void ADDReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double512VectorTests::ADDReduceMasked, Double512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double512VectorTests::ADDReduceMasked, Double512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double MULReduce(double[] a, int idx) { @@ -2283,7 +2286,7 @@ static void MULReduceDouble512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2329,7 +2332,7 @@ static void MULReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double512VectorTests::MULReduceMasked, Double512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double512VectorTests::MULReduceMasked, Double512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Double64VectorTests.java b/test/jdk/jdk/incubator/vector/Double64VectorTests.java index 04ba2729398cd..4414e36b0deb6 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorTests.java @@ -63,8 +63,11 @@ public class Double64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (double)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(double[] r, double rc, double[] a, static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa, - double relativeError) { + double relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2194,7 +2197,7 @@ static void ADDReduceDouble64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2240,7 +2243,7 @@ static void ADDReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double64VectorTests::ADDReduceMasked, Double64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double64VectorTests::ADDReduceMasked, Double64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double MULReduce(double[] a, int idx) { @@ -2283,7 +2286,7 @@ static void MULReduceDouble64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2329,7 +2332,7 @@ static void MULReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double64VectorTests::MULReduceMasked, Double64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Double64VectorTests::MULReduceMasked, Double64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java index 474ef0043c9eb..271d1f12aba56 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java @@ -68,8 +68,11 @@ static VectorShape getMaxBit() { private static final int Max = 256; // juts so we can do N/Max - // for floating point reduction ops that may introduce rounding errors - private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (double)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); @@ -134,16 +137,16 @@ static void assertReductionArraysEquals(double[] r, double rc, double[] a, static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa, - double relativeError) { + double relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2199,7 +2202,7 @@ static void ADDReduceDoubleMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2245,7 +2248,7 @@ static void ADDReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - DoubleMaxVectorTests::ADDReduceMasked, DoubleMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + DoubleMaxVectorTests::ADDReduceMasked, DoubleMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static double MULReduce(double[] a, int idx) { @@ -2288,7 +2291,7 @@ static void MULReduceDoubleMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { @@ -2334,7 +2337,7 @@ static void MULReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - DoubleMaxVectorTests::MULReduceMasked, DoubleMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + DoubleMaxVectorTests::MULReduceMasked, DoubleMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static double MINReduce(double[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float128VectorTests.java b/test/jdk/jdk/incubator/vector/Float128VectorTests.java index 5d503860ee94b..a634aeffe5431 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorTests.java @@ -63,8 +63,11 @@ public class Float128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (float)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(float[] r, float rc, float[] a, static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa, - float relativeError) { + float relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2205,7 +2208,7 @@ static void ADDReduceFloat128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2251,7 +2254,7 @@ static void ADDReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float128VectorTests::ADDReduceMasked, Float128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float128VectorTests::ADDReduceMasked, Float128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float MULReduce(float[] a, int idx) { @@ -2294,7 +2297,7 @@ static void MULReduceFloat128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2340,7 +2343,7 @@ static void MULReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float128VectorTests::MULReduceMasked, Float128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float128VectorTests::MULReduceMasked, Float128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float256VectorTests.java b/test/jdk/jdk/incubator/vector/Float256VectorTests.java index 7c38b370b12a4..d9509ff3152b7 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorTests.java @@ -63,8 +63,11 @@ public class Float256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (float)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(float[] r, float rc, float[] a, static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa, - float relativeError) { + float relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2205,7 +2208,7 @@ static void ADDReduceFloat256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2251,7 +2254,7 @@ static void ADDReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float256VectorTests::ADDReduceMasked, Float256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float256VectorTests::ADDReduceMasked, Float256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float MULReduce(float[] a, int idx) { @@ -2294,7 +2297,7 @@ static void MULReduceFloat256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2340,7 +2343,7 @@ static void MULReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float256VectorTests::MULReduceMasked, Float256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float256VectorTests::MULReduceMasked, Float256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float512VectorTests.java b/test/jdk/jdk/incubator/vector/Float512VectorTests.java index 70e4e1ea24f6f..83326abe54e0f 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorTests.java @@ -63,8 +63,11 @@ public class Float512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (float)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(float[] r, float rc, float[] a, static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa, - float relativeError) { + float relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2205,7 +2208,7 @@ static void ADDReduceFloat512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2251,7 +2254,7 @@ static void ADDReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float512VectorTests::ADDReduceMasked, Float512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float512VectorTests::ADDReduceMasked, Float512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float MULReduce(float[] a, int idx) { @@ -2294,7 +2297,7 @@ static void MULReduceFloat512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2340,7 +2343,7 @@ static void MULReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float512VectorTests::MULReduceMasked, Float512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float512VectorTests::MULReduceMasked, Float512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/Float64VectorTests.java b/test/jdk/jdk/incubator/vector/Float64VectorTests.java index 65a71504561ed..efae7f1fc3c97 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorTests.java @@ -63,8 +63,11 @@ public class Float64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - // for floating point reduction ops that may introduce rounding errors - private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (float)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); @@ -129,16 +132,16 @@ static void assertReductionArraysEquals(float[] r, float rc, float[] a, static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa, - float relativeError) { + float relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2205,7 +2208,7 @@ static void ADDReduceFloat64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2251,7 +2254,7 @@ static void ADDReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float64VectorTests::ADDReduceMasked, Float64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float64VectorTests::ADDReduceMasked, Float64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float MULReduce(float[] a, int idx) { @@ -2294,7 +2297,7 @@ static void MULReduceFloat64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2340,7 +2343,7 @@ static void MULReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float64VectorTests::MULReduceMasked, Float64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + Float64VectorTests::MULReduceMasked, Float64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java index f6b05fb6bc7ec..fdd3e042f77f2 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java @@ -68,8 +68,11 @@ static VectorShape getMaxBit() { private static final int Max = 256; // juts so we can do N/Max - // for floating point reduction ops that may introduce rounding errors - private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR_FACTOR_MUL = (float)50.0; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); @@ -134,16 +137,16 @@ static void assertReductionArraysEquals(float[] r, float rc, float[] a, static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa, - float relativeError) { + float relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } @@ -2210,7 +2213,7 @@ static void ADDReduceFloatMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); + FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2256,7 +2259,7 @@ static void ADDReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - FloatMaxVectorTests::ADDReduceMasked, FloatMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); + FloatMaxVectorTests::ADDReduceMasked, FloatMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } static float MULReduce(float[] a, int idx) { @@ -2299,7 +2302,7 @@ static void MULReduceFloatMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); + FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { @@ -2345,7 +2348,7 @@ static void MULReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - FloatMaxVectorTests::MULReduceMasked, FloatMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); + FloatMaxVectorTests::MULReduceMasked, FloatMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } static float MINReduce(float[] a, int idx) { diff --git a/test/jdk/jdk/incubator/vector/gen-template.sh b/test/jdk/jdk/incubator/vector/gen-template.sh index 5a7167c0b31cf..375287bef2d44 100644 --- a/test/jdk/jdk/incubator/vector/gen-template.sh +++ b/test/jdk/jdk/incubator/vector/gen-template.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -221,7 +221,6 @@ function gen_op_tmpl { replace_variables $unit_filename $unit_output "$kernel" "$test" "$op" "$init" "$guard" "$masked" "$op_name" "$kernel_smoke" local gen_perf_tests=$generate_perf_tests - gen_perf_tests=true if [[ $template == *"-Broadcast-"* ]] || [[ $template == "Miscellaneous" ]] || [[ $template == *"Compare-Masked"* ]] || [[ $template == *"Compare-Broadcast"* ]]; then gen_perf_tests=false diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template index 3b306ce387aac..3c68b72215dda 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template @@ -4,7 +4,7 @@ [[KERNEL]] assertReductionArraysEqualsMasked(r, ra, a, mask, #if[FP] - $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked, RELATIVE_ROUNDING_ERROR); + $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked, RELATIVE_ROUNDING_ERROR_FACTOR_[[TEST]]); #else[FP] $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked); #end[FP] diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template index 5c68968870840..5638940045dc6 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template @@ -4,7 +4,7 @@ [[KERNEL]] assertReductionArraysEquals(r, ra, a, #if[FP] - $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll, RELATIVE_ROUNDING_ERROR); + $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_[[TEST]]); #else[FP] $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); #end[FP] diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index bc1ee75a5c665..38ab617824437 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -96,8 +96,11 @@ public class $vectorteststype$ extends AbstractVectorTest { private static final $type$ CONST_SHIFT = $Boxtype$.SIZE / 2; #end[BITWISE] #if[FP] - // for floating point reduction ops that may introduce rounding errors - private static final $type$ RELATIVE_ROUNDING_ERROR = ($type$)0.000001; + // for floating point addition reduction ops that may introduce rounding errors + private static final $type$ RELATIVE_ROUNDING_ERROR_FACTOR_ADD = ($type$)10.0; + + // for floating point multiplication reduction ops that may introduce rounding errors + private static final $type$ RELATIVE_ROUNDING_ERROR_FACTOR_MUL = ($type$)50.0; #end[FP] static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / $bits$); @@ -177,16 +180,16 @@ public class $vectorteststype$ extends AbstractVectorTest { static void assertReductionArraysEquals($type$[] r, $type$ rc, $type$[] a, FReductionOp f, FReductionAllOp fa, - $type$ relativeError) { + $type$ relativeErrorFactor) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.ulp(rc) * relativeErrorFactor, "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.ulp(r[i]) * relativeErrorFactor, "at index #" + i); } } #end[FP] diff --git a/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java b/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java index 2f1da20e052f9..e6bc191a3ba49 100644 --- a/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java +++ b/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -426,7 +426,7 @@ void testAwaitAllWithTimeout1(ThreadFactory factory) throws Exception { long startMillis = millisTime(); boolean done = flock.awaitAll(Duration.ofSeconds(30)); assertTrue(done); - checkDuration(startMillis, 1900, 4000); + checkDuration(startMillis, 1900, 20_000); } } @@ -453,7 +453,7 @@ void testAwaitAllWithTimeout2(ThreadFactory factory) throws Exception { flock.awaitAll(Duration.ofSeconds(2)); fail("awaitAll did not throw"); } catch (TimeoutException e) { - checkDuration(startMillis, 1900, 4000); + checkDuration(startMillis, 1900, 20_000); } } finally { latch.countDown(); diff --git a/test/jdk/jdk/jfr/api/settings/TestSettingControl.java b/test/jdk/jdk/jfr/api/settings/TestSettingControl.java new file mode 100644 index 0000000000000..5543953482d63 --- /dev/null +++ b/test/jdk/jdk/jfr/api/settings/TestSettingControl.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.jfr.api.settings; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; + +/** + * @test + * @summary Tests that methods on all SettingControls have expected behavior. + * @key jfr + * @requires vm.hasJFR + * @library /test/lib /test/jdk + * @run main/othervm jdk.jfr.api.settings.TestSettingControl + */ +public class TestSettingControl { + record SettingTest(String setting, String defaultValue, String event, List exampleValues) { + public String eventSettingName() { + return event + "#" + setting; + } + } + + // Example values should be listed in precedence order with the lowest precedence first. + static List SETTING_TESTS = List.of( + new SettingTest("enabled", "false", "jdk.JavaMonitorWait",List.of("false", "true")), + new SettingTest("stackTrace", "true", "jdk.JavaMonitorWait", List.of("false", "true")), + new SettingTest("threshold", "0 ns", "jdk.JavaMonitorWait", List.of("infinity", "10 ms", "0 ns")), + new SettingTest("level", "forRemoval", "jdk.DeprecatedInvocation", List.of("off", "forRemoval")), + new SettingTest("period", "everyChunk", "jdk.ExceptionStatistics", List.of("everyChunk", "60 s", "1 s")), + new SettingTest("cutoff", "infinity", "jdk.OldObjectSample", List.of("0 ms", "1 s", "infinity")), + new SettingTest("throttle", "off", "jdk.ObjectAllocationSample", List.of("off", "100/s", "10/ms")) + ); + + public static void main(String... args) throws Exception { + testTesting(); + testDefault(); + testDefaultWithInvalid(); + testPrecedence(); + testPrecedenceWithInvalid(); + } + + // Ensure that all known SettingControl/types are tested at least once + private static void testTesting() throws Exception { + Set foundSettings = new HashSet<>(); + for (EventType eventType : allEventTypes()) { + for (SettingDescriptor s : eventType.getSettingDescriptors()) { + foundSettings.add(s.getName()); + } + } + for (SettingTest st : SETTING_TESTS) { + foundSettings.remove(st.setting()); + } + if (!foundSettings.isEmpty()) { + throw new Exception("All event SettingControls should be tested. Missing test for " + foundSettings); + } + } + + // Ensure that the default values for all SettingControls are correct + private static void testDefault() throws Exception { + for (SettingTest settingTest : SETTING_TESTS) { + SettingDescriptor s = findSettingDescriptor(settingTest); + if (!settingTest.defaultValue().equals(s.getDefaultValue())) { + String message = "Incorrect default value " + quote(s.getDefaultValue()); + message += " for setting " + settingTest.eventSettingName() + ". "; + message += "Expected " + quote(settingTest.defaultValue()); + throw new Exception(message); + } + } + } + + // Ensure that default settings are used if an invalid setting is specified. + private static void testDefaultWithInvalid() throws Exception { + Map settings = createEnabledMap(); + for (SettingTest settingTest : SETTING_TESTS) { + settings.put(settingTest.eventSettingName(), "%#&2672g"); + } + Map result = check("testDefaultWithInvalid", List.of(settings)); + for (var entry : new ArrayList<>(result.entrySet())) { + String key = entry.getKey(); + String removed = result.remove(key); + if (removed == null) { + throw new Exception("Expected setting " + quote(key) + " to exist"); + } + String setting = key.substring(key.indexOf("#") + 1); + SettingTest st = findSettingTest(setting); + if (st == null) { + throw new Exception("Found unexpected setting " + quote(key)); + } + if (!removed.equals(st.defaultValue())) { + String message = "Expected default value " + quote(st.defaultValue()); + message += " for setting " + quote(setting) + " when"; + message += " an invalid settings value was specified"; + throw new Exception(message); + } + } + if (!result.isEmpty()) { + throw new Exception("Found unexpected setting when testing preserved default"); + } + } + + // Only enabled events will use settings + private static Map createEnabledMap() { + Map settings = new TreeMap<>(); + for (SettingTest settingTest : SETTING_TESTS) { + settings.put(settingTest.event + "#enabled", "true"); + } + return settings; + } + + // Ensure that precedence are respected when multiple settings are specified + private static void testPrecedence() throws Exception { + testPrecedence("testPrecedence"); + } + + // Ensure that precedence are respected when an incorrect setting is used + private static void testPrecedenceWithInvalid() throws Exception { + testPrecedence("testPrecedenceWithInvalid"); + } + + // * * * HELPER METHODS * * * + + private static void testPrecedence(String testName) throws Exception { + List> settingsList = new ArrayList<>(); + int maxExamples = 0; + for (SettingTest t : SETTING_TESTS) { + maxExamples = Math.max(t.exampleValues().size(), maxExamples); + } + for (int i = 0; i < maxExamples; i++) { + Map settings = createEnabledMap(); + for (SettingTest settingTest : SETTING_TESTS) { + List examples = settingTest.exampleValues(); + String name = settingTest.eventSettingName(); + if (i < examples.size()) { + settings.put(name, examples.get(i)); + } + // Insert the invalid setting first + if (testName.contains("Invalid") && i == 0) { + settings.put(name, "%#&2672g"); + } + } + settingsList.add(settings); + } + Map results = check(testName, settingsList); + Map reversed = check(testName + "-reversed", settingsList.reversed()); + if (!reversed.equals(results)) { + throw new Exception("Active settings should not depend on the order event settings are applied"); + } + for (SettingTest t : SETTING_TESTS) { + String expected = t.exampleValues().get(t.exampleValues().size() - 1); + String found = results.get(t.eventSettingName()); + if (!expected.equals(found)) { + throw new Exception("Expected " + expected + " to be used with setting " + quote(t.setting()) + ", not " + quote(found)); + } + } + } + + private static Map check(String testName, List> settingsList) throws Exception { + System.out.println("*** Check for " + testName + " ****"); + System.out.println("Input:"); + int index = 0; + for (var settings : settingsList) { + System.out.println("Setting[" + index + "] = {"); + for (var e : settings.entrySet()) { + System.out.println(" " + e.getKey() + "=" + e.getValue()); + } + System.out.println("}"); + index++; + } + int settingsCount = settingsList.size(); + + // Start a recording for each settings + List recordings = new ArrayList<>(); + for (int i = 0; i < settingsCount; i++) { + Recording r = new Recording(); + Map settings = settingsList.get(i); + settings.put("jdk.ActiveSetting#enabled", "true"); + r.setSettings(settings); + r.start(); + recordings.add(r); + } + + // Stop all recordings + for (Recording r : recordings) { + r.stop(); + } + + // Dump the innermost recording + Path p = Path.of("recording.jfr"); + Recording inner = recordings.get(settingsCount - 1); + inner.dump(p); + + // Close all recordings + for (Recording r : recordings) { + r.close(); + } + System.out.println("Result:"); + Map r = lastSettings(p); + for (var e : r.entrySet()) { + System.out.println(e.getKey() + "=" + e.getValue()); + } + System.out.println("*************"); + System.out.println(); + Files.delete(p); + return r; + } + + private static SettingTest findSettingTest(String name) throws Exception { + for (SettingTest settingTest : SETTING_TESTS) { + if (name.equals(settingTest.setting())) { + return settingTest; + } + } + return null; + } + + private static Map lastSettings(Path p) throws Exception { + List events = RecordingFile.readAllEvents(p); + Instant timestamp = findLastActiveSetting(events); + Map lastInnerMostSettings = new HashMap<>(); + for (SettingTest t : SETTING_TESTS) { + long id = eventTypeNameToId(t.event()); + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals("jdk.ActiveSetting")) { + if (event.getStartTime().equals(timestamp) && id == event.getLong("id")) { + String name = event.getString("name"); + String value = event.getString("value"); + if (t.setting.equals(name)) { + String fullName = t.event() + "#" + name; + String previous = lastInnerMostSettings.put(fullName, value); + if (previous != null) { + throw new Exception("Expected only one ActiveSetting event per event setting"); + } + } + } + } + } + } + return lastInnerMostSettings; + } + + private static Instant findLastActiveSetting(List events) { + Instant lastTimestamp = null; + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals("jdk.ActiveSetting")) { + Instant t = event.getStartTime(); + if (lastTimestamp == null || t.isBefore(lastTimestamp)) { + lastTimestamp = t; + } + } + } + return lastTimestamp; + } + + private static long eventTypeNameToId(String name) throws Exception { + for (EventType eventType : allEventTypes()) { + if (eventType.getName().equals(name)) { + return eventType.getId(); + } + } + throw new Exception("Could not find event type with name " + name); + } + + private static SettingDescriptor findSettingDescriptor(SettingTest settingTest) throws Exception { + for (EventType eventType : allEventTypes()) { + if (eventType.getName().equals(settingTest.event())) { + for (SettingDescriptor s : eventType.getSettingDescriptors()) { + if (settingTest.setting().equals(s.getName())) { + return s; + } + } + } + } + throw new Exception("Could not find setting with name " + settingTest.event() + "#" + settingTest.setting()); + } + + private static List allEventTypes() { + return FlightRecorder.getFlightRecorder().getEventTypes(); + } + + private static String quote(String text) { + return "'" + text + "'"; + } +} diff --git a/test/jdk/jdk/jfr/event/compiler/TestCodeSweeper.java b/test/jdk/jdk/jfr/event/compiler/TestCodeSweeper.java index cb2a3f41b5582..df95af5b9be0a 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCodeSweeper.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCodeSweeper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,9 +27,11 @@ import java.lang.reflect.Method; import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import jdk.jfr.Recording; +import jdk.jfr.Event; +import jdk.jfr.consumer.RecordingStream; import jdk.jfr.consumer.RecordedEvent; import jdk.test.lib.Asserts; import jdk.test.lib.jfr.EventNames; @@ -39,7 +41,7 @@ import jdk.test.whitebox.code.CodeBlob; /** - * Test for events: vm/code_cache/full vm/compiler/failure + * Test for events: jdk.CodeCacheFull jdk.CompilationFailure * * We verify that we should get at least one of each of the events listed above. * @@ -58,13 +60,15 @@ */ public class TestCodeSweeper { + static class ProvocationEvent extends Event { + } private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); private static final int COMP_LEVEL_SIMPLE = 1; private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; private static final int SIZE = 1; private static final String METHOD_NAME = "verifyFullEvent"; - private static final String pathFull = EventNames.CodeCacheFull; - private static final String pathFailure = EventNames.CompilationFailure; + private static final String EVENT_CODE_CACHE_FULL = EventNames.CodeCacheFull; + private static final String EVENT_COMPILATION_FAILURE = EventNames.CompilationFailure; public static final long SEGMENT_SIZE = WhiteBox.getWhiteBox().getUintxVMFlag("CodeCacheSegmentSize"); public static final long MIN_BLOCK_LENGTH = WhiteBox.getWhiteBox().getUintxVMFlag("CodeCacheMinBlockLength"); public static final long MIN_ALLOCATION = SEGMENT_SIZE * MIN_BLOCK_LENGTH; @@ -77,26 +81,41 @@ public static void main(String[] args) throws Throwable { System.out.println("This test will warn that the code cache is full."); System.out.println("That is expected and is the purpose of the test."); System.out.println("************************************************"); - - Recording r = new Recording(); - r.enable(pathFull); - r.enable(pathFailure); - r.start(); - provokeEvents(); - r.stop(); + List events = Collections.synchronizedList(new ArrayList<>()); + try (RecordingStream rs = new RecordingStream()) { + rs.setReuse(false); + rs.enable(EVENT_CODE_CACHE_FULL); + rs.enable(EVENT_COMPILATION_FAILURE); + rs.onEvent(EVENT_CODE_CACHE_FULL, events::add); + rs.onEvent(EVENT_COMPILATION_FAILURE, events::add); + rs.onEvent(ProvocationEvent.class.getName(), e -> { + if (!events.isEmpty()) { + rs.close(); + return; + } + // Retry if CodeCacheFull or CompilationFailure events weren't provoked + try { + provokeEvents(); + } catch (Exception ex) { + ex.printStackTrace(); + rs.close(); + } + }); + rs.startAsync(); + provokeEvents(); + rs.awaitTermination(); + } int countEventFull = 0; int countEventFailure = 0; - - List events = Events.fromRecording(r); Events.hasEvents(events); - for (RecordedEvent event : events) { + for (RecordedEvent event : new ArrayList<>(events)) { switch (event.getEventType().getName()) { - case pathFull: + case EVENT_CODE_CACHE_FULL: countEventFull++; verifyFullEvent(event); break; - case pathFailure: + case EVENT_COMPILATION_FAILURE: countEventFailure++; verifyFailureEvent(event); break; @@ -115,6 +134,8 @@ private static boolean canAllocate(double size, long maxSize, MemoryPoolMXBean b } private static void provokeEvents() throws NoSuchMethodException, InterruptedException { + System.out.println("provokeEvents()"); + ProvocationEvent provocationEvent = new ProvocationEvent(); // Prepare for later, since we don't want to trigger any compilation // setting this up. Method method = TestCodeSweeper.class.getDeclaredMethod(METHOD_NAME, new Class[] { RecordedEvent.class }); @@ -159,6 +180,7 @@ private static void provokeEvents() throws NoSuchMethodException, InterruptedExc for (Long blob : blobs) { WHITE_BOX.freeCodeBlob(blob); } + provocationEvent.commit(); } private static void verifyFullEvent(RecordedEvent event) throws Throwable { diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java index 0715d3c5c2deb..4d8f697d83719 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java @@ -39,7 +39,7 @@ public static void main(String[] args) throws Exception { String testID = "ParallelOld"; String[] vmFlags = {"-XX:+UseParallelGC"}; String[] gcNames = {GCHelper.gcParallelScavenge, GCHelper.gcParallelOld}; - String[] gcCauses = {"Allocation Failure", "Ergonomics", "System.gc()", "GCLocker Initiated GC"}; + String[] gcCauses = {"Allocation Failure", "System.gc()", "GCLocker Initiated GC"}; GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); } } diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java index 2f290a748c20d..fa91d77e2fbee 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Serial" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseSerialGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestDefNewAllocationPendingStackTrace + * @run main/othervm -XX:+UseSerialGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestDefNewAllocationPendingStackTrace */ public class TestDefNewAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java index 2df5d95f5240e..ad633ea26de25 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseG1GC -Xlog:gc* -Xmx64M -XX:InitiatingHeapOccupancyPercent=100 jdk.jfr.event.gc.stacktrace.TestG1HumongousAllocationPendingStackTrace + * @run main/othervm -XX:+UseG1GC -Xlog:gc* -Xmx64M -XX:InitiatingHeapOccupancyPercent=100 + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestG1HumongousAllocationPendingStackTrace */ public class TestG1HumongousAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java index 3541f117e1e07..1cf75ff8fac1c 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx128M -XX:+UseG1GC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestG1OldAllocationPendingStackTrace + * @run main/othervm -XX:MaxNewSize=10M -Xmx128M -XX:+UseG1GC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestG1OldAllocationPendingStackTrace */ public class TestG1OldAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java index e5b7f849173b3..7259ff5f4457a 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseG1GC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestG1YoungAllocationPendingStackTrace + * @run main/othervm -XX:+UseG1GC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestG1YoungAllocationPendingStackTrace */ public class TestG1YoungAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java index e7f5e709d15b7..c06862ad2e6f9 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Serial" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseSerialGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestMarkSweepCompactAllocationPendingStackTrace + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseSerialGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMarkSweepCompactAllocationPendingStackTrace */ public class TestMarkSweepCompactAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java index 81cf4191c6ed3..c74eeefa793f4 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java @@ -28,9 +28,10 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseG1GC -Xlog:gc* -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceG1GCAllocationPendingStackTrace + * @run main/othervm -XX:+UseG1GC -Xlog:gc* -XX:MaxMetaspaceSize=64M + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMetaspaceG1GCAllocationPendingStackTrace */ - public class TestMetaspaceG1GCAllocationPendingStackTrace { public static void main(String[] args) throws Exception { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java index ab31fe644bf75..cc66902401504 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Parallel" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseParallelGC -Xlog:gc* -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceParallelGCAllocationPendingStackTrace + * @run main/othervm -XX:+UseParallelGC -Xlog:gc* -XX:MaxMetaspaceSize=64M + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMetaspaceParallelGCAllocationPendingStackTrace */ public class TestMetaspaceParallelGCAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java index 83aa4fb522ef8..7f0ce364b6a88 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Serial" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseSerialGC -Xlog:gc* -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceSerialGCAllocationPendingStackTrace + * @run main/othervm -XX:+UseSerialGC -Xlog:gc* -XX:MaxMetaspaceSize=64M + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMetaspaceSerialGCAllocationPendingStackTrace */ public class TestMetaspaceSerialGCAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java index 5ab67d2561a6f..ebd1efccfeef9 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Parallel" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseParallelGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestParallelMarkSweepAllocationPendingStackTrace + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseParallelGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestParallelMarkSweepAllocationPendingStackTrace */ public class TestParallelMarkSweepAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java index 08ec7ffd05c89..6dbbc16e2836b 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Parallel" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseParallelGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestParallelScavengeAllocationPendingStackTrace + * @run main/othervm -XX:+UseParallelGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestParallelScavengeAllocationPendingStackTrace */ public class TestParallelScavengeAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/jcmd/TestJcmdView.java b/test/jdk/jdk/jfr/jcmd/TestJcmdView.java index bf3e3f6ba88e4..44db54e785038 100644 --- a/test/jdk/jdk/jfr/jcmd/TestJcmdView.java +++ b/test/jdk/jdk/jfr/jcmd/TestJcmdView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,7 @@ public class TestJcmdView { public static void main(String... args) throws Throwable { CountDownLatch jvmInformation = new CountDownLatch(1); CountDownLatch systemGC = new CountDownLatch(1); + CountDownLatch threadSleep = new CountDownLatch(1); CountDownLatch gcHeapSummary = new CountDownLatch(1); CountDownLatch oldCollection = new CountDownLatch(1); CountDownLatch garbageCollection = new CountDownLatch(1); @@ -56,6 +57,7 @@ public static void main(String... args) throws Throwable { rs.setMaxSize(Long.MAX_VALUE); rs.enable("jdk.JVMInformation").with("period", "beginChunk"); rs.enable("jdk.SystemGC"); + rs.enable("jdk.ThreadSleep").withoutThreshold().withStackTrace(); rs.enable("jdk.GCHeapSummary"); rs.enable("jdk.GarbageCollection"); rs.enable("jdk.OldGarbageCollection"); @@ -70,6 +72,11 @@ public static void main(String... args) throws Throwable { System.out.println(e); storeLastTimestamp(e); }); + rs.onEvent("jdk.ThreadSleep", e -> { + threadSleep.countDown(); + System.out.println(e); + storeLastTimestamp(e); + }); rs.onEvent("jdk.GCHeapSummary", e -> { gcHeapSummary.countDown(); System.out.println(e); @@ -90,9 +97,12 @@ public static void main(String... args) throws Throwable { System.gc(); System.gc(); System.gc(); + // Emit thread sleep event + Thread.sleep(1); // Wait for them being in the repository jvmInformation.await(); systemGC.await(); + threadSleep.await(); gcHeapSummary.await(); oldCollection.await(); // Wait for Instant.now() to advance 1 s past the last event timestamp. @@ -160,13 +170,13 @@ private static void testTableView() throws Throwable { private static void testEventType() throws Throwable { OutputAnalyzer output = JcmdHelper.jcmd( - "JFR.view", "verbose=true", "width=300", "cell-height=100", "SystemGC"); + "JFR.view", "verbose=true", "width=300", "cell-height=100", "ThreadSleep"); // Verify title - output.shouldContain("System GC"); + output.shouldContain("Thread Sleep"); // Verify headings - output.shouldContain("Invoked Concurrent"); + output.shouldContain("Sleep Time"); // Verify verbose headings - output.shouldContain("invokedConcurrent"); + output.shouldContain("time"); // Verify thread value output.shouldContain(Thread.currentThread().getName()); // Verify stack frame diff --git a/test/jdk/jdk/jfr/jvm/TestHiddenWait.java b/test/jdk/jdk/jfr/jvm/TestHiddenWait.java index f4b810f192e48..b064285329c0f 100644 --- a/test/jdk/jdk/jfr/jvm/TestHiddenWait.java +++ b/test/jdk/jdk/jfr/jvm/TestHiddenWait.java @@ -77,6 +77,15 @@ public static void main(String... args) throws Exception { s.start(); } List events = Events.fromRecording(r); + // Only keep events from the test thread and the JFR threads + String testThread = Thread.currentThread().getName(); + events.removeIf(event -> { + String threadName = event.getThread().getJavaName(); + if (threadName.equals(testThread) || threadName.contains("JFR")) { + return false; + } + return true; + }); for (RecordedEvent event : events) { if (!event.getEventType().getName().equals(PERIODIC_EVENT_NAME)) { System.out.println(event); diff --git a/test/jdk/sun/security/krb5/auto/KdcPolicy.java b/test/jdk/sun/security/krb5/auto/KdcPolicy.java index 27c2ace4e542d..6c0cd07cea297 100644 --- a/test/jdk/sun/security/krb5/auto/KdcPolicy.java +++ b/test/jdk/sun/security/krb5/auto/KdcPolicy.java @@ -38,7 +38,7 @@ /* * @test - * @bug 8164656 8181461 8194486 + * @bug 8164656 8181461 8194486 8333772 * @summary krb5.kdc.bad.policy test * @library /test/lib * @run main jdk.test.lib.FileInstaller TestHosts TestHosts @@ -219,13 +219,13 @@ static void writeConf(int max, int to, int... ports) throws Exception { inDefaults += "udp_preference_limit = 10000\n"; } else if (r.nextBoolean()) { inRealm += " udp_preference_limit = 10000\n"; - inDefaults += "udp_preference_limit = 1\n"; + inDefaults += "udp_preference_limit = 0\n"; } // else no settings means UDP } else { if (r.nextBoolean()) { - inDefaults += "udp_preference_limit = 1\n"; + inDefaults += "udp_preference_limit = 0\n"; } else { - inRealm += " udp_preference_limit = 1\n"; + inRealm += " udp_preference_limit = 0\n"; inDefaults += "udp_preference_limit = 10000\n"; } } diff --git a/test/jdk/sun/security/krb5/auto/RealmSpecificValues.java b/test/jdk/sun/security/krb5/auto/RealmSpecificValues.java new file mode 100644 index 0000000000000..23dcf10448fbb --- /dev/null +++ b/test/jdk/sun/security/krb5/auto/RealmSpecificValues.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.Asserts; +import sun.security.krb5.Config; + +/* + * @test + * @bug 8333772 + * @summary check krb5.conf reading on default and realm-specific values + * @library /test/lib + * @run main/othervm RealmSpecificValues + */ +public class RealmSpecificValues { + + static DebugMatcher cm = new DebugMatcher(); + + public static void main(String[] args) throws Exception { + System.setProperty("sun.security.krb5.debug", "true"); + System.setProperty("java.security.krb5.conf", "alternative-krb5.conf"); + + // Defaults + writeConf(-1, -1, -1, -1, -1, -1); + test(true, 3, 30000); + + // Below has settings. For each setting we provide 3 cases: + // 1. Set in defaults, 2, set in realms, 3, both + + // udp = 0 is useful + writeConf(0, -1, -1, -1, -1, -1); + test(false, 3, 30000); + writeConf(-1, -1, -1, 0, -1, -1); + test(false, 3, 30000); + writeConf(1, -1, -1, 0, -1, -1); + test(false, 3, 30000); + + // max_retries = 0 is ignored + writeConf(-1, 0, -1, -1, -1, -1); + test(true, 3, 30000); + writeConf(-1, -1, -1, -1, 0, -1); + test(true, 3, 30000); + writeConf(-1, 6, -1, -1, 0, -1); // Note: 0 is ignored, it does not reset to default + test(true, 6, 30000); + + // max_retries = 1 is useful + writeConf(-1, 1, -1, -1, -1, -1); + test(true, 1, 30000); + writeConf(-1, -1, -1, -1, 1, -1); + test(true, 1, 30000); + writeConf(-1, 3, -1, -1, 1, -1); + test(true, 1, 30000); + + // timeout = 0 is ignored + writeConf(-1, -1, 0, -1, -1, -1); + test(true, 3, 30000); + writeConf(-1, -1, -1, -1, -1, 0); + test(true, 3, 30000); + writeConf(-1, -1, 10000, -1, -1, 0); + test(true, 3, 10000); + + // timeout > 0 is useful + writeConf(-1, -1, 10000, -1, -1, -1); + test(true, 3, 10000); + writeConf(-1, -1, -1, -1, -1, 10000); + test(true, 3, 10000); + writeConf(-1, -1, 20000, -1, -1, 10000); + test(true, 3, 10000); + } + + static void writeConf(int limit, int retries, int timeout, + int limitR, int retriesR, int timeoutR) throws Exception { + + String inDefaults = ""; + if (limit >= 0) inDefaults += "udp_preference_limit = " + limit + "\n"; + if (retries >= 0) inDefaults += "max_retries = " + retries + "\n"; + if (timeout >= 0) inDefaults += "kdc_timeout = " + timeout + "\n"; + + String inRealm = ""; + if (limitR >= 0) inRealm += "udp_preference_limit = " + limitR + "\n"; + if (retriesR >= 0) inRealm += "max_retries = " + retriesR + "\n"; + if (timeoutR >= 0) inRealm += "kdc_timeout = " + timeoutR + "\n"; + + String conf = "[libdefaults]\n" + + "default_realm = " + OneKDC.REALM + "\n" + + inDefaults + + "\n" + + "[realms]\n" + + OneKDC.REALM + " = {\n" + + "kdc = " + OneKDC.KDCHOST + ":12345\n" + + inRealm + + "}\n"; + + Files.writeString(Paths.get("alternative-krb5.conf"), conf); + } + + static void test(boolean isUDP, int retries, int timeout) throws Exception { + + PrintStream oldErr = System.err; + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + System.setErr(new PrintStream(bo)); + try { + Config.refresh(); + Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + } catch (Exception e) { + // will happen + } finally { + System.setErr(oldErr); + } + + String[] lines = new String(bo.toByteArray()).split("\n"); + for (String line: lines) { + if (cm.match(line)) { + System.out.println(line); + Asserts.assertEQ(cm.isUDP(), isUDP); + Asserts.assertEQ(cm.timeout(), timeout); + Asserts.assertEQ(cm.retries(), retries); + return; + } + } + Asserts.fail("Should not reach here"); + } + + /** + * A helper class to match the krb5 debug output: + * >>> KrbKdcReq send: kdc=kdc.rabbit.hole TCP:12345, timeout=30000, + * number of retries =3, #bytes=141 + */ + static class DebugMatcher { + + static final Pattern re = Pattern.compile( + ">>> KrbKdcReq send: kdc=\\S+ (TCP|UDP):\\d+, " + + "timeout=(\\d+), number of retries\\s*=(\\d+)"); + + Matcher matcher; + + boolean match(String line) { + matcher = re.matcher(line); + return matcher.find(); + } + + boolean isUDP() { + return matcher.group(1).equals("UDP"); + } + + int timeout() { + return Integer.parseInt(matcher.group(2)); + } + + int retries() { + return Integer.parseInt(matcher.group(3)); + } + } +} diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java index 2810040e376a6..9cb29eb8507a1 100644 --- a/test/jdk/sun/security/pkcs11/PKCS11Test.java +++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java @@ -82,7 +82,7 @@ public abstract class PKCS11Test { // Version of the NSS artifact. This coincides with the version of // the NSS version - private static final String NSS_BUNDLE_VERSION = "3.96"; + private static final String NSS_BUNDLE_VERSION = "3.101"; private static final String NSSLIB = "jpg.tests.jdk.nsslib"; static double nss_version = -1; diff --git a/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java b/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java index a2d7f67fa32cc..b2ab96c90c62a 100644 --- a/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java +++ b/test/jdk/sun/security/pkcs11/Signature/TestDSAKeyLength.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,8 +48,12 @@ public static void main(String[] args) throws Exception { @Override protected boolean skipTest(Provider provider) { - if (isNSS(provider) && (getNSSVersion() == 0.0 || getNSSVersion() >= 3.14)) { - System.out.println("Skip testing NSS " + getNSSVersion()); + double version = getNSSVersion(); + String[] versionStrs = Double.toString(version).split("\\."); + int major = Integer.parseInt(versionStrs[0]); + int minor = Integer.parseInt(versionStrs[1]); + if (isNSS(provider) && (version == 0.0 || (major >= 3 && minor >= 14))) { + System.out.println("Skip testing NSS " + version); return true; } diff --git a/test/jdk/sun/security/util/DerInputBuffer/B8336667/PoC.java b/test/jdk/sun/security/util/DerInputBuffer/B8336667/PoC.java new file mode 100644 index 0000000000000..d14c213f7b255 --- /dev/null +++ b/test/jdk/sun/security/util/DerInputBuffer/B8336667/PoC.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8336667 + * @summary Ensure the unused bytes are calculated correctly when converting + * indefinite length BER to DER + * @modules java.base/sun.security.util + * @library /test/lib + */ +import jdk.test.lib.Asserts; +import sun.security.util.DerInputStream; + +import java.util.HexFormat; + +public class PoC { + public static void main(String[] args) throws Exception { + // A BER indefinite encoding with some unused bytes at the end + var data = HexFormat.of().parseHex(""" + 2480 0401AA 0401BB 0000 -- 2 byte string + 010100 -- boolean false + 12345678 -- 4 unused bytes""" + .replaceAll("(\\s|--.*)", "")); + var dis = new DerInputStream(data, 0, data.length - 4, true); + Asserts.assertEQ(dis.getDerValue().getOctetString().length, 2); + Asserts.assertFalse(dis.getDerValue().getBoolean()); + dis.atEnd(); + } +} diff --git a/test/jdk/sun/security/util/DerInputBuffer/B8336667/Reproducer.java b/test/jdk/sun/security/util/DerInputBuffer/B8336667/Reproducer.java new file mode 100644 index 0000000000000..f1e458035ee92 --- /dev/null +++ b/test/jdk/sun/security/util/DerInputBuffer/B8336667/Reproducer.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8336667 + * @summary Ensure the unused bytes are calculated correctly when converting + * indefinite length BER to DER + */ +import java.io.ByteArrayInputStream; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.util.Base64; + +public class Reproducer { + private static final String INPUT = """ + MIIBljCCAVMwgAaB/////////yb////////////////////9////AgDv//////////////////// + /////2RjPWNvbf////8k/////////yb///////////////////9vbf////8k/////////yb///// + ////////////////////AgD/////////////b23/////JP////////8m/////yf///////////// + /////wIA//////////////////////////////////////8AAABl//////8m/////////y1CRUdJ + TiA9Y290cnVlVlZWVlZWVlZWVjEAAAAAAAAArQdVUwNVBAsTA0RvRDEaMBhAA1UAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAEXDTAzMDcxNTE2MjAwNFqgHzAdMA8GA1UdHAEB/wQFMAPyAf8wCgYDVR0P + BAMCAQIwCwYHKoZIzjgEAwUAAzBkARkTA2NvbTEYMBYGCgmSJomT8ixkARkTCG15VGVzdENBMBIC + AQHyAjZG+RfHdO4="""; + + Reproducer(byte[] data) { + try { + java.security.cert.CertificateFactory. + getInstance("X.509").generateCRLs(new ByteArrayInputStream(data)); + } catch (CertificateException | CRLException e) { + if (System.getProperty("dbg", "false").equals("true")) { + e.printStackTrace(); + } + } + } + + public static void main(String[] a) throws Exception { + byte[] decodedBytes = Base64.getMimeDecoder().decode(INPUT); + new Reproducer(decodedBytes); + } +} diff --git a/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java b/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java new file mode 100644 index 0000000000000..4af5bf1ff6755 --- /dev/null +++ b/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Red Hat Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * 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 8334492 + * @summary Test to verify jcmd accepts %p in output filenames and substitutes for PID + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver TestJcmdPIDSubstitution + * + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.test.lib.Platform; + +public class TestJcmdPIDSubstitution { + + private static final String FILENAME = "myfile%p"; + + public static void main(String[] args) throws Exception { + verifyOutputFilenames("Thread.dump_to_file", FILENAME); + verifyOutputFilenames("GC.heap_dump", FILENAME); + if (Platform.isLinux()) { + verifyOutputFilenames("Compiler.perfmap", FILENAME); + verifyOutputFilenames("System.dump_map", "-F=%s".formatted(FILENAME)); + } + } + + private static void verifyOutputFilenames(String... args) throws Exception { + long pid = ProcessTools.getProcessId(); + String test_dir = System.getProperty("test.dir", "."); + Path path = Paths.get("%s/myfile%d".formatted(test_dir, pid)); + OutputAnalyzer output = JcmdBase.jcmd(args); + output.shouldHaveExitValue(0); + if (Files.exists(path)) { + Files.delete(path); + } else { + throw new Exception("File %s was not created as expected for diagnostic cmd %s".formatted(path, args[0])); + } + } +} diff --git a/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java b/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java index 11be90bc45622..b599488b563f1 100644 --- a/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java +++ b/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java @@ -26,12 +26,17 @@ import java.nio.file.Path; public class ChildProcessAppLauncher { - - public static void main(String[] args) throws IOException { - String calcPath = Path.of(System.getenv("SystemRoot"), "system32", "calc.exe").toString(); - ProcessBuilder processBuilder = new ProcessBuilder(calcPath); + public static void main(String[] args) throws IOException, InterruptedException { + if (args.length == 1 && "noexit".equals(args[0])) { + var lock = new Object(); + synchronized (lock) { + lock.wait(); + } + } else { + var childPath = System.getProperty("jpackage.app-path"); // get the path to the current jpackage app launcher + ProcessBuilder processBuilder = new ProcessBuilder(childPath, "noexit"); //ChildProcessAppLauncher acts as third party app Process process = processBuilder.start(); - System.out.println("Calc id=" + process.pid()); - System.exit(0); + System.out.println("Child id=" + process.pid()); + } } } diff --git a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java index 2928f7f1c5ffe..4fa7581d2b5c9 100644 --- a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java +++ b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java @@ -53,7 +53,7 @@ public class WinChildProcessTest { @Test public static void test() throws Throwable { - long calcPid = 0; + long childPid = 0; try { JPackageCommand cmd = JPackageCommand .helloAppImage(TEST_APP_JAVA + "*Hello"); @@ -68,21 +68,20 @@ public static void test() throws Throwable { .execute(0).getOutput(); String pidStr = output.get(0); - // parse calculator PID - calcPid = Long.parseLong(pidStr.split("=", 2)[1]); + // parse child PID + childPid = Long.parseLong(pidStr.split("=", 2)[1]); // Check whether the termination of third party application launcher // also terminating the launched third party application // If third party application is not terminated the test is // successful else failure - Optional processHandle = ProcessHandle.of(calcPid); + Optional processHandle = ProcessHandle.of(childPid); boolean isAlive = processHandle.isPresent() && processHandle.get().isAlive(); - System.out.println("Is Alive " + isAlive); - TKit.assertTrue(isAlive, "Check is calculator process is alive"); + TKit.assertTrue(isAlive, "Check is child process is alive"); } finally { - // Kill only a specific calculator instance - Runtime.getRuntime().exec("taskkill /F /PID " + calcPid); + // Kill only a specific child instance + Runtime.getRuntime().exec("taskkill /F /PID " + childPid); } } -} \ No newline at end of file +} diff --git a/test/jdk/tools/launcher/DisableBestFitMappingTest.java b/test/jdk/tools/launcher/DisableBestFitMappingTest.java new file mode 100644 index 0000000000000..6602aae60a9fe --- /dev/null +++ b/test/jdk/tools/launcher/DisableBestFitMappingTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8337506 + * @summary Verify Command Line arguments are not mapped with + * "best-fit" mappings on Windows + * @requires (os.family == "windows") + * @library /test/lib + * @run junit DisableBestFitMappingTest + */ +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.stream.Stream; +import jdk.test.lib.process.ProcessTools; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; + +public class DisableBestFitMappingTest { + private static final CharsetEncoder NATIVE_ENC = + Charset.forName(System.getProperty("native.encoding")).newEncoder(); + private static final String REPLACEMENT = + NATIVE_ENC.charset().decode(ByteBuffer.wrap(NATIVE_ENC.replacement())).toString(); + private static final int EXIT_SUCCESS = 0; + private static final int EXIT_FAILURE = -1; + + static Stream CMD_ARGS() { + return Stream.of( + Arguments.of("aa\uff02 \uff02bb", "aa" + REPLACEMENT + " " + REPLACEMENT + "bb"), + Arguments.of("aa\uff01bb", "aa" + REPLACEMENT + "bb"), + Arguments.of("aa\u221ebb", "aa" + REPLACEMENT + "bb") + ); + } + + @ParameterizedTest + @MethodSource("CMD_ARGS") + void testDisableBestFitMapping(String arg, String expected) throws Exception { + // Only execute if the arg cannot be encoded + assumeFalse(NATIVE_ENC.canEncode(arg), + "native.encoding (%s) can encode the argument '%s'. Test ignored." + .formatted(NATIVE_ENC.charset(), arg)); + + var result= ProcessTools.executeTestJava( + DisableBestFitMappingTest.class.getSimpleName(), arg, expected); + result.asLines().forEach(System.out::println); + assertEquals(EXIT_SUCCESS, result.getExitValue(), + "Disabling best-fit mapping failed"); + } + + public static void main(String... args) { + System.out.println(args[0]); + System.out.println(args[1]); + System.exit(args[0].equals(args[1]) ? EXIT_SUCCESS : EXIT_FAILURE); + } +} diff --git a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java index b26f2afe96be1..f103ef4e608f0 100644 --- a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java +++ b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java @@ -26,7 +26,8 @@ * @test * @bug 8267574 * @summary check stylesheet names against HtmlStyle - * @modules jdk.javadoc/jdk.javadoc.internal.doclets.formats.html.markup + * @modules jdk.javadoc/jdk.javadoc.internal.html + * jdk.javadoc/jdk.javadoc.internal.doclets.formats.html.markup * jdk.javadoc/jdk.javadoc.internal.doclets.formats.html.resources:open */ @@ -44,10 +45,11 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.html.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; /** - * This test compares the set of CSS class names defined in HtmlStyle + * This test compares the set of CSS class names defined in HtmlStyles * and other files (such as search.js) against the set of CSS class names * defined in the main stylesheet.css provided by the doclet. * @@ -185,7 +187,7 @@ void error(String message) { } Set getHtmlStyleNames() { - return Arrays.stream(HtmlStyle.values()) + return Arrays.stream(HtmlStyles.values()) .map(HtmlStyle::cssName) .collect(Collectors.toCollection(TreeSet::new)); } @@ -193,7 +195,7 @@ Set getHtmlStyleNames() { Set getStylesheetNames() throws IOException { Set names = new TreeSet<>(); String stylesheet = "/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css"; - URL url = HtmlStyle.class.getResource(stylesheet); + URL url = HtmlStyles.class.getResource(stylesheet); readStylesheet(url, names); return names; } diff --git a/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java b/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java index 2bd9da5957837..165fadc8e051f 100644 --- a/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java +++ b/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,7 +102,6 @@ public void test() { checkOutput("pkg/TestAnnotationType.html", true, """ -
      @Deprecated(forRemoval=true) @Documented public @interface
      @Deprecated(forRemoval=true) public class TestClass extends java.lang.Object
      @@ -212,7 +210,6 @@ public void test() { checkOutput("pkg/TestEnum.html", true, """ -
      @Deprecated(forRemoval=true) public enum TestEnum extends java.lang.Enum<
      @Deprecated(forRemoval=true) public class TestError extends java.lang.Error
      @@ -244,7 +240,6 @@ public void test() { checkOutput("pkg/TestException.html", true, """ -
      @Deprecated(forRemoval=true) public class TestException extends java.lang.Exception
      @@ -255,7 +250,6 @@ public void test() { checkOutput("pkg/TestInterface.html", true, """ -
      @Deprecated(forRemoval=true) public class TestInterface extends java.lang.Object
      diff --git a/test/langtools/jdk/javadoc/doclet/testDirectedInheritance/TestDirectedInheritance.java b/test/langtools/jdk/javadoc/doclet/testDirectedInheritance/TestDirectedInheritance.java index 89eb68db1dfdc..0f2b398e626bc 100644 --- a/test/langtools/jdk/javadoc/doclet/testDirectedInheritance/TestDirectedInheritance.java +++ b/test/langtools/jdk/javadoc/doclet/testDirectedInheritance/TestDirectedInheritance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -154,8 +154,8 @@ public interface E1 extends I1, I2 {
      I2: main description
      """, """
      Type Parameters:
      -
      E - I2: first type parameter
      -
      F - I2: second type parameter
      +
      E - I2: first type parameter
      +
      F - I2: second type parameter
      Parameters:
      eObj - I2: parameter
      Returns:
      diff --git a/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java b/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java index 3c9f9b6fd9d3b..ee2ec08b884e6 100644 --- a/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java +++ b/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java @@ -82,15 +82,19 @@ class Y { }

      Constructor Summary

      Constructors
      -
      -
      Constructor
      +
      +
      Modifier
      +
      Constructor
      Description
      +
       
      \ Foo(T arg)
       
      +
       <T extends X>
      \ Foo(T arg)
       
      +
       <T extends Y>
      \ Foo(T arg)
       
      @@ -188,13 +192,16 @@ class X { }

      Constructor Summary

      Constructors
      -
      -
      Constructor
      +
      +
      Modifier
      +
      Constructor
      Description
      +
       
      \ Foo\ - (T arg)
      + (T arg)
       
      +
       <T extends X>
      \ Foo(T arg)
       
      @@ -220,10 +227,10 @@ class X { } // methods checkOutput("Foo.html", true, """
      abstract T
      + method-summary-table-tab3">abstract T
      m\ - (T arg)
      + (T arg)
       
      Test Feature
      """); } -} \ No newline at end of file +} diff --git a/test/langtools/jdk/javadoc/doclet/testGenericMethodLinkTaglet/TestGenericMethodLinkTaglet.java b/test/langtools/jdk/javadoc/doclet/testGenericMethodLinkTaglet/TestGenericMethodLinkTaglet.java index a9a4be1b262b9..db23064e9ca8d 100644 --- a/test/langtools/jdk/javadoc/doclet/testGenericMethodLinkTaglet/TestGenericMethodLinkTaglet.java +++ b/test/langtools/jdk/javadoc/doclet/testGenericMethodLinkTaglet/TestGenericMethodLinkTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8188248 + * @bug 8188248 8313931 * @summary NullPointerException on generic methods * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -67,7 +67,7 @@ public void test(Path base) throws Exception { checkOutput("pkg/A.html", true, """ - A"""); + param T"""); } void createTestClass(Path srcDir) throws Exception { diff --git a/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java b/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java index f21ad223fb459..b9dff5ff2a897 100644 --- a/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java +++ b/test/langtools/jdk/javadoc/doclet/testHref/TestHref.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,7 +75,8 @@ public void test() { checkOutput("pkg/C4.html", true, //Header does not link to the page itself. - "Class C4<E extends C4<E>>", + """ + Class C4<E extends C4<E>>""", //Signature does not link to the page itself. """ public abstract class out a link."); p.add(pContent); body.add(p); - HtmlTree p1 = new HtmlTree(TagName.P); + HtmlTree p1 = new HtmlTree(HtmlTag.P); // Test another version of A tag. - HtmlTree anchor = new HtmlTree(TagName.A); + HtmlTree anchor = new HtmlTree(HtmlTag.A); anchor.put(HtmlAttr.HREF, "testLink.html"); anchor.put(HtmlAttr.ID, "Another version of a tag"); p1.add(anchor); body.add(p1); // Test for empty tags. - HtmlTree dl = new HtmlTree(TagName.DL); + HtmlTree dl = new HtmlTree(HtmlTag.DL); html.add(dl); // Test for empty nested tags. - HtmlTree dlTree = new HtmlTree(TagName.DL); - dlTree.add(new HtmlTree(TagName.DT)); - dlTree.add(new HtmlTree (TagName.DD)); + HtmlTree dlTree = new HtmlTree(HtmlTag.DL); + dlTree.add(new HtmlTree(HtmlTag.DT)); + dlTree.add(new HtmlTree (HtmlTag.DD)); html.add(dlTree); - HtmlTree dlDisplay = new HtmlTree(TagName.DL); - dlDisplay.add(new HtmlTree(TagName.DT)); - HtmlTree dd = new HtmlTree (TagName.DD); + HtmlTree dlDisplay = new HtmlTree(HtmlTag.DL); + dlDisplay.add(new HtmlTree(HtmlTag.DT)); + HtmlTree dd = new HtmlTree (HtmlTag.DD); TextBuilder ddContent = new TextBuilder("Test DD"); dd.add(ddContent); dlDisplay.add(dd); @@ -130,7 +132,7 @@ public static String generateHtmlTree() { body.add(emptyString); Comment emptyComment = new Comment(""); body.add(emptyComment); - HtmlTree hr = new HtmlTree(TagName.HR); + HtmlTree hr = new HtmlTree(HtmlTag.HR); body.add(hr); html.add(body); HtmlDocument htmlDoc = new HtmlDocument(html); diff --git a/test/langtools/jdk/javadoc/doclet/testHtmlTableStyles/TestHtmlTableStyles.java b/test/langtools/jdk/javadoc/doclet/testHtmlTableStyles/TestHtmlTableStyles.java index 2225de2f9c5d4..c11e6d5f720fa 100644 --- a/test/langtools/jdk/javadoc/doclet/testHtmlTableStyles/TestHtmlTableStyles.java +++ b/test/langtools/jdk/javadoc/doclet/testHtmlTableStyles/TestHtmlTableStyles.java @@ -50,7 +50,7 @@ public void test() { checkOutput(Output.OUT, true, "attribute not supported in HTML5: summary", """ - attribute "border" for table only accepts "" or "1": BORDER""", + attribute "border" for table only accepts "" or "1": border""", "attribute not supported in HTML5: cellpadding", "attribute not supported in HTML5: cellspacing", "attribute not supported in HTML5: align"); diff --git a/test/langtools/jdk/javadoc/doclet/testInterface/TestInterface.java b/test/langtools/jdk/javadoc/doclet/testInterface/TestInterface.java index cdba068ea4d78..f213d393fdfd1 100644 --- a/test/langtools/jdk/javadoc/doclet/testInterface/TestInterface.java +++ b/test/langtools/jdk/javadoc/doclet/testInterface/TestInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,12 +99,12 @@ public void test() {
      Specified by:
      method in interface&\ nbsp;Interface<CE>
      """, + href="#type-param-CE" title="type parameter in Child">CE>""", //Make sure "Overrides" has substituted type parameters. """
      Overrides:
      method in class Parent<Parent<CE>
      """); checkOutput("pkg/Parent.html", true, @@ -190,7 +190,7 @@ public void test1() {
      Overrides:
      method1 in class&\ nbsp;GrandParent<<\ - a href="Child.html" title="type parameter in Child">CE>"""); + a href="#type-param-CE" title="type parameter in Child">CE>"""); } @Test @@ -209,17 +209,17 @@ public void test2() { erface in pkg2">Spliterator Spliterator.\ OfDouble, Spliter\ - ator.OfInt<Integer>, Spliterator.OfPrimitive<T,T_C\ - ONS,T,T_C\ + ONS,T_SPLITR extends Spliterator.OfPrimitive<T,<\ - a href="Spliterator.OfPrimitive.html" title="type parameter in Spliterator.OfPri\ - mitive">T_CONS,T,<\ + a href="Spliterator.OfPrimitive.html#type-param-T_CONS" title="type parameter in Spliterator.OfPri\ + mitive">T_CONS,T_SPLITR>>"""); checkOutput("pkg2/Spliterator.html", true, """ @@ -236,21 +236,21 @@ public void test2() {
      static interface 
      Spliterator.OfInt<Integer&\ + Spliterator.OfInt.html#type-param-Integer" title="type parameter in Spliterator.OfInt">Integer&\ gt;
       
      static interface 
      Spliterator.OfPrimitive\ - <T,T,T_CONS,T_SPLITR extends <\ + e.html#type-param-T_SPLITR" title="type parameter in Spliterator.OfPrimitive">T_SPLITR extends <\ a href="Spliterator.OfPrimitive.html" title="interface in pkg2">Spliterator.OfPr\ - imitive<T,<T,T_CONS,T_SPLITRT_SPLITR>>
       
      """); diff --git a/test/langtools/jdk/javadoc/doclet/testLinkTaglet/TestLinkTagletTypeParam.java b/test/langtools/jdk/javadoc/doclet/testLinkTaglet/TestLinkTagletTypeParam.java new file mode 100644 index 0000000000000..5cb1f687d452f --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testLinkTaglet/TestLinkTagletTypeParam.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8313931 + * @summary Javadoc: links to type parameters actually generate links to classes + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @run main TestLinkTagletTypeParam + */ + +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +import java.io.IOException; +import java.nio.file.Path; + +public class TestLinkTagletTypeParam extends JavadocTester { + + public static void main(String... args) throws Exception { + var tester = new TestLinkTagletTypeParam(); + tester.runTests(); + } + + ToolBox tb = new ToolBox(); + + @JavadocTester.Test + public void testClassTypeParameterLink(Path base) throws IOException { + Path src = base.resolve("src"); + + tb.writeJavaFiles(src, + """ + /** + * Link to {@link F}. + * + * @param the first type param + * @param an Appendable + * + * @see APND the second type parameter + */ + public class Test { + private Test() {} + } + """); + + javadoc("-Xdoclint:none", + "-d", base.resolve("api").toString(), + "-sourcepath", src.toString(), + src.resolve("Test.java").toString()); + checkExit(JavadocTester.Exit.OK); + + checkOrder("Test.html", + """ +
      Type Parameters:
      +
      F - the first type param
      +
      APND - an Appendable
      """, + """ + Link to
      F.""", + """ +
      See Also:
      +
      + """); + } + + @JavadocTester.Test + public void testMethodTypeParameterLink(Path base) throws IOException { + Path src = base.resolve("src"); + + tb.writeJavaFiles(src, + """ + /** + * Class comment. + */ + public class Test { + /** + * Link to {@link T} and {@linkplain T link with label}. + * + * @param the T + * @param appendable the appendable + */ + public T append(final T appendable) { + return appendable; + } + } + """); + + javadoc("-Xdoclint:reference", + "-d", base.resolve("api").toString(), + "-sourcepath", src.toString(), + src.resolve("Test.java").toString()); + + checkOutput(JavadocTester.Output.OUT, true, + ""); + + checkOutput("Test.html", true, + """ + Link to T and link with label."""); + } +} diff --git a/test/langtools/jdk/javadoc/doclet/testMemberInheritance/TestMemberInheritance.java b/test/langtools/jdk/javadoc/doclet/testMemberInheritance/TestMemberInheritance.java index 705cae327e3a3..559f7b08d3877 100644 --- a/test/langtools/jdk/javadoc/doclet/testMemberInheritance/TestMemberInheritance.java +++ b/test/langtools/jdk/javadoc/doclet/testMemberInheritance/TestMemberInheritance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -120,8 +120,8 @@ interface in pkg">BaseInterface
      checkOutput("pkg2/DocumentedNonGenericChild.html", true, """
      -

      +
      public abstract class DocumentedNonGenericChild extends java.lang.Object
      diff --git a/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java b/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java index 79bad12f95db2..4d3628544f81e 100644 --- a/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java +++ b/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java @@ -1360,8 +1360,8 @@ void checkLinkSource(boolean includePrivate) { checkOutput("moduleA/testpkgmdlA/TestClassInModuleA.html", true, """
      -

      +
      public class TestClassInModuleA diff --git a/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java b/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java index a54fbbcff75f0..eafad8c3245f2 100644 --- a/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java +++ b/test/langtools/jdk/javadoc/doclet/testNewLanguageFeatures/TestNewLanguageFeatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,7 +117,7 @@ void checkTypeParameters() { // Check class type parameters section. """
      Type Parameters:
      -
      E - the type parameter for this class.""", +
      E - the type parameter for this class.
      """, // Type parameters in @see/@link """
      @@ -130,12 +130,14 @@ void checkTypeParameters() {
      """, // Method that uses class type parameter. """ - (E param)""", + (E param)""", // Method type parameter section. """
      Type Parameters:
      -
      T - This is the first type parameter.
      -
      V - This is the second type parameter.""", +
      T - Th\ + is is the first type parameter.
      +
      V - Th\ + is is the second type parameter.
      """, // Signature of method with type parameters """
      public E[]
      methodThatReturnsTypeParameterA(E[] \ + ref="#type-param-E" title="type parameter in TypeParameters">E[] \ e)""", """
      public E[] methodThatReturnsTypePa\ - rameterA((E[] e)
      """, """ @@ -176,7 +178,7 @@ void checkTypeParameters() { """
      <X extends java.lang.Throwable>
      E
      \ + href="#type-param-E" title="type parameter in TypeParameters">E
      \
      Type Parameters: -
      T2 - type 2
      +
      T2 - type 2
      Parameters:
      t1 - param 1
      t3 - param 3
      @@ -92,7 +92,7 @@ public void test() { checkOutput("pkg/C.Nested.html", true, """
      Type Parameters:
      -
      T1 - type 1
      +
      T1 - type 1
      """); } } diff --git a/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java b/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java index 7ce046c07575c..94ad1a8040f6a 100644 --- a/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java +++ b/test/langtools/jdk/javadoc/doclet/testProperty/TestProperty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -110,7 +110,7 @@ class in pkg">ObjectProperty
      <public final <\ span class="return-type">Obje\ - ctProperty<java.util.List<T>> listProperty<\ /div>
      This is an Object property where the Object is a single List<T>.
      diff --git a/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java b/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java index f9aa3cfd6fa49..3b6a8a4fc72e4 100644 --- a/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java +++ b/test/langtools/jdk/javadoc/doclet/testRecordTypes/TestRecordTypes.java @@ -175,7 +175,7 @@ public record R(int r1) { }"""); """
      Type Parameters:
      -
      T - This is a type parameter.
      +
      T - This is a type parameter.
      Record Components:
      r1 - This is a component.
      """, diff --git a/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java b/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java index 9bf2ac362c611..af3e36aa7d897 100644 --- a/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java +++ b/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -219,6 +219,7 @@ public void test2() { """ Fields[] singleArray""", """ - java.lang.Class<E> someClass"""); + java.lang.Class<E> someClass"""); } } diff --git a/test/langtools/jdk/javadoc/doclet/testThrows/TestThrows.java b/test/langtools/jdk/javadoc/doclet/testThrows/TestThrows.java index ca4592ea5f84c..e4c578578998f 100644 --- a/test/langtools/jdk/javadoc/doclet/testThrows/TestThrows.java +++ b/test/langtools/jdk/javadoc/doclet/testThrows/TestThrows.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,7 +83,7 @@ public interface C { """
      Type Parameters:
      -
      T - the throwable
      +
      T - the throwable
      Throws:
      T - if a specific error occurs
      java.lang.Exception - if an exception occurs
      diff --git a/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java b/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java index 7f586b3539d76..dad610acb302e 100644 --- a/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java +++ b/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,12 +23,13 @@ /* * @test - * @bug 4927167 4974929 7010344 8025633 8081854 8182765 8187288 8261976 + * @bug 4927167 4974929 6381729 7010344 8025633 8081854 8182765 8187288 8261976 8313931 * @summary When the type parameters are more than 10 characters in length, * make sure there is a line break between type params and return type * in member summary. Also, test for type parameter links in package-summary and * class-use pages. The class/annotation pages should check for type * parameter links in the class/annotation signature section when -linksource is set. + * Verify that generic type parameters on constructors are documented. * @library ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build javadoc.tester.* @@ -94,4 +95,37 @@ public class ParamTest2<java.util.List<? extends Foo4>>>"""); } + + @Test + public void test3() { + javadoc("-d", "out-3", + "-Xdoclint:none", + "--no-platform-links", + "-sourcepath", testSrc, + "pkg"); + checkExit(Exit.OK); + + checkOutput("pkg/CtorTypeParam.html", true, + """ +
       <T extends java.lang.Runnable>
      + +
      +
      Generic constructor.
      """, + """ +
      public\ +  <T extends java.lang.Runnable>\ +  CtorTypeParam()
      """, + """ + T""", + """ +
      Type Parameters:
      +
      T - the type parameter
      """, + """ +
      See Also:
      +
      + """); + } } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.hpp b/test/langtools/jdk/javadoc/doclet/testTypeParams/pkg/CtorTypeParam.java similarity index 60% rename from src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.hpp rename to test/langtools/jdk/javadoc/doclet/testTypeParams/pkg/CtorTypeParam.java index c20c483c77daf..690471861dbd9 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.hpp +++ b/test/langtools/jdk/javadoc/doclet/testTypeParams/pkg/CtorTypeParam.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -19,24 +19,17 @@ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. - * */ -#ifndef SHARE_GC_SHENANDOAH_MODE_SHENANDOAHIUMODE_HPP -#define SHARE_GC_SHENANDOAH_MODE_SHENANDOAHIUMODE_HPP - -#include "gc/shenandoah/mode/shenandoahMode.hpp" - -class ShenandoahHeuristics; - -class ShenandoahIUMode : public ShenandoahMode { -public: - virtual void initialize_flags() const; - virtual ShenandoahHeuristics* initialize_heuristics() const; - - virtual const char* name() { return "Incremental-Update (IU)"; } - virtual bool is_diagnostic() { return false; } - virtual bool is_experimental() { return true; } -}; +package pkg; -#endif // SHARE_GC_SHENANDOAH_MODE_SHENANDOAHIUMODE_HPP +public class CtorTypeParam { + /** + * Generic constructor. {@link T} + * + * @param the type parameter + * @see T link to type parameter + */ + public CtorTypeParam() { + } +} diff --git a/test/langtools/jdk/javadoc/doclet/testUnicode/TestUnicode.java b/test/langtools/jdk/javadoc/doclet/testUnicode/TestUnicode.java index 76008260343ca..cf30488ec7bc2 100644 --- a/test/langtools/jdk/javadoc/doclet/testUnicode/TestUnicode.java +++ b/test/langtools/jdk/javadoc/doclet/testUnicode/TestUnicode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,7 @@ public class Code<##> { """
      Type Parameters:
      -
      ## - the ##
      +
      ## - the ##
      """.replaceAll("##", chineseElephant), """ diff --git a/test/langtools/jdk/javadoc/doclet/testVoidHtmlElements/TestVoidHtmlElements.java b/test/langtools/jdk/javadoc/doclet/testVoidHtmlElements/TestVoidHtmlElements.java index 6229533af4a55..bd44fe8814119 100644 --- a/test/langtools/jdk/javadoc/doclet/testVoidHtmlElements/TestVoidHtmlElements.java +++ b/test/langtools/jdk/javadoc/doclet/testVoidHtmlElements/TestVoidHtmlElements.java @@ -25,13 +25,12 @@ * @test * @bug 8266856 * @modules jdk.javadoc/jdk.javadoc.internal.doclint - * jdk.javadoc/jdk.javadoc.internal.doclets.formats.html.markup + * jdk.javadoc/jdk.javadoc.internal.html * @run main TestVoidHtmlElements */ -import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; -import jdk.javadoc.internal.doclets.formats.html.markup.TagName; -import jdk.javadoc.internal.doclint.HtmlTag; +import jdk.javadoc.internal.html.HtmlTree; +import jdk.javadoc.internal.html.HtmlTag; public class TestVoidHtmlElements { @@ -42,9 +41,8 @@ public static void main(String[] args) { // check that the definition of void-ness is the same. for (HtmlTag htmlTag : HtmlTag.values()) { try { - TagName tagName = TagName.valueOf(htmlTag.name()); checks++; - check(htmlTag, tagName); + check(htmlTag); } catch (IllegalArgumentException e) { // no matching TagName } @@ -56,8 +54,8 @@ public static void main(String[] args) { System.out.println(checks + " checks passed"); } - private static void check(HtmlTag htmlTag, TagName tagName) { - boolean elementIsVoid = new HtmlTree(tagName).isVoid(); + private static void check(HtmlTag htmlTag) { + boolean elementIsVoid = new HtmlTree(htmlTag).isVoid(); boolean elementHasNoEndTag = htmlTag.endKind == HtmlTag.EndKind.NONE; if (elementIsVoid != elementHasNoEndTag) { throw new AssertionError(htmlTag + ", " + elementIsVoid + ", " + elementHasNoEndTag); diff --git a/test/langtools/tools/doclint/CoverageExtras.java b/test/langtools/tools/doclint/CoverageExtras.java index 5a6c90c4b7462..55ac63d6ab3d4 100644 --- a/test/langtools/tools/doclint/CoverageExtras.java +++ b/test/langtools/tools/doclint/CoverageExtras.java @@ -26,13 +26,15 @@ * @bug 8006263 * @summary Supplementary test cases needed for doclint * @modules jdk.javadoc/jdk.javadoc.internal.doclint + * jdk.javadoc/jdk.javadoc.internal.html */ import java.util.Objects; import jdk.javadoc.internal.doclint.Checker; -import jdk.javadoc.internal.doclint.HtmlTag; import jdk.javadoc.internal.doclint.Messages; +import jdk.javadoc.internal.html.HtmlAttr; +import jdk.javadoc.internal.html.HtmlTag; public class CoverageExtras { public static void main(String... args) { @@ -41,8 +43,8 @@ public static void main(String... args) { void run() { check(HtmlTag.A, HtmlTag.valueOf("A"), HtmlTag.values()); - check(HtmlTag.Attr.ABBR, HtmlTag.Attr.valueOf("ABBR"), HtmlTag.Attr.values()); - check(HtmlTag.AttrKind.INVALID, HtmlTag.AttrKind.valueOf("INVALID"), HtmlTag.AttrKind.values()); + check(HtmlAttr.ABBR, HtmlAttr.valueOf("ABBR"), HtmlAttr.values()); + check(HtmlAttr.AttrKind.INVALID, HtmlAttr.AttrKind.valueOf("INVALID"), HtmlAttr.AttrKind.values()); check(HtmlTag.BlockType.BLOCK, HtmlTag.BlockType.valueOf("BLOCK"), HtmlTag.BlockType.values()); check(HtmlTag.EndKind.NONE, HtmlTag.EndKind.valueOf("NONE"), HtmlTag.EndKind.values()); check(HtmlTag.Flag.EXPECT_CONTENT, HtmlTag.Flag.valueOf("EXPECT_CONTENT"), HtmlTag.Flag.values()); diff --git a/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.java b/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.java index 3b8f29d2d62af..6ef32c881bb78 100644 --- a/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.java +++ b/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.java @@ -100,10 +100,6 @@ public void SupportedAttrs_html5() { } * * hgroup no longer supported in HTML5. *
      - *
      - * Summary - *

      Details and Summary no longer supported in HTML5

      - *
      */ public void notSupportedTags_html5() { } @@ -152,6 +148,10 @@ public void notSupportedTags_html5() { } * *

      Test current time is at night

      *

      Testtext

      + *
      + * Summary + *

      Details

      + *
      */ public void SupportedTags_html5() { } diff --git a/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.out b/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.out index 69b31d53831f4..badd9ea998273 100644 --- a/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.out +++ b/test/langtools/tools/doclint/html/HtmlVersionTagsAttrsTest.out @@ -226,7 +226,7 @@ HtmlVersionTagsAttrsTest.java:67: error: attribute not supported in HTML5: width HtmlVersionTagsAttrsTest.java:68: error: attribute not supported in HTML5: name * Anchor Test ^ -HtmlVersionTagsAttrsTest.java:69: error: attribute "border" for table only accepts "" or "1": BORDER +HtmlVersionTagsAttrsTest.java:69: error: attribute "border" for table only accepts "" or "1": 0 * ^ HtmlVersionTagsAttrsTest.java:71: error: no caption for table @@ -259,19 +259,7 @@ HtmlVersionTagsAttrsTest.java:97: error: unknown tag: hgroup HtmlVersionTagsAttrsTest.java:100: error: unknown tag: hgroup * ^ -HtmlVersionTagsAttrsTest.java:103: error: unknown tag: details - *
      - ^ -HtmlVersionTagsAttrsTest.java:104: error: unknown tag: summary - * Summary - ^ -HtmlVersionTagsAttrsTest.java:104: error: unknown tag: summary - * Summary - ^ -HtmlVersionTagsAttrsTest.java:106: error: unknown tag: details - *
      - ^ -HtmlVersionTagsAttrsTest.java:129: error: element not allowed in documentation comments:
      +HtmlVersionTagsAttrsTest.java:125: error: element not allowed in documentation comments:
      *
      ^ HtmlVersionTagsAttrsTest.java:161: error: heading not found for @@ -298,7 +286,7 @@ HtmlVersionTagsAttrsTest.java:181: error: tag not allowed here:
      HtmlVersionTagsAttrsTest.java:184: error: element not allowed in documentation comments:
      *
      ^ -HtmlVersionTagsAttrsTest.java:189: error: attribute "border" for table only accepts "" or "1": BORDER +HtmlVersionTagsAttrsTest.java:189: error: attribute "border" for table only accepts "" or "1": 2 *
      ^ HtmlVersionTagsAttrsTest.java:191: error: no caption for table @@ -310,5 +298,5 @@ HtmlVersionTagsAttrsTest.java:202: error: no caption for table HtmlVersionTagsAttrsTest.java:205: error: no caption for table *
      ^ -102 errors +98 errors 2 warnings diff --git a/test/langtools/tools/doclint/html/OtherTagsTest.java b/test/langtools/tools/doclint/html/OtherTagsTest.java index e64b12a71a8b2..6da198b3cef4c 100644 --- a/test/langtools/tools/doclint/html/OtherTagsTest.java +++ b/test/langtools/tools/doclint/html/OtherTagsTest.java @@ -16,7 +16,7 @@ public class OtherTagsTest { * * *
      - * + * * * * diff --git a/test/langtools/tools/doclint/html/OtherTagsTest.out b/test/langtools/tools/doclint/html/OtherTagsTest.out index 571df0f20a3df..f8e06bb6bb6c0 100644 --- a/test/langtools/tools/doclint/html/OtherTagsTest.out +++ b/test/langtools/tools/doclint/html/OtherTagsTest.out @@ -13,9 +13,6 @@ OtherTagsTest.java:17: error: element not allowed in documentation comments: ^ -OtherTagsTest.java:19: error: element not allowed in documentation comments: - * - ^ OtherTagsTest.java:20: error: element not allowed in documentation comments: * ^ @@ -25,4 +22,4 @@ OtherTagsTest.java:21: error: tag not supported in HTML5: noframes OtherTagsTest.java:23: error: element not allowed in documentation comments: * <title> ^ -9 errors +8 errors diff --git a/test/langtools/tools/javac/annotations/repeatingAnnotations/CompletionErrorOnRepeatingAnnosTest.java b/test/langtools/tools/javac/annotations/repeatingAnnotations/CompletionErrorOnRepeatingAnnosTest.java new file mode 100644 index 0000000000000..b075a51015557 --- /dev/null +++ b/test/langtools/tools/javac/annotations/repeatingAnnotations/CompletionErrorOnRepeatingAnnosTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8332850 + * @summary javac crashes if container for repeatable annotation is not found + * @library /tools/javac/lib /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +import toolbox.*; +import toolbox.Task.*; + +public class CompletionErrorOnRepeatingAnnosTest { + ToolBox tb = new ToolBox(); + + public static void main(String... args) throws Exception { + CompletionErrorOnRepeatingAnnosTest t = new CompletionErrorOnRepeatingAnnosTest(); + //t.testMissingContainerAnno(); + t.testMissingContainerTypeAnno(); + } + + void testMissingContainerTypeAnno() throws Exception { + doTest( + """ + import java.lang.annotation.*; + import static java.lang.annotation.RetentionPolicy.*; + import static java.lang.annotation.ElementType.*; + @Target({TYPE_USE,FIELD}) @Repeatable( As.class) @interface A { } + @Target({TYPE_USE,FIELD}) @interface As { A[] value(); } + """, + """ + class T { + @A @A String data = "test"; + } + """, + List.of( + "T.java:2:5: compiler.err.cant.access: As, (compiler.misc.class.file.not.found: As)", + "T.java:2:8: compiler.err.invalid.repeatable.annotation.no.value: As", + "2 errors" + ) + ); + } + + void testMissingContainerAnno() throws Exception { + doTest( + """ + import java.lang.annotation.Repeatable; + @Repeatable(As.class) + @interface A {} + @interface As { + A[] value(); + } + """, + "@A @A class T {}", + List.of( + "T.java:1:1: compiler.err.cant.access: As, (compiler.misc.class.file.not.found: As)", + "T.java:1:4: compiler.err.invalid.repeatable.annotation.no.value: As", + "2 errors" + ) + ); + } + + private void doTest(String annosSrc, String annotatedSrc, List expectedOutput) throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + tb.createDirectories(src); + tb.writeJavaFiles(src, annosSrc); + Path out = base.resolve("out"); + tb.createDirectories(out); + new JavacTask(tb) + .outdir(out) + .files(tb.findJavaFiles(src)) + .run(); + // let's now compile T.java which uses repeated annotations, we want to load the anno classes from the CP + tb.deleteFiles(src.resolve("A.java")); + tb.writeJavaFiles(src, annotatedSrc); + new JavacTask(tb) + .outdir(out) + .classpath(out) + .files(tb.findJavaFiles(src)) + .run(); + // now if we remove As.class there will be an error but javac should not crash + tb.deleteFiles(out.resolve("As.class")); + List log = new JavacTask(tb) + .outdir(out) + .classpath(out) + .options("-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .run(Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + if (!expectedOutput.equals(log)) + throw new Exception("expected output not found: " + log); + } +} diff --git a/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultVerifier.java b/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultVerifier.java index a5598d1f24270..d85b37aa3a395 100644 --- a/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultVerifier.java +++ b/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,8 +70,8 @@ private TestElementValue get(int tag) { private abstract class TestElementValue { public void testLength(TestResult testCase, AnnotationDefaultAttribute attr) { - BufWriter buf = new BufWriterImpl(ConstantPoolBuilder.of(), (ClassFileImpl) ClassFile.of()); - attr.defaultValue().writeTo(buf); + var buf = new BufWriterImpl(ConstantPoolBuilder.of(), (ClassFileImpl) ClassFile.of()); + AnnotationReader.writeAnnotationValue(buf, attr.defaultValue()); testCase.checkEquals(((BoundAttribute)attr).payloadLen(), buf.size(), "attribute_length"); } diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestUnexpectedIUBarrierEA.java b/test/langtools/tools/javac/lambda/VoidReturnBoxing.java similarity index 50% rename from test/hotspot/jtreg/gc/shenandoah/compiler/TestUnexpectedIUBarrierEA.java rename to test/langtools/tools/javac/lambda/VoidReturnBoxing.java index 8662130c37877..2dc57af1b796e 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestUnexpectedIUBarrierEA.java +++ b/test/langtools/tools/javac/lambda/VoidReturnBoxing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, Alphabet LLC. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,52 +23,41 @@ /* * @test - * bug 8280885 - * @summary Shenandoah: Some tests failed with "EA: missing allocation reference path" - * @requires vm.gc.Shenandoah - * - * @run main/othervm -XX:-BackgroundCompilation -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=iu - * -XX:CompileCommand=dontinline,TestUnexpectedIUBarrierEA::notInlined TestUnexpectedIUBarrierEA + * @bug 8336491 + * @summary Verify that void returning expression lambdas don't box their result + * @modules jdk.compiler + * jdk.jdeps/com.sun.tools.javap */ -public class TestUnexpectedIUBarrierEA { +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Path; - private static Object field; +public class VoidReturnBoxing { public static void main(String[] args) { - for (int i = 0; i < 20_000; i++) { - test(false); - } + new VoidReturnBoxing().run(); } - private static void test(boolean flag) { - A a = new A(); - B b = new B(); - b.field = a; - notInlined(); - Object o = b.field; - if (!(o instanceof A)) { - + void run() { + Path path = Path.of(System.getProperty("test.classes"), "T.class"); + StringWriter s; + String out; + try (PrintWriter pw = new PrintWriter(s = new StringWriter())) { + com.sun.tools.javap.Main.run(new String[] {"-p", "-c", path.toString()}, pw); + out = s.toString(); } - C c = new C(); - c.field = o; - if (flag) { - field = c.field; + if (out.contains("java/lang/Integer.valueOf")) { + throw new AssertionError( + "Unnecessary boxing of void returning expression lambda result:\n\n" + out); } } +} - private static void notInlined() { - - } - - private static class A { - } - - private static class B { - public Object field; +class T { + int g() { + return 0; } - private static class C { - public Object field; - } + Runnable r = () -> g(); } diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index 5e70e84482d32..e3ed1c3e8af51 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8192920 8204588 8246774 8248843 8268869 8235876 8328339 + * @bug 8192920 8204588 8246774 8248843 8268869 8235876 8328339 8335896 * @summary Test source launcher * @library /tools/lib * @enablePreview @@ -276,6 +276,27 @@ public void testSystemProperty(Path base) throws IOException { checkEqual("stdout", log.trim(), file.toAbsolutePath().toString()); } + @Test + public void testThreadContextClassLoader(Path base) throws IOException { + tb.writeJavaFiles(base, //language=java + """ + class ThreadContextClassLoader { + public static void main(String... args) { + var expected = ThreadContextClassLoader.class.getClassLoader(); + var actual = Thread.currentThread().getContextClassLoader(); + System.out.println(expected == actual); + } + } + """); + + Path file = base.resolve("ThreadContextClassLoader.java"); + String log = new JavaTask(tb) + .className(file.toString()) + .run(Task.Expect.SUCCESS) + .getOutput(Task.OutputKind.STDOUT); + checkEqual("stdout", log.trim(), "true"); + } + void testSuccess(Path file, String expect) throws IOException { Result r = run(file, Collections.emptyList(), List.of("1", "2", "3")); checkEqual("stdout", r.stdOut, expect); diff --git a/test/langtools/tools/javac/patterns/T8336781.java b/test/langtools/tools/javac/patterns/T8336781.java new file mode 100644 index 0000000000000..01876ed3c0a4b --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8336781.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8336781 + * @summary Erroneous exhaustivity check with boolean switch + * @enablePreview + * @compile -XDshould-stop.at=FLOW T8336781.java + */ +public class T8336781 { + public static void test() { + Boolean bool = null; + var _ = switch (bool) { + case null -> "nothing"; + case true -> "something true"; + case false -> "something false"; + }; + } +} diff --git a/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java b/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java index 2404394690ebd..c87796da47e7e 100644 --- a/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java +++ b/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java @@ -323,6 +323,7 @@ private static void generateDockerFile(Path dockerfile, String baseImage, String baseImageVersion) throws Exception { String template = "FROM %s:%s\n" + + "RUN apt-get install libubsan1\n" + "COPY /jdk /jdk\n" + "ENV JAVA_HOME=/jdk\n" + "CMD [\"/bin/bash\"]\n"; diff --git a/test/lib/jdk/test/lib/process/ProcessTools.java b/test/lib/jdk/test/lib/process/ProcessTools.java index aafb6fe3f4c74..6ab36c8bfd3b9 100644 --- a/test/lib/jdk/test/lib/process/ProcessTools.java +++ b/test/lib/jdk/test/lib/process/ProcessTools.java @@ -42,6 +42,7 @@ import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -965,6 +966,15 @@ public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException return rslt; } + @Override + public boolean waitFor(Duration duration) throws InterruptedException { + boolean rslt = p.waitFor(duration); + if (rslt) { + waitForStreams(); + } + return rslt; + } + private void waitForStreams() throws InterruptedException { try { stdoutTask.get(); diff --git a/test/jdk/java/lang/Thread/virtual/ThreadBuilders.java b/test/lib/jdk/test/lib/thread/VThreadScheduler.java similarity index 63% rename from test/jdk/java/lang/Thread/virtual/ThreadBuilders.java rename to test/lib/jdk/test/lib/thread/VThreadScheduler.java index a6a5d6abdbca5..46b75fbe5eb9c 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadBuilders.java +++ b/test/lib/jdk/test/lib/thread/VThreadScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,28 +21,50 @@ * questions. */ +package jdk.test.lib.thread; + import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; /** - * Helper class for creating Thread buidlers. + * Helper class to allow tests run virtual threads with a custom scheduler. * * Tests using this class need to open java.base/java.lang. */ -class ThreadBuilders { - private ThreadBuilders() { } +public class VThreadScheduler { + private VThreadScheduler() { } - private static final Constructor VTBUILDER_CTOR; - static { + /** + * Returns the scheduler for the given virtual thread. + */ + public static Executor scheduler(Thread thread) { + if (!thread.isVirtual()) + throw new IllegalArgumentException("Not a virtual thread"); try { - Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); - Constructor ctor = clazz.getDeclaredConstructor(Executor.class); - ctor.setAccessible(true); - VTBUILDER_CTOR = ctor; + Field scheduler = Class.forName("java.lang.VirtualThread") + .getDeclaredField("scheduler"); + scheduler.setAccessible(true); + return (Executor) scheduler.get(thread); } catch (Exception e) { - throw new InternalError(e); + throw new RuntimeException(e); + } + } + + /** + * Return true if custom schedulers are supported. + */ + public static boolean supportsCustomScheduler() { + try (var pool = Executors.newCachedThreadPool()) { + try { + virtualThreadBuilder(pool); + return true; + } catch (UnsupportedOperationException e) { + return false; + } } } @@ -50,9 +72,12 @@ private ThreadBuilders() { } * Returns a builder to create virtual threads that use the given scheduler. * @throws UnsupportedOperationException if custom schedulers are not supported */ - static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { + public static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { try { - return (Thread.Builder.OfVirtual) VTBUILDER_CTOR.newInstance(scheduler); + Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); + Constructor ctor = clazz.getDeclaredConstructor(Executor.class); + ctor.setAccessible(true); + return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException re) { @@ -65,16 +90,10 @@ static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { } /** - * Return true if custom schedulers are supported. + * Returns a ThreadFactory to create virtual threads that use the given scheduler. + * @throws UnsupportedOperationException if custom schedulers are not supported */ - static boolean supportsCustomScheduler() { - try (var pool = Executors.newCachedThreadPool()) { - try { - virtualThreadBuilder(pool); - return true; - } catch (UnsupportedOperationException e) { - return false; - } - } + public static ThreadFactory virtualThreadFactory(Executor scheduler) { + return virtualThreadBuilder(scheduler).factory(); } } diff --git a/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java b/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java index adb699bac6a41..6b7efa2dc8752 100644 --- a/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java +++ b/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ public class DiagnosticCommand { public enum DiagnosticArgumentType { - JLONG, BOOLEAN, STRING, NANOTIME, STRINGARRAY, MEMORYSIZE + JLONG, BOOLEAN, STRING, NANOTIME, STRINGARRAY, MEMORYSIZE, FILE } private String name; diff --git a/test/micro/org/openjdk/bench/java/lang/StringConcat.java b/test/micro/org/openjdk/bench/java/lang/StringConcat.java index c4d2d1bfe1155..015ad224631f1 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringConcat.java +++ b/test/micro/org/openjdk/bench/java/lang/StringConcat.java @@ -219,4 +219,16 @@ public String concat23StringConst() { * questions. """; } + + public static void main(String... args) { + StringConcat concat = new StringConcat(); + concat.concat4String(); + concat.concat123String(); + concat.concat6String(); + concat.concat13String(); + concat.concat23String(); + concat.concatConstInt(); + } + + } diff --git a/test/micro/org/openjdk/bench/java/lang/StringConcatStartup.java b/test/micro/org/openjdk/bench/java/lang/StringConcatStartup.java new file mode 100644 index 0000000000000..d146fbf9885a9 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/StringConcatStartup.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +import java.util.concurrent.TimeUnit; + +/** + * Benchmarks stressing String concat startup. Provides a main method that takes names of the sub-benchmarks + * of choice as arguments to work well as a standalone startup test/diagnostic + * + * StringSingle + * MixedSmall - small number of mixed expressions + * StringLarge - large number of expressions with a mix of String arguments and constants + * MixedLarge - large number of expressions with a mix of constants, Strings and primivitive arguments + */ +public class StringConcatStartup { + + public static void main(String... args) { + String[] selection = new String[] { "StringLarge", "MixedSmall", "StringSingle", "MixedLarge" }; + if (args.length > 0) { + selection = args; + } + for (String select : selection) { + switch (select) { + case "StringSingle" -> new StringSingle().run(); + case "MixedSmall" -> new MixedSmall().run(); + case "StringLarge" -> new StringLarge().run(); + case "MixedLarge" -> new MixedLarge().run(); + } + } + } + + @BenchmarkMode(Mode.SingleShotTime) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @State(Scope.Thread) + @Fork(value = 40, warmups = 2) + public static class StringSingle { + + public String s = "foo"; + + @Benchmark + public String run() { + return "" + s; + } + } + + + @BenchmarkMode(Mode.SingleShotTime) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @State(Scope.Thread) + @Fork(value = 20, warmups = 2) + public static class MixedSmall { + + public String s = "foo"; + public int i = 17; + public long l = 21L; + public char c = 'a'; + public boolean z = true; + + @Benchmark + public String run() { + String concat; + concat = "foo" + s + "bar" + i + "baz" + l + "bur" + c + "dub" + z + "foo"; + concat = "bar" + i + "baz" + l + c + "dub" + z + "foo"; + concat = "bar" + i + "baz" + l + "dub" + z; + concat = s + "bar" + i + s + "bur" + c + "dub" + s + "foo"; + return concat; + } + } + + @BenchmarkMode(Mode.SingleShotTime) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @State(Scope.Thread) + @Fork(value = 10, warmups = 2) + public static class StringLarge { + + public String i = "1"; + public String l = "2"; + public String b = "3"; + public String s = "4"; + public String c = "5"; + public String S = "6"; + public String z = "7"; + public String f = "8"; + public String d = "9"; + + @Benchmark + public void run() { + String concat; + concat = "" + "S" + f + l + z + f + "S" + d + S + d + S; + concat = "" + "S" + S + i + b + b + z + i + s + S + b + "S"; + concat = "" + S + f + f + f + b + f + "S" + S + S + i + b; + concat = "" + b + l + i + l + b + S + i + i + f + z; + concat = "" + f + z + d + b + "S" + c + S + f + s + s + d; + concat = "" + f + b + d + d + l + s + s + b + l + c + z; + concat = "" + S + z + l + s + s + i + f + c + i + i + d; + concat = "" + b + "S" + c + d + "S" + d + s + "S" + f + c + l + "S" + i + z + d + "S"; + concat = "" + S + "S" + S + i + c + z + i + i + S + b; + concat = "" + S + S + d + s + z + f + z + i + b + s + s + "S"; + concat = "" + i + z + f + d + f + S + c + "S" + i; + concat = "" + c + c + c + "S" + S + l; + concat = "" + z + d + s + i + l + i + z + c + i + f + l + s + b + S + S + s + z + "S" + c + z; + concat = "" + d + b + l + S + s + b + "S" + c + d + c + c + l + d + S + b + l + b + S + d + "S"; + concat = "" + c + z + c + d + b + S + c + b + S + "S" + d + s + c + s + b + c + b + z + s + i; + concat = "" + l + S + "S"; + concat = "" + s + i + f + S + f + i + s + d + S + l + i + "S" + i + S + d + i + l + c + i + d; + concat = "" + S + l + s + i + b + f + z + c + S + d + s + f + l + i + s + b + f + s + d + l; + concat = "" + i + d + b + d + S + b + d + "S" + "S" + i + l + i + b + "S" + "S" + s + "S" + i + b + c; + concat = "" + "S" + l + "S" + s + d + l + i + l + z + s + i + z + b + b + c + S + d + d + s + i; + concat = "" + b + c + i + b + z + d + z + z + d + z + l + b + z + f + b + c + d + c + z + c; + concat = "" + b + z + f + b + z + f + s + z + f + "S" + l + f + l + z + b + z + i + l + i + S; + concat = "" + c + b + "S" + z; + concat = "" + b + "S" + i + "S" + S + i + l + c + i + c + z + z + d + "S" + z + z + c + z + z + i; + concat = "" + f + c + c + "S" + c + s + i + z + b + s + f + b + i + i + z + f + d + f + i + i; + concat = "" + d + s + z + l + s + d + S + i + S + s + i + c + b + c + s + "S" + d + S + f + s; + concat = "" + S + f + s + z + d + d + S + s + s + z + f + z + "S" + i + d + d + S + c + S + "S"; + concat = "" + c + c + b + S + "S" + "S" + d + S + s + b + c + d + z + c + b + i + S + z + i + s; + concat = "" + l + l + d + z + s + s + i + i + l + c + f + z + i + f + l + z + s + d + f + l; + concat = "" + f + d + "S" + s; + concat = "" + d + S + "S" + S + f + "S" + c + i + s + b + c + b + l + f + S + c + c + i + z + s; + concat = "" + z + "S" + s + S + s + d + d + s + f + "S" + f + "S" + i + S + "S" + c + l + b + f + f; + concat = "" + l + f + d + b + s + f + d + "S" + l + s + "S" + b + b + s + S + S + "S" + "S" + d + b; + concat = "" + b + l + f + b + S + f + z + s + S + f + b + b + s + s + b + s + l + d + l; + concat = "" + b + b + S + S + S + z + z + d + "S" + l + "S" + s + i + "S" + c + f + S + f + i; + concat = "" + l + l + f + i + S + s + "S" + "S" + z + d + "S" + l + d + b + f + f + l + b + b; + concat = "" + l + f + "S" + f + f + i + l + l + i + S + b + f + d + i + c + c + d + d + i; + concat = "" + l + b + s + d + i + i + d + c + "S" + s + f + d + z + d + S + c; + concat = "" + f + s + "S" + z + s + "S" + b + b + b + d + d + b + z + l + c + b; + concat = "" + l + d + "S" + b + z + z + f + c + z + c + c + c + c + d; + concat = "" + z + d + l + "S" + i + s + b + b + d + s + s; + concat = "" + f + i + d + S + f + f + i + s + d + S + c + l + d + s + c + i; + concat = "" + f + c + i + "S" + "S" + c + f + b + l + i + s + c + i + S + S + i; + concat = "" + z + S + z + d + d + S + "S" + f + d + s + s + "S" + l + z + l + c; + concat = "" + b + c + s + f + S + l + b + f + "S" + l + "S" + c + c + z + b + b; + concat = "" + c + b + z + s + d + l + l + S + l + "S" + f + S + c + f + s + f; + concat = "" + z + z + d + i + z + s + z + S + f + S + "S" + "S" + l + d + c + d; + concat = "" + c + S + s + f + c + i + b + l + S + c + l + f + f + l + i + l; + concat = "" + "S" + i + f + d + s + S + S + l + s + S + l + "S" + b + l + s + l + d + d + f + S; + concat = "" + l + z + c + l + f + f + d + s + l + b + d + f + S + S + "S" + i + i + s + f + i; + concat = "" + S + S + l + S + z + d + s + c + "S" + d + f + d + f + f + z + i + f + l + S + s; + concat = "" + z + d + z + l + f + s + d + z + i + S + S + d + i + z + c + i + i + f + b + "S"; + concat = "" + b + d + "S" + f + f + d + s + i + b + l + i + b + f + f + b + f + l + i + z + l; + concat = "" + c + z + s + "S" + z + f + "S" + i + f + s + l + i + "S" + d + i + b + i + S + b + l; + concat = "" + d + l + s + c + l + d + "S" + "S" + s + S + f + z + b + s + b + f + z + z + l + l; + concat = "" + f + b + "S" + s + i + "S" + s + f + c + f + c + f + i + i + b + i + i + b + S + S; + concat = "" + i + i + s + i + s + S + s + "S" + c + c + f + s + d + l + l + d + f + l + i + S; + concat = "" + z + d + z + "S" + c + i + f + s + b + S + i + c + s + b + c + f + s + z + f + c; + concat = "" + f + s + f + b + l + z + f + f + f + c + z + S + b + s + z + i + s + S + i + b; + concat = "" + d + i + S + b + i + "S" + l + S + S + S + z + i + z + b; + concat = "" + "S" + S + s + l + f + i + l + b + f + S + d + c + b + d; + concat = "" + c + i + i + d + S + z + c + i + c + S + f + i + c + c; + concat = "" + "S" + "S" + c + d + z + l + d + z + f + b + d + z + S + f; + concat = "" + b + d + z + d + i + z + d + b + d + "S" + c + f + d; + concat = "" + d + s + f + c + i + "S" + b + b + S + i + s + d + "S" + f; + concat = "" + l + S + d + b + S + s + "S" + s + s + l + S + "S" + c + d; + concat = "" + c + s + z + c + S + S + "S" + l + S + f + f + c + S + f; + concat = "" + d + i + s + c + z + "S" + d + f + "S" + S + c + b + "S" + c; + concat = "" + i + b + "S" + l + S + d + "S" + c + b + s + f + l + f + "S"; + concat = "" + c + b + f + "S" + S + s + i + l + s + z + z + f + l + b; + concat = "" + S + s + "S" + d + s + z + "S" + i + i + z + S + b + f + i; + concat = "" + z + S + S + "S" + S + S + z + b + S + z + b + f + s + l; + concat = "" + s + z + d + "S" + z + l + f + z + s + z + d + l + s + l; + concat = "" + l + d + i + s + i + c + i + f + b + f + s + b + s + s; + concat = "" + z + "S" + S + "S" + "S" + i + "S" + s + d + z + l; + concat = "" + i + S + S + "S" + f + "S" + "S" + z + S + z + b + z + c + b; + concat = "" + i + f + f + d + z + f + z + b + "S" + c + l + l + z + s + S + s; + concat = "" + b + b + z + "S" + f + s + "S" + l +c + S + i + i + b + "S" + S; + concat = "" + i + "S" + d + d + d + "S" + f + "S" + b + s + S + i + "S" + d + b; + concat = "" + s + f + b + d + c + d + c + S + S + b + i + b + z + c; + concat = "" + l + l + S + l + f + s + i + c + z + f + d + l + f + b + l + f + f + i + i + z; + concat = "" + l + l + l + l + s + s + f + i + i + f + z + c + S + s + f + "S" + "S" + s + z + s; + concat = "" + S + z + f + b + l + c + i + l; + concat = "" + c + z + b + f + i + i + f + d + f + f + d + d + l + d + S + "S" + i + c + b + f; + concat = "" + s + d + S + d + b + l + l + f + b + "S" + i + z + b + S + S + c + S + f + S + z; + concat = "" + l + S + S + i + l + s + d + f + z + i + "S" + b + f + c + z + c + S + c + i + s; + concat = "" + l + S + S + s + f + S + s + "S" + c + c + c; + concat = "" + s + "S" + c + d + z + c + l + c + z + S + i + f + c + c + s + "S" + S + z + s + "S"; + concat = "" + c + i + z + s + b + s + s + b + "S" + d + "S" + z + f + "S" + c + S + s + S + b + i; + concat = "" + s + c + d + d + "S" + "S" + l + s + i + l + l + f + S + f + f + i + S + d + l + c; + concat = "" + "S" + S + b + c + i + "S" + c + c + s + i + "S" + b + i + b + b + S + f + l + s + "S"; + concat = "" + l + l + b + f + i + i + f + z + c + S + b + f + z + "S" + s + z + "S" + f + S + s; + concat = "" + i + c + b + i + b + z + "S" + i + c + i + l + "S" + z + b + b + i + i + c + i + f; + concat = "" + "S" + c + d + z + d + f + c + c + b + "S" + l + f + d + "S" + s + s + S + i + s + i; + concat = "" + S + "S" + d + c + "S" + S + "S" + b + f + z + "S" + l + d + f + "S" + S + d + b + c + c; + concat = "" + f + S + l + s + l + z + S + d + S + b + f + c + s + b + "S" + z + "S" + "S" + b + z; + concat = "" + f + s + c + i + S + b + s + S + i + S + c + b + s + d + i + "S" + s + l + c + s; + concat = "" + l + f + s + b + d + b + i + c + c + b + s + f + i + z + s + i + s + "S" + l + z; + concat = "" + d + z + z + c + b + b + s + b + S + l + d + i + S + d + "S" + i + S + i + b + S; + concat = "" + c + d + "S" + f + i + b + d + c + z + f + "S" + i + d + b + f + s + "S" + c + S + i; + concat = "" + i + z + "S" + b + S + s + c + s + f + S + S + f + z + s + b + d + z + i + s + z; + concat = "" + z + s + z + l + "S" + S + s + "S" + i + b + c + s + l + l + s + i + c + i + i + d; + concat = "" + "S" + b + l + z + c + f + l + S + "S" + l + i + z + z + l + S + "S" + z + S + z + c + "S"; + concat = "" + "S" + f + S + i + i + i + "S" + i + i + l + c + l + S + S + z + b + i + c + f + S; + concat = "" + c + z + S + S + b + i + c; + concat = "" + S + s + S + c; + } + } + + @BenchmarkMode(Mode.SingleShotTime) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @State(Scope.Thread) + @Fork(value = 10, warmups = 2) + public static class MixedLarge { + + public int i = 17; + public long l = 21L; + public byte b = (byte)17; + public short s = (short)17; + public char c = 'a'; + public String S = "S"; + public float f = 1.0f; + public double d = 2.0; + public boolean z = true; + + @Benchmark + public void run() { + String concat; + concat = "" + "S" + f + l + z + f + "S" + d + S + d + S; + concat = "" + "S" + S + i + b + b + z + i + s + S + b + "S"; + concat = "" + S + f + f + f + b + f + "S" + S + S + i + b; + concat = "" + b + l + i + l + b + S + i + i + f + z; + concat = "" + f + z + d + b + "S" + c + S + f + s + s + d; + concat = "" + f + b + d + d + l + s + s + b + l + c + z; + concat = "" + S + z + l + s + s + i + f + c + i + i + d; + concat = "" + b + "S" + c + d + "S" + d + s + "S" + f + c + l + "S" + i + z + d + "S"; + concat = "" + S + "S" + S + i + c + z + i + i + S + b; + concat = "" + S + S + d + s + z + f + z + i + b + s + s + "S"; + concat = "" + i + z + f + d + f + S + c + "S" + i; + concat = "" + c + c + c + "S" + S + l; + concat = "" + z + d + s + i + l + i + z + c + i + f + l + s + b + S + S + s + z + "S" + c + z; + concat = "" + d + b + l + S + s + b + "S" + c + d + c + c + l + d + S + b + l + b + S + d + "S"; + concat = "" + c + z + c + d + b + S + c + b + S + "S" + d + s + c + s + b + c + b + z + s + i; + concat = "" + l + S + "S"; + concat = "" + s + i + f + S + f + i + s + d + S + l + i + "S" + i + S + d + i + l + c + i + d; + concat = "" + S + l + s + i + b + f + z + c + S + d + s + f + l + i + s + b + f + s + d + l; + concat = "" + i + d + b + d + S + b + d + "S" + "S" + i + l + i + b + "S" + "S" + s + "S" + i + b + c; + concat = "" + "S" + l + "S" + s + d + l + i + l + z + s + i + z + b + b + c + S + d + d + s + i; + concat = "" + b + c + i + b + z + d + z + z + d + z + l + b + z + f + b + c + d + c + z + c; + concat = "" + b + z + f + b + z + f + s + z + f + "S" + l + f + l + z + b + z + i + l + i + S; + concat = "" + c + b + "S" + z; + concat = "" + b + "S" + i + "S" + S + i + l + c + i + c + z + z + d + "S" + z + z + c + z + z + i; + concat = "" + f + c + c + "S" + c + s + i + z + b + s + f + b + i + i + z + f + d + f + i + i; + concat = "" + d + s + z + l + s + d + S + i + S + s + i + c + b + c + s + "S" + d + S + f + s; + concat = "" + S + f + s + z + d + d + S + s + s + z + f + z + "S" + i + d + d + S + c + S + "S"; + concat = "" + c + c + b + S + "S" + "S" + d + S + s + b + c + d + z + c + b + i + S + z + i + s; + concat = "" + l + l + d + z + s + s + i + i + l + c + f + z + i + f + l + z + s + d + f + l; + concat = "" + f + d + "S" + s; + concat = "" + d + S + "S" + S + f + "S" + c + i + s + b + c + b + l + f + S + c + c + i + z + s; + concat = "" + z + "S" + s + S + s + d + d + s + f + "S" + f + "S" + i + S + "S" + c + l + b + f + f; + concat = "" + l + f + d + b + s + f + d + "S" + l + s + "S" + b + b + s + S + S + "S" + "S" + d + b; + concat = "" + b + l + f + b + S + f + z + s + S + f + b + b + s + s + b + s + l + d + l; + concat = "" + b + b + S + S + S + z + z + d + "S" + l + "S" + s + i + "S" + c + f + S + f + i; + concat = "" + l + l + f + i + S + s + "S" + "S" + z + d + "S" + l + d + b + f + f + l + b + b; + concat = "" + l + f + "S" + f + f + i + l + l + i + S + b + f + d + i + c + c + d + d + i; + concat = "" + l + b + s + d + i + i + d + c + "S" + s + f + d + z + d + S + c; + concat = "" + f + s + "S" + z + s + "S" + b + b + b + d + d + b + z + l + c + b; + concat = "" + l + d + "S" + b + z + z + f + c + z + c + c + c + c + d; + concat = "" + z + d + l + "S" + i + s + b + b + d + s + s; + concat = "" + f + i + d + S + f + f + i + s + d + S + c + l + d + s + c + i; + concat = "" + f + c + i + "S" + "S" + c + f + b + l + i + s + c + i + S + S + i; + concat = "" + z + S + z + d + d + S + "S" + f + d + s + s + "S" + l + z + l + c; + concat = "" + b + c + s + f + S + l + b + f + "S" + l + "S" + c + c + z + b + b; + concat = "" + c + b + z + s + d + l + l + S + l + "S" + f + S + c + f + s + f; + concat = "" + z + z + d + i + z + s + z + S + f + S + "S" + "S" + l + d + c + d; + concat = "" + c + S + s + f + c + i + b + l + S + c + l + f + f + l + i + l; + concat = "" + "S" + i + f + d + s + S + S + l + s + S + l + "S" + b + l + s + l + d + d + f + S; + concat = "" + l + z + c + l + f + f + d + s + l + b + d + f + S + S + "S" + i + i + s + f + i; + concat = "" + S + S + l + S + z + d + s + c + "S" + d + f + d + f + f + z + i + f + l + S + s; + concat = "" + z + d + z + l + f + s + d + z + i + S + S + d + i + z + c + i + i + f + b + "S"; + concat = "" + b + d + "S" + f + f + d + s + i + b + l + i + b + f + f + b + f + l + i + z + l; + concat = "" + c + z + s + "S" + z + f + "S" + i + f + s + l + i + "S" + d + i + b + i + S + b + l; + concat = "" + d + l + s + c + l + d + "S" + "S" + s + S + f + z + b + s + b + f + z + z + l + l; + concat = "" + f + b + "S" + s + i + "S" + s + f + c + f + c + f + i + i + b + i + i + b + S + S; + concat = "" + i + i + s + i + s + S + s + "S" + c + c + f + s + d + l + l + d + f + l + i + S; + concat = "" + z + d + z + "S" + c + i + f + s + b + S + i + c + s + b + c + f + s + z + f + c; + concat = "" + f + s + f + b + l + z + f + f + f + c + z + S + b + s + z + i + s + S + i + b; + concat = "" + d + i + S + b + i + "S" + l + S + S + S + z + i + z + b; + concat = "" + "S" + S + s + l + f + i + l + b + f + S + d + c + b + d; + concat = "" + c + i + i + d + S + z + c + i + c + S + f + i + c + c; + concat = "" + "S" + "S" + c + d + z + l + d + z + f + b + d + z + S + f; + concat = "" + b + d + z + d + i + z + d + b + d + "S" + c + f + d; + concat = "" + d + s + f + c + i + "S" + b + b + S + i + s + d + "S" + f; + concat = "" + l + S + d + b + S + s + "S" + s + s + l + S + "S" + c + d; + concat = "" + c + s + z + c + S + S + "S" + l + S + f + f + c + S + f; + concat = "" + d + i + s + c + z + "S" + d + f + "S" + S + c + b + "S" + c; + concat = "" + i + b + "S" + l + S + d + "S" + c + b + s + f + l + f + "S"; + concat = "" + c + b + f + "S" + S + s + i + l + s + z + z + f + l + b; + concat = "" + S + s + "S" + d + s + z + "S" + i + i + z + S + b + f + i; + concat = "" + z + S + S + "S" + S + S + z + b + S + z + b + f + s + l; + concat = "" + s + z + d + "S" + z + l + f + z + s + z + d + l + s + l; + concat = "" + l + d + i + s + i + c + i + f + b + f + s + b + s + s; + concat = "" + z + "S" + S + "S" + "S" + i + "S" + s + d + z + l; + concat = "" + i + S + S + "S" + f + "S" + "S" + z + S + z + b + z + c + b; + concat = "" + i + f + f + d + z + f + z + b + "S" + c + l + l + z + s + S + s; + concat = "" + b + b + z + "S" + f + s + "S" + l +c + S + i + i + b + "S" + S; + concat = "" + i + "S" + d + d + d + "S" + f + "S" + b + s + S + i + "S" + d + b; + concat = "" + s + f + b + d + c + d + c + S + S + b + i + b + z + c; + concat = "" + l + l + S + l + f + s + i + c + z + f + d + l + f + b + l + f + f + i + i + z; + concat = "" + l + l + l + l + s + s + f + i + i + f + z + c + S + s + f + "S" + "S" + s + z + s; + concat = "" + S + z + f + b + l + c + i + l; + concat = "" + c + z + b + f + i + i + f + d + f + f + d + d + l + d + S + "S" + i + c + b + f; + concat = "" + s + d + S + d + b + l + l + f + b + "S" + i + z + b + S + S + c + S + f + S + z; + concat = "" + l + S + S + i + l + s + d + f + z + i + "S" + b + f + c + z + c + S + c + i + s; + concat = "" + l + S + S + s + f + S + s + "S" + c + c + c; + concat = "" + s + "S" + c + d + z + c + l + c + z + S + i + f + c + c + s + "S" + S + z + s + "S"; + concat = "" + c + i + z + s + b + s + s + b + "S" + d + "S" + z + f + "S" + c + S + s + S + b + i; + concat = "" + s + c + d + d + "S" + "S" + l + s + i + l + l + f + S + f + f + i + S + d + l + c; + concat = "" + "S" + S + b + c + i + "S" + c + c + s + i + "S" + b + i + b + b + S + f + l + s + "S"; + concat = "" + l + l + b + f + i + i + f + z + c + S + b + f + z + "S" + s + z + "S" + f + S + s; + concat = "" + i + c + b + i + b + z + "S" + i + c + i + l + "S" + z + b + b + i + i + c + i + f; + concat = "" + "S" + c + d + z + d + f + c + c + b + "S" + l + f + d + "S" + s + s + S + i + s + i; + concat = "" + S + "S" + d + c + "S" + S + "S" + b + f + z + "S" + l + d + f + "S" + S + d + b + c + c; + concat = "" + f + S + l + s + l + z + S + d + S + b + f + c + s + b + "S" + z + "S" + "S" + b + z; + concat = "" + f + s + c + i + S + b + s + S + i + S + c + b + s + d + i + "S" + s + l + c + s; + concat = "" + l + f + s + b + d + b + i + c + c + b + s + f + i + z + s + i + s + "S" + l + z; + concat = "" + d + z + z + c + b + b + s + b + S + l + d + i + S + d + "S" + i + S + i + b + S; + concat = "" + c + d + "S" + f + i + b + d + c + z + f + "S" + i + d + b + f + s + "S" + c + S + i; + concat = "" + i + z + "S" + b + S + s + c + s + f + S + S + f + z + s + b + d + z + i + s + z; + concat = "" + z + s + z + l + "S" + S + s + "S" + i + b + c + s + l + l + s + i + c + i + i + d; + concat = "" + "S" + b + l + z + c + f + l + S + "S" + l + i + z + z + l + S + "S" + z + S + z + c + "S"; + concat = "" + "S" + f + S + i + i + i + "S" + i + i + l + c + l + S + S + z + b + i + c + f + S; + concat = "" + c + z + S + S + b + i + c; + concat = "" + S + s + S + c; + } + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/runtime/SwitchEnum.java b/test/micro/org/openjdk/bench/java/lang/runtime/SwitchEnum.java new file mode 100644 index 0000000000000..bfec0d321b29d --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/runtime/SwitchEnum.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.runtime; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(3) +public class SwitchEnum { + + public E[] inputs; + @Setup + public void setup() { + inputs = E.values(); + } + + @Benchmark + public int enumSwitchWithBootstrap() { + int sum = 0; + for (E e : inputs) { + sum += switch (e) { + case null -> -1; + case E0 -> 10; + case E1 -> 11; + case E2 -> 12; + case E3 -> 13; + case E4 -> 14; + case E5 -> 15; + case E6 -> 16; + case E7 -> 17; + case E8 -> 18; + case E9 -> 19; + default -> 17; + }; + } + return sum; + } + + @Benchmark + public int enumSwitchTraditional() { + int sum = 0; + for (E e : inputs) { + sum += switch (e) { + case E0 -> 10; + case E1 -> 11; + case E2 -> 12; + case E3 -> 13; + case E4 -> 14; + case E5 -> 15; + case E6 -> 16; + case E7 -> 17; + case E8 -> 18; + case E9 -> 19; + default -> 17; + }; + } + return sum; + } + + public static void main(String[] args) { + SwitchEnum s = new SwitchEnum(); + s.setup(); + System.out.println(s.enumSwitchWithBootstrap()); + System.out.println(s.enumSwitchTraditional()); + } + + enum E { + E0, E1, E2, E3, E4, E5, E6, E7, E8, E9; + } +} diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegerSquareRoot.java b/test/micro/org/openjdk/bench/java/math/BigIntegerSquareRoot.java new file mode 100644 index 0000000000000..4b78b4cd8fa7f --- /dev/null +++ b/test/micro/org/openjdk/bench/java/math/BigIntegerSquareRoot.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.math; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.math.BigInteger; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class BigIntegerSquareRoot { + + private BigInteger[] xsArray, sArray, mArray, lArray, xlArray; + private static final int TESTSIZE = 1000; + + @Setup + public void setup() { + Random r = new Random(1123); + + xsArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 64 bits + * in size + */ + sArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 256 bits + * in size + */ + mArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 1024 bits + * in size + */ + lArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 4096 bits + * in size + */ + xlArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 16384 bits + * in size + */ + + for (int i = 0; i < TESTSIZE; i++) { + xsArray[i] = new BigInteger(r.nextInt(64), r); + sArray[i] = new BigInteger(r.nextInt(256), r); + mArray[i] = new BigInteger(r.nextInt(1024), r); + lArray[i] = new BigInteger(r.nextInt(4096), r); + xlArray[i] = new BigInteger(r.nextInt(16384), r); + } + } + + /** Test BigInteger.sqrt() with numbers long at most 64 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testSqrtXS(Blackhole bh) { + for (BigInteger s : xsArray) { + bh.consume(s.sqrt()); + } + } + + /** Test BigInteger.sqrt() with numbers long at most 256 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testSqrtS(Blackhole bh) { + for (BigInteger s : sArray) { + bh.consume(s.sqrt()); + } + } + + /** Test BigInteger.sqrt() with numbers long at most 1024 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testSqrtM(Blackhole bh) { + for (BigInteger s : mArray) { + bh.consume(s.sqrt()); + } + } + + /** Test BigInteger.sqrt() with numbers long at most 4096 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testSqrtL(Blackhole bh) { + for (BigInteger s : lArray) { + bh.consume(s.sqrt()); + } + } + + /** Test BigInteger.sqrt() with numbers long at most 16384 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testSqrtXL(Blackhole bh) { + for (BigInteger s : xlArray) { + bh.consume(s.sqrt()); + } + } +} diff --git a/test/micro/org/openjdk/bench/java/nio/ByteBuffers.java b/test/micro/org/openjdk/bench/java/nio/ByteBuffers.java index c2f4e93313c91..48f75a3ec8117 100644 --- a/test/micro/org/openjdk/bench/java/nio/ByteBuffers.java +++ b/test/micro/org/openjdk/bench/java/nio/ByteBuffers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -923,9 +923,4 @@ public double testDirectLoopGetDouble() { } return r; } - - @Benchmark - public int testHeapHashCode() { - return heapByteBuffer.hashCode(); - } } diff --git a/test/micro/org/openjdk/bench/java/text/DateFormatterBench.java b/test/micro/org/openjdk/bench/java/text/DateFormatterBench.java new file mode 100644 index 0000000000000..f9a6340a0fb29 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/text/DateFormatterBench.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.text; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Benchmark) +public class DateFormatterBench { + + private Date date; + + private Object objDate; + + @Setup + public void setup() { + date = new Date(); + objDate = new Date(); + } + + private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, Locale.ENGLISH); + + @Benchmark + public String testFormatDate() { + return dateFormat.format(date); + } + + @Benchmark + public String testFormatObject() { + return dateFormat.format(objDate); + } + + public static void main(String... args) throws Exception { + Options opts = new OptionsBuilder().include(DateFormatterBench.class.getSimpleName()).shouldDoGC(true).build(); + new Runner(opts).run(); + } +} diff --git a/test/micro/org/openjdk/bench/java/text/ListFormatterBench.java b/test/micro/org/openjdk/bench/java/text/ListFormatterBench.java new file mode 100644 index 0000000000000..008f4b17148bb --- /dev/null +++ b/test/micro/org/openjdk/bench/java/text/ListFormatterBench.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.text; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.text.ListFormat; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Benchmark) +public class ListFormatterBench { + + private List data; + + @Setup + public void setup() { + data = List.of("foo", "bar", "baz", "qux", "quux", "quuz"); + } + + private ListFormat listFormat = ListFormat.getInstance(); + + @Benchmark + public String testListFormat() { + return listFormat.format(data); + } + + public static void main(String... args) throws Exception { + Options opts = new OptionsBuilder().include(ListFormatterBench.class.getSimpleName()).shouldDoGC(true).build(); + new Runner(opts).run(); + } +} diff --git a/test/micro/org/openjdk/bench/java/text/MessageFormatterBench.java b/test/micro/org/openjdk/bench/java/text/MessageFormatterBench.java new file mode 100644 index 0000000000000..5d3399cb46d6d --- /dev/null +++ b/test/micro/org/openjdk/bench/java/text/MessageFormatterBench.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.text; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Benchmark) +public class MessageFormatterBench { + + private Object[][] values; + + @Setup + public void setup() { + values = new Object[][]{ + new Object[]{Integer.valueOf(13), "MyDisk1"}, + new Object[]{Float.valueOf(25.6f), "MyDisk2"}, + new Object[]{Double.valueOf(123.89), "MyDisk3"}, + new Object[]{Long.valueOf(1234567), "MyDisk4"}, + }; + } + + private MessageFormat messageFormat = new MessageFormat("There is {0} GB of free space on the {1}.", Locale.ENGLISH); + + @Benchmark + @OperationsPerInvocation(4) + public void testMessageFormat(final Blackhole bh) { + for (Object[] value : values) { + bh.consume(messageFormat.format(value)); + } + } + + public static void main(String... args) throws Exception { + Options opts = new OptionsBuilder().include(MessageFormatterBench.class.getSimpleName()).shouldDoGC(true).build(); + new Runner(opts).run(); + } +} diff --git a/test/micro/org/openjdk/bench/java/time/ToStringBench.java b/test/micro/org/openjdk/bench/java/time/ToStringBench.java new file mode 100644 index 0000000000000..3d62e21ba8fab --- /dev/null +++ b/test/micro/org/openjdk/bench/java/time/ToStringBench.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.time; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +import java.util.Locale; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Thread) +public class ToStringBench { + private static final Instant[] INSTANTS; + private static final ZonedDateTime[] ZONED_DATE_TIMES; + private static final LocalDateTime[] LOCAL_DATE_TIMES; + private static final LocalDate[] LOCAL_DATES; + private static final LocalTime[] LOCAL_TIMES; + + static { + Instant loInstant = Instant.EPOCH.plus(Duration.ofDays(365*50)); // 2020-01-01 + Instant hiInstant = loInstant.plus(Duration.ofDays(1)); + long maxOffsetNanos = Duration.between(loInstant, hiInstant).toNanos(); + Random random = new Random(0); + INSTANTS = IntStream + .range(0, 1_000) + .mapToObj(ignored -> { + final long offsetNanos = (long) Math.floor(random.nextDouble() * maxOffsetNanos); + return loInstant.plus(offsetNanos, ChronoUnit.NANOS); + }) + .toArray(Instant[]::new); + + ZONED_DATE_TIMES = Stream.of(INSTANTS) + .map(instant -> ZonedDateTime.ofInstant(instant, ZoneOffset.UTC)) + .toArray(ZonedDateTime[]::new); + + LOCAL_DATE_TIMES = Stream.of(ZONED_DATE_TIMES) + .map(zdt -> zdt.toLocalDateTime()) + .toArray(LocalDateTime[]::new); + + LOCAL_DATES = Stream.of(LOCAL_DATE_TIMES) + .map(ldt -> ldt.toLocalDate()) + .toArray(LocalDate[]::new); + + LOCAL_TIMES = Stream.of(LOCAL_DATE_TIMES) + .map(ldt -> ldt.toLocalTime()) + .toArray(LocalTime[]::new); + } + + @Benchmark + public void zonedDateTimeToString(Blackhole bh) { + for (final ZonedDateTime zonedDateTime : ZONED_DATE_TIMES) { + bh.consume(zonedDateTime.toString()); + } + } + + @Benchmark + public void localDateTimeToString(Blackhole bh) { + for (LocalDateTime localDateTime : LOCAL_DATE_TIMES) { + bh.consume(localDateTime.toString()); + } + } + + @Benchmark + public void localDateToString(Blackhole bh) { + for (LocalDate localDate : LOCAL_DATES) { + bh.consume(localDate.toString()); + } + } + + @Benchmark + public void localTimeToString(Blackhole bh) { + for (LocalTime localTime : LOCAL_TIMES) { + bh.consume(localTime.toString()); + } + } +} \ No newline at end of file diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Write.java b/test/micro/org/openjdk/bench/jdk/classfile/Write.java index 59d07b05927b7..e5304f0e51c77 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Write.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Write.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.classfile; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassFile; import java.lang.classfile.TypeKind; @@ -30,6 +29,8 @@ import jdk.internal.org.objectweb.asm.*; import org.openjdk.jmh.annotations.*; import java.io.FileOutputStream; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.nio.file.Files; @@ -148,7 +149,7 @@ public byte[] jdkTree() { ); for (int xi = 0; xi < 40; ++xi) { cb.withMethod("main" + ((xi == 0) ? "" : "" + xi), MTD_void_StringArray, - AccessFlags.ofMethod(AccessFlag.STATIC, AccessFlag.PUBLIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { java.lang.classfile.Label loopTop = c0.newLabel(); java.lang.classfile.Label loopEnd = c0.newLabel(); @@ -196,7 +197,7 @@ public byte[] jdkTreePrimitive() { ); for (int xi = 0; xi < 40; ++xi) { cb.withMethod("main" + ((xi == 0) ? "" : "" + xi), MTD_void_StringArray, - AccessFlags.ofMethod(AccessFlag.STATIC, AccessFlag.PUBLIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { java.lang.classfile.Label loopTop = c0.newLabel(); java.lang.classfile.Label loopEnd = c0.newLabel(); diff --git a/test/micro/org/openjdk/bench/vm/compiler/MergeStoreBench.java b/test/micro/org/openjdk/bench/vm/compiler/MergeStoreBench.java new file mode 100644 index 0000000000000..26c8287c4de67 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/MergeStoreBench.java @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2024, 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. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.nio.ByteOrder; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import jdk.internal.misc.Unsafe; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Fork(value = 3, jvmArgsAppend = {"--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED"}) +public class MergeStoreBench { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + final static VarHandle INT_L = MethodHandles.byteArrayViewVarHandle(int[].class , ByteOrder.LITTLE_ENDIAN); + final static VarHandle INT_B = MethodHandles.byteArrayViewVarHandle(int[].class , ByteOrder.BIG_ENDIAN); + final static VarHandle LONG_L = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + final static VarHandle LONG_B = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + final static VarHandle CHAR_L = MethodHandles.byteArrayViewVarHandle(char[].class, ByteOrder.LITTLE_ENDIAN); + final static VarHandle CHAR_B = MethodHandles.byteArrayViewVarHandle(char[].class, ByteOrder.BIG_ENDIAN); + + final static int NUMBERS = 8192; + + final byte[] bytes4 = new byte[NUMBERS * 4]; + final byte[] bytes8 = new byte[NUMBERS * 8]; + final int [] ints = new int [NUMBERS ]; + final long[] longs = new long[NUMBERS ]; + final char[] chars = new char[NUMBERS ]; + + @Setup + public void setup() { + Random r = new Random(); + for (int i = 0; i < ints.length; i++) { + ints[i] = r.nextInt(); + INT_L.set(bytes4, i * 4, i); + } + + for (int i = 0; i < longs.length; i++) { + longs[i] = r.nextLong(); + LONG_L.set(bytes8, i * 8, i); + } + } + + /* + * The names of these cases have the following `B/L/V/U` suffixes, which are: + * ``` + * B BigEndian + * L LittleEndian + * V VarHandle + * U Unsafe + * R ReverseBytes + * C Unsafe.getChar & putChar + * S Unsafe.getShort & putShort + * ``` + */ + + @Benchmark + public void getIntB(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntB(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntBU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntBU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntBV(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (int) INT_B.get(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntL(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntL(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntLU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntLU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntLV(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (int) INT_L.get(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRB(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRB(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRBU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRBU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRL(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRL(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRLU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRLU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += Integer.reverseBytes( + UNSAFE.getInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4)); + } + BH.consume(sum); + } + + @Benchmark + public void getIntU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += UNSAFE.getInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void setIntB(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntB(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntBU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntBU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntBV(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + INT_B.set(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntL(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntL(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntLU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntLU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + INT_L.set(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRB(bytes4, i * 4, ints[i]); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRBU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRL(bytes4, i * 4, ints[i]); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRLU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + v = Integer.reverseBytes(v); + UNSAFE.putInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + UNSAFE.putInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void getLongB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongB(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongBU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (long) LONG_B.get(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongL(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongLU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (long) LONG_L.get(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRB(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRBU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRL(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRLU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += Long.reverseBytes( + UNSAFE.getLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8)); + } + BH.consume(sum); + } + + @Benchmark + public void getLongU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += UNSAFE.getLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void setLongB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongB(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongBU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + LONG_B.set(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongL(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongLU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + LONG_L.set(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRB(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRBU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRL(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRLU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + v = Long.reverseBytes(v); + UNSAFE.putLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + UNSAFE.putLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void getCharB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharB(bytes4, i); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = (char) CHAR_B.get(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharBU(bytes4, i); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharL(bytes4, i); + sum += c; + } + BH.consume(sum); + } + @Benchmark + public void getCharLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharLU(bytes4, i); + sum += c; + } + BH.consume(sum); + } + + + @Benchmark + public void getCharLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = (char) CHAR_L.get(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharC(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = UNSAFE.getChar(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharBS(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + putShortB(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + CHAR_B.set(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharLS(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + putShortL(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + CHAR_L.set(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharC(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + UNSAFE.putChar(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2, c); + sum += c; + } + BH.consume(sum); + } + + /* + * putChars4 Test whether four constant chars can be MergeStored + * + */ + @Benchmark + public void putChars4B(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4B(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4BU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4BU(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4BV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4BV(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4L(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4L(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4LU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4LU(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4LV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4LV(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4C(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4C(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4S(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4S(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + static int getIntB(byte[] array, int offset) { + return ((array[offset ] & 0xff) << 24) + | ((array[offset + 1] & 0xff) << 16) + | ((array[offset + 2] & 0xff) << 8) + | ((array[offset + 3] & 0xff) ); + } + + static int getIntBU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return ((UNSAFE.getByte(array, address ) & 0xff) << 24) + | ((UNSAFE.getByte(array, address + 1) & 0xff) << 16) + | ((UNSAFE.getByte(array, address + 2) & 0xff) << 8) + | ((UNSAFE.getByte(array, address + 3) & 0xff) ); + } + + static int getIntL(byte[] array, int offset) { + return ((array[offset ] & 0xff) ) + | ((array[offset + 1] & 0xff) << 8) + | ((array[offset + 2] & 0xff) << 16) + | ((array[offset + 3] & 0xff) << 24); + } + + static int getIntRB(byte[] array, int offset) { + return Integer.reverseBytes(getIntB(array, offset)); + } + + static int getIntRBU(byte[] array, int offset) { + return Integer.reverseBytes(getIntBU(array, offset)); + } + + static int getIntRL(byte[] array, int offset) { + return Integer.reverseBytes(getIntL(array, offset)); + } + + static int getIntRLU(byte[] array, int offset) { + return Integer.reverseBytes(getIntLU(array, offset)); + } + + static void setIntB(byte[] array, int offset, int value) { + array[offset ] = (byte) (value >> 24); + array[offset + 1] = (byte) (value >> 16); + array[offset + 2] = (byte) (value >> 8); + array[offset + 3] = (byte) (value ); + } + + static void setIntBU(byte[] array, int offset, int value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) (value >> 24)); + UNSAFE.putByte(array, address + 1, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 3, (byte) (value )); + } + + public static void setIntL(byte[] array, int offset, int value) { + array[offset ] = (byte) value; + array[offset + 1] = (byte) (value >> 8); + array[offset + 2] = (byte) (value >> 16); + array[offset + 3] = (byte) (value >> 24); + } + + public static void setIntLU(byte[] array, int offset, int value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) value ); + UNSAFE.putByte(array, address + 1, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 3, (byte) (value >> 24)); + } + + public static void setIntRL(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntL(array, offset, value); + } + + public static void setIntRLU(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntLU(array, offset, value); + } + + public static void setIntRB(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntB(array, offset, value); + } + + public static void setIntRBU(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntBU(array, offset, value); + } + + static long getLongB(byte[] array, int offset) { + return (((long) array[offset ] & 0xff) << 56) + | (((long) array[offset + 1] & 0xff) << 48) + | (((long) array[offset + 2] & 0xff) << 40) + | (((long) array[offset + 3] & 0xff) << 32) + | (((long) array[offset + 4] & 0xff) << 24) + | (((long) array[offset + 5] & 0xff) << 16) + | (((long) array[offset + 6] & 0xff) << 8) + | (((long) array[offset + 7] & 0xff) ); + } + + static long getLongBU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return (((long)(UNSAFE.getByte(array, address) & 0xff)) << 56) + | (((long)(UNSAFE.getByte(array, address + 1) & 0xff)) << 48) + | (((long)(UNSAFE.getByte(array, address + 2) & 0xff)) << 40) + | (((long)(UNSAFE.getByte(array, address + 3) & 0xff)) << 32) + | (((long)(UNSAFE.getByte(array, address + 4) & 0xff)) << 24) + | (((long)(UNSAFE.getByte(array, address + 5) & 0xff)) << 16) + | (((long)(UNSAFE.getByte(array, address + 6) & 0xff)) << 8) + | (((long)(UNSAFE.getByte(array, address + 7) & 0xff)) ); + } + + public static long getLongL(byte[] array, int offset) { + return (((long) array[offset ] & 0xff) ) + | (((long) array[offset + 1] & 0xff) << 8) + | (((long) array[offset + 2] & 0xff) << 16) + | (((long) array[offset + 3] & 0xff) << 24) + | (((long) array[offset + 4] & 0xff) << 32) + | (((long) array[offset + 5] & 0xff) << 40) + | (((long) array[offset + 6] & 0xff) << 48) + | (((long) array[offset + 7] & 0xff) << 56); + } + + static long getLongLU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return (((long)(UNSAFE.getByte(array, address ) & 0xff)) ) + | (((long)(UNSAFE.getByte(array, address + 1) & 0xff)) << 8) + | (((long)(UNSAFE.getByte(array, address + 2) & 0xff)) << 16) + | (((long)(UNSAFE.getByte(array, address + 3) & 0xff)) << 24) + | (((long)(UNSAFE.getByte(array, address + 4) & 0xff)) << 32) + | (((long)(UNSAFE.getByte(array, address + 5) & 0xff)) << 40) + | (((long)(UNSAFE.getByte(array, address + 6) & 0xff)) << 48) + | (((long)(UNSAFE.getByte(array, address + 7) & 0xff)) << 56); + } + + static long getLongRB(byte[] array, int offset) { + return getLongB(array, offset); + } + + static long getLongRBU(byte[] array, int offset) { + return getLongBU(array, offset); + } + + static long getLongRL(byte[] array, int offset) { + return getLongL(array, offset); + } + + static long getLongRLU(byte[] array, int offset) { + return getLongLU(array, offset); + } + + static void setLongB(byte[] array, int offset, long value) { + array[offset] = (byte) (value >> 56); + array[offset + 1] = (byte) (value >> 48); + array[offset + 2] = (byte) (value >> 40); + array[offset + 3] = (byte) (value >> 32); + array[offset + 4] = (byte) (value >> 24); + array[offset + 5] = (byte) (value >> 16); + array[offset + 6] = (byte) (value >> 8); + array[offset + 7] = (byte) (value ); + } + + public static void setLongL(byte[] array, int offset, long value) { + array[offset] = (byte) value ; + array[offset + 1] = (byte) (value >> 8 ); + array[offset + 2] = (byte) (value >> 16); + array[offset + 3] = (byte) (value >> 24); + array[offset + 4] = (byte) (value >> 32); + array[offset + 5] = (byte) (value >> 40); + array[offset + 6] = (byte) (value >> 48); + array[offset + 7] = (byte) (value >> 56); + } + + public static void setLongRL(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongL(array, offset, value); + } + + public static void setLongRLU(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongLU(array, offset, value); + } + + public static void setLongRB(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongB(array, offset, value); + } + + public static void setLongRBU(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongBU(array, offset, value); + } + + public static void setLongBU(byte[] array, int offset, long value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) (value >> 56)); + UNSAFE.putByte(array, address + 1, (byte) (value >> 48)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 40)); + UNSAFE.putByte(array, address + 3, (byte) (value >> 32)); + UNSAFE.putByte(array, address + 4, (byte) (value >> 24)); + UNSAFE.putByte(array, address + 5, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 6, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 7, (byte) value ); + } + + public static void setLongLU(byte[] array, int offset, long value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) value ); + UNSAFE.putByte(array, address + 1, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 3, (byte) (value >> 24)); + UNSAFE.putByte(array, address + 4, (byte) (value >> 32)); + UNSAFE.putByte(array, address + 5, (byte) (value >> 40)); + UNSAFE.putByte(array, address + 6, (byte) (value >> 48)); + UNSAFE.putByte(array, address + 7, (byte) (value >> 56)); + } + + public static int getIntLU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return ((UNSAFE.getByte(array, address ) & 0xff) ) + | ((UNSAFE.getByte(array, address + 1) & 0xff) << 8) + | ((UNSAFE.getByte(array, address + 2) & 0xff) << 16) + | ((UNSAFE.getByte(array, address + 3) & 0xff) << 24); + } + + public static char getCharB(byte[] val, int index) { + index <<= 1; + return (char)(((val[index ] & 0xff) << 8) + | ((val[index + 1] & 0xff))); + } + + public static char getCharBR(byte[] val, int index) { + return Character.reverseBytes(getCharB(val, index)); + } + + public static char getCharL(byte[] val, int index) { + index <<= 1; + return (char)(((val[index ] & 0xff)) + | ((val[index + 1] & 0xff) << 8)); + } + + public static char getCharLR(byte[] val, int index) { + return Character.reverseBytes(getCharL(val, index)); + } + + public static char getCharBU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + return (char) (((UNSAFE.getByte(array, address ) & 0xff) << 8) + | ((UNSAFE.getByte(array, address + 1) & 0xff) )); + } + + public static char getCharLU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + return (char) (((UNSAFE.getByte(array, address ) & 0xff) ) + | ((UNSAFE.getByte(array, address + 1) & 0xff) << 8)); + } + + public void putChars4B(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortB(bytes, offset , c0); + putShortB(bytes, offset + 1, c1); + putShortB(bytes, offset + 2, c2); + putShortB(bytes, offset + 3, c3); + } + + public void putChars4BU(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortBU(bytes, offset , c0); + putShortBU(bytes, offset + 1, c1); + putShortBU(bytes, offset + 2, c2); + putShortBU(bytes, offset + 3, c3); + } + + public void putChars4BV(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + offset <<= 1; + CHAR_B.set(bytes, offset , c0); + CHAR_B.set(bytes, offset + 2, c1); + CHAR_B.set(bytes, offset + 4, c2); + CHAR_B.set(bytes, offset + 6, c3); + } + + public void putChars4L(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortL(bytes, offset , c0); + putShortL(bytes, offset + 1, c1); + putShortL(bytes, offset + 2, c2); + putShortL(bytes, offset + 3, c3); + } + + public void putChars4LV(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + offset <<= 1; + CHAR_L.set(bytes, offset , c0); + CHAR_L.set(bytes, offset + 2, c1); + CHAR_L.set(bytes, offset + 4, c2); + CHAR_L.set(bytes, offset + 6, c3); + } + + public void putChars4LU(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortLU(bytes, offset , c0); + putShortLU(bytes, offset + 1, c1); + putShortLU(bytes, offset + 2, c2); + putShortLU(bytes, offset + 3, c3); + } + + public void putChars4C(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putChar(bytes, address , c0); + UNSAFE.putChar(bytes, address + 2, c1); + UNSAFE.putChar(bytes, address + 4, c2); + UNSAFE.putChar(bytes, address + 6, c3); + } + + public void putChars4S(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putShort(bytes, address , (short) c0); + UNSAFE.putShort(bytes, address + 2, (short) c1); + UNSAFE.putShort(bytes, address + 4, (short) c2); + UNSAFE.putShort(bytes, address + 6, (short) c3); + } + + private static void putShortB(byte[] val, int index, int c) { + index <<= 1; + val[index ] = (byte)(c >> 8); + val[index + 1] = (byte)(c ); + } + + public static void putShortBU(byte[] array, int offset, int c) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putByte(array, address , (byte) (c >> 8)); + UNSAFE.putByte(array, address + 1, (byte) (c )); + } + + private static void putShortL(byte[] val, int index, int c) { + index <<= 1; + val[index ] = (byte)(c ); + val[index + 1] = (byte)(c >> 8); + } + + public static void putShortLU(byte[] array, int offset, int c) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putByte(array, address , (byte) (c )); + UNSAFE.putByte(array, address + 1, (byte) (c >> 8)); + } +}