diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 067548b9768..1dc0b25c345 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ on: platforms: description: 'Platform(s) to execute on (comma separated, e.g. "linux-x64, macos, aarch64")' required: true - default: 'linux-x64, linux-x86, linux-x64-variants, linux-cross-compile, macos-x64, macos-aarch64, windows-x64, windows-aarch64, docs' + default: 'linux-x64, linux-x86-hs, linux-x64-variants, linux-cross-compile, macos-x64, macos-aarch64, windows-x64, windows-aarch64, docs' configure-arguments: description: 'Additional configure arguments' required: false @@ -59,7 +59,7 @@ jobs: runs-on: ubuntu-22.04 outputs: linux-x64: ${{ steps.include.outputs.linux-x64 }} - linux-x86: ${{ steps.include.outputs.linux-x86 }} + linux-x86-hs: ${{ steps.include.outputs.linux-x86-hs }} linux-x64-variants: ${{ steps.include.outputs.linux-x64-variants }} linux-cross-compile: ${{ steps.include.outputs.linux-cross-compile }} macos-x64: ${{ steps.include.outputs.macos-x64 }} @@ -111,7 +111,7 @@ jobs: } echo "linux-x64=$(check_platform linux-x64 linux x64)" >> $GITHUB_OUTPUT - echo "linux-x86=$(check_platform linux-x86 linux x86)" >> $GITHUB_OUTPUT + echo "linux-x86-hs=$(check_platform linux-x86-hs linux x86)" >> $GITHUB_OUTPUT echo "linux-x64-variants=$(check_platform linux-x64-variants variants)" >> $GITHUB_OUTPUT echo "linux-cross-compile=$(check_platform linux-cross-compile cross-compile)" >> $GITHUB_OUTPUT echo "macos-x64=$(check_platform macos-x64 macos x64)" >> $GITHUB_OUTPUT @@ -135,12 +135,13 @@ jobs: make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.select.outputs.linux-x64 == 'true' - build-linux-x86: - name: linux-x86 + build-linux-x86-hs: + name: linux-x86-hs needs: select uses: ./.github/workflows/build-linux.yml with: platform: linux-x86 + make-target: 'hotspot' gcc-major-version: '10' gcc-package-suffix: '-multilib' apt-architecture: 'i386' @@ -150,7 +151,7 @@ jobs: extra-conf-options: '--with-target-bits=32 --enable-fallback-linker --enable-libffi-bundling' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.select.outputs.linux-x86 == 'true' + if: needs.select.outputs.linux-x86-hs == 'true' build-linux-x64-hs-nopch: name: linux-x64-hs-nopch @@ -300,16 +301,6 @@ jobs: bootjdk-platform: linux-x64 runs-on: ubuntu-22.04 - test-linux-x86: - name: linux-x86 - needs: - - build-linux-x86 - uses: ./.github/workflows/test.yml - with: - platform: linux-x86 - bootjdk-platform: linux-x64 - runs-on: ubuntu-22.04 - test-macos-x64: name: macos-x64 needs: @@ -347,7 +338,7 @@ jobs: if: always() needs: - build-linux-x64 - - build-linux-x86 + - build-linux-x86-hs - build-linux-x64-hs-nopch - build-linux-x64-hs-zero - build-linux-x64-hs-minimal @@ -358,31 +349,27 @@ jobs: - build-windows-x64 - build-windows-aarch64 - test-linux-x64 - - test-linux-x86 - test-macos-x64 - 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?per_page=100')" + 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/doc/ide.html b/doc/ide.html index 0dd16b7b9e6..ef6f434013e 100644 --- a/doc/ide.html +++ b/doc/ide.html @@ -63,11 +63,12 @@
Alternative indexers

The main vscode-project target configures the default C++ support in Visual Studio Code. There are also other source indexers that can be installed, that may provide additional features. It's -currently possible to generate configuration for two such indexers, clangd and rtags. These can be -configured by appending the name of the indexer to the make target, such -as:

+currently possible to generate configuration for three such indexers, clangd, ccls +and rtags. These can +be configured by appending the name of the indexer to the make target, +such as:

make vscode-project-clangd

Additional instructions for configuring the given indexer will be displayed after the workspace has been generated.

diff --git a/doc/ide.md b/doc/ide.md index 40e3430a438..d6ebb7b742a 100644 --- a/doc/ide.md +++ b/doc/ide.md @@ -32,7 +32,8 @@ choose `File -> Open Workspace...` in Visual Studio Code. The main `vscode-project` target configures the default C++ support in Visual Studio Code. There are also other source indexers that can be installed, that may provide additional features. It's currently possible to generate -configuration for two such indexers, [clangd](https://clang.llvm.org/extra/clangd/) +configuration for three such indexers, [clangd](https://clang.llvm.org/extra/clangd/), +[ccls](https://github.com/MaskRay/ccls/wiki/Visual-Studio-Code) and [rtags](https://github.com/Andersbakken/rtags). These can be configured by appending the name of the indexer to the make target, such as: diff --git a/make/Docs.gmk b/make/Docs.gmk index 192843de9cd..08a9ac662cd 100644 --- a/make/Docs.gmk +++ b/make/Docs.gmk @@ -1,4 +1,4 @@ -# 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 @@ -714,7 +714,7 @@ SPEC_HEADER_BLOCK := \ \ diff --git a/make/InitSupport.gmk b/make/InitSupport.gmk index b6a0a8d5d6f..ed9cd136c3f 100644 --- a/make/InitSupport.gmk +++ b/make/InitSupport.gmk @@ -63,14 +63,14 @@ ifeq ($(HAS_SPEC),) # The variable MAKEOVERRIDES contains variable assignments from the command # line, but in reverse order to what the user entered. - # The '\#' <=> '\ 'dance is needed to keep values with space in them connected. - COMMAND_LINE_VARIABLES := $(subst \#,\ , $(call reverse, $(subst \ ,\#,$(MAKEOVERRIDES)))) + # The '§' <=> '\ 'dance is needed to keep values with space in them connected. + COMMAND_LINE_VARIABLES := $(subst §,\ , $(call reverse, $(subst \ ,§,$(MAKEOVERRIDES)))) # A list like FOO="val1" BAR="val2" containing all user-supplied make # variables that we should propagate. - # The '\#' <=> '\ 'dance is needed to keep values with space in them connected. - USER_MAKE_VARS := $(subst \#,\ , $(filter-out $(addsuffix =%, $(INIT_CONTROL_VARIABLES)), \ - $(subst \ ,\#,$(MAKEOVERRIDES)))) + # The '§' <=> '\ 'dance is needed to keep values with space in them connected. + USER_MAKE_VARS := $(subst §,\ , $(filter-out $(addsuffix =%, $(INIT_CONTROL_VARIABLES)), \ + $(subst \ ,§,$(MAKEOVERRIDES)))) # Setup information about available configurations, if any. ifneq ($(CUSTOM_ROOT), ) diff --git a/make/RunTests.gmk b/make/RunTests.gmk index b0291e4eff4..8cfee458923 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 6afa36ac18d..f7e9844a643 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/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index a2d0d4e606a..fab8bec789b 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -71,7 +71,9 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r" if test "x$OPENJDK_TARGET_OS" = xlinux; then + # Clang needs the lld linker to work correctly BASIC_LDFLAGS="-fuse-ld=lld -Wl,--exclude-libs,ALL" + UTIL_REQUIRE_PROGS(LLD, lld) fi if test "x$OPENJDK_TARGET_OS" = xaix; then BASIC_LDFLAGS="-Wl,-b64 -Wl,-brtl -Wl,-bnorwexec -Wl,-bnolibpath -Wl,-bnoexpall \ diff --git a/make/autoconf/lib-alsa.m4 b/make/autoconf/lib-alsa.m4 index 19a91f94809..8d0fb324cd0 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 b1902a432a1..6849b4a26c7 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/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 index 57064df1ed7..12d450cc708 100644 --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 @@ -678,6 +678,9 @@ AC_DEFUN_ONCE([TOOLCHAIN_DETECT_TOOLCHAIN_EXTRA], test_metal=`$METAL --version 2>&1` if test $? -ne 0; then AC_MSG_RESULT([no]) + AC_MSG_NOTICE([A full XCode is required to build the JDK (not only command line tools)]) + AC_MSG_NOTICE([If you have XCode installed, you might need to reset the Xcode active developer directory]) + AC_MSG_NOTICE([using 'sudo xcode-select -r']) AC_MSG_ERROR([XCode tool 'metal' neither found in path nor with xcrun]) else AC_MSG_RESULT([yes, will be using '$METAL']) diff --git a/make/common/native/Link.gmk b/make/common/native/Link.gmk index 2090218ffbb..ca03c6ee6b1 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 deb5e36f605..3cb56b47b50 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/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index 696adb0c95f..424743ba279 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -57,7 +57,8 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \ $(GTEST_FRAMEWORK_SRC)/googletest/src \ $(GTEST_FRAMEWORK_SRC)/googlemock/src, \ INCLUDE_FILES := gtest-all.cc gmock-all.cc, \ - DISABLED_WARNINGS_gcc := undef unused-result format-nonliteral maybe-uninitialized, \ + DISABLED_WARNINGS_gcc := undef unused-result format-nonliteral \ + maybe-uninitialized zero-as-null-pointer-constant, \ DISABLED_WARNINGS_clang := undef unused-result format-nonliteral, \ DEFAULT_CFLAGS := false, \ CFLAGS := $(JVM_CFLAGS) \ diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index c8a58465d52..a0136870d8c 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -84,7 +84,7 @@ ifneq ($(call check-jvm-feature, jvmti), true) jvmtiImpl.cpp jvmtiManageCapabilities.cpp jvmtiRawMonitor.cpp jvmtiUtil.cpp jvmtiTrace.cpp \ jvmtiCodeBlobEvents.cpp jvmtiEnv.cpp jvmtiRedefineClasses.cpp jvmtiEnvBase.cpp jvmtiEnvThreadState.cpp \ jvmtiTagMap.cpp jvmtiEventController.cpp evmCompat.cpp jvmtiEnter.xsl jvmtiExport.cpp \ - jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp jvmtiAgent.cpp jvmtiAgentList.cpp + jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp jvmtiAgent.cpp jvmtiAgentList.cpp jfrJvmtiAgent.cpp endif ifneq ($(call check-jvm-feature, jvmci), true) diff --git a/make/jdk/src/classes/build/tools/fixuppandoc/Main.java b/make/jdk/src/classes/build/tools/fixuppandoc/Main.java index 4f76cbdfaf8..3d76457b596 100644 --- a/make/jdk/src/classes/build/tools/fixuppandoc/Main.java +++ b/make/jdk/src/classes/build/tools/fixuppandoc/Main.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 @@ -638,7 +638,7 @@ void write(PrintWriter out) { index++; } boolean updateEndTd = false; - Pattern styleAttr = Pattern.compile("(?.*style=\")(? + + +

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 97c0d97f8f9..379dcddb9c8 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/AdaptCodeTest.java b/test/jdk/jdk/classfile/AdaptCodeTest.java index 2b9dff58819..ca9145d6885 100644 --- a/test/jdk/jdk/classfile/AdaptCodeTest.java +++ b/test/jdk/jdk/classfile/AdaptCodeTest.java @@ -114,7 +114,7 @@ void testSevenOfThirteenIterator() throws Exception { void testCopy() throws Exception { var cc = ClassFile.of(); ClassModel cm = cc.parse(testClassPath); - byte[] newBytes = cc.build(cm.thisClass().asSymbol(), cb -> cm.forEachElement(cb)); + byte[] newBytes = cc.build(cm.thisClass().asSymbol(), cm::forEach); // TestUtil.writeClass(newBytes, "TestClass.class"); String result = (String) new ByteArrayClassLoader(AdaptCodeTest.class.getClassLoader(), testClassName, newBytes) diff --git a/test/jdk/jdk/classfile/AnnotationTest.java b/test/jdk/jdk/classfile/AnnotationTest.java index 0226affe7c2..6b6b88b3de1 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 @@ -28,6 +28,8 @@ */ import java.lang.constant.ClassDesc; import static java.lang.constant.ConstantDescs.*; + +import java.lang.constant.ConstantDesc; import java.lang.constant.MethodTypeDesc; import java.util.AbstractMap; import java.util.ArrayList; @@ -39,6 +41,8 @@ import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; import java.lang.classfile.*; import java.lang.classfile.constantpool.ConstantPoolBuilder; +import java.util.stream.Stream; + import org.junit.jupiter.api.Test; import static java.util.stream.Collectors.toList; @@ -53,26 +57,30 @@ class AnnotationTest { enum E {C}; - private static Map constants + // name -> (value, poolValue) + private static final Map> constants = Map.ofEntries( - new AbstractMap.SimpleImmutableEntry<>("i", 1), - new AbstractMap.SimpleImmutableEntry<>("j", 1L), - new AbstractMap.SimpleImmutableEntry<>("s", 1), - new AbstractMap.SimpleImmutableEntry<>("b", 1), - new AbstractMap.SimpleImmutableEntry<>("f", 1.0f), - new AbstractMap.SimpleImmutableEntry<>("d", 1.0d), - new AbstractMap.SimpleImmutableEntry<>("z", 1), - new AbstractMap.SimpleImmutableEntry<>("c", (int) '1'), - new AbstractMap.SimpleImmutableEntry<>("st", "1"), - new AbstractMap.SimpleImmutableEntry<>("cl", ClassDesc.of("foo.Bar")), - new AbstractMap.SimpleImmutableEntry<>("en", E.C), - new AbstractMap.SimpleImmutableEntry<>("arr", new Object[] {1, "1", 1.0f}) + Map.entry("i", Map.entry(1, 1)), + Map.entry("j", Map.entry(1L, 1L)), + Map.entry("s", Map.entry((short) 1, 1)), + Map.entry("b", Map.entry((byte) 1, 1)), + Map.entry("f", Map.entry(1.0f, 1.0f)), + Map.entry("d", Map.entry(1.0d, 1.0d)), + Map.entry("z", Map.entry(true, 1)), + Map.entry("c", Map.entry('1', (int) '1')), + Map.entry("st", Map.entry("1", "1")) ); - private static final List constantElements = + private static final List constantElements = Stream.concat( constants.entrySet().stream() - .map(e -> AnnotationElement.of(e.getKey(), AnnotationValue.of(e.getValue()))) - .toList(); + .map(e -> Map.entry(e.getKey(), e.getValue().getKey())), + Stream.of( + Map.entry("cl", ClassDesc.of("foo.Bar")), + Map.entry("en", E.C), + Map.entry("arr", new Object[] {1, "1", 1.0f}) + )) + .map(e -> AnnotationElement.of(e.getKey(), AnnotationValue.of(e.getValue()))) + .toList(); private static List elements() { List list = new ArrayList<>(constantElements); @@ -88,9 +96,12 @@ private static boolean assertAnno(Annotation a, String annoClassDescriptor, bool names.add(evp.name().stringValue()); switch (evp.name().stringValue()) { case "i", "j", "s", "b", "f", "d", "z", "c", "st": - assertTrue (evp.value() instanceof AnnotationValue.OfConstant c); - assertEquals(((AnnotationValue.OfConstant) evp.value()).constantValue(), - constants.get(evp.name().stringValue())); + if (!(evp.value() instanceof AnnotationValue.OfConstant c)) + return fail(); + assertEquals(c.resolvedValue(), + constants.get(evp.name().stringValue()).getKey()); + assertEquals(c.constant().constantValue(), + constants.get(evp.name().stringValue()).getValue()); break; case "cl": assertTrue (evp.value() instanceof AnnotationValue.OfClass c @@ -105,8 +116,9 @@ private static boolean assertAnno(Annotation a, String annoClassDescriptor, bool && assertAnno(c.annotation(), "LBar;", false)); break; case "arr": - assertTrue (evp.value() instanceof AnnotationValue.OfArray); - List values = ((AnnotationValue.OfArray) evp.value()).values(); + if (!(evp.value() instanceof AnnotationValue.OfArray arr)) + return fail(); + List values = arr.values(); assertEquals(values.stream().map(v -> ((AnnotationValue.OfConstant) v).constant().constantValue()).collect(toSet()), Set.of(1, 1.0f, "1")); break; @@ -207,4 +219,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/BasicBlockTest.java b/test/jdk/jdk/classfile/BasicBlockTest.java index 9c4b931857e..0781c5a10c1 100644 --- a/test/jdk/jdk/classfile/BasicBlockTest.java +++ b/test/jdk/jdk/classfile/BasicBlockTest.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 @@ -57,7 +57,7 @@ void testPatternsCausingBasicBlockTroubles() throws IOException { try (InputStream in = BasicBlockTest.class.getResourceAsStream("BasicBlockTest.class")) { var cc = ClassFile.of(); var classModel = cc.parse(in.readAllBytes()); - cc.build(classModel.thisClass().asSymbol(), cb -> classModel.forEachElement(cb)); + cc.build(classModel.thisClass().asSymbol(), classModel::forEach); } } } diff --git a/test/jdk/jdk/classfile/BoundAttributeTest.java b/test/jdk/jdk/classfile/BoundAttributeTest.java index 103bcffa898..6a164bec2f9 100644 --- a/test/jdk/jdk/classfile/BoundAttributeTest.java +++ b/test/jdk/jdk/classfile/BoundAttributeTest.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 @@ -23,19 +23,24 @@ /* * @test - * @bug 8304837 + * @bug 8304837 8336585 * @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; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + import java.lang.classfile.Attributes; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; import java.lang.classfile.attribute.MethodParameterInfo; import java.lang.classfile.attribute.MethodParametersAttribute; -import org.junit.jupiter.api.Test; -import org.opentest4j.AssertionFailedError; - +import java.lang.classfile.constantpool.ConstantPoolException; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; @@ -67,4 +72,33 @@ void testReadMethodParametersAttributeWithoutParameterName() { List parameters = assertDoesNotThrow(methodParametersAttribute::parameters); assertTrue(parameters.get(0).name().isEmpty()); } + + @Test + void testBadEntryTypeInList() { + var cf = ClassFile.of(); + + // Craft an attribute list with index to badly typed attributes + var bytes = cf.build(ClassDesc.of("Test"), clb -> { + var cp = clb.constantPool(); + var oneClassString = cp.utf8Entry("Test$Ape"); + var oneClass = cp.classEntry(oneClassString); + ((DirectClassBuilder) clb).writeAttribute(new UnboundAttribute.AdHocAttribute<>(Attributes.nestMembers()) { + @Override + public void writeBody(BufWriterImpl b) { + b.writeU2(2); + b.writeIndex(oneClass); + b.writeIndex(oneClassString); + } + }); + }); + + var nm = cf.parse(bytes).findAttribute(Attributes.nestMembers()).orElseThrow(); + Assertions.assertThrows(ConstantPoolException.class, () -> { + int sum = 0; + // this should throw CPE upon encountering non-ClassEntry + for (var member : nm.nestMembers()) { + sum += member.index(); + } + }); + } } diff --git a/test/jdk/jdk/classfile/BuilderBlockTest.java b/test/jdk/jdk/classfile/BuilderBlockTest.java index c8e13b79f72..30a44491e6c 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 666b36e11a7..192125c4bb6 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/ClassHierarchyInfoTest.java b/test/jdk/jdk/classfile/ClassHierarchyInfoTest.java index 4065f1d5e2f..d4a9e3a4297 100644 --- a/test/jdk/jdk/classfile/ClassHierarchyInfoTest.java +++ b/test/jdk/jdk/classfile/ClassHierarchyInfoTest.java @@ -127,7 +127,7 @@ void transformAndVerifySingle(ClassHierarchyResolver res) throws Exception { if (cle instanceof MethodModel mm) { clb.transformMethod(mm, (mb, me) -> { if (me instanceof CodeModel cm) { - mb.withCode(cob -> cm.forEachElement(cob)); + mb.withCode(cm::forEach); } else mb.with(me); diff --git a/test/jdk/jdk/classfile/ClassPrinterTest.java b/test/jdk/jdk/classfile/ClassPrinterTest.java index 9c25ff7e13a..bd95075a08e 100644 --- a/test/jdk/jdk/classfile/ClassPrinterTest.java +++ b/test/jdk/jdk/classfile/ClassPrinterTest.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,6 +23,7 @@ /* * @test + * @bug 8335927 * @summary Testing ClassFile ClassPrinter. * @run junit ClassPrinterTest */ @@ -65,7 +66,7 @@ ClassModel getClassModel() { RuntimeInvisibleTypeAnnotationsAttribute.of( TypeAnnotation.of(TypeAnnotation.TargetInfo.ofField(), List.of(TypeAnnotation.TypePathComponent.WILDCARD), - ClassDesc.of("Boo"), List.of())))))) + Annotation.of(ClassDesc.of("Boo"), List.of()))))))) .with(RuntimeInvisibleAnnotationsAttribute.of(Annotation.of(ClassDesc.of("Phoo"), AnnotationElement.ofFloat("flfl", 2), AnnotationElement.ofFloat("frfl", 3)))) .with(PermittedSubclassesAttribute.ofSymbols(ClassDesc.of("Boo"), ClassDesc.of("Phoo"))) .withField("f", ConstantDescs.CD_String, fb -> fb @@ -100,7 +101,7 @@ ClassModel getClassModel() { tryb.with(RuntimeInvisibleTypeAnnotationsAttribute.of( TypeAnnotation.of(TypeAnnotation.TargetInfo.ofField(), List.of(TypeAnnotation.TypePathComponent.WILDCARD), - ClassDesc.of("Boo"), List.of()))); + Annotation.of(ClassDesc.of("Boo"), List.of())))); tryb.invokedynamic(DynamicCallSiteDesc.of( MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, ClassDesc.of("Phoo"), "phee", MethodTypeDesc.of(ClassDesc.of("Boo"))), "intfMethod", @@ -115,7 +116,7 @@ ClassModel getClassModel() { .with(RuntimeVisibleTypeAnnotationsAttribute.of( TypeAnnotation.of(TypeAnnotation.TargetInfo.ofField(), List.of(TypeAnnotation.TypePathComponent.ARRAY), - ClassDesc.of("Fee"), List.of(AnnotationElement.ofBoolean("yes", false))))) + Annotation.of(ClassDesc.of("Fee"), List.of(AnnotationElement.ofBoolean("yes", false)))))) )))); } @@ -249,7 +250,7 @@ record components: flags: [PROTECTED] method type: (ZLjava/lang/Throwable;)Ljava/lang/Void; attributes: [AnnotationDefault, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, Exceptions, Code] - annotation default: {array: [{boolean: true}, {byte: 12}, {char: 99}, {class: LPhee;}, {double: 1.3}, {enum class: LBoo;, constant name: BOO}, {float: 3.7}, {int: 33}, {long: 3333}, {short: 25}, {string: BOO}, {annotation class: LPhoo;}]} + annotation default: {array: [{boolean: true}, {byte: 12}, {char: c}, {class: LPhee;}, {double: 1.3}, {enum class: LBoo;, constant name: BOO}, {float: 3.7}, {int: 33}, {long: 3333}, {short: 25}, {string: BOO}, {annotation class: LPhoo;}]} visible parameter annotations: parameter 1: [{annotation class: LPhoo;, values: [{name: flfl, value: {float: 22.0}}, {name: frfl, value: {float: 11.0}}]}] invisible parameter annotations: @@ -500,7 +501,7 @@ void testPrintJsonTraceAll() throws IOException { "flags": ["PROTECTED"], "method type": "(ZLjava/lang/Throwable;)Ljava/lang/Void;", "attributes": ["AnnotationDefault", "RuntimeVisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations", "Exceptions", "Code"], - "annotation default": {"array": [{"boolean": "true"}, {"byte": "12"}, {"char": "99"}, {"class": "LPhee;"}, {"double": "1.3"}, {"enum class": "LBoo;", "constant name": "BOO"}, {"float": "3.7"}, {"int": "33"}, {"long": "3333"}, {"short": "25"}, {"string": "BOO"}, {"annotation class": "LPhoo;"}]}, + "annotation default": {"array": [{"boolean": "true"}, {"byte": "12"}, {"char": "c"}, {"class": "LPhee;"}, {"double": "1.3"}, {"enum class": "LBoo;", "constant name": "BOO"}, {"float": "3.7"}, {"int": "33"}, {"long": "3333"}, {"short": "25"}, {"string": "BOO"}, {"annotation class": "LPhoo;"}]}, "visible parameter annotations": { "parameter 1": [{"annotation class": "LPhoo;", "values": [{"name": "flfl", "value": {"float": "22.0"}}, {"name": "frfl", "value": {"float": "11.0"}}]}]}, "invisible parameter annotations": { @@ -756,7 +757,7 @@ void testPrintXmlTraceAll() throws IOException { PROTECTED (ZLjava/lang/Throwable;)Ljava/lang/Void; AnnotationDefaultRuntimeVisibleParameterAnnotationsRuntimeInvisibleParameterAnnotationsExceptionsCode - true1299LPhee;1.3LBoo;BOO3.733333325BOOLPhoo; + true12cLPhee;1.3LBoo;BOO3.733333325BOOLPhoo; LPhoo;flfl22.0frfl11.0 @@ -907,6 +908,6 @@ private static void assertOut(StringBuilder out, String expected) { // System.out.println("-----------------"); // System.out.println(out.toString()); // System.out.println("-----------------"); - assertArrayEquals(out.toString().trim().split(" *\r?\n"), expected.trim().split("\n")); + assertArrayEquals(expected.trim().split("\n"), out.toString().trim().split(" *\r?\n")); } } diff --git a/test/jdk/jdk/classfile/PrimitiveClassConstantTest.java b/test/jdk/jdk/classfile/ConstantDescSymbolsTest.java similarity index 53% rename from test/jdk/jdk/classfile/PrimitiveClassConstantTest.java rename to test/jdk/jdk/classfile/ConstantDescSymbolsTest.java index 89cf43751f8..7c97c9dd5a9 100644 --- a/test/jdk/jdk/classfile/PrimitiveClassConstantTest.java +++ b/test/jdk/jdk/classfile/ConstantDescSymbolsTest.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 @@ -23,12 +23,14 @@ /* * @test - * @bug 8304031 - * @summary Testing that primitive class descs are encoded properly as loadable constants. - * @run junit PrimitiveClassConstantTest + * @bug 8304031 8338406 + * @summary Testing handling of various constant descriptors in ClassFile API. + * @run junit ConstantDescSymbolsTest */ import java.lang.constant.ClassDesc; +import java.lang.constant.DynamicConstantDesc; +import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -37,18 +39,16 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static java.lang.constant.ConstantDescs.CD_Class; -import static java.lang.constant.ConstantDescs.CD_Object; -import static java.lang.constant.ConstantDescs.CD_int; -import static java.lang.constant.ConstantDescs.CD_long; -import static java.lang.constant.ConstantDescs.INIT_NAME; -import static java.lang.constant.ConstantDescs.MTD_void; import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.constant.ConstantDescs.*; -public final class PrimitiveClassConstantTest { +import static org.junit.jupiter.api.Assertions.*; +final class ConstantDescSymbolsTest { + + // Testing that primitive class descs are encoded properly as loadable constants. @Test - public void test() throws Throwable { + void testPrimitiveClassDesc() throws Throwable { ClassDesc ape = ClassDesc.of("Ape"); var lookup = MethodHandles.lookup(); Class a = lookup.defineClass(ClassFile.of().build(ape, clb -> { @@ -73,7 +73,33 @@ public void test() throws Throwable { Supplier t = (Supplier) lookup.findConstructor(a, MethodType.methodType(void.class)) .asType(MethodType.methodType(Supplier.class)) .invokeExact(); - Assertions.assertSame(int.class, t.get()); + assertSame(int.class, t.get()); } + // Tests that condy symbols with non-static-method bootstraps are using the right lookup descriptor. + @Test + void testConstantDynamicNonStaticBootstrapMethod() throws Throwable { + record CondyBoot(MethodHandles.Lookup lookup, String name, Class type) {} + var bootClass = CondyBoot.class.describeConstable().orElseThrow(); + var bootMhDesc = MethodHandleDesc.ofConstructor(bootClass, CD_MethodHandles_Lookup, CD_String, CD_Class); + var condyDesc = DynamicConstantDesc.of(bootMhDesc); + + var targetCd = ClassDesc.of("Bat"); + var lookup = MethodHandles.lookup(); + Class a = lookup.defineClass(ClassFile.of().build(targetCd, clb -> { + clb.withInterfaceSymbols(Supplier.class.describeConstable().orElseThrow()) + .withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> cob + .aload(0).invokespecial(CD_Object, INIT_NAME, MTD_void).return_()) + .withMethodBody("get", MethodTypeDesc.of(CD_Object), ACC_PUBLIC, cob -> cob + .loadConstant(condyDesc).areturn()); + })); + @SuppressWarnings("unchecked") + Supplier t = (Supplier) lookup.findConstructor(a, MethodType.methodType(void.class)) + .asType(MethodType.methodType(Supplier.class)).invokeExact(); + var cb = t.get(); + assertEquals(MethodHandles.Lookup.ORIGINAL, cb.lookup.lookupModes() & MethodHandles.Lookup.ORIGINAL); + assertSame(a, cb.lookup.lookupClass()); + assertEquals(DEFAULT_NAME, cb.name); + assertEquals(CondyBoot.class, cb.type); + } } diff --git a/test/jdk/jdk/classfile/CorpusTest.java b/test/jdk/jdk/classfile/CorpusTest.java index 67a2ebabb31..631e2f8afaa 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); @@ -200,7 +203,7 @@ void testNullAdaptations(Path path) throws Exception { byte[] newBytes = cc.build( classModel.thisClass().asSymbol(), - classModel::forEachElement); + classModel::forEach); var newModel = cc.parse(newBytes); assertEqualsDeep(ClassRecord.ofClassModel(newModel, CompatibilityFilter.By_ClassBuilder), ClassRecord.ofClassModel(classModel, CompatibilityFilter.By_ClassBuilder), @@ -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 9070f0b1d45..5e51e585634 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 1e7639de4bb..7379218840e 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 40ae8a5fb1f..9c7b8d9e72d 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 + * @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; @@ -39,10 +38,13 @@ import java.lang.classfile.attribute.LineNumberTableAttribute; import java.lang.classfile.attribute.LocalVariableInfo; import java.lang.classfile.attribute.LocalVariableTableAttribute; +import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.ConstantPoolException; 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; @@ -129,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); @@ -154,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); @@ -167,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 @@ -190,4 +214,10 @@ void testLocalVariableOutOfBounds() { cob.constantPool().utf8Entry("a"), cob.constantPool().utf8Entry("A"), 0)))) ))).methods().get(0).code().get().elementList()); } + + @Test + void testZeroHashCPEntry() { + var cpb = ConstantPoolBuilder.of(); + cpb.intEntry(-cpb.intEntry(0).hashCode()); + } } diff --git a/test/jdk/jdk/classfile/LowAdaptTest.java b/test/jdk/jdk/classfile/LowAdaptTest.java index 46d033a8676..d47141a885a 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 7fb289cc175..d95a4fb7b29 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 { @@ -88,7 +84,7 @@ void getLVTEntries() { .orElseThrow(); List lvs = new ArrayList<>(); - co.forEachElement(e -> { + co.forEach(e -> { if (e instanceof LocalVariable l) lvs.add(l); }); @@ -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(); @@ -198,7 +194,7 @@ void getLVTTEntries() { .orElseThrow(); List lvts = new ArrayList<>(); - co.forEachElement(e -> { + co.forEach(e -> { if (e instanceof LocalVariableType l) lvts.add(l); }); @@ -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(); @@ -304,11 +300,11 @@ void testCreateLoadLVTT() throws Exception { void skipDebugSkipsLVT() { ClassModel c = ClassFile.of(ClassFile.DebugElementsOption.DROP_DEBUG).parse(fileBytes); - c.forEachElement(e -> { + c.forEach(e -> { if (e instanceof MethodModel m) { - m.forEachElement(el -> { + m.forEach(el -> { if (el instanceof CodeModel cm) { - cm.forEachElement(elem -> { + cm.forEach(elem -> { assertFalse(elem instanceof LocalVariable); assertFalse(elem instanceof LocalVariableType); }); diff --git a/test/jdk/jdk/classfile/MethodBuilderStaticFlagTest.java b/test/jdk/jdk/classfile/MethodBuilderStaticFlagTest.java new file mode 100644 index 00000000000..d5a24a144ea --- /dev/null +++ b/test/jdk/jdk/classfile/MethodBuilderStaticFlagTest.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 jdk.internal.classfile.impl.ChainedClassBuilder; +import org.junit.jupiter.api.Test; + +import java.lang.classfile.ClassBuilder; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +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.MTD_void; +import static org.junit.jupiter.api.Assertions.*; + +/* + * @test + * @bug 8336777 + * @summary Testing MethodBuilder correctly rejecting resetting the static + * access flag. + * @run junit MethodBuilderStaticFlagTest + */ +class MethodBuilderStaticFlagTest { + + void testClassBuilder(ClassBuilder clb) { + clb.withMethod("staticToStatic", MTD_void, ACC_STATIC, mb -> mb.withFlags(ACC_PUBLIC | ACC_STATIC)); + assertThrows(IllegalArgumentException.class, () -> + clb.withMethod("staticToInstance", MTD_void, ACC_STATIC, mb -> mb.withFlags(ACC_PUBLIC))); + assertThrows(IllegalArgumentException.class, () -> + clb.withMethod("instanceToStatic", MTD_void, 0, mb -> mb.withFlags(ACC_PUBLIC | ACC_STATIC))); + clb.withMethod("instanceToInstance", MTD_void, 0, mb -> mb.withFlags(ACC_PUBLIC)); + } + + @Test + void testDirectBuilder() { + ClassFile.of().build(ClassDesc.of("C1"), this::testClassBuilder); + } + + @Test + void testBufferedBuilder() { + var cf = ClassFile.of(); + var bytes = cf.build(ClassDesc.of("C2"), _ -> {}); + var cm = cf.parse(bytes); + + cf.transformClass(cm, new ClassTransform() { + @Override + public void accept(ClassBuilder builder, ClassElement element) { + builder.with(element); + } + + @Override + public void atEnd(ClassBuilder clb) { + assertInstanceOf(ChainedClassBuilder.class, clb); + testClassBuilder(clb); + } + }.andThen(ClassBuilder::with)); + } +} diff --git a/test/jdk/jdk/classfile/OneToOneTest.java b/test/jdk/jdk/classfile/OneToOneTest.java index b20d07025e6..4e21cbebbe3 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/OptionsTest.java b/test/jdk/jdk/classfile/OptionsTest.java index 10e3855b060..fda155cfd2a 100644 --- a/test/jdk/jdk/classfile/OptionsTest.java +++ b/test/jdk/jdk/classfile/OptionsTest.java @@ -117,6 +117,6 @@ void testNoUnstable(Path path, ClassFileElement e) { if (e instanceof AttributedElement ae) ae.attributes().forEach(a -> assertTrue(AttributeMapper.AttributeStability.UNSTABLE.ordinal() >= a.attributeMapper().stability().ordinal(), () -> "class " + path + " contains unexpected " + a)); - if (e instanceof CompoundElement ce) ce.forEachElement(ee -> testNoUnstable(path, (ClassFileElement)ee)); + if (e instanceof CompoundElement ce) ce.forEach(ee -> testNoUnstable(path, (ClassFileElement)ee)); } } diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index 137f5bac486..a5109dd2a18 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; @@ -270,7 +271,7 @@ private static void testTransformedStackMaps(byte[] originalBytes, ClassFile.Opt // classModel.superclass().ifPresent(cb::withSuperclass); // cb.withInterfaces(classModel.interfaces()); // cb.withVersion(classModel.majorVersion(), classModel.minorVersion()); - classModel.forEachElement(cb); + classModel.forEach(cb); }); //then verify transformed bytecode @@ -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 10c0d66d513..71ea6189aa6 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/TestRecordComponent.java b/test/jdk/jdk/classfile/TestRecordComponent.java index b39029154a0..7512e2e3167 100644 --- a/test/jdk/jdk/classfile/TestRecordComponent.java +++ b/test/jdk/jdk/classfile/TestRecordComponent.java @@ -105,7 +105,7 @@ void testChagne() throws Exception { void testOptions() throws Exception { AtomicInteger count = new AtomicInteger(0); ClassModel cm = ClassFile.of().parse(Files.readAllBytes(testClassPath)); - cm.forEachElement((ce) -> { + cm.forEach((ce) -> { if (ce instanceof RecordAttribute rm) { count.addAndGet(rm.components().size()); }}); diff --git a/test/jdk/jdk/classfile/TransformTests.java b/test/jdk/jdk/classfile/TransformTests.java index 1df7b73bda5..b78da8b4311 100644 --- a/test/jdk/jdk/classfile/TransformTests.java +++ b/test/jdk/jdk/classfile/TransformTests.java @@ -23,24 +23,43 @@ /* * @test + * @bug 8335935 8336588 * @summary Testing ClassFile transformations. * @run junit TransformTests */ -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import helpers.ByteArrayClassLoader; +import java.lang.classfile.ClassBuilder; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassTransform; -import java.lang.classfile.ClassFile; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; import java.lang.classfile.CodeModel; import java.lang.classfile.CodeTransform; +import java.lang.classfile.FieldModel; +import java.lang.classfile.FieldTransform; +import java.lang.classfile.Label; import java.lang.classfile.MethodModel; +import java.lang.classfile.MethodTransform; +import java.lang.classfile.instruction.BranchInstruction; import java.lang.classfile.instruction.ConstantInstruction; +import java.lang.classfile.instruction.LabelTarget; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.AccessFlag; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Set; + +import helpers.ByteArrayClassLoader; import org.junit.jupiter.api.Test; +import static java.lang.classfile.ClassFile.*; +import static java.lang.constant.ConstantDescs.*; import static org.junit.jupiter.api.Assertions.*; /** @@ -126,6 +145,190 @@ void testSeqN() throws Exception { assertEquals(invoke(cc.transformClass(cm, transformCode(foo2foo.andThen(foo2bar).andThen(bar2baz)))), "baz"); } + /** + * Test to ensure class elements, such as field and + * methods defined with transform/with, are visible + * to next transforms. + */ + @Test + void testClassChaining() throws Exception { + var bytes = Files.readAllBytes(testClassPath); + var cf = ClassFile.of(); + var cm = cf.parse(bytes); + var otherCm = cf.parse(cf.build(ClassDesc.of("Temp"), clb -> clb + .withMethodBody("baz", MTD_void, ACC_STATIC, CodeBuilder::return_) + .withField("baz", CD_long, ACC_STATIC))); + + var methodBaz = otherCm.methods().getFirst(); + var fieldBaz = otherCm.fields().getFirst(); + + ClassTransform transform1 = ClassTransform.endHandler(cb -> { + ClassBuilder ret; + ret = cb.withMethodBody("bar", MTD_void, ACC_STATIC, CodeBuilder::return_); + assertSame(cb, ret); + ret = cb.transformMethod(methodBaz, MethodTransform.ACCEPT_ALL); + assertSame(cb, ret); + ret = cb.withField("bar", CD_int, ACC_STATIC); + assertSame(cb, ret); + ret = cb.transformField(fieldBaz, FieldTransform.ACCEPT_ALL); + assertSame(cb, ret); + }); + + Set methodNames = new HashSet<>(); + Set fieldNames = new HashSet<>(); + ClassTransform transform2 = (cb, ce) -> { + if (ce instanceof MethodModel mm) { + methodNames.add(mm.methodName().stringValue()); + } + if (ce instanceof FieldModel fm) { + fieldNames.add(fm.fieldName().stringValue()); + } + cb.with(ce); + }; + + cf.transformClass(cm, transform1.andThen(transform2)); + + assertEquals(Set.of(INIT_NAME, "foo", "bar", "baz"), methodNames); + assertEquals(Set.of("bar", "baz"), fieldNames); + } + + /** + * Test to ensure method elements, such as generated + * or transformed code, are visible to transforms. + */ + @Test + void testMethodChaining() throws Exception { + var mtd = MethodTypeDesc.of(CD_String); + + var cf = ClassFile.of(); + + // withCode + var cm = cf.parse(cf.build(ClassDesc.of("Temp"), clb -> clb + .withMethod("baz", mtd, ACC_STATIC | ACC_NATIVE, _ -> {}))); + + MethodTransform transform1 = MethodTransform.endHandler(mb -> { + var ret = mb.withCode(cob -> cob.loadConstant("foo").areturn()); + assertSame(mb, ret); + }); + + boolean[] sawWithCode = { false }; + MethodTransform transform2 = (mb, me) -> { + if (me instanceof CodeModel) { + sawWithCode[0] = true; + } + mb.with(me); + }; + + cf.transformClass(cm, ClassTransform.transformingMethods(transform1.andThen(transform2))); + + assertTrue(sawWithCode[0], "Code attribute generated not visible"); + + // transformCode + var outerCm = cf.parse(testClassPath); + var foo = outerCm.methods().stream() + .filter(m -> m.flags().has(AccessFlag.STATIC)) + .findFirst().orElseThrow(); + + MethodTransform transform3 = MethodTransform.endHandler(mb -> { + var ret = mb.transformCode(foo.code().orElseThrow(), CodeTransform.ACCEPT_ALL); + assertSame(mb, ret); + }); + + boolean[] sawTransformCode = { false }; + MethodTransform transform4 = (mb, me) -> { + if (me instanceof CodeModel) { + sawTransformCode[0] = true; + } + mb.with(me); + }; + + cf.transformClass(cm, ClassTransform.transformingMethods(transform3.andThen(transform4))); + + assertTrue(sawTransformCode[0], "Code attribute transformed not visible"); + } + + /** + * Test to ensure code elements, such as code block + * begin and end labels, are visible to transforms. + */ + @Test + void testCodeChaining() throws Exception { + var bytes = Files.readAllBytes(testClassPath); + var cf = ClassFile.of(); + var cm = cf.parse(bytes); + + CodeTransform transform1 = new CodeTransform() { + @Override + public void atStart(CodeBuilder builder) { + builder.block(bcb -> { + bcb.loadConstant(9876L); + bcb.goto_(bcb.endLabel()); + }); + } + + @Override + public void accept(CodeBuilder builder, CodeElement element) { + builder.with(element); + } + }; + Set