diff --git a/doc/testing.html b/doc/testing.html index 6285fab1682..c1c0f15fed9 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -411,6 +411,13 @@ instrumentation special target jcov-test instead of test, e.g. make jcov-test TEST=jdk_lang. This will make sure the JCov image is built, and that JCov reporting is enabled.

+

To include JCov coverage for just a subset of all modules, you can +use the --with-jcov-modules arguments to +configure, e.g. +--with-jcov-modules=jdk.compiler,java.desktop.

+

For more fine-grained control, you can pass arbitrary filters to JCov +using --with-jcov-filters, and you can specify a specific +JDK to instrument using --with-jcov-input-jdk.

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

Please note that running with JCov reporting can be very memory diff --git a/doc/testing.md b/doc/testing.md index 351690c5e60..f6a25457b5e 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -345,6 +345,14 @@ The simplest way to run tests with JCov coverage report is to use the special target `jcov-test` instead of `test`, e.g. `make jcov-test TEST=jdk_lang`. This will make sure the JCov image is built, and that JCov reporting is enabled. +To include JCov coverage for just a subset of all modules, you can use the +`--with-jcov-modules` arguments to `configure`, e.g. +`--with-jcov-modules=jdk.compiler,java.desktop`. + +For more fine-grained control, you can pass arbitrary filters to JCov using +`--with-jcov-filters`, and you can specify a specific JDK to instrument +using `--with-jcov-input-jdk`. + The JCov report is stored in `build/$BUILD/test-results/jcov-output/report`. Please note that running with JCov reporting can be very memory intensive. diff --git a/make/Coverage.gmk b/make/Coverage.gmk index 2fd4e4ec6d4..a375c343185 100644 --- a/make/Coverage.gmk +++ b/make/Coverage.gmk @@ -34,21 +34,28 @@ else JCOV_INPUT_IMAGE_DIR := $(JDK_IMAGE_DIR) endif +JCOV_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/jcov + #moving instrumented jdk image in and out of jcov_temp because of CODETOOLS-7902299 -JCOV_TEMP := $(SUPPORT_OUTPUTDIR)/jcov_temp +JCOV_TEMP := $(JCOV_SUPPORT_DIR)/temp + +ifneq ($(JCOV_MODULES), ) + JCOV_MODULES_FILTER := $(foreach m, $(JCOV_MODULES), -include_module $m) +endif $(JCOV_IMAGE_DIR)/release: $(JCOV_INPUT_IMAGE_DIR)/release $(call LogWarn, Creating instrumented jdk image with JCov) $(call MakeDir, $(JCOV_TEMP) $(IMAGES_OUTPUTDIR)) $(RM) -r $(JCOV_IMAGE_DIR) $(JCOV_TEMP)/* $(CP) -r $(JCOV_INPUT_IMAGE_DIR) $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR) - $(JAVA) -Xmx3g -jar $(JCOV_HOME)/lib/jcov.jar JREInstr \ + $(call ExecuteWithLog, $(JCOV_SUPPORT_DIR)/run-jcov, \ + $(JAVA) -Xmx3g -jar $(JCOV_HOME)/lib/jcov.jar JREInstr \ -t $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR)/template.xml \ -rt $(JCOV_HOME)/lib/jcov_network_saver.jar \ -exclude 'java.lang.Object' \ -exclude jdk.test.Main -exclude '**\$Proxy*' \ - $(JCOV_FILTERS) \ - $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR) + $(JCOV_MODULES_FILTER) $(JCOV_FILTERS) \ + $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR)) $(MV) $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR) $(JCOV_IMAGE_DIR) $(RMDIR) $(JCOV_TEMP) diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 69dc47a0c61..3c0280353a9 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -115,6 +115,7 @@ JTREG_COV_OPTIONS := ifeq ($(TEST_OPTS_JCOV), true) JCOV_OUTPUT_DIR := $(TEST_RESULTS_DIR)/jcov-output + JCOV_SUPPORT_DIR := $(TEST_SUPPORT_DIR)/jcov-support JCOV_GRABBER_LOG := $(JCOV_OUTPUT_DIR)/grabber.log JCOV_RESULT_FILE := $(JCOV_OUTPUT_DIR)/result.xml JCOV_REPORT := $(JCOV_OUTPUT_DIR)/report @@ -1343,12 +1344,14 @@ TARGETS += run-all-tests pre-run-test post-run-test run-test-report run-test ifeq ($(TEST_OPTS_JCOV), true) + JCOV_VM_OPTS := -Xmx4g -Djdk.xml.totalEntitySizeLimit=0 -Djdk.xml.maxGeneralEntitySizeLimit=0 + jcov-do-start-grabber: $(call MakeDir, $(JCOV_OUTPUT_DIR)) if $(JAVA) -jar $(JCOV_HOME)/lib/jcov.jar GrabberManager -status 1>/dev/null 2>&1 ; then \ $(JAVA) -jar $(JCOV_HOME)/lib/jcov.jar GrabberManager -stop -stoptimeout 3600 ; \ fi - $(JAVA) -Xmx4g -jar $(JCOV_HOME)/lib/jcov.jar Grabber -v -t \ + $(JAVA) $(JCOV_VM_OPTS) -jar $(JCOV_HOME)/lib/jcov.jar Grabber -v -t \ $(JCOV_IMAGE_DIR)/template.xml -o $(JCOV_RESULT_FILE) \ 1>$(JCOV_GRABBER_LOG) 2>&1 & @@ -1361,6 +1364,10 @@ ifeq ($(TEST_OPTS_JCOV), true) $(JAVA) -jar $(JCOV_HOME)/lib/jcov.jar GrabberManager -stop -stoptimeout 3600 JCOV_REPORT_TITLE := JDK code coverage report
+ ifneq ($(JCOV_MODULES), ) + JCOV_MODULES_FILTER := $(foreach m, $(JCOV_MODULES), -include_module $m) + JCOV_REPORT_TITLE += Included modules: $(JCOV_MODULES)
+ endif ifneq ($(JCOV_FILTERS), ) JCOV_REPORT_TITLE += Code filters: $(JCOV_FILTERS)
endif @@ -1368,11 +1375,12 @@ ifeq ($(TEST_OPTS_JCOV), true) jcov-gen-report: jcov-stop-grabber $(call LogWarn, Generating JCov report ...) - $(JAVA) -Xmx4g -jar $(JCOV_HOME)/lib/jcov.jar RepGen -sourcepath \ + $(call ExecuteWithLog, $(JCOV_SUPPORT_DIR)/run-jcov-repgen, \ + $(JAVA) $(JCOV_VM_OPTS) -jar $(JCOV_HOME)/lib/jcov.jar RepGen -sourcepath \ `$(ECHO) $(TOPDIR)/src/*/share/classes/ | $(TR) ' ' ':'` -fmt html \ - $(JCOV_FILTERS) \ + $(JCOV_MODULES_FILTER) $(JCOV_FILTERS) \ -mainReportTitle "$(JCOV_REPORT_TITLE)" \ - -o $(JCOV_REPORT) $(JCOV_RESULT_FILE) + -o $(JCOV_REPORT) $(JCOV_RESULT_FILE)) TARGETS += jcov-do-start-grabber jcov-start-grabber jcov-stop-grabber \ jcov-gen-report @@ -1392,7 +1400,7 @@ ifeq ($(TEST_OPTS_JCOV), true) jcov-gen-diffcoverage: jcov-stop-grabber $(call LogWarn, Generating diff coverage with changeset $(TEST_OPTS_JCOV_DIFF_CHANGESET) ... ) $(DIFF_COMMAND) - $(JAVA) -Xmx4g -jar $(JCOV_HOME)/lib/jcov.jar \ + $(JAVA) $(JCOV_VM_OPTS) -jar $(JCOV_HOME)/lib/jcov.jar \ DiffCoverage -replaceDiff "src/.*/classes/:" -all \ $(JCOV_RESULT_FILE) $(JCOV_SOURCE_DIFF) > \ $(JCOV_DIFF_COVERAGE_REPORT) diff --git a/make/RunTestsPrebuilt.gmk b/make/RunTestsPrebuilt.gmk index ba9731789b0..f5fe1d33830 100644 --- a/make/RunTestsPrebuilt.gmk +++ b/make/RunTestsPrebuilt.gmk @@ -217,9 +217,9 @@ else ifeq ($(OPENJDK_TARGET_OS), macosx) else ifeq ($(OPENJDK_TARGET_OS), windows) NUM_CORES := $(NUMBER_OF_PROCESSORS) MEMORY_SIZE := $(shell \ - $(EXPR) `wmic computersystem get totalphysicalmemory -value \ - | $(GREP) = | $(SED) 's/\\r//g' \ - | $(CUT) -d "=" -f 2-` / 1024 / 1024 \ + $(EXPR) `powershell -Command \ + "(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory" \ + | $(SED) 's/\\r//g' ` / 1024 / 1024 \ ) endif ifeq ($(NUM_CORES), ) diff --git a/make/autoconf/boot-jdk.m4 b/make/autoconf/boot-jdk.m4 index d39e6e75a94..feb16c7d179 100644 --- a/make/autoconf/boot-jdk.m4 +++ b/make/autoconf/boot-jdk.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -180,11 +180,13 @@ AC_DEFUN([BOOTJDK_CHECK_JAVA_HOME], # Test: Is there a java or javac in the PATH, which is a symlink to the JDK? AC_DEFUN([BOOTJDK_CHECK_JAVA_IN_PATH_IS_SYMLINK], [ - UTIL_LOOKUP_PROGS(JAVAC_CHECK, javac, , NOFIXPATH) - UTIL_LOOKUP_PROGS(JAVA_CHECK, java, , NOFIXPATH) - BINARY="$JAVAC_CHECK" - if test "x$JAVAC_CHECK" = x; then - BINARY="$JAVA_CHECK" + UTIL_LOOKUP_PROGS(JAVAC_CHECK, javac) + UTIL_GET_EXECUTABLE(JAVAC_CHECK) # Will setup JAVAC_CHECK_EXECUTABLE + UTIL_LOOKUP_PROGS(JAVA_CHECK, java) + UTIL_GET_EXECUTABLE(JAVA_CHECK) # Will setup JAVA_CHECK_EXECUTABLE + BINARY="$JAVAC_CHECK_EXECUTABLE" + if test "x$JAVAC_CHECK_EXECUTABLE" = x; then + BINARY="$JAVA_CHECK_EXECUTABLE" fi if test "x$BINARY" != x; then # So there is a java(c) binary, it might be part of a JDK. diff --git a/make/autoconf/build-performance.m4 b/make/autoconf/build-performance.m4 index 4414ea0d93c..10e86e75199 100644 --- a/make/autoconf/build-performance.m4 +++ b/make/autoconf/build-performance.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -75,7 +75,8 @@ AC_DEFUN([BPERF_CHECK_MEMORY_SIZE], FOUND_MEM=yes elif test "x$OPENJDK_BUILD_OS" = xwindows; then # Windows, but without cygwin - MEMORY_SIZE=`wmic computersystem get totalphysicalmemory -value | grep = | cut -d "=" -f 2-` + MEMORY_SIZE=`powershell -Command \ + "(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory" | $SED 's/\\r//g' ` MEMORY_SIZE=`expr $MEMORY_SIZE / 1024 / 1024` FOUND_MEM=yes fi diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index f8a512f503d..289ed935fdf 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -405,10 +405,19 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_CODE_COVERAGE], JCOV_FILTERS="$with_jcov_filters" fi fi + + UTIL_ARG_WITH(NAME: jcov-modules, TYPE: string, + DEFAULT: [], RESULT: JCOV_MODULES_COMMMA_SEPARATED, + DESC: [which modules to include in jcov (comma-separated)], + OPTIONAL: true) + + # Replace "," with " ". + JCOV_MODULES=${JCOV_MODULES_COMMMA_SEPARATED//,/ } AC_SUBST(JCOV_ENABLED) AC_SUBST(JCOV_HOME) AC_SUBST(JCOV_INPUT_JDK) AC_SUBST(JCOV_FILTERS) + AC_SUBST(JCOV_MODULES) ]) ################################################################################ diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template index ebaa487b40a..e720916d88a 100644 --- a/make/autoconf/spec.gmk.template +++ b/make/autoconf/spec.gmk.template @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -454,6 +454,7 @@ JCOV_ENABLED := @JCOV_ENABLED@ JCOV_HOME := @JCOV_HOME@ JCOV_INPUT_JDK := @JCOV_INPUT_JDK@ JCOV_FILTERS := @JCOV_FILTERS@ +JCOV_MODULES := @JCOV_MODULES@ # AddressSanitizer ASAN_ENABLED := @ASAN_ENABLED@ diff --git a/make/autoconf/util_paths.m4 b/make/autoconf/util_paths.m4 index 9e3e5472c9e..c3cc3955deb 100644 --- a/make/autoconf/util_paths.m4 +++ b/make/autoconf/util_paths.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -186,7 +186,6 @@ AC_DEFUN([UTIL_CHECK_WINENV_EXEC_TYPE], # it need to be in the PATH. # $1: The name of the variable to fix # $2: Where to look for the command (replaces $PATH) -# $3: set to NOFIXPATH to skip prefixing FIXPATH, even if needed on platform AC_DEFUN([UTIL_FIXUP_EXECUTABLE], [ input="[$]$1" @@ -282,10 +281,6 @@ AC_DEFUN([UTIL_FIXUP_EXECUTABLE], fi fi - if test "x$3" = xNOFIXPATH; then - fixpath_prefix="" - fi - # Now join together the path and the arguments once again new_complete="$fixpath_prefix$new_path$arguments" $1="$new_complete" @@ -379,7 +374,6 @@ AC_DEFUN([UTIL_SETUP_TOOL], # $1: variable to set # $2: executable name (or list of names) to look for # $3: [path] -# $4: set to NOFIXPATH to skip prefixing FIXPATH, even if needed on platform AC_DEFUN([UTIL_LOOKUP_PROGS], [ UTIL_SETUP_TOOL($1, [ @@ -421,10 +415,8 @@ AC_DEFUN([UTIL_LOOKUP_PROGS], # If we have FIXPATH enabled, strip all instances of it and prepend # a single one, to avoid double fixpath prefixing. - if test "x$4" != xNOFIXPATH; then - [ if [[ $FIXPATH != "" && $result =~ ^"$FIXPATH " ]]; then ] - result="\$FIXPATH ${result#"$FIXPATH "}" - fi + [ if [[ $FIXPATH != "" && $result =~ ^"$FIXPATH " ]]; then ] + result="\$FIXPATH ${result#"$FIXPATH "}" fi AC_MSG_RESULT([$result]) break 2; @@ -515,6 +507,24 @@ AC_DEFUN([UTIL_ADD_FIXPATH], fi ]) +################################################################################ +# Return a path to the executable binary from a command line, stripping away +# any FIXPATH prefix or arguments. The resulting value can be checked for +# existence using "test -e". The result is returned in a variable named +# "$1_EXECUTABLE". +# +# $1: variable describing the command to get the binary for +AC_DEFUN([UTIL_GET_EXECUTABLE], +[ + # Strip the FIXPATH prefix, if any + fixpath_stripped="[$]$1" + [ if [[ $FIXPATH != "" && $fixpath_stripped =~ ^"$FIXPATH " ]]; then ] + fixpath_stripped="${fixpath_stripped#"$FIXPATH "}" + fi + # Remove any arguments following the binary + $1_EXECUTABLE="${fixpath_stripped%% *}" +]) + ################################################################################ AC_DEFUN([UTIL_REMOVE_SYMBOLIC_LINKS], [ diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index f0cb6f451a3..172ed74f4cd 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -241,10 +241,10 @@ var getJibProfilesCommon = function (input, data) { // List of the main profile names used for iteration common.main_profile_names = [ - "linux-x64", "linux-x86", "macosx-x64", "macosx-aarch64", + "macosx-x64", "macosx-aarch64", "windows-x64", "windows-aarch64", - "linux-aarch64", "linux-arm32", "linux-ppc64le", "linux-s390x", - "linux-riscv64" + "linux-x64", "linux-aarch64", + "linux-arm32", "linux-ppc64le", "linux-s390x", "linux-riscv64" ]; // These are the base settings for all the main build profiles. @@ -283,9 +283,6 @@ var getJibProfilesCommon = function (input, data) { labels: "open" }; - common.configure_args_64bit = ["--with-target-bits=64"]; - common.configure_args_32bit = ["--with-target-bits=32"]; - /** * Define common artifacts template for all main profiles * @param o - Object containing data for artifacts @@ -412,58 +409,34 @@ var getJibProfilesProfiles = function (input, common, data) { // Main SE profiles var profiles = { - - "linux-x64": { - target_os: "linux", - target_cpu: "x64", - dependencies: ["devkit", "gtest", "build_devkit", "graphviz", "pandoc", "tidy"], - configure_args: concat( - (input.build_cpu == "x64" ? common.configure_args_64bit - : "--openjdk-target=x86_64-linux-gnu"), - "--with-zlib=system", "--disable-dtrace", - (isWsl(input) ? [ "--host=x86_64-unknown-linux-gnu", - "--build=x86_64-unknown-linux-gnu" ] : [])), - }, - - "linux-x86": { - target_os: "linux", - target_cpu: "x86", - build_cpu: "x64", - dependencies: ["devkit", "gtest", "libffi"], - configure_args: concat(common.configure_args_32bit, [ - "--with-jvm-variants=minimal,server", - "--with-zlib=system", - "--with-libffi=" + input.get("libffi", "home_path"), - "--enable-libffi-bundling", - "--enable-fallback-linker" - ]) - }, - "macosx-x64": { target_os: "macosx", target_cpu: "x64", dependencies: ["devkit", "gtest", "graphviz", "pandoc", "tidy"], - configure_args: concat(common.configure_args_64bit, "--with-zlib=system", + configure_args: [ + "--with-zlib=system", "--with-macosx-version-max=11.00.00", "--enable-compatible-cds-alignment", // Use system SetFile instead of the one in the devkit as the // devkit one may not work on Catalina. - "SETFILE=/usr/bin/SetFile"), + "SETFILE=/usr/bin/SetFile" + ], }, "macosx-aarch64": { target_os: "macosx", target_cpu: "aarch64", dependencies: ["devkit", "gtest", "graphviz", "pandoc", "tidy"], - configure_args: concat(common.configure_args_64bit, - "--with-macosx-version-max=11.00.00"), + configure_args: [ + "--with-macosx-version-max=11.00.00" + ], }, "windows-x64": { target_os: "windows", target_cpu: "x64", dependencies: ["devkit", "gtest", "pandoc"], - configure_args: concat(common.configure_args_64bit), + configure_args: [], }, "windows-aarch64": { @@ -475,7 +448,19 @@ var getJibProfilesProfiles = function (input, common, data) { ], }, - "linux-aarch64": { + "linux-x64": { + target_os: "linux", + target_cpu: "x64", + dependencies: ["devkit", "gtest", "build_devkit", "graphviz", "pandoc", "tidy"], + configure_args: concat( + "--with-zlib=system", + "--disable-dtrace", + (cross_compiling ? [ "--openjdk-target=x86_64-linux-gnu" ] : []), + (isWsl(input) ? [ "--host=x86_64-unknown-linux-gnu", + "--build=x86_64-unknown-linux-gnu" ] : [])), + }, + + "linux-aarch64": { target_os: "linux", target_cpu: "aarch64", dependencies: ["devkit", "gtest", "build_devkit", "graphviz", "pandoc", "tidy"], @@ -492,8 +477,10 @@ var getJibProfilesProfiles = function (input, common, data) { build_cpu: "x64", dependencies: ["devkit", "gtest", "build_devkit"], configure_args: [ - "--openjdk-target=arm-linux-gnueabihf", "--with-freetype=bundled", - "--with-abi-profile=arm-vfp-hflt", "--disable-warnings-as-errors" + "--openjdk-target=arm-linux-gnueabihf", + "--with-freetype=bundled", + "--with-abi-profile=arm-vfp-hflt", + "--disable-warnings-as-errors" ], }, @@ -503,7 +490,8 @@ var getJibProfilesProfiles = function (input, common, data) { build_cpu: "x64", dependencies: ["devkit", "gtest", "build_devkit"], configure_args: [ - "--openjdk-target=ppc64le-linux-gnu", "--with-freetype=bundled", + "--openjdk-target=ppc64le-linux-gnu", + "--with-freetype=bundled", "--disable-warnings-as-errors" ], }, @@ -514,7 +502,8 @@ var getJibProfilesProfiles = function (input, common, data) { build_cpu: "x64", dependencies: ["devkit", "gtest", "build_devkit"], configure_args: [ - "--openjdk-target=s390x-linux-gnu", "--with-freetype=bundled", + "--openjdk-target=s390x-linux-gnu", + "--with-freetype=bundled", "--disable-warnings-as-errors" ], }, @@ -525,7 +514,8 @@ var getJibProfilesProfiles = function (input, common, data) { build_cpu: "x64", dependencies: ["devkit", "gtest", "build_devkit"], configure_args: [ - "--openjdk-target=riscv64-linux-gnu", "--with-freetype=bundled", + "--openjdk-target=riscv64-linux-gnu", + "--with-freetype=bundled", "--disable-warnings-as-errors" ], }, @@ -586,24 +576,24 @@ var getJibProfilesProfiles = function (input, common, data) { target_os: "linux", target_cpu: "x64", dependencies: ["devkit", "gtest", "libffi"], - configure_args: concat(common.configure_args_64bit, [ + configure_args: [ "--with-zlib=system", "--with-jvm-variants=zero", "--with-libffi=" + input.get("libffi", "home_path"), "--enable-libffi-bundling", - ]) + ] }, "linux-aarch64-zero": { target_os: "linux", target_cpu: "aarch64", dependencies: ["devkit", "gtest", "libffi"], - configure_args: concat(common.configure_args_64bit, [ + configure_args: [ "--with-zlib=system", "--with-jvm-variants=zero", "--with-libffi=" + input.get("libffi", "home_path"), "--enable-libffi-bundling" - ]) + ] }, "linux-x86-zero": { @@ -611,12 +601,13 @@ var getJibProfilesProfiles = function (input, common, data) { target_cpu: "x86", build_cpu: "x64", dependencies: ["devkit", "gtest", "libffi"], - configure_args: concat(common.configure_args_32bit, [ + configure_args: [ + "--with-target-bits=32", "--with-zlib=system", "--with-jvm-variants=zero", "--with-libffi=" + input.get("libffi", "home_path"), "--enable-libffi-bundling" - ]) + ] } } profiles = concatObjects(profiles, zeroProfiles); @@ -635,8 +626,10 @@ var getJibProfilesProfiles = function (input, common, data) { target_os: "linux", target_cpu: "x64", dependencies: ["devkit", "gtest"], - configure_args: concat(common.configure_args_64bit, - "--with-zlib=system", "--disable-precompiled-headers"), + configure_args: [ + "--with-zlib=system", + "--disable-precompiled-headers" + ], }, }; profiles = concatObjects(profiles, noPchProfiles); @@ -693,9 +686,6 @@ var getJibProfilesProfiles = function (input, common, data) { "linux-x64": { platform: "linux-x64", }, - "linux-x86": { - platform: "linux-x86", - }, "macosx-x64": { platform: "macos-x64", jdk_subdir: "jdk-" + data.version + ".jdk/Contents/Home", diff --git a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk index 92f40472c3c..347ca44d25f 100644 --- a/make/test/BuildMicrobenchmark.gmk +++ b/make/test/BuildMicrobenchmark.gmk @@ -119,7 +119,6 @@ $(JMH_UNPACKED_JARS_DONE): $(JMH_RUNTIME_JARS) $(foreach jar, $(JMH_RUNTIME_JARS), \ $$($(UNZIP) -oq $(jar) -d $(JMH_UNPACKED_DIR))) $(RM) -r $(JMH_UNPACKED_DIR)/META-INF - $(RM) $(JMH_UNPACKED_DIR)/*.xml $(TOUCH) $@ # Copy dependency files for inclusion in the benchmark JARs diff --git a/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp b/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp index 3015206dadc..071dd2c4179 100644 --- a/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp @@ -180,6 +180,7 @@ bool CodeInstaller::pd_relocate(address pc, jint mark) { case POLL_RETURN_FAR: _instructions->relocate(pc, relocInfo::poll_return_type); return true; +#if INCLUDE_ZGC case Z_BARRIER_RELOCATION_FORMAT_LOAD_GOOD_BEFORE_TB_X: _instructions->relocate(pc, barrier_Relocation::spec(), ZBarrierRelocationFormatLoadGoodBeforeTbX); return true; @@ -192,6 +193,7 @@ bool CodeInstaller::pd_relocate(address pc, jint mark) { case Z_BARRIER_RELOCATION_FORMAT_STORE_BAD_BEFORE_MOV: _instructions->relocate(pc, barrier_Relocation::spec(), ZBarrierRelocationFormatStoreBadBeforeMov); return true; +#endif } return false; diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 4773043e1ba..63d5d4f3e61 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -1904,8 +1904,14 @@ enum VectorMask { INSN(vand_vv, 0b1010111, 0b000, 0b001001); // Vector Single-Width Integer Add and Subtract - INSN(vsub_vv, 0b1010111, 0b000, 0b000010); INSN(vadd_vv, 0b1010111, 0b000, 0b000000); + INSN(vsub_vv, 0b1010111, 0b000, 0b000010); + + // Vector Saturating Integer Add and Subtract + INSN(vsadd_vv, 0b1010111, 0b000, 0b100001); + INSN(vsaddu_vv, 0b1010111, 0b000, 0b100000); + INSN(vssub_vv, 0b1010111, 0b000, 0b100011); + INSN(vssubu_vv, 0b1010111, 0b000, 0b100010); // Vector Register Gather Instructions INSN(vrgather_vv, 0b1010111, 0b000, 0b001100); diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index 58c78874797..b6b8395dbb7 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -251,7 +251,6 @@ instruct vmaskcmp_fp(vRegMask dst, vReg src1, vReg src2, immI cond) %{ predicate(Matcher::vector_element_basic_type(n) == T_FLOAT || Matcher::vector_element_basic_type(n) == T_DOUBLE); match(Set dst (VectorMaskCmp (Binary src1 src2) cond)); - effect(TEMP_DEF dst); format %{ "vmaskcmp_fp $dst, $src1, $src2, $cond" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); @@ -651,6 +650,144 @@ instruct vsubL_vx_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} +// -------- vector saturating integer operations + +// vector saturating signed integer addition + +instruct vsadd(vReg dst, vReg src1, vReg src2) %{ + predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned()); + match(Set dst (SaturatingAddV src1 src2)); + ins_cost(VEC_COST); + format %{ "vsadd $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vsadd_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +// vector saturating unsigned integer addition + +instruct vsaddu(vReg dst, vReg src1, vReg src2) %{ + predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned()); + match(Set dst (SaturatingAddV src1 src2)); + ins_cost(VEC_COST); + format %{ "vsaddu $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vsaddu_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +// vector saturating signed integer addition (predicated) + +instruct vsadd_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{ + predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned()); + match(Set dst_src (SaturatingAddV (Binary dst_src src1) v0)); + ins_cost(VEC_COST); + format %{ "vsadd_masked $dst_src, $dst_src, $src1, $v0" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vsadd_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg), + as_VectorRegister($src1$$reg), Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +// vector saturating unsigned integer addition (predicated) + +instruct vsaddu_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{ + predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned()); + match(Set dst_src (SaturatingAddV (Binary dst_src src1) v0)); + ins_cost(VEC_COST); + format %{ "vsaddu_masked $dst_src, $dst_src, $src1, $v0" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vsaddu_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg), + as_VectorRegister($src1$$reg), Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +// vector saturating signed integer subtraction + +instruct vssub(vReg dst, vReg src1, vReg src2) %{ + predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned()); + match(Set dst (SaturatingSubV src1 src2)); + ins_cost(VEC_COST); + format %{ "vssub $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vssub_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +// vector saturating unsigned integer subtraction + +instruct vssubu(vReg dst, vReg src1, vReg src2) %{ + predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned()); + match(Set dst (SaturatingSubV src1 src2)); + ins_cost(VEC_COST); + format %{ "vssubu $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vssubu_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +// vector saturating signed integer subtraction (predicated) + +instruct vssub_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{ + predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned()); + match(Set dst_src (SaturatingSubV (Binary dst_src src1) v0)); + ins_cost(VEC_COST); + format %{ "vssub_masked $dst_src, $dst_src, $src1, $v0" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vssub_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg), + as_VectorRegister($src1$$reg), Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +// vector saturating unsigned integer subtraction (predicated) + +instruct vssubu_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{ + predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned()); + match(Set dst_src (SaturatingSubV (Binary dst_src src1) v0)); + ins_cost(VEC_COST); + format %{ "vssubu_masked $dst_src, $dst_src, $src1, $v0" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vssubu_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg), + as_VectorRegister($src1$$reg), Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + // vector and instruct vand(vReg dst, vReg src1, vReg src2) %{ diff --git a/src/hotspot/cpu/s390/frame_s390.inline.hpp b/src/hotspot/cpu/s390/frame_s390.inline.hpp index b033b3f939b..ede7039ce0a 100644 --- a/src/hotspot/cpu/s390/frame_s390.inline.hpp +++ b/src/hotspot/cpu/s390/frame_s390.inline.hpp @@ -203,11 +203,14 @@ inline intptr_t* frame::interpreter_frame_expression_stack() const { // Also begin is one past last monitor. inline intptr_t* frame::interpreter_frame_top_frame_sp() { - return (intptr_t*)ijava_state()->top_frame_sp; + intptr_t n = *addr_at(_z_ijava_idx(top_frame_sp)); + return &fp()[n]; // return relativized locals } inline void frame::interpreter_frame_set_top_frame_sp(intptr_t* top_frame_sp) { - ijava_state()->top_frame_sp = (intptr_t) top_frame_sp; + assert(is_interpreted_frame(), "interpreted frame expected"); + // set relativized top_frame_sp + ijava_state()->top_frame_sp = (intptr_t) (top_frame_sp - fp()); } inline void frame::interpreter_frame_set_sender_sp(intptr_t* sender_sp) { diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index a7bea358f4e..9717d260279 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -576,7 +576,10 @@ void InterpreterMacroAssembler::prepare_to_jump_from_interpreted(Register method // Satisfy interpreter calling convention (see generate_normal_entry()). z_lgr(Z_R10, Z_SP); // Set sender sp (aka initial caller sp, aka unextended sp). // Record top_frame_sp, because the callee might modify it, if it's compiled. - z_stg(Z_SP, _z_ijava_state_neg(top_frame_sp), Z_fp); + assert_different_registers(Z_R1, method); + z_sgrk(Z_R1, Z_SP, Z_fp); + z_srag(Z_R1, Z_R1, Interpreter::logStackElementSize); + z_stg(Z_R1, _z_ijava_state_neg(top_frame_sp), Z_fp); save_bcp(); save_esp(); z_lgr(Z_method, method); // Set Z_method (kills Z_fp!). diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index 4edf8046833..04fe574088e 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -637,6 +637,8 @@ address TemplateInterpreterGenerator::generate_return_entry_for (TosState state, Register sp_before_i2c_extension = Z_bcp; __ z_lg(Z_fp, _z_abi(callers_sp), Z_SP); // Restore frame pointer. __ z_lg(sp_before_i2c_extension, Address(Z_fp, _z_ijava_state_neg(top_frame_sp))); + __ z_slag(sp_before_i2c_extension, sp_before_i2c_extension, Interpreter::logStackElementSize); + __ z_agr(sp_before_i2c_extension, Z_fp); __ resize_frame_absolute(sp_before_i2c_extension, Z_locals/*tmp*/, true/*load_fp*/); // TODO(ZASM): necessary?? diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 19e25cde2ec..177be6e59f7 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -5731,7 +5731,7 @@ void C2_MacroAssembler::vector_compress_expand_avx2(int opcode, XMMRegister dst, // in a permute table row contains either a valid permute index or a -1 (default) // value, this can potentially be used as a blending mask after // compressing/expanding the source vector lanes. - vblendvps(dst, dst, xtmp, permv, vec_enc, false, permv); + vblendvps(dst, dst, xtmp, permv, vec_enc, true, permv); } void C2_MacroAssembler::vector_compress_expand(int opcode, XMMRegister dst, XMMRegister src, KRegister mask, diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index 8471d04b572..9df28d61bbc 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -1030,6 +1030,7 @@ bool AOTClassLocationConfig::validate(bool has_aot_linked_classes, bool* has_ext } } else { log_warning(cds)("%s%s", mismatch_msg, hint_msg); + MetaspaceShared::report_loading_error(nullptr); } } return success; diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 3de4cdec187..549b7b3ba6a 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -313,7 +313,7 @@ address ArchiveBuilder::reserve_buffer() { ReservedSpace rs = MemoryReserver::reserve(buffer_size, MetaspaceShared::core_region_alignment(), os::vm_page_size(), - mtClassShared); + mtNone); if (!rs.is_reserved()) { log_error(cds)("Failed to reserve %zu bytes of output buffer.", buffer_size); MetaspaceShared::unrecoverable_writing_error(); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 701b2584774..463bfe3a98d 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -647,6 +647,14 @@ bool CDSConfig::is_using_archive() { return UseSharedSpaces; } +bool CDSConfig::is_using_only_default_archive() { + return is_using_archive() && + input_static_archive_path() != nullptr && + default_archive_path() != nullptr && + strcmp(input_static_archive_path(), default_archive_path()) == 0 && + input_dynamic_archive_path() == nullptr; +} + bool CDSConfig::is_logging_lambda_form_invokers() { return ClassListWriter::is_enabled() || is_dumping_dynamic_archive(); } diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index e82acebd175..0fb6cc1ec6a 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -103,6 +103,7 @@ public: // input archive(s) static bool is_using_archive() NOT_CDS_RETURN_(false); + static bool is_using_only_default_archive() NOT_CDS_RETURN_(false); // static_archive static bool is_dumping_static_archive() { return CDS_ONLY(_is_dumping_static_archive) NOT_CDS(false); } diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index fbef3c9532d..ff537ef96d6 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -326,7 +326,7 @@ bool FileMapInfo::validate_class_location() { if (header()->has_full_module_graph() && has_extra_module_paths) { CDSConfig::stop_using_optimized_module_handling(); - log_info(cds)("optimized module handling: disabled because extra module path(s) are specified"); + MetaspaceShared::report_loading_error("optimized module handling: disabled because extra module path(s) are specified"); } if (CDSConfig::is_dumping_dynamic_archive()) { @@ -1256,7 +1256,7 @@ char* FileMapInfo::map_bitmap_region() { char* bitmap_base = map_memory(_fd, _full_path, r->file_offset(), requested_addr, r->used_aligned(), read_only, allow_exec, mtClassShared); if (bitmap_base == nullptr) { - log_info(cds)("failed to map relocation bitmap"); + MetaspaceShared::report_loading_error("failed to map relocation bitmap"); return nullptr; } @@ -1454,9 +1454,9 @@ void FileMapInfo::map_or_load_heap_region() { success = ArchiveHeapLoader::load_heap_region(this); } else { if (!UseCompressedOops && !ArchiveHeapLoader::can_map()) { - log_info(cds)("Cannot use CDS heap data. Selected GC not compatible -XX:-UseCompressedOops"); + MetaspaceShared::report_loading_error("Cannot use CDS heap data. Selected GC not compatible -XX:-UseCompressedOops"); } else { - log_info(cds)("Cannot use CDS heap data. UseEpsilonGC, UseG1GC, UseSerialGC, UseParallelGC, or UseShenandoahGC are required."); + MetaspaceShared::report_loading_error("Cannot use CDS heap data. UseEpsilonGC, UseG1GC, UseSerialGC, UseParallelGC, or UseShenandoahGC are required."); } } } @@ -1468,8 +1468,10 @@ void FileMapInfo::map_or_load_heap_region() { // all AOT-linked classes are visible. // // We get here because the heap is too small. The app will fail anyway. So let's quit. - MetaspaceShared::unrecoverable_loading_error("CDS archive has aot-linked classes but the archived " - "heap objects cannot be loaded. Try increasing your heap size."); + log_error(cds)("%s has aot-linked classes but the archived " + "heap objects cannot be loaded. Try increasing your heap size.", + CDSConfig::type_of_archive_being_loaded()); + MetaspaceShared::unrecoverable_loading_error(); } CDSConfig::stop_using_full_module_graph("archive heap loading failed"); } @@ -1635,7 +1637,7 @@ bool FileMapInfo::map_heap_region_impl() { // allocate from java heap HeapWord* start = G1CollectedHeap::heap()->alloc_archive_region(word_size, (HeapWord*)requested_start); if (start == nullptr) { - log_info(cds)("UseSharedSpaces: Unable to allocate java heap region for archive heap."); + MetaspaceShared::report_loading_error("UseSharedSpaces: Unable to allocate java heap region for archive heap."); return false; } @@ -1670,7 +1672,7 @@ bool FileMapInfo::map_heap_region_impl() { if (VerifySharedSpaces && !r->check_region_crc(base)) { dealloc_heap_region(); - log_info(cds)("UseSharedSpaces: mapped heap region is corrupt"); + MetaspaceShared::report_loading_error("UseSharedSpaces: mapped heap region is corrupt"); return false; } } @@ -1694,7 +1696,7 @@ bool FileMapInfo::map_heap_region_impl() { if (_heap_pointers_need_patching) { char* bitmap_base = map_bitmap_region(); if (bitmap_base == nullptr) { - log_info(cds)("CDS heap cannot be used because bitmap region cannot be mapped"); + MetaspaceShared::report_loading_error("CDS heap cannot be used because bitmap region cannot be mapped"); dealloc_heap_region(); _heap_pointers_need_patching = false; return false; @@ -1807,16 +1809,16 @@ bool FileMapInfo::open_as_input() { // are replaced at runtime by JVMTI ClassFileLoadHook. All of those classes are resolved // during the JVMTI "early" stage, so we can still use CDS if // JvmtiExport::has_early_class_hook_env() is false. - log_info(cds)("CDS is disabled because early JVMTI ClassFileLoadHook is in use."); + MetaspaceShared::report_loading_error("CDS is disabled because early JVMTI ClassFileLoadHook is in use."); return false; } if (!open_for_read() || !init_from_file(_fd) || !validate_header()) { if (_is_static) { - log_info(cds)("Loading static archive failed."); + MetaspaceShared::report_loading_error("Loading static archive failed."); return false; } else { - log_info(cds)("Loading dynamic archive failed."); + MetaspaceShared::report_loading_error("Loading dynamic archive failed."); if (AutoCreateSharedArchive) { CDSConfig::enable_dumping_dynamic_archive(_full_path); } @@ -1831,29 +1833,34 @@ bool FileMapInfo::validate_aot_class_linking() { // These checks need to be done after FileMapInfo::initialize(), which gets called before Universe::heap() // is available. if (header()->has_aot_linked_classes()) { + const char* archive_type = CDSConfig::type_of_archive_being_loaded(); CDSConfig::set_has_aot_linked_classes(true); if (JvmtiExport::should_post_class_file_load_hook()) { - log_error(cds)("CDS archive has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use."); + log_error(cds)("%s has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.", + archive_type); return false; } if (JvmtiExport::has_early_vmstart_env()) { - log_error(cds)("CDS archive has aot-linked classes. It cannot be used when JVMTI early vm start is in use."); + log_error(cds)("%s has aot-linked classes. It cannot be used when JVMTI early vm start is in use.", + archive_type); return false; } if (!CDSConfig::is_using_full_module_graph()) { - log_error(cds)("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used."); + log_error(cds)("%s has aot-linked classes. It cannot be used when archived full module graph is not used.", + archive_type); return false; } const char* prop = Arguments::get_property("java.security.manager"); if (prop != nullptr && strcmp(prop, "disallow") != 0) { - log_error(cds)("CDS archive has aot-linked classes. It cannot be used with -Djava.security.manager=%s.", prop); + log_error(cds)("%s has aot-linked classes. It cannot be used with -Djava.security.manager=%s.", + archive_type, prop); return false; } #if INCLUDE_JVMTI if (Arguments::has_jdwp_agent()) { - log_error(cds)("CDS archive has aot-linked classes. It cannot be used with JDWP agent"); + log_error(cds)("%s has aot-linked classes. It cannot be used with JDWP agent", archive_type); return false; } #endif @@ -1913,8 +1920,8 @@ bool FileMapHeader::validate() { const char* prop = Arguments::get_property("java.system.class.loader"); if (prop != nullptr) { if (has_aot_linked_classes()) { - log_error(cds)("CDS archive has aot-linked classes. It cannot be used when the " - "java.system.class.loader property is specified."); + log_error(cds)("%s has aot-linked classes. It cannot be used when the " + "java.system.class.loader property is specified.", CDSConfig::type_of_archive_being_loaded()); return false; } log_warning(cds)("Archived non-system classes are disabled because the " diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 5f7b496f8d2..654c2e7ecd7 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -937,7 +937,7 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS if (CDSConfig::is_dumping_heap()) { assert(CDSConfig::allow_only_single_java_thread(), "Required"); if (!HeapShared::is_archived_boot_layer_available(THREAD)) { - log_info(cds)("archivedBootLayer not available, disabling full module graph"); + report_loading_error("archivedBootLayer not available, disabling full module graph"); CDSConfig::stop_dumping_full_module_graph(); } // Do this before link_shared_classes(), as the following line may load new classes. @@ -1118,10 +1118,7 @@ bool MetaspaceShared::is_shared_static(void* p) { // - When -XX:+RequireSharedSpaces is specified, AND the JVM cannot load the archive(s) due // to version or classpath mismatch. void MetaspaceShared::unrecoverable_loading_error(const char* message) { - log_error(cds)("An error has occurred while processing the %s.", CDSConfig::type_of_archive_being_loaded()); - if (message != nullptr) { - log_error(cds)("%s", message); - } + report_loading_error(message); if (CDSConfig::is_dumping_final_static_archive()) { vm_exit_during_initialization("Must be a valid AOT configuration generated by the current JVM", AOTConfiguration); @@ -1132,6 +1129,30 @@ void MetaspaceShared::unrecoverable_loading_error(const char* message) { } } +void MetaspaceShared::report_loading_error(const char* format, ...) { + // If the user doesn't specify any CDS options, we will try to load the default CDS archive, which + // may fail due to incompatible VM options. Print at the info level to avoid excessive verbosity. + // However, if the user has specified a CDS archive (or AOT cache), they would be interested in + // knowing that the loading fails, so we print at the error level. + Log(cds) log; + LogStream ls_error(log.error()); + LogStream ls_info(log.info()); + LogStream& ls = (!CDSConfig::is_using_archive()) || CDSConfig::is_using_only_default_archive() ? ls_info : ls_error; + + static bool printed_error = false; + if (!printed_error) { // No need for locks. Loading error checks happen only in main thread. + ls.print_cr("An error has occurred while processing the %s. Run with -Xlog:cds for details.", CDSConfig::type_of_archive_being_loaded()); + printed_error = true; + } + + if (format != nullptr) { + va_list ap; + va_start(ap, format); + ls.vprint_cr(format, ap); + va_end(ap); + } +} + // This function is called when the JVM is unable to write the specified CDS archive due to an // unrecoverable error. void MetaspaceShared::unrecoverable_writing_error(const char* message) { @@ -1193,11 +1214,14 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() { // The base archive cannot be mapped. We cannot dump the dynamic shared archive. AutoCreateSharedArchive = false; CDSConfig::disable_dumping_dynamic_archive(); - log_info(cds)("Unable to map shared spaces"); if (PrintSharedArchiveAndExit) { MetaspaceShared::unrecoverable_loading_error("Unable to use shared archive."); - } else if (RequireSharedSpaces) { - MetaspaceShared::unrecoverable_loading_error("Unable to map shared spaces"); + } else { + if (RequireSharedSpaces) { + MetaspaceShared::unrecoverable_loading_error("Unable to map shared spaces"); + } else { + report_loading_error("Unable to map shared spaces"); + } } } @@ -1712,8 +1736,8 @@ MapArchiveResult MetaspaceShared::map_archive(FileMapInfo* mapinfo, char* mapped mapinfo->set_is_mapped(false); if (mapinfo->core_region_alignment() != (size_t)core_region_alignment()) { - log_info(cds)("Unable to map CDS archive -- core_region_alignment() expected: %zu" - " actual: %zu", mapinfo->core_region_alignment(), core_region_alignment()); + report_loading_error("Unable to map CDS archive -- core_region_alignment() expected: %zu" + " actual: %zu", mapinfo->core_region_alignment(), core_region_alignment()); return MAP_ARCHIVE_OTHER_FAILURE; } diff --git a/src/hotspot/share/cds/metaspaceShared.hpp b/src/hotspot/share/cds/metaspaceShared.hpp index 875801cc0e6..8fb92196acc 100644 --- a/src/hotspot/share/cds/metaspaceShared.hpp +++ b/src/hotspot/share/cds/metaspaceShared.hpp @@ -110,7 +110,8 @@ public: static bool is_shared_dynamic(void* p) NOT_CDS_RETURN_(false); static bool is_shared_static(void* p) NOT_CDS_RETURN_(false); - static void unrecoverable_loading_error(const char* message = nullptr); + static void unrecoverable_loading_error(const char* message = nullptr) ATTRIBUTE_PRINTF(1, 0); + static void report_loading_error(const char* format, ...) ATTRIBUTE_PRINTF(1, 0); static void unrecoverable_writing_error(const char* message = nullptr); static void writing_error(const char* message = nullptr); diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index a506c4502a4..463bd765701 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -604,21 +604,21 @@ void Modules::ArchivedProperty::runtime_check() const { bool disable = false; if (runtime_value == nullptr) { if (_archived_value != nullptr) { - log_info(cds)("Mismatched values for property %s: %s specified during dump time but not during runtime", _prop, _archived_value); + MetaspaceShared::report_loading_error("Mismatched values for property %s: %s specified during dump time but not during runtime", _prop, _archived_value); disable = true; } } else { if (_archived_value == nullptr) { - log_info(cds)("Mismatched values for property %s: %s specified during runtime but not during dump time", _prop, runtime_value); + MetaspaceShared::report_loading_error("Mismatched values for property %s: %s specified during runtime but not during dump time", _prop, runtime_value); disable = true; } else if (strcmp(runtime_value, _archived_value) != 0) { - log_info(cds)("Mismatched values for property %s: runtime %s dump time %s", _prop, runtime_value, _archived_value); + MetaspaceShared::report_loading_error("Mismatched values for property %s: runtime %s dump time %s", _prop, runtime_value, _archived_value); disable = true; } } if (disable) { - log_info(cds)("Disabling optimized module handling"); + MetaspaceShared::report_loading_error("Disabling optimized module handling"); CDSConfig::stop_using_optimized_module_handling(); } } diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 68a8e9ed70a..12706ef3e99 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -48,6 +48,9 @@ #if INCLUDE_G1GC #include "gc/g1/g1BarrierSetRuntime.hpp" #endif +#if INCLUDE_SHENANDOAHGC +#include "gc/shenandoah/shenandoahRuntime.hpp" +#endif #if INCLUDE_ZGC #include "gc/z/zBarrierSetRuntime.hpp" #endif @@ -976,9 +979,9 @@ ImmutableOopMapSet* AOTCodeReader::read_oop_map_set() { // [_blobs_base, _blobs_base + _blobs_max -1], // ... // [_c_str_base, _c_str_base + _c_str_max -1], -#define _extrs_max 10 +#define _extrs_max 13 #define _blobs_max 10 -#define _all_max 20 +#define _all_max 23 #define _extrs_base 0 #define _blobs_base (_extrs_base + _extrs_max) @@ -1012,6 +1015,11 @@ void AOTCodeAddressTable::init_extrs() { SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_post_entry); SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_pre_entry); #endif +#if INCLUDE_SHENANDOAHGC + SET_ADDRESS(_extrs, ShenandoahRuntime::write_ref_field_pre); + SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom); + SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow); +#endif #if INCLUDE_ZGC SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); #if defined(AMD64) diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index e3bd3131b45..ed6167dac22 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -28,6 +28,7 @@ #include "code/dependencies.hpp" #include "code/nativeInst.hpp" #include "code/nmethod.inline.hpp" +#include "code/relocInfo.hpp" #include "code/scopeDesc.hpp" #include "compiler/abstractCompiler.hpp" #include "compiler/compilationLog.hpp" @@ -1323,8 +1324,9 @@ nmethod::nmethod( _unwind_handler_offset = 0; CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); - int metadata_size = align_up(code_buffer->total_metadata_size(), wordSize); - JVMCI_ONLY( _jvmci_data_size = 0; ) + uint16_t metadata_size; + CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize)); + JVMCI_ONLY( _metadata_size = metadata_size; ) assert(_mutable_data_size == _relocation_size + metadata_size, "wrong mutable data size: %d != %d + %d", _mutable_data_size, _relocation_size, metadata_size); @@ -1497,9 +1499,10 @@ nmethod::nmethod( } CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); - uint16_t metadata_size = (uint16_t)align_up(code_buffer->total_metadata_size(), wordSize); - JVMCI_ONLY(CHECKED_CAST(_jvmci_data_size, uint16_t, align_up(compiler->is_jvmci() ? jvmci_data->size() : 0, oopSize))); - int jvmci_data_size = 0 JVMCI_ONLY(+ _jvmci_data_size); + uint16_t metadata_size; + CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize)); + JVMCI_ONLY( _metadata_size = metadata_size; ) + int jvmci_data_size = 0 JVMCI_ONLY( + align_up(compiler->is_jvmci() ? jvmci_data->size() : 0, oopSize)); assert(_mutable_data_size == _relocation_size + metadata_size + jvmci_data_size, "wrong mutable data size: %d != %d + %d + %d", _mutable_data_size, _relocation_size, metadata_size, jvmci_data_size); @@ -1646,6 +1649,10 @@ void nmethod::maybe_print_nmethod(const DirectiveSet* directive) { } void nmethod::print_nmethod(bool printmethod) { + // Enter a critical section to prevent a race with deopts that patch code and updates the relocation info. + // Unfortunately, we have to lock the NMethodState_lock before the tty lock due to the deadlock rules and + // cannot lock in a more finely grained manner. + ConditionalMutexLocker ml(NMethodState_lock, !NMethodState_lock->owned_by_self(), Mutex::_no_safepoint_check_flag); ttyLocker ttyl; // keep the following output all in one block if (xtty != nullptr) { xtty->begin_head("print_nmethod"); @@ -2035,6 +2042,17 @@ bool nmethod::make_not_entrant(const char* reason) { // cache call. NativeJump::patch_verified_entry(entry_point(), verified_entry_point(), SharedRuntime::get_handle_wrong_method_stub()); + + // Update the relocation info for the patched entry. + // First, get the old relocation info... + RelocIterator iter(this, verified_entry_point(), verified_entry_point() + 8); + if (iter.next() && iter.addr() == verified_entry_point()) { + Relocation* old_reloc = iter.reloc(); + // ...then reset the iterator to update it. + RelocIterator iter(this, verified_entry_point(), verified_entry_point() + 8); + relocInfo::change_reloc_info_for_address(&iter, verified_entry_point(), old_reloc->type(), + relocInfo::relocType::runtime_call_type); + } } if (update_recompile_counts()) { diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 1c536a5d7e1..2ce6e5cd361 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -237,7 +237,9 @@ class nmethod : public CodeBlob { uint16_t _oops_size; #if INCLUDE_JVMCI - uint16_t _jvmci_data_size; + // _metadata_size is not specific to JVMCI. In the non-JVMCI case, it can be derived as: + // _metadata_size = mutable_data_size - relocation_size + uint16_t _metadata_size; #endif // Offset in immutable data section @@ -537,8 +539,8 @@ public: // mutable data Metadata** metadata_begin () const { return (Metadata**) (mutable_data_begin() + _relocation_size); } #if INCLUDE_JVMCI - Metadata** metadata_end () const { return (Metadata**) (mutable_data_end() - _jvmci_data_size); } - address jvmci_data_begin () const { return mutable_data_end() - _jvmci_data_size; } + Metadata** metadata_end () const { return (Metadata**) (mutable_data_begin() + _relocation_size + _metadata_size); } + address jvmci_data_begin () const { return mutable_data_begin() + _relocation_size + _metadata_size; } address jvmci_data_end () const { return mutable_data_end(); } #else Metadata** metadata_end () const { return (Metadata**) mutable_data_end(); } diff --git a/src/hotspot/share/code/nmethod.inline.hpp b/src/hotspot/share/code/nmethod.inline.hpp index 49af1e0b95f..1e556b68250 100644 --- a/src/hotspot/share/code/nmethod.inline.hpp +++ b/src/hotspot/share/code/nmethod.inline.hpp @@ -33,21 +33,12 @@ inline bool nmethod::is_deopt_pc(address pc) { return is_deopt_entry(pc) || is_deopt_mh_entry(pc); } -// When using JVMCI the address might be off by the size of a call instruction. inline bool nmethod::is_deopt_entry(address pc) { - return pc == deopt_handler_begin() -#if INCLUDE_JVMCI - || (is_compiled_by_jvmci() && pc == (deopt_handler_begin() + NativeCall::byte_size())) -#endif - ; + return pc == deopt_handler_begin(); } inline bool nmethod::is_deopt_mh_entry(address pc) { - return pc == deopt_mh_handler_begin() -#if INCLUDE_JVMCI - || (is_compiled_by_jvmci() && pc == (deopt_mh_handler_begin() + NativeCall::byte_size())) -#endif - ; + return pc == deopt_mh_handler_begin(); } // class ExceptionCache methods diff --git a/src/hotspot/share/compiler/compileTask.cpp b/src/hotspot/share/compiler/compileTask.cpp index 87d4a11e528..fd066a9bc87 100644 --- a/src/hotspot/share/compiler/compileTask.cpp +++ b/src/hotspot/share/compiler/compileTask.cpp @@ -471,7 +471,7 @@ void CompileTask::print_inlining_inner_message(outputStream* st, InliningResult } void CompileTask::print_ul(const char* msg){ - LogTarget(Debug, jit, compilation) lt; + LogTarget(Info, jit, compilation) lt; if (lt.is_enabled()) { LogStream ls(lt); print(&ls, msg, /* short form */ true, /* cr */ true); @@ -479,7 +479,7 @@ void CompileTask::print_ul(const char* msg){ } void CompileTask::print_ul(const nmethod* nm, const char* msg) { - LogTarget(Debug, jit, compilation) lt; + LogTarget(Info, jit, compilation) lt; if (lt.is_enabled()) { LogStream ls(lt); print_impl(&ls, nm->method(), nm->compile_id(), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 41faf3efa24..353da67ed88 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -98,6 +98,9 @@ #include "utilities/events.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" +#if INCLUDE_JVMCI +#include "jvmci/jvmci.hpp" +#endif #if INCLUDE_JFR #include "gc/shenandoah/shenandoahJfrSupport.hpp" #endif @@ -2233,6 +2236,9 @@ void ShenandoahHeap::stw_unload_classes(bool full_gc) { ShenandoahGCWorkerPhase worker_phase(phase); bool unloading_occurred = SystemDictionary::do_unloading(gc_timer()); + // Clean JVMCI metadata handles. + JVMCI_ONLY(JVMCI::do_unloading(unloading_occurred)); + uint num_workers = _workers->active_workers(); ShenandoahClassUnloadingTask unlink_task(phase, num_workers, unloading_occurred); _workers->run_task(&unlink_task); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp index d2a5f71bca6..97ba5012efa 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp @@ -48,6 +48,10 @@ JRT_LEAF(void, ShenandoahRuntime::write_ref_field_pre(oopDesc * orig, JavaThread ShenandoahBarrierSet::satb_mark_queue_set().enqueue_known_active(queue, orig); JRT_END +void ShenandoahRuntime::write_barrier_pre(oopDesc* orig) { + write_ref_field_pre(orig, JavaThread::current()); +} + JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_strong(oopDesc* src, oop* load_addr)) return ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(src, load_addr); JRT_END diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp index 4ad8fc997ea..0ed8959d95e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp @@ -37,6 +37,7 @@ public: static void arraycopy_barrier_narrow_oop(narrowOop* src, narrowOop* dst, size_t length); static void write_ref_field_pre(oopDesc* orig, JavaThread* thread); + static void write_barrier_pre(oopDesc* orig); static oopDesc* load_reference_barrier_strong(oopDesc* src, oop* load_addr); static oopDesc* load_reference_barrier_strong_narrow(oopDesc* src, narrowOop* load_addr); diff --git a/src/hotspot/share/gc/z/zBarrierSet.cpp b/src/hotspot/share/gc/z/zBarrierSet.cpp index a4893fc351a..43ea791a260 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.cpp +++ b/src/hotspot/share/gc/z/zBarrierSet.cpp @@ -115,7 +115,9 @@ static void deoptimize_allocation(JavaThread* thread) { assert(caller_frame.is_compiled_frame(), "must be compiled"); const nmethod* const nm = caller_frame.cb()->as_nmethod(); - if (nm->is_compiled_by_c2() && !caller_frame.is_deoptimized_frame()) { + if ((nm->is_compiled_by_c2() || nm->is_compiled_by_jvmci()) && !caller_frame.is_deoptimized_frame()) { + // The JIT might have elided barriers on this object so deoptimize the frame and let the + // intepreter deal with it. Deoptimization::deoptimize_frame(thread, caller_frame.id()); } } diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp index b44b4bb9116..f9643c6394e 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp @@ -111,6 +111,10 @@ class CompilerToVM { #if INCLUDE_ZGC static int sizeof_ZStoreBarrierEntry; #endif +#if INCLUDE_SHENANDOAHGC + static address shenandoah_in_cset_fast_test_addr; + static int shenandoah_region_size_bytes_shift; +#endif #ifdef X86 static int L1_line_size; diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index a729e34a279..591f30e7b19 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -52,6 +52,10 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/resourceHash.hpp" +#if INCLUDE_SHENANDOAHGC +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#endif int CompilerToVM::Data::oopDesc_klass_offset_in_bytes; int CompilerToVM::Data::arrayOopDesc_length_offset_in_bytes; @@ -86,6 +90,11 @@ address CompilerToVM::Data::ZPointerVectorLoadBadMask_address; address CompilerToVM::Data::ZPointerVectorStoreBadMask_address; address CompilerToVM::Data::ZPointerVectorStoreGoodMask_address; +#if INCLUDE_SHENANDOAHGC +address CompilerToVM::Data::shenandoah_in_cset_fast_test_addr; +int CompilerToVM::Data::shenandoah_region_size_bytes_shift; +#endif + bool CompilerToVM::Data::continuations_enabled; #ifdef AARCH64 @@ -227,12 +236,24 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { assert(base != nullptr, "unexpected byte_map_base"); cardtable_start_address = base; cardtable_shift = CardTable::card_shift(); +#if INCLUDE_SHENANDOAHGC + } else if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + cardtable_start_address = nullptr; + cardtable_shift = CardTable::card_shift(); +#endif } else { // No card mark barriers cardtable_start_address = nullptr; cardtable_shift = 0; } +#if INCLUDE_SHENANDOAHGC + if (UseShenandoahGC) { + shenandoah_in_cset_fast_test_addr = ShenandoahHeap::in_cset_fast_test_addr(); + shenandoah_region_size_bytes_shift = ShenandoahHeapRegion::region_size_bytes_shift_jint(); + } +#endif + #ifdef X86 L1_line_size = VM_Version::L1_line_size(); #endif diff --git a/src/hotspot/share/jvmci/jvmci_globals.cpp b/src/hotspot/share/jvmci/jvmci_globals.cpp index a41351218f8..d4d52afd069 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.cpp +++ b/src/hotspot/share/jvmci/jvmci_globals.cpp @@ -229,7 +229,7 @@ bool JVMCIGlobals::enable_jvmci_product_mode(JVMFlagOrigin origin, bool use_graa } bool JVMCIGlobals::gc_supports_jvmci() { - return UseSerialGC || UseParallelGC || UseG1GC || UseZGC || UseEpsilonGC; + return UseSerialGC || UseParallelGC || UseG1GC || UseZGC || UseShenandoahGC || UseEpsilonGC; } void JVMCIGlobals::check_jvmci_supported_gc() { diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 8ca13a3a6c3..dfc15368cf6 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -53,6 +53,10 @@ #include "gc/z/zBarrierSetRuntime.hpp" #include "gc/z/zThreadLocalData.hpp" #endif +#if INCLUDE_SHENANDOAHGC +#include "gc/shenandoah/shenandoahRuntime.hpp" +#include "gc/shenandoah/shenandoahThreadLocalData.hpp" +#endif #define VM_STRUCTS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field) \ static_field(CompilerToVM::Data, oopDesc_klass_offset_in_bytes, int) \ @@ -129,6 +133,8 @@ static_field(CompilerToVM::Data, sizeof_arrayOopDesc, int) \ static_field(CompilerToVM::Data, sizeof_BasicLock, int) \ ZGC_ONLY(static_field(CompilerToVM::Data, sizeof_ZStoreBarrierEntry, int)) \ + SHENANDOAHGC_ONLY(static_field(CompilerToVM::Data, shenandoah_in_cset_fast_test_addr, address)) \ + SHENANDOAHGC_ONLY(static_field(CompilerToVM::Data, shenandoah_region_size_bytes_shift,int)) \ \ static_field(CompilerToVM::Data, dsin, address) \ static_field(CompilerToVM::Data, dcos, address) \ @@ -895,6 +901,13 @@ declare_function(JVMCIRuntime::load_and_clear_exception) \ G1GC_ONLY(declare_function(JVMCIRuntime::write_barrier_pre)) \ G1GC_ONLY(declare_function(JVMCIRuntime::write_barrier_post)) \ + SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_strong)) \ + SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_strong_narrow)) \ + SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_weak)) \ + SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_weak_narrow)) \ + SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_phantom)) \ + SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_phantom_narrow)) \ + SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::write_barrier_pre)) \ declare_function(JVMCIRuntime::validate_object) \ \ declare_function(JVMCIRuntime::test_deoptimize_call_int) @@ -941,6 +954,15 @@ #endif // INCLUDE_ZGC +#if INCLUDE_SHENANDOAHGC + +#define VM_INT_CONSTANTS_JVMCI_SHENANDOAH(declare_constant, declare_constant_with_value, declare_preprocessor_constant) \ + declare_constant_with_value("ShenandoahThreadLocalData::gc_state_offset", in_bytes(ShenandoahThreadLocalData::gc_state_offset())) \ + declare_constant_with_value("ShenandoahThreadLocalData::satb_mark_queue_index_offset", in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())) \ + declare_constant_with_value("ShenandoahThreadLocalData::satb_mark_queue_buffer_offset", in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())) \ + declare_constant_with_value("ShenandoahThreadLocalData::card_table_offset", in_bytes(ShenandoahThreadLocalData::card_table_offset())) \ + +#endif #ifdef LINUX @@ -1063,6 +1085,11 @@ VMIntConstantEntry JVMCIVMStructs::localHotSpotVMIntConstants[] = { GENERATE_VM_INT_CONSTANT_WITH_VALUE_ENTRY, GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY) #endif +#if INCLUDE_SHENANDOAHGC + VM_INT_CONSTANTS_JVMCI_SHENANDOAH(GENERATE_VM_INT_CONSTANT_ENTRY, + GENERATE_VM_INT_CONSTANT_WITH_VALUE_ENTRY, + GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY) +#endif #ifdef VM_INT_CPU_FEATURE_CONSTANTS VM_INT_CPU_FEATURE_CONSTANTS #endif diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index effc7e21354..10846a32626 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -5205,32 +5205,39 @@ IdealGraphPrinter* Compile::_debug_network_printer = nullptr; // Called from debugger. Prints method to the default file with the default phase name. // This works regardless of any Ideal Graph Visualizer flags set or not. -void igv_print() { - Compile::current()->igv_print_method_to_file(); +// Use in debugger (gdb/rr): p igv_print($sp, $fp, $pc). +void igv_print(void* sp, void* fp, void* pc) { + frame fr(sp, fp, pc); + Compile::current()->igv_print_method_to_file(nullptr, false, &fr); } // Same as igv_print() above but with a specified phase name. -void igv_print(const char* phase_name) { - Compile::current()->igv_print_method_to_file(phase_name); +void igv_print(const char* phase_name, void* sp, void* fp, void* pc) { + frame fr(sp, fp, pc); + Compile::current()->igv_print_method_to_file(phase_name, false, &fr); } // Called from debugger. Prints method with the default phase name to the default network or the one specified with // the network flags for the Ideal Graph Visualizer, or to the default file depending on the 'network' argument. // This works regardless of any Ideal Graph Visualizer flags set or not. -void igv_print(bool network) { +// Use in debugger (gdb/rr): p igv_print(true, $sp, $fp, $pc). +void igv_print(bool network, void* sp, void* fp, void* pc) { + frame fr(sp, fp, pc); if (network) { - Compile::current()->igv_print_method_to_network(); + Compile::current()->igv_print_method_to_network(nullptr, &fr); } else { - Compile::current()->igv_print_method_to_file(); + Compile::current()->igv_print_method_to_file(nullptr, false, &fr); } } -// Same as igv_print(bool network) above but with a specified phase name. -void igv_print(bool network, const char* phase_name) { +// Same as igv_print(bool network, ...) above but with a specified phase name. +// Use in debugger (gdb/rr): p igv_print(true, "MyPhase", $sp, $fp, $pc). +void igv_print(bool network, const char* phase_name, void* sp, void* fp, void* pc) { + frame fr(sp, fp, pc); if (network) { - Compile::current()->igv_print_method_to_network(phase_name); + Compile::current()->igv_print_method_to_network(phase_name, &fr); } else { - Compile::current()->igv_print_method_to_file(phase_name); + Compile::current()->igv_print_method_to_file(phase_name, false, &fr); } } @@ -5242,16 +5249,20 @@ void igv_print_default() { // Called from debugger, especially when replaying a trace in which the program state cannot be altered like with rr replay. // A method is appended to an existing default file with the default phase name. This means that igv_append() must follow // an earlier igv_print(*) call which sets up the file. This works regardless of any Ideal Graph Visualizer flags set or not. -void igv_append() { - Compile::current()->igv_print_method_to_file("Debug", true); +// Use in debugger (gdb/rr): p igv_append($sp, $fp, $pc). +void igv_append(void* sp, void* fp, void* pc) { + frame fr(sp, fp, pc); + Compile::current()->igv_print_method_to_file(nullptr, true, &fr); } -// Same as igv_append() above but with a specified phase name. -void igv_append(const char* phase_name) { - Compile::current()->igv_print_method_to_file(phase_name, true); +// Same as igv_append(...) above but with a specified phase name. +// Use in debugger (gdb/rr): p igv_append("MyPhase", $sp, $fp, $pc). +void igv_append(const char* phase_name, void* sp, void* fp, void* pc) { + frame fr(sp, fp, pc); + Compile::current()->igv_print_method_to_file(phase_name, true, &fr); } -void Compile::igv_print_method_to_file(const char* phase_name, bool append) { +void Compile::igv_print_method_to_file(const char* phase_name, bool append, const frame* fr) { const char* file_name = "custom_debug.xml"; if (_debug_file_printer == nullptr) { _debug_file_printer = new IdealGraphPrinter(C, file_name, append); @@ -5259,23 +5270,23 @@ void Compile::igv_print_method_to_file(const char* phase_name, bool append) { _debug_file_printer->update_compiled_method(C->method()); } tty->print_cr("Method %s to %s", append ? "appended" : "printed", file_name); - _debug_file_printer->print_graph(phase_name); + _debug_file_printer->print_graph(phase_name, fr); } -void Compile::igv_print_method_to_network(const char* phase_name) { +void Compile::igv_print_method_to_network(const char* phase_name, const frame* fr) { ResourceMark rm; GrowableArray empty_list; - igv_print_graph_to_network(phase_name, empty_list); + igv_print_graph_to_network(phase_name, empty_list, fr); } -void Compile::igv_print_graph_to_network(const char* name, GrowableArray& visible_nodes) { +void Compile::igv_print_graph_to_network(const char* name, GrowableArray& visible_nodes, const frame* fr) { if (_debug_network_printer == nullptr) { _debug_network_printer = new IdealGraphPrinter(C); } else { _debug_network_printer->update_compiled_method(C->method()); } tty->print_cr("Method printed over network stream to IGV"); - _debug_network_printer->print(name, C->root(), visible_nodes); + _debug_network_printer->print(name, C->root(), visible_nodes, fr); } #endif diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 33b9187e262..6d43948964e 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -673,13 +673,13 @@ public: void init_igv(); void dump_igv(const char* graph_name, int level = 3) { if (should_print_igv(level)) { - _igv_printer->print_graph(graph_name); + _igv_printer->print_graph(graph_name, nullptr); } } - void igv_print_method_to_file(const char* phase_name = "Debug", bool append = false); - void igv_print_method_to_network(const char* phase_name = "Debug"); - void igv_print_graph_to_network(const char* name, GrowableArray& visible_nodes); + void igv_print_method_to_file(const char* phase_name = nullptr, bool append = false, const frame* fr = nullptr); + void igv_print_method_to_network(const char* phase_name = nullptr, const frame* fr = nullptr); + void igv_print_graph_to_network(const char* name, GrowableArray& visible_nodes, const frame* fr); static IdealGraphPrinter* debug_file_printer() { return _debug_file_printer; } static IdealGraphPrinter* debug_network_printer() { return _debug_network_printer; } #endif diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp index 9f17e350059..9a337739be5 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.cpp +++ b/src/hotspot/share/opto/idealGraphPrinter.cpp @@ -27,8 +27,10 @@ #include "opto/idealGraphPrinter.hpp" #include "opto/machnode.hpp" #include "opto/parse.hpp" +#include "runtime/arguments.hpp" #include "runtime/threadCritical.hpp" #include "runtime/threadSMR.hpp" +#include "utilities/decoder.hpp" #include "utilities/stringUtils.hpp" #ifndef PRODUCT @@ -49,6 +51,13 @@ const char *IdealGraphPrinter::REMOVE_EDGE_ELEMENT = "removeEdge"; const char *IdealGraphPrinter::REMOVE_NODE_ELEMENT = "removeNode"; const char *IdealGraphPrinter::COMPILATION_ID_PROPERTY = "compilationId"; const char *IdealGraphPrinter::COMPILATION_OSR_PROPERTY = "osr"; +const char *IdealGraphPrinter::COMPILATION_ARGUMENTS_PROPERTY = "arguments"; +const char *IdealGraphPrinter::COMPILATION_MACHINE_PROPERTY = "machine"; +const char *IdealGraphPrinter::COMPILATION_CPU_FEATURES_PROPERTY = "cpuFeatures"; +const char *IdealGraphPrinter::COMPILATION_VM_VERSION_PROPERTY = "vm"; +const char *IdealGraphPrinter::COMPILATION_DATE_TIME_PROPERTY = "dateTime"; +const char *IdealGraphPrinter::COMPILATION_PROCESS_ID_PROPERTY = "processId"; +const char *IdealGraphPrinter::COMPILATION_THREAD_ID_PROPERTY = "threadId"; const char *IdealGraphPrinter::METHOD_NAME_PROPERTY = "name"; const char *IdealGraphPrinter::METHOD_IS_PUBLIC_PROPERTY = "public"; const char *IdealGraphPrinter::METHOD_IS_STATIC_PROPERTY = "static"; @@ -336,6 +345,42 @@ void IdealGraphPrinter::begin_method() { print_prop(COMPILATION_ID_PROPERTY, C->compile_id()); + stringStream args; + Arguments::print_jvm_args_on(&args); + print_prop(COMPILATION_ARGUMENTS_PROPERTY, args.freeze()); + + stringStream machine; + buffer[0] = 0; + os::print_summary_info(&machine, buffer, sizeof(buffer) - 1); + print_prop(COMPILATION_MACHINE_PROPERTY, machine.freeze()); + + print_prop(COMPILATION_CPU_FEATURES_PROPERTY, VM_Version::features_string()); + + stringStream version; + buffer[0] = 0; + JDK_Version::current().to_string(buffer, sizeof(buffer) - 1); + const char* runtime_name = JDK_Version::runtime_name() != nullptr ? + JDK_Version::runtime_name() : ""; + const char* runtime_version = JDK_Version::runtime_version() != nullptr ? + JDK_Version::runtime_version() : ""; + const char* vendor_version = JDK_Version::runtime_vendor_version() != nullptr ? + JDK_Version::runtime_vendor_version() : ""; + const char* jdk_debug_level = VM_Version::printable_jdk_debug_level() != nullptr ? + VM_Version::printable_jdk_debug_level() : ""; + + version.print_cr("%s%s%s (%s) (%sbuild %s)", runtime_name, + (*vendor_version != '\0') ? " " : "", vendor_version, + buffer, jdk_debug_level, runtime_version); + print_prop(COMPILATION_VM_VERSION_PROPERTY, version.freeze()); + + stringStream time; + buffer[0] = 0; + os::print_date_and_time(&time, buffer, sizeof(buffer) - 1); + print_prop(COMPILATION_DATE_TIME_PROPERTY, time.freeze()); + + print_prop(COMPILATION_PROCESS_ID_PROPERTY, os::current_process_id()); + print_prop(COMPILATION_THREAD_ID_PROPERTY, os::current_thread_id()); + tail(PROPERTIES_ELEMENT); _should_send_method = true; @@ -862,24 +907,91 @@ void IdealGraphPrinter::walk_nodes(Node* start, bool edges) { } } -void IdealGraphPrinter::print_graph(const char* name) { +// Whether the stack walk should skip the given frame when producing a C2 stack +// trace. We consider IGV- and debugger-specific frames uninteresting. +static bool should_skip_frame(const char* name) { + return strstr(name, "IdealGraphPrinter") != nullptr || + strstr(name, "Compile::print_method") != nullptr || + strstr(name, "Compile::igv_print_graph") != nullptr || + strstr(name, "PrintBFS") != nullptr || + strstr(name, "Node::dump_bfs") != nullptr; +} + +// Whether the stack walk should be considered done when visiting a certain +// frame. The purpose of walking the stack is producing a C2 trace, so we +// consider all frames below (and including) C2Compiler::compile_method(..) +// uninteresting. +static bool should_end_stack_walk(const char* name) { + return strstr(name, "C2Compiler::compile_method") != nullptr; +} + +void IdealGraphPrinter::print_stack(const frame* initial_frame, outputStream* graph_name) { + char buf[O_BUFLEN]; + frame fr = initial_frame == nullptr ? os::current_frame() : *initial_frame; + int frame = 0; + for (int count = 0; count < StackPrintLimit && fr.pc() != nullptr; count++) { + int offset; + buf[0] = '\0'; + bool found = os::dll_address_to_function_name(fr.pc(), buf, sizeof(buf), &offset); + if (!found || should_end_stack_walk(buf)) { + break; + } + if (!should_skip_frame(buf)) { + stringStream frame_loc; + frame_loc.print("%s", buf); + buf[0] = '\0'; + int line_no; + if (Decoder::get_source_info(fr.pc(), buf, sizeof(buf), &line_no, count != 1)) { + frame_loc.print(" (%s:%d)", buf, line_no); + if (graph_name != nullptr) { + // Extract a debug graph name and return. + graph_name->print("%s:%d", buf, line_no); + return; + } + } + if (graph_name == nullptr) { + // Print frame as IGV property and continue to the next frame. + stringStream frame_number_str; + frame_number_str.print("frame %d:", frame); + print_prop(frame_number_str.freeze(), frame_loc.freeze()); + frame++; + } + } + fr = frame::next_frame(fr, Thread::current_or_null()); + } +} + +void IdealGraphPrinter::print_graph(const char* name, const frame* fr) { ResourceMark rm; GrowableArray empty_list; - print(name, (Node*) C->root(), empty_list); + print(name, (Node*) C->root(), empty_list, fr); } // Print current ideal graph -void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray& visible_nodes) { +void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray& visible_nodes, const frame* fr) { if (!_current_method || !_should_send_method || node == nullptr) return; + if (name == nullptr) { + stringStream graph_name; + print_stack(fr, &graph_name); + name = graph_name.freeze(); + if (strlen(name) == 0) { + name = "Debug"; + } + } + // Warning, unsafe cast? _chaitin = (PhaseChaitin *)C->regalloc(); begin_head(GRAPH_ELEMENT); - print_attr(GRAPH_NAME_PROPERTY, (const char *)name); + print_attr(GRAPH_NAME_PROPERTY, name); end_head(); + head(PROPERTIES_ELEMENT); + print_stack(fr, nullptr); + tail(PROPERTIES_ELEMENT); + head(NODES_ELEMENT); if (C->cfg() != nullptr) { // Compute the maximum estimated frequency in the current graph. @@ -1047,7 +1159,6 @@ void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray { static const char *ALL_PROPERTY; static const char *COMPILATION_ID_PROPERTY; static const char *COMPILATION_OSR_PROPERTY; + static const char *COMPILATION_ARGUMENTS_PROPERTY; + static const char *COMPILATION_MACHINE_PROPERTY; + static const char *COMPILATION_CPU_FEATURES_PROPERTY; + static const char *COMPILATION_VM_VERSION_PROPERTY; + static const char *COMPILATION_DATE_TIME_PROPERTY; + static const char *COMPILATION_PROCESS_ID_PROPERTY; + static const char *COMPILATION_THREAD_ID_PROPERTY; static const char *METHOD_NAME_PROPERTY; static const char *BLOCK_NAME_PROPERTY; static const char *BLOCK_DOMINATOR_PROPERTY; @@ -110,6 +117,10 @@ class IdealGraphPrinter : public CHeapObj { double _max_freq; bool _append; + // Walk the native stack and print relevant C2 frames as IGV properties (if + // graph_name == nullptr) or the graph name based on the highest C2 frame (if + // graph_name != nullptr). + void print_stack(const frame* initial_frame, outputStream* graph_name); void print_method(ciMethod* method, int bci, InlineTree* tree); void print_inline_tree(InlineTree* tree); void visit_node(Node* n, bool edges); @@ -149,8 +160,8 @@ class IdealGraphPrinter : public CHeapObj { void print_inlining(); void begin_method(); void end_method(); - void print_graph(const char* name); - void print(const char* name, Node* root, GrowableArray& hidden_nodes); + void print_graph(const char* name, const frame* fr = nullptr); + void print(const char* name, Node* root, GrowableArray& hidden_nodes, const frame* fr = nullptr); void set_compile(Compile* compile) {C = compile; } void update_compiled_method(ciMethod* current_method); }; diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index a635abebec1..3875c6ce325 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -1774,8 +1774,8 @@ Node* Node::find(const int idx, bool only_ctrl) { class PrintBFS { public: - PrintBFS(const Node* start, const int max_distance, const Node* target, const char* options, outputStream* st) - : _start(start), _max_distance(max_distance), _target(target), _options(options), _output(st), + PrintBFS(const Node* start, const int max_distance, const Node* target, const char* options, outputStream* st, const frame* fr) + : _start(start), _max_distance(max_distance), _target(target), _options(options), _output(st), _frame(fr), _dcc(this), _info_uid(cmpkey, hashkey) {} void run(); @@ -1796,6 +1796,7 @@ private: const Node* _target; const char* _options; outputStream* _output; + const frame* _frame; // options bool _traverse_inputs = false; @@ -2057,7 +2058,7 @@ void PrintBFS::print() { if (_print_igv) { Compile* C = Compile::current(); C->init_igv(); - C->igv_print_graph_to_network("PrintBFS", _print_list); + C->igv_print_graph_to_network(nullptr, _print_list, _frame); } } else { _output->print_cr("No nodes to print."); @@ -2102,6 +2103,8 @@ void PrintBFS::print_options_help(bool print_examples) { _output->print_cr(" B: print scheduling blocks (if available)"); _output->print_cr(" $: dump only, no header, no other columns"); _output->print_cr(" !: show nodes on IGV (sent over network stream)"); + _output->print_cr(" (use preferably with dump_bfs(int, Node*, char*, void*, void*, void*)"); + _output->print_cr(" to produce a C2 stack trace along with the graph dump, see examples below)"); _output->print_cr(""); _output->print_cr("recursively follow edges to nodes with permitted visit types,"); _output->print_cr("on the boundary additionally display nodes allowed in boundary types"); @@ -2151,6 +2154,9 @@ void PrintBFS::print_options_help(bool print_examples) { _output->print_cr(" find all paths (A) between two nodes of length at most 8"); _output->print_cr(" find_node(741)->dump_bfs(7, find_node(741), \"c+A\")"); _output->print_cr(" find all control loops for this node"); + _output->print_cr(" find_node(741)->dump_bfs(7, find_node(741), \"c+A!\", $sp, $fp, $pc)"); + _output->print_cr(" same as above, but printing the resulting subgraph"); + _output->print_cr(" along with a C2 stack trace on IGV"); } } @@ -2409,8 +2415,8 @@ void Node::dump_bfs(const int max_distance, Node* target, const char* options) c } // Used to dump to stream. -void Node::dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st) const { - PrintBFS bfs(this, max_distance, target, options, st); +void Node::dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st, const frame* fr) const { + PrintBFS bfs(this, max_distance, target, options, st, fr); bfs.run(); } @@ -2419,6 +2425,13 @@ void Node::dump_bfs(const int max_distance) const { dump_bfs(max_distance, nullptr, nullptr); } +// Call this from debugger, with stack handling register arguments for IGV dumps. +// Example: p find_node(741)->dump_bfs(7, find_node(741), "c+A!", $sp, $fp, $pc). +void Node::dump_bfs(const int max_distance, Node* target, const char* options, void* sp, void* fp, void* pc) const { + frame fr(sp, fp, pc); + dump_bfs(max_distance, target, options, tty, &fr); +} + // -----------------------------dump_idx--------------------------------------- void Node::dump_idx(bool align, outputStream* st, DumpConfig* dc) const { if (dc != nullptr) { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index e1a4ee22661..843baf48cf8 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1298,9 +1298,10 @@ public: public: Node* find(int idx, bool only_ctrl = false); // Search the graph for the given idx. Node* find_ctrl(int idx); // Search control ancestors for the given idx. - void dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st) const; + void dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st, const frame* fr = nullptr) const; void dump_bfs(const int max_distance, Node* target, const char* options) const; // directly to tty void dump_bfs(const int max_distance) const; // dump_bfs(max_distance, nullptr, nullptr) + void dump_bfs(const int max_distance, Node* target, const char* options, void* sp, void* fp, void* pc) const; class DumpConfig { public: // overridden to implement coloring of node idx diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index b2af12b08a6..6a1f451bb74 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -988,6 +988,10 @@ JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvm self_tobj = Handle(current, thread_oop); continue; // self suspend after all other suspends } + if (java_lang_VirtualThread::is_instance(thread_oop)) { + oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_oop); + java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread); + } results[i] = suspend_thread(thread_oop, java_thread, /* single_suspend */ true); } } @@ -1114,6 +1118,10 @@ JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmt continue; } } + if (java_lang_VirtualThread::is_instance(thread_oop)) { + oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_oop); + java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread); + } results[i] = resume_thread(thread_oop, java_thread, /* single_resume */ true); } // per-thread resume results returned via results parameter diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 9e0929e69cf..3b6602f2420 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -1398,8 +1398,7 @@ JvmtiEnvBase::is_vthread_suspended(oop vt_oop, JavaThread* jt) { bool suspended = false; if (java_lang_VirtualThread::is_instance(vt_oop)) { suspended = JvmtiVTSuspender::is_vthread_suspended(vt_oop); - } - if (vt_oop->is_a(vmClasses::BoundVirtualThread_klass())) { + } else if (vt_oop->is_a(vmClasses::BoundVirtualThread_klass())) { suspended = jt->is_suspended(); } return suspended; @@ -1757,57 +1756,46 @@ JvmtiEnvBase::suspend_thread(oop thread_oop, JavaThread* java_thread, bool singl Handle thread_h(current, thread_oop); bool is_virtual = java_lang_VirtualThread::is_instance(thread_h()); - if (is_virtual) { - if (single_suspend) { - if (JvmtiVTSuspender::is_vthread_suspended(thread_h())) { - return JVMTI_ERROR_THREAD_SUSPENDED; - } - JvmtiVTSuspender::register_vthread_suspend(thread_h()); - // Check if virtual thread is mounted and there is a java_thread. - // A non-null java_thread is always passed in the !single_suspend case. - oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_h()); - java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread); + // Unmounted vthread case. + + if (is_virtual && java_thread == nullptr) { + assert(single_suspend, "sanity check"); + if (JvmtiVTSuspender::is_vthread_suspended(thread_h())) { + return JVMTI_ERROR_THREAD_SUSPENDED; } - // The java_thread can be still blocked in VTMS transition after a previous JVMTI resume call. - // There is no need to suspend the java_thread in this case. After vthread unblocking, - // it will check for ext_suspend request and suspend itself if necessary. - if (java_thread == nullptr || java_thread->is_suspended()) { - // We are done if the virtual thread is unmounted or - // the java_thread is externally suspended. - return JVMTI_ERROR_NONE; - } - // The virtual thread is mounted: suspend the java_thread. + JvmtiVTSuspender::register_vthread_suspend(thread_h()); + return JVMTI_ERROR_NONE; } + + // Platform thread or mounted vthread cases. + + assert(java_thread != nullptr, "sanity check"); + assert(!java_thread->is_in_VTMS_transition(), "sanity check"); + // Don't allow hidden thread suspend request. if (java_thread->is_hidden_from_external_view()) { return JVMTI_ERROR_NONE; } - bool is_thread_carrying = is_thread_carrying_vthread(java_thread, thread_h()); - - // A case of non-virtual thread. - if (!is_virtual) { - // Thread.suspend() is used in some tests. It sets jt->is_suspended() only. - if (java_thread->is_carrier_thread_suspended() || - (!is_thread_carrying && java_thread->is_suspended())) { - return JVMTI_ERROR_THREAD_SUSPENDED; - } - java_thread->set_carrier_thread_suspended(); - } - assert(!java_thread->is_in_VTMS_transition(), "sanity check"); - - assert(!single_suspend || (!is_virtual && java_thread->is_carrier_thread_suspended()) || - (is_virtual && JvmtiVTSuspender::is_vthread_suspended(thread_h())), - "sanity check"); // An attempt to handshake-suspend a thread carrying a virtual thread will result in // suspension of mounted virtual thread. So, we just mark it as suspended // and it will be actually suspended at virtual thread unmount transition. - if (!is_thread_carrying) { + bool is_thread_carrying = is_thread_carrying_vthread(java_thread, thread_h()); + if (is_thread_carrying) { + return java_thread->set_carrier_thread_suspended() ? JVMTI_ERROR_NONE : JVMTI_ERROR_THREAD_SUSPENDED; + } else { + // Platform thread (not carrying vthread) or mounted vthread cases. assert(thread_h() != nullptr, "sanity check"); assert(single_suspend || thread_h()->is_a(vmClasses::BaseVirtualThread_klass()), "SuspendAllVirtualThreads should never suspend non-virtual threads"); - // Case of mounted virtual or attached carrier thread. - if (!java_thread->java_suspend()) { + + // Ideally we would just need to check java_thread->is_suspended(), but we have to + // consider the case of trying to suspend a thread that was previously suspended while + // carrying a vthread but has already unmounted it. + if (java_thread->is_suspended() || (!is_virtual && java_thread->is_carrier_thread_suspended())) { + return JVMTI_ERROR_THREAD_SUSPENDED; + } + if (!java_thread->java_suspend(is_virtual && single_suspend)) { // Thread is already suspended or in process of exiting. if (java_thread->is_exiting()) { // The thread was in the process of exiting. @@ -1815,8 +1803,8 @@ JvmtiEnvBase::suspend_thread(oop thread_oop, JavaThread* java_thread, bool singl } return JVMTI_ERROR_THREAD_SUSPENDED; } + return JVMTI_ERROR_NONE; } - return JVMTI_ERROR_NONE; } // java_thread - protected by ThreadsListHandle @@ -1827,54 +1815,51 @@ JvmtiEnvBase::resume_thread(oop thread_oop, JavaThread* java_thread, bool single Handle thread_h(current, thread_oop); bool is_virtual = java_lang_VirtualThread::is_instance(thread_h()); - if (is_virtual) { - if (single_resume) { - if (!JvmtiVTSuspender::is_vthread_suspended(thread_h())) { - return JVMTI_ERROR_THREAD_NOT_SUSPENDED; - } - JvmtiVTSuspender::register_vthread_resume(thread_h()); - // Check if virtual thread is mounted and there is a java_thread. - // A non-null java_thread is always passed in the !single_resume case. - oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_h()); - java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread); + // Unmounted vthread case. + + if (is_virtual && java_thread == nullptr) { + assert(single_resume, "sanity check"); + if (!JvmtiVTSuspender::is_vthread_suspended(thread_h())) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; } - // The java_thread can be still blocked in VTMS transition after a previous JVMTI suspend call. - // There is no need to resume the java_thread in this case. After vthread unblocking, - // it will check for is_vthread_suspended request and remain resumed if necessary. - if (java_thread == nullptr || !java_thread->is_suspended()) { - // We are done if the virtual thread is unmounted or - // the java_thread is not externally suspended. - return JVMTI_ERROR_NONE; - } - // The virtual thread is mounted and java_thread is supended: resume the java_thread. + JvmtiVTSuspender::register_vthread_resume(thread_h()); + return JVMTI_ERROR_NONE; } + + // Platform thread or mounted vthread cases. + + assert(java_thread != nullptr, "sanity check"); + assert(!java_thread->is_in_VTMS_transition(), "sanity check"); + // Don't allow hidden thread resume request. if (java_thread->is_hidden_from_external_view()) { return JVMTI_ERROR_NONE; } + bool is_thread_carrying = is_thread_carrying_vthread(java_thread, thread_h()); + if (is_thread_carrying) { + return java_thread->clear_carrier_thread_suspended() ? JVMTI_ERROR_NONE : JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } else { + // Platform thread (not carrying vthread) or mounted vthread cases. - // A case of a non-virtual thread. - if (!is_virtual) { - if (!java_thread->is_carrier_thread_suspended() && - (is_thread_carrying || !java_thread->is_suspended())) { - return JVMTI_ERROR_THREAD_NOT_SUSPENDED; - } - java_thread->clear_carrier_thread_suspended(); - } - assert(!java_thread->is_in_VTMS_transition(), "sanity check"); - - if (!is_thread_carrying) { assert(thread_h() != nullptr, "sanity check"); assert(single_resume || thread_h()->is_a(vmClasses::BaseVirtualThread_klass()), "ResumeAllVirtualThreads should never resume non-virtual threads"); - if (java_thread->is_suspended()) { - if (!java_thread->java_resume()) { - return JVMTI_ERROR_THREAD_NOT_SUSPENDED; - } + + // Ideally we would not have to check this but we have to consider the case + // of trying to resume a thread that was previously suspended while carrying + // a vthread but has already unmounted it. + if (!is_virtual && java_thread->is_carrier_thread_suspended()) { + bool res = java_thread->clear_carrier_thread_suspended(); + assert(res, "resume operations running concurrently?"); + return JVMTI_ERROR_NONE; } + + if (!java_thread->java_resume(is_virtual && single_resume)) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + } + return JVMTI_ERROR_NONE; } - return JVMTI_ERROR_NONE; } ResourceTracker::ResourceTracker(JvmtiEnv* env) { diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index 9caea2de520..0059636099e 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -658,7 +658,7 @@ vframe *VM_GetOrSetLocal::get_vframe() { javaVFrame *VM_GetOrSetLocal::get_java_vframe() { vframe* vf = get_vframe(); - if (!(_self || _thread->is_carrier_thread_suspended())) { + if (!_self && !_thread->is_suspended() && !_thread->is_carrier_thread_suspended()) { _result = JVMTI_ERROR_THREAD_NOT_SUSPENDED; return nullptr; } diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index b4119825bd3..40d7800de79 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -254,7 +254,10 @@ JvmtiVTMSTransitionDisabler::print_info() { // disable VTMS transitions for one virtual thread // disable VTMS transitions for all threads if thread is nullptr or a platform thread JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread) - : _is_SR(false), _thread(thread) + : _is_SR(false), + _is_virtual(false), + _is_self(false), + _thread(thread) { if (!Continuations::enabled()) { return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads @@ -262,21 +265,26 @@ JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread) if (Thread::current_or_null() == nullptr) { return; // Detached thread, can be a call from Agent_OnLoad. } + JavaThread* current = JavaThread::current(); + oop thread_oop = JNIHandles::resolve_external_guard(thread); + _is_virtual = java_lang_VirtualThread::is_instance(thread_oop); + + if (thread == nullptr || + (!_is_virtual && thread_oop == current->threadObj()) || + (_is_virtual && thread_oop == current->vthread())) { + _is_self = true; + return; // no need for current thread to disable and enable transitions for itself + } if (!sync_protocol_enabled_permanently()) { JvmtiVTMSTransitionDisabler::inc_sync_protocol_enabled_count(); } - oop thread_oop = JNIHandles::resolve_external_guard(thread); // Target can be virtual or platform thread. // If target is a platform thread then we have to disable VTMS transitions for all threads. // It is by several reasons: // - carrier threads can mount virtual threads which may cause incorrect behavior // - there is no mechanism to disable transitions for a specific carrier thread yet - if (!java_lang_VirtualThread::is_instance(thread_oop)) { - _thread = nullptr; // target is a platform thread, switch to disabling VTMS transitions for all threads - } - - if (_thread != nullptr) { + if (_is_virtual) { VTMS_transition_disable_for_one(); // disable VTMS transitions for one virtual thread } else { VTMS_transition_disable_for_all(); // disable VTMS transitions for all virtual threads @@ -285,7 +293,10 @@ JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread) // disable VTMS transitions for all virtual threads JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(bool is_SR) - : _is_SR(is_SR), _thread(nullptr) + : _is_SR(is_SR), + _is_virtual(false), + _is_self(false), + _thread(nullptr) { if (!Continuations::enabled()) { return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads @@ -309,7 +320,10 @@ JvmtiVTMSTransitionDisabler::~JvmtiVTMSTransitionDisabler() { if (Thread::current_or_null() == nullptr) { return; // Detached thread, can be a call from Agent_OnLoad. } - if (_thread != nullptr) { + if (_is_self) { + return; // no need for current thread to disable and enable transitions for itself + } + if (_is_virtual) { VTMS_transition_enable_for_one(); // enable VTMS transitions for one virtual thread } else { VTMS_transition_enable_for_all(); // enable VTMS transitions for all virtual threads @@ -684,7 +698,7 @@ JvmtiVTSuspender::_not_suspended_list = new VirtualThreadList(); void JvmtiVTSuspender::register_all_vthreads_suspend() { - MonitorLocker ml(JvmtiVTMSTransition_lock); + MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag); _SR_mode = SR_all; _suspended_list->invalidate(); @@ -693,7 +707,7 @@ JvmtiVTSuspender::register_all_vthreads_suspend() { void JvmtiVTSuspender::register_all_vthreads_resume() { - MonitorLocker ml(JvmtiVTMSTransition_lock); + MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag); _SR_mode = SR_none; _suspended_list->invalidate(); @@ -703,7 +717,7 @@ JvmtiVTSuspender::register_all_vthreads_resume() { void JvmtiVTSuspender::register_vthread_suspend(oop vt) { int64_t id = java_lang_Thread::thread_id(vt); - MonitorLocker ml(JvmtiVTMSTransition_lock); + MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag); if (_SR_mode == SR_all) { assert(_not_suspended_list->contains(id), @@ -720,7 +734,7 @@ JvmtiVTSuspender::register_vthread_suspend(oop vt) { void JvmtiVTSuspender::register_vthread_resume(oop vt) { int64_t id = java_lang_Thread::thread_id(vt); - MonitorLocker ml(JvmtiVTMSTransition_lock); + MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag); if (_SR_mode == SR_all) { assert(!_not_suspended_list->contains(id), diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index c9362d5b8cb..53144607a3c 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,6 +86,8 @@ class JvmtiVTMSTransitionDisabler { static volatile bool _sync_protocol_enabled_permanently; // seen a suspender: JvmtiVTMSTransitionDisabler protocol is enabled permanently bool _is_SR; // is suspender or resumer + bool _is_virtual; // target thread is virtual + bool _is_self; // JvmtiVTMSTransitionDisabler is a no-op for current platform, carrier or virtual thread jthread _thread; // virtual thread to disable transitions for, no-op if it is a platform thread DEBUG_ONLY(static void print_info();) diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index 26d3be7c36a..6bed5e9d546 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -28,6 +28,7 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiThreadState.hpp" #include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/handshake.hpp" @@ -729,7 +730,7 @@ class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure { virtual bool is_suspend() { return true; } }; -bool HandshakeState::suspend_with_handshake() { +bool HandshakeState::suspend_with_handshake(bool register_vthread_SR) { assert(_handshakee->threadObj() != nullptr, "cannot suspend with a null threadObj"); if (_handshakee->is_exiting()) { log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_handshakee)); @@ -744,7 +745,7 @@ bool HandshakeState::suspend_with_handshake() { // Target is going to wake up and leave suspension. // Let's just stop the thread from doing that. log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_handshakee)); - set_suspended(true); + set_suspended(true, register_vthread_SR); return true; } } @@ -752,7 +753,7 @@ bool HandshakeState::suspend_with_handshake() { assert(!is_suspended(), "cannot be suspended without a suspend request"); // Thread is safe, so it must execute the request, thus we can count it as suspended // from this point. - set_suspended(true); + set_suspended(true, register_vthread_SR); set_async_suspend_handshake(true); log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_handshakee)); ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake(); @@ -762,17 +763,19 @@ bool HandshakeState::suspend_with_handshake() { // This is the closure that synchronously honors the suspend request. class SuspendThreadHandshake : public HandshakeClosure { + bool _register_vthread_SR; bool _did_suspend; public: - SuspendThreadHandshake() : HandshakeClosure("SuspendThread"), _did_suspend(false) {} + SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"), + _register_vthread_SR(register_vthread_SR), _did_suspend(false) {} void do_thread(Thread* thr) { JavaThread* target = JavaThread::cast(thr); - _did_suspend = target->handshake_state()->suspend_with_handshake(); + _did_suspend = target->handshake_state()->suspend_with_handshake(_register_vthread_SR); } bool did_suspend() { return _did_suspend; } }; -bool HandshakeState::suspend() { +bool HandshakeState::suspend(bool register_vthread_SR) { JVMTI_ONLY(assert(!_handshakee->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");) JavaThread* self = JavaThread::current(); if (_handshakee == self) { @@ -780,31 +783,42 @@ bool HandshakeState::suspend() { // and just suspend directly ThreadBlockInVM tbivm(self); MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); - set_suspended(true); + set_suspended(true, register_vthread_SR); do_self_suspend(); return true; } else { - SuspendThreadHandshake st; + SuspendThreadHandshake st(register_vthread_SR); Handshake::execute(&st, _handshakee); return st.did_suspend(); } } -bool HandshakeState::resume() { - if (!is_suspended()) { - return false; - } +bool HandshakeState::resume(bool register_vthread_SR) { MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag); if (!is_suspended()) { assert(!_handshakee->is_suspended(), "cannot be suspended without a suspend request"); return false; } // Resume the thread. - set_suspended(false); + set_suspended(false, register_vthread_SR); _lock.notify(); return true; } +void HandshakeState::set_suspended(bool is_suspend, bool register_vthread_SR) { +#if INCLUDE_JVMTI + if (register_vthread_SR) { + assert(_handshakee->is_vthread_mounted(), "sanity check"); + if (is_suspend) { + JvmtiVTSuspender::register_vthread_suspend(_handshakee->vthread()); + } else { + JvmtiVTSuspender::register_vthread_resume(_handshakee->vthread()); + } + } +#endif + Atomic::store(&_suspended, is_suspend); +} + void HandshakeState::handle_unsafe_access_error() { if (is_suspended()) { // A suspend handshake was added to the queue after the diff --git a/src/hotspot/share/runtime/handshake.hpp b/src/hotspot/share/runtime/handshake.hpp index f754b744de9..9e963003053 100644 --- a/src/hotspot/share/runtime/handshake.hpp +++ b/src/hotspot/share/runtime/handshake.hpp @@ -173,18 +173,18 @@ class HandshakeState { bool _async_suspend_handshake; // Called from the suspend handshake. - bool suspend_with_handshake(); + bool suspend_with_handshake(bool register_vthread_SR); // Called from the async handshake (the trap) // to stop a thread from continuing execution when suspended. void do_self_suspend(); bool is_suspended() { return Atomic::load(&_suspended); } - void set_suspended(bool to) { return Atomic::store(&_suspended, to); } + void set_suspended(bool to, bool register_vthread_SR); bool has_async_suspend_handshake() { return _async_suspend_handshake; } void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; } - bool suspend(); - bool resume(); + bool suspend(bool register_vthread_SR); + bool resume(bool register_vthread_SR); }; #endif // SHARE_RUNTIME_HANDSHAKE_HPP diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 8a6da52d762..9f5dd07c4dc 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -1193,7 +1193,7 @@ void JavaThread::set_is_VTMS_transition_disabler(bool val) { // - Target thread will not execute any new bytecode. // - Target thread will not enter any new monitors. // -bool JavaThread::java_suspend() { +bool JavaThread::java_suspend(bool register_vthread_SR) { #if INCLUDE_JVMTI // Suspending a JavaThread in VTMS transition or disabling VTMS transitions can cause deadlocks. assert(!is_in_VTMS_transition(), "no suspend allowed in VTMS transition"); @@ -1202,13 +1202,13 @@ bool JavaThread::java_suspend() { guarantee(Thread::is_JavaThread_protected(/* target */ this), "target JavaThread is not protected in calling context."); - return this->handshake_state()->suspend(); + return this->handshake_state()->suspend(register_vthread_SR); } -bool JavaThread::java_resume() { +bool JavaThread::java_resume(bool register_vthread_SR) { guarantee(Thread::is_JavaThread_protected_by_TLH(/* target */ this), "missing ThreadsListHandle in calling context."); - return this->handshake_state()->resume(); + return this->handshake_state()->resume(register_vthread_SR); } // Wait for another thread to perform object reallocation and relocking on behalf of diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 01da26a97e8..8c62319f8dc 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -698,8 +698,8 @@ private: // Suspend/resume support for JavaThread // higher-level suspension/resume logic called by the public APIs - bool java_suspend(); - bool java_resume(); + bool java_suspend(bool register_vthread_SR); + bool java_resume(bool register_vthread_SR); bool is_suspended() { return _handshake.is_suspended(); } // Check for async exception in addition to safepoint. @@ -710,11 +710,11 @@ private: void wait_for_object_deoptimization(); #if INCLUDE_JVMTI - inline void set_carrier_thread_suspended(); - inline void clear_carrier_thread_suspended(); + inline bool set_carrier_thread_suspended(); + inline bool clear_carrier_thread_suspended(); bool is_carrier_thread_suspended() const { - return _carrier_thread_suspended; + return Atomic::load(&_carrier_thread_suspended); } bool is_in_VTMS_transition() const { return _is_in_VTMS_transition; } diff --git a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp index f434fc161bb..de492fda50b 100644 --- a/src/hotspot/share/runtime/javaThread.inline.hpp +++ b/src/hotspot/share/runtime/javaThread.inline.hpp @@ -71,11 +71,11 @@ inline void JavaThread::clear_obj_deopt_flag() { } #if INCLUDE_JVMTI -inline void JavaThread::set_carrier_thread_suspended() { - _carrier_thread_suspended = true; +inline bool JavaThread::set_carrier_thread_suspended() { + return Atomic::cmpxchg(&_carrier_thread_suspended, false, true) == false; } -inline void JavaThread::clear_carrier_thread_suspended() { - _carrier_thread_suspended = false; +inline bool JavaThread::clear_carrier_thread_suspended() { + return Atomic::cmpxchg(&_carrier_thread_suspended, true, false) == true; } #endif diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 8cb239636d2..74df0fa9188 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -50,6 +50,7 @@ Monitor* JNICritical_lock = nullptr; Mutex* JvmtiThreadState_lock = nullptr; Monitor* EscapeBarrier_lock = nullptr; Monitor* JvmtiVTMSTransition_lock = nullptr; +Mutex* JvmtiVThreadSuspend_lock = nullptr; Monitor* Heap_lock = nullptr; #if INCLUDE_PARALLELGC Mutex* PSOldGenExpand_lock = nullptr; @@ -260,6 +261,7 @@ void mutex_init() { MUTEX_DEFN(DirectivesStack_lock , PaddedMutex , nosafepoint); MUTEX_DEFN(JvmtiVTMSTransition_lock , PaddedMonitor, safepoint); // used for Virtual Thread Mount State transition management + MUTEX_DEFN(JvmtiVThreadSuspend_lock , PaddedMutex, nosafepoint-1); MUTEX_DEFN(EscapeBarrier_lock , PaddedMonitor, nosafepoint); // Used to synchronize object reallocation/relocking triggered by JVMTI MUTEX_DEFN(Management_lock , PaddedMutex , safepoint); // used for JVM management diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index c307f70ba80..b21a738c8ff 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -48,6 +48,7 @@ extern Monitor* JNICritical_lock; // a lock used while synchroniz extern Mutex* JvmtiThreadState_lock; // a lock on modification of JVMTI thread data extern Monitor* EscapeBarrier_lock; // a lock to sync reallocating and relocking objects because of JVMTI access extern Monitor* JvmtiVTMSTransition_lock; // a lock for Virtual Thread Mount State transition (VTMS transition) management +extern Mutex* JvmtiVThreadSuspend_lock; // a lock for virtual threads suspension extern Monitor* Heap_lock; // a lock on the heap #if INCLUDE_PARALLELGC extern Mutex* PSOldGenExpand_lock; // a lock on expanding the heap diff --git a/src/hotspot/share/sanitizers/ub.hpp b/src/hotspot/share/sanitizers/ub.hpp index 66cd014c35c..d5901f6821c 100644 --- a/src/hotspot/share/sanitizers/ub.hpp +++ b/src/hotspot/share/sanitizers/ub.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024 SAP SE. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025 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 @@ -33,7 +33,10 @@ // Useful if the function or method is known to do something special or even 'dangerous', for // example causing desired signals/crashes. #ifdef UNDEFINED_BEHAVIOR_SANITIZER -#if defined(__clang__) || defined(__GNUC__) +#if defined(__clang__) +#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined","float-divide-by-zero"))) +#endif +#if defined(__GNUC__) && !defined(__clang__) #define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined"))) #endif #endif diff --git a/src/java.base/share/classes/java/lang/ProcessHandle.java b/src/java.base/share/classes/java/lang/ProcessHandle.java index 51d13457b3f..750c93c9c57 100644 --- a/src/java.base/share/classes/java/lang/ProcessHandle.java +++ b/src/java.base/share/classes/java/lang/ProcessHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,7 +89,6 @@ import java.util.stream.Stream; * @see Process * @since 9 */ -@jdk.internal.ValueBased public interface ProcessHandle extends Comparable { /** @@ -114,7 +113,7 @@ public interface ProcessHandle extends Comparable { * @throws UnsupportedOperationException if the implementation * does not support this operation */ - public static Optional of(long pid) { + static Optional of(long pid) { return ProcessHandleImpl.get(pid); } @@ -126,7 +125,7 @@ public interface ProcessHandle extends Comparable { * @throws UnsupportedOperationException if the implementation * does not support this operation */ - public static ProcessHandle current() { + static ProcessHandle current() { return ProcessHandleImpl.current(); } @@ -204,21 +203,21 @@ public interface ProcessHandle extends Comparable { * and actions if the value is available. * @since 9 */ - public interface Info { + interface Info { /** * Returns the executable pathname of the process. * * @return an {@code Optional} of the executable pathname * of the process */ - public Optional command(); + Optional command(); /** * Returns the command line of the process. *

* If {@link #command command()} and {@link #arguments arguments()} return * non-empty optionals, this is simply a convenience method which concatenates - * the values of the two functions separated by spaces. Otherwise it will return a + * the values of the two functions separated by spaces. Otherwise, it will return a * best-effort, platform dependent representation of the command line. * * @apiNote Note that the returned executable pathname and the @@ -233,7 +232,7 @@ public interface ProcessHandle extends Comparable { * @return an {@code Optional} of the command line * of the process */ - public Optional commandLine(); + Optional commandLine(); /** * Returns an array of Strings of the arguments of the process. @@ -244,28 +243,28 @@ public interface ProcessHandle extends Comparable { * * @return an {@code Optional} of the arguments of the process */ - public Optional arguments(); + Optional arguments(); /** * Returns the start time of the process. * * @return an {@code Optional} of the start time of the process */ - public Optional startInstant(); + Optional startInstant(); /** * Returns the total cputime accumulated of the process. * * @return an {@code Optional} for the accumulated total cputime */ - public Optional totalCpuDuration(); + Optional totalCpuDuration(); /** * Return the user of the process. * * @return an {@code Optional} for the user of the process */ - public Optional user(); + Optional user(); } /** @@ -285,13 +284,11 @@ public interface ProcessHandle extends Comparable { * {@link java.util.concurrent.CompletableFuture#isDone done} or to * {@link java.util.concurrent.Future#get() wait} for it to terminate. * {@link java.util.concurrent.Future#cancel(boolean) Cancelling} - * the CompleteableFuture does not affect the Process. + * the {@linkplain CompletableFuture CompletableFuture} does not affect the Process. * @apiNote * The process may be observed to have terminated with {@link #isAlive} - * before the ComputableFuture is completed and dependent actions are invoked. - * + * before the {@code CompletableFuture} is completed and dependent actions are invoked. * @return a new {@code CompletableFuture} for the ProcessHandle - * * @throws IllegalStateException if the process is the current process */ CompletableFuture onExit(); diff --git a/src/java.base/share/classes/java/lang/StringConcatHelper.java b/src/java.base/share/classes/java/lang/StringConcatHelper.java index 3b057ecb028..f82e392a1fb 100644 --- a/src/java.base/share/classes/java/lang/StringConcatHelper.java +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -432,7 +432,7 @@ final class StringConcatHelper { @ForceInline static String doConcat(String s1, String s2) { byte coder = (byte) (s1.coder() | s2.coder()); - int newLength = (s1.length() + s2.length()) << coder; + int newLength = checkOverflow(s1.length() + s2.length()) << coder; byte[] buf = newArray(newLength); s1.getBytes(buf, 0, coder); s2.getBytes(buf, s1.length(), coder); 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 9eafaa2df9c..6b1751476f7 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java @@ -2483,7 +2483,7 @@ public sealed interface CodeBuilder var cpArgs = ref.bootstrapArgs(); List bsArguments = new ArrayList<>(cpArgs.length); for (var constantValue : cpArgs) { - bsArguments.add(BytecodeHelpers.constantEntry(constantPool(), constantValue)); + bsArguments.add(constantPool().loadableConstantEntry(requireNonNull(constantValue))); } BootstrapMethodEntry bm = constantPool().bsmEntry(bsMethod, bsArguments); NameAndTypeEntry nameAndType = constantPool().nameAndTypeEntry(ref.invocationName(), ref.invocationType()); @@ -2941,7 +2941,7 @@ public sealed interface CodeBuilder * @see ConstantInstruction.LoadConstantInstruction */ default CodeBuilder ldc(ConstantDesc value) { - return ldc(BytecodeHelpers.constantEntry(constantPool(), value)); + return ldc(constantPool().loadableConstantEntry(requireNonNull(value))); } /** 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 b7145eeb59c..6b75126a90f 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 @@ -36,6 +36,7 @@ import java.lang.invoke.MethodHandleInfo; import java.util.List; import java.util.function.Consumer; +import jdk.internal.classfile.impl.AbstractPoolEntry; import jdk.internal.classfile.impl.AbstractPoolEntry.ClassEntryImpl; import jdk.internal.classfile.impl.ClassReaderImpl; import jdk.internal.classfile.impl.SplitConstantPool; @@ -385,11 +386,13 @@ public sealed interface ConstantPoolBuilder default MethodHandleEntry methodHandleEntry(DirectMethodHandleDesc descriptor) { var owner = classEntry(descriptor.owner()); var nat = nameAndTypeEntry(utf8Entry(descriptor.methodName()), utf8Entry(descriptor.lookupDescriptor())); - return methodHandleEntry(descriptor.refKind(), switch (descriptor.kind()) { + var ret = methodHandleEntry(descriptor.refKind(), switch (descriptor.kind()) { case GETTER, SETTER, STATIC_GETTER, STATIC_SETTER -> fieldRefEntry(owner, nat); case INTERFACE_STATIC, INTERFACE_VIRTUAL, INTERFACE_SPECIAL -> interfaceMethodRefEntry(owner, nat); case STATIC, VIRTUAL, SPECIAL, CONSTRUCTOR -> methodRefEntry(owner, nat); }); + ((AbstractPoolEntry.MethodHandleEntryImpl) ret).sym = descriptor; + return ret; } /** @@ -414,7 +417,9 @@ public sealed interface ConstantPoolBuilder * @see InvokeDynamicEntry#asSymbol() InvokeDynamicEntry::asSymbol */ default InvokeDynamicEntry invokeDynamicEntry(DynamicCallSiteDesc dcsd) { - return invokeDynamicEntry(bsmEntry((DirectMethodHandleDesc)dcsd.bootstrapMethod(), List.of(dcsd.bootstrapArgs())), nameAndTypeEntry(dcsd.invocationName(), dcsd.invocationType())); + var ret = invokeDynamicEntry(bsmEntry((DirectMethodHandleDesc)dcsd.bootstrapMethod(), List.of(dcsd.bootstrapArgs())), nameAndTypeEntry(dcsd.invocationName(), dcsd.invocationType())); + ((AbstractPoolEntry.InvokeDynamicEntryImpl) ret).sym = dcsd; + return ret; } /** @@ -440,7 +445,9 @@ public sealed interface ConstantPoolBuilder * @see ConstantDynamicEntry#asSymbol() ConstantDynamicEntry::asSymbol */ default ConstantDynamicEntry constantDynamicEntry(DynamicConstantDesc dcd) { - return constantDynamicEntry(bsmEntry(dcd.bootstrapMethod(), List.of(dcd.bootstrapArgs())), nameAndTypeEntry(dcd.constantName(), dcd.constantType())); + var ret = constantDynamicEntry(bsmEntry(dcd.bootstrapMethod(), List.of(dcd.bootstrapArgs())), nameAndTypeEntry(dcd.constantName(), dcd.constantType())); + ((AbstractPoolEntry.ConstantDynamicEntryImpl) ret).sym = dcd; + return ret; } /** diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 3251de9bc4b..007834ed5f4 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -501,7 +501,7 @@ public final class MethodHandles { * * * {@link java.lang.invoke.MethodHandles.Lookup#findGetter lookup.findGetter(C.class,"f",FT.class)} - * {@code FT f;}{@code (T) this.f;} + * {@code FT f;}{@code (FT) this.f;} * * * {@link java.lang.invoke.MethodHandles.Lookup#findStaticGetter lookup.findStaticGetter(C.class,"f",FT.class)} 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 bf558ddef16..962a2057585 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 @@ -295,9 +295,13 @@ public abstract sealed class AbstractPoolEntry { @Override public Utf8EntryImpl clone(ConstantPoolBuilder cp) { - return (state == State.STRING && rawBytes == null) + var ret = (state == State.STRING && rawBytes == null) ? (Utf8EntryImpl) cp.utf8Entry(stringValue) : ((SplitConstantPool) cp).maybeCloneUtf8Entry(this); + var mySym = this.typeSym; + if (ret.typeSym == null && mySym != null) + ret.typeSym = mySym; + return ret; } @Override @@ -939,6 +943,8 @@ public abstract sealed class AbstractPoolEntry { extends AbstractDynamicConstantPoolEntry implements InvokeDynamicEntry { + public @Stable DynamicCallSiteDesc sym; + InvokeDynamicEntryImpl(ConstantPool cpm, int index, int hash, BootstrapMethodEntryImpl bootstrapMethod, NameAndTypeEntryImpl nameAndType) { super(cpm, index, hash, bootstrapMethod, nameAndType); @@ -957,13 +963,27 @@ public abstract sealed class AbstractPoolEntry { @Override public InvokeDynamicEntry clone(ConstantPoolBuilder cp) { - return cp.invokeDynamicEntry(bootstrap(), nameAndType()); + var ret = (InvokeDynamicEntryImpl) cp.invokeDynamicEntry(bootstrap(), nameAndType()); + var mySym = this.sym; + if (ret.sym == null && mySym != null) + ret.sym = mySym; + return ret; + } + + @Override + public DynamicCallSiteDesc asSymbol() { + var cache = this.sym; + if (cache != null) + return cache; + return this.sym = InvokeDynamicEntry.super.asSymbol(); } } public static final class ConstantDynamicEntryImpl extends AbstractDynamicConstantPoolEntry implements ConstantDynamicEntry { + public @Stable DynamicConstantDesc sym; + ConstantDynamicEntryImpl(ConstantPool cpm, int index, int hash, BootstrapMethodEntryImpl bootstrapMethod, NameAndTypeEntryImpl nameAndType) { super(cpm, index, hash, bootstrapMethod, nameAndType); @@ -982,7 +1002,19 @@ public abstract sealed class AbstractPoolEntry { @Override public ConstantDynamicEntry clone(ConstantPoolBuilder cp) { - return cp.constantDynamicEntry(bootstrap(), nameAndType()); + var ret = (ConstantDynamicEntryImpl) cp.constantDynamicEntry(bootstrap(), nameAndType()); + var mySym = this.sym; + if (ret.sym == null && mySym != null) + ret.sym = mySym; + return ret; + } + + @Override + public DynamicConstantDesc asSymbol() { + var cache = this.sym; + if (cache != null) + return cache; + return this.sym = ConstantDynamicEntry.super.asSymbol(); } } @@ -991,6 +1023,7 @@ public abstract sealed class AbstractPoolEntry { private final int refKind; private final AbstractPoolEntry.AbstractMemberRefEntry reference; + public @Stable DirectMethodHandleDesc sym; MethodHandleEntryImpl(ConstantPool cpm, int index, int hash, int refKind, AbstractPoolEntry.AbstractMemberRefEntry reference) { @@ -1023,7 +1056,14 @@ public abstract sealed class AbstractPoolEntry { @Override public DirectMethodHandleDesc asSymbol() { - return MethodHandleDesc.of( + var cache = this.sym; + if (cache != null) + return cache; + return computeSymbol(); + } + + private DirectMethodHandleDesc computeSymbol() { + return this.sym = MethodHandleDesc.of( DirectMethodHandleDesc.Kind.valueOf(kind(), reference() instanceof InterfaceMethodRefEntry), ((MemberRefEntry) reference()).owner().asSymbol(), ((MemberRefEntry) reference()).nameAndType().name().stringValue(), @@ -1037,7 +1077,11 @@ public abstract sealed class AbstractPoolEntry { @Override public MethodHandleEntry clone(ConstantPoolBuilder cp) { - return cp.methodHandleEntry(refKind, reference); + var ret = (MethodHandleEntryImpl) cp.methodHandleEntry(refKind, reference); + var mySym = this.sym; + if (ret.sym == null && mySym != null) + ret.sym = mySym; + return ret; } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java index 25daf4e4648..a51728eb3e9 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,12 +29,10 @@ import java.lang.classfile.BootstrapMethodEntry; import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; import java.lang.classfile.constantpool.*; -import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicConstantDesc; -import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandleInfo; import java.util.ArrayList; import java.util.List; @@ -515,38 +513,6 @@ public class BytecodeHelpers { : Opcode.LDC; } - public static LoadableConstantEntry constantEntry(ConstantPoolBuilder constantPool, - ConstantDesc constantValue) { - // this method is invoked during JVM bootstrap - cannot use pattern switch - if (constantValue instanceof Integer value) { - return constantPool.intEntry(value); - } - if (constantValue instanceof String value) { - return constantPool.stringEntry(value); - } - if (constantValue instanceof ClassDesc value && !value.isPrimitive()) { - return constantPool.classEntry(value); - } - if (constantValue instanceof Long value) { - return constantPool.longEntry(value); - } - if (constantValue instanceof Float value) { - return constantPool.floatEntry(value); - } - if (constantValue instanceof Double value) { - return constantPool.doubleEntry(value); - } - if (constantValue instanceof MethodTypeDesc value) { - return constantPool.methodTypeEntry(value); - } - if (constantValue instanceof DirectMethodHandleDesc value) { - return handleDescToHandleInfo(constantPool, value); - } if (constantValue instanceof DynamicConstantDesc value) { - return handleConstantDescToHandleInfo(constantPool, value); - } - throw new UnsupportedOperationException("not yet: " + requireNonNull(constantValue)); - } - public static ConstantDesc intrinsicConstantValue(Opcode opcode) { return switch (opcode) { case ACONST_NULL -> ConstantDescs.NULL; 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 2ea6fa943a7..5a5f2908ae6 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 @@ -30,6 +30,7 @@ import java.lang.classfile.ClassReader; import java.lang.classfile.attribute.BootstrapMethodsAttribute; import java.lang.classfile.constantpool.*; import java.lang.constant.ClassDesc; +import java.lang.constant.DynamicCallSiteDesc; import java.lang.constant.MethodTypeDesc; import java.util.Arrays; import java.util.List; @@ -521,6 +522,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder { AbstractPoolEntry.ClassEntryImpl cloneClassEntry(AbstractPoolEntry.ClassEntryImpl e) { var ce = tryFindClassEntry(e.hashCode(), e.ref1); if (ce != null) { + var mySym = e.sym; + if (ce.sym == null && mySym != null) { + ce.sym = mySym; + } return ce; } 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 7e16aea5a52..eb17e99a94d 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -716,7 +716,7 @@ public final class StackMapGenerator { case TAG_METHOD_TYPE -> currentFrame.pushStack(Type.METHOD_TYPE); case TAG_DYNAMIC -> - currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).asSymbol().constantType()); + currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).typeSymbol()); default -> throw generatorError("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag())); } diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index e8dc89161fd..5ed1ca2bfbc 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -154,7 +154,7 @@ java.launcher.X.usage=\n\ \ -Xmixed mixed mode execution (default)\n\ \ -Xmn sets the initial and maximum size (in bytes) of the heap\n\ \ for the young generation (nursery)\n\ -\ -Xms set initial Java heap size\n\ +\ -Xms set minimum and initial Java heap size\n\ \ -Xmx set maximum Java heap size\n\ \ -Xnoclassgc disable class garbage collection\n\ \ -Xrs reduce use of OS signals by Java/VM (see documentation)\n\ diff --git a/src/java.base/share/classes/sun/security/util/Debug.java b/src/java.base/share/classes/sun/security/util/Debug.java index 74d33c036ef..f6c0c523165 100644 --- a/src/java.base/share/classes/sun/security/util/Debug.java +++ b/src/java.base/share/classes/sun/security/util/Debug.java @@ -31,8 +31,6 @@ import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.HexFormat; -import java.util.regex.Pattern; -import java.util.regex.Matcher; import java.util.Locale; /** @@ -65,7 +63,7 @@ public class Debug { } if (args != null) { - args = marshal(args); + args = args.toLowerCase(Locale.ENGLISH); if (args.equals("help")) { Help(); } else if (args.contains("all")) { @@ -349,69 +347,6 @@ public class Debug { return sb.toString(); } - /** - * change a string into lower case except permission classes and URLs. - */ - private static String marshal(String args) { - if (args != null) { - StringBuilder target = new StringBuilder(); - StringBuilder source = new StringBuilder(args); - - // obtain the "permission=" options - // the syntax of classname: IDENTIFIER.IDENTIFIER - // the regular express to match a class name: - // "[a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*" - String keyReg = "[Pp][Ee][Rr][Mm][Ii][Ss][Ss][Ii][Oo][Nn]="; - String keyStr = "permission="; - String reg = keyReg + - "[a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*"; - Pattern pattern = Pattern.compile(reg); - Matcher matcher = pattern.matcher(source); - StringBuilder left = new StringBuilder(); - while (matcher.find()) { - String matched = matcher.group(); - target.append(matched.replaceFirst(keyReg, keyStr)); - target.append(" "); - - // delete the matched sequence - matcher.appendReplacement(left, ""); - } - matcher.appendTail(left); - source = left; - - // obtain the "codebase=" options - // the syntax of URL is too flexible, and here assumes that the - // URL contains no space, comma(','), and semicolon(';'). That - // also means those characters also could be used as separator - // after codebase option. - // However, the assumption is incorrect in some special situation - // when the URL contains comma or semicolon - keyReg = "[Cc][Oo][Dd][Ee][Bb][Aa][Ss][Ee]="; - keyStr = "codebase="; - reg = keyReg + "[^, ;]*"; - pattern = Pattern.compile(reg); - matcher = pattern.matcher(source); - left = new StringBuilder(); - while (matcher.find()) { - String matched = matcher.group(); - target.append(matched.replaceFirst(keyReg, keyStr)); - target.append(" "); - - // delete the matched sequence - matcher.appendReplacement(left, ""); - } - matcher.appendTail(left); - source = left; - - // convert the rest to lower-case characters - target.append(source.toString().toLowerCase(Locale.ENGLISH)); - - return target.toString(); - } - - return null; - } - public static String toString(byte[] b) { if (b == null) { return "(null)"; diff --git a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties index 7e6acccf6e5..d2d9ff1035b 100644 --- a/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties +++ b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -261,6 +261,7 @@ VES=VES VND=VND VUV=VUV WST=WST +XAD=XAD XAF=XAF XAG=XAG XAU=XAU @@ -488,6 +489,7 @@ ves=Venezuelan Bolívar Soberano vnd=Vietnamese Dong vuv=Vanuatu Vatu wst=Samoan Tala +xad=Arab Accounting Dinar xaf=CFA Franc BEAC xag=Silver xau=Gold diff --git a/src/java.base/share/data/currency/CurrencyData.properties b/src/java.base/share/data/currency/CurrencyData.properties index 5a28abccd00..ff2d1f87ed3 100644 --- a/src/java.base/share/data/currency/CurrencyData.properties +++ b/src/java.base/share/data/currency/CurrencyData.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ formatVersion=3 # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=177 +dataVersion=179 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -54,7 +54,7 @@ all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036 SBD090-SCR690-SDD736-SDG938-SEK752-SGD702-SHP654-SIT705-SKK703-SLE925-SLL694-SOS706-\ SRD968-SRG740-SSP728-STD678-STN930-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TMT934-TND788-TOP776-\ TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\ - UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ + UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAD396-XAF950-XAG961-XAU959-XBA955-\ XBB956-XBC957-XBD958-XCD951-XCG532-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWG924-\ ZWL932-ZWN942-ZWR935 diff --git a/src/java.base/share/native/libjava/ub.h b/src/java.base/share/native/libjava/ub.h index d6e0cac3bea..3019e672694 100644 --- a/src/java.base/share/native/libjava/ub.h +++ b/src/java.base/share/native/libjava/ub.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024 SAP SE. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025 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 @@ -32,7 +32,10 @@ * following function or method. */ #ifdef UNDEFINED_BEHAVIOR_SANITIZER -#if defined(__clang__) || defined(__GNUC__) +#if defined(__clang__) +#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined","float-divide-by-zero"))) +#endif +#if defined(__GNUC__) && !defined(__clang__) #define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined"))) #endif #endif diff --git a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java index 9260ce532f1..6bc1dab1f8a 100644 --- a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java +++ b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java @@ -81,6 +81,8 @@ public enum SourceVersion { * switch in second preview, module Import Declarations in second * preview, simple source files and instance main in fourth * preview, flexible constructor bodies in third preview) + * 25: module import declarations, compact source files and + * instance main methods, */ /** @@ -449,11 +451,18 @@ public enum SourceVersion { * The version introduced by the Java Platform, Standard Edition * 25. * + * Additions in this release include module import declarations + * and compact source files and instance main methods. + * * @since 25 * * @see * The Java Language Specification, Java SE 25 Edition + * @see + * JEP 511: Module Import Declarations + * @see + * JEP 512: Compact Source Files and Instance Main Methods */ RELEASE_25, ; // Reduce code churn when appending new constants diff --git a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java index 7f0b493572e..5d97674c53d 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -778,6 +778,12 @@ public interface Elements { * elements.getTypeElement("I")); * } * + * @apiNote This method examines the method's name, signature, subclass relationship, and accessibility + * in determining whether one method overrides another, as specified in JLS {@jls 8.4.8.1}. + * In addition, an implementation may have stricter checks including method modifiers, return types and + * exception types as described in JLS {@jls 8.4.8.1} and {@jls 8.4.8.3}. + * Note that such additional compile-time checks are not guaranteed and may vary between implementations. + * * @param overrider the first method, possible overrider * @param overridden the second method, possibly being overridden * @param type the class or interface of which the first method is a member diff --git a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp index bf4f2e143a0..ae0ffa5f2ca 100644 --- a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp +++ b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_Ports.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -124,9 +124,12 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, kAudioHardwarePropertyDevices, &size); if (err == noErr) { int count = size/sizeof(AudioDeviceID); - AudioDeviceID devices[count]; + AudioDeviceID *devices = (AudioDeviceID *) malloc(size); + if (!devices) { + break; + } err = GetAudioObjectProperty(kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal, - kAudioHardwarePropertyDevices, count*sizeof(AudioDeviceID), devices, 1); + kAudioHardwarePropertyDevices, size, devices, 1); if (err == noErr) { bool found = false; for (int j = 0; j < count; j++) { @@ -139,6 +142,7 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, invalid = true; } } + free(devices); } break; case kAudioObjectPropertyOwnedObjects: @@ -148,9 +152,12 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, kAudioObjectPropertyOwnedObjects, &size); if (err == noErr) { int count = size / sizeof(AudioObjectID); - AudioObjectID controlIDs[count]; + AudioObjectID *controlIDs = (AudioObjectID *) malloc(size); + if (!controlIDs) { + break; + } err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyOwnedObjects, count * sizeof(AudioObjectID), &controlIDs, 1); + kAudioObjectPropertyOwnedObjects, size, controlIDs, 1); if (err == noErr) { for (PortControl *ctrl = mixer->portControls; ctrl != NULL; ctrl = ctrl->next) { for (int i = 0; i < ctrl->controlCount; i++) { @@ -168,6 +175,7 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, } } } + free(controlIDs); } } } @@ -229,6 +237,9 @@ INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* mixer void* PORT_Open(INT32 mixerIndex) { TRACE1("\n>>PORT_Open (mixerIndex=%d)\n", (int)mixerIndex); PortMixer *mixer = (PortMixer *)calloc(1, sizeof(PortMixer)); + if (!mixer) { + return nullptr; + } mixer->deviceID = deviceCache.GetDeviceID(mixerIndex); if (mixer->deviceID != 0) { @@ -480,16 +491,20 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { mixer->deviceControlCount = size / sizeof(AudioObjectID); TRACE1(" PORT_GetControls: detected %d owned objects\n", mixer->deviceControlCount); - AudioObjectID controlIDs[mixer->deviceControlCount]; + AudioObjectID *controlIDs = (AudioObjectID *) malloc(size); + if (!controlIDs) { + return; + } err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyOwnedObjects, sizeof(controlIDs), controlIDs, 1); + kAudioObjectPropertyOwnedObjects, size, controlIDs, 1); if (err) { OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject values", portIndex); } else { mixer->deviceControls = (AudioControl *)calloc(mixer->deviceControlCount, sizeof(AudioControl)); if (mixer->deviceControls == NULL) { + free(controlIDs); return; } @@ -513,6 +528,7 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel); } } + free(controlIDs); } } @@ -524,10 +540,14 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0); // collect volume and mute controls - AudioControl* volumeControls[totalChannels+1]; // 0 - for master channel - memset(&volumeControls, 0, sizeof(AudioControl *) * (totalChannels+1)); - AudioControl* muteControls[totalChannels+1]; // 0 - for master channel - memset(&muteControls, 0, sizeof(AudioControl *) * (totalChannels+1)); + size_t totalAndMaster = totalChannels + 1; // 0 - for master channel + AudioControl **volumeControls = (AudioControl **) calloc(totalAndMaster, sizeof(AudioControl *)); + AudioControl **muteControls = (AudioControl **) calloc(totalAndMaster, sizeof(AudioControl *)); + if (!volumeControls || !muteControls) { + free(muteControls); + free(volumeControls); + return; + } for (int i=0; ideviceControlCount; i++) { AudioControl *control = &mixer->deviceControls[i]; @@ -626,6 +646,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { CFIndex length = CFStringGetLength(cfname) + 1; channelName = (char *)malloc(length); if (channelName == NULL) { + free(muteControls); + free(volumeControls); return; } CFStringGetCString(cfname, channelName, length, kCFStringEncodingUTF8); @@ -633,6 +655,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { } else { channelName = (char *)malloc(16); if (channelName == NULL) { + free(muteControls); + free(volumeControls); return; } snprintf(channelName, 16, "Ch %d", ch); @@ -657,7 +681,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) { } AddChangeListeners(mixer); - + free(muteControls); + free(volumeControls); TRACE1("<controlCount]; + Float32 *subVolumes = (Float32 *) malloc(control->controlCount * sizeof(Float32)); + if (!subVolumes) { + return DEFAULT_VOLUME_VALUE; + } Float32 maxVolume; switch (control->type) { case PortControl::Volume: if (!TestPortControlValidity(control)) { - return DEFAULT_VOLUME_VALUE; + result = DEFAULT_VOLUME_VALUE; + break; } if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return DEFAULT_VOLUME_VALUE; + result = DEFAULT_VOLUME_VALUE; + break; } result = maxVolume; break; case PortControl::Balance: if (!TestPortControlValidity(control)) { - return DEFAULT_BALANCE_VALUE; + result = DEFAULT_BALANCE_VALUE; + break; } // balance control always has 2 volume controls if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return DEFAULT_VOLUME_VALUE; + result = DEFAULT_VOLUME_VALUE; + break; } // calculate balance value if (subVolumes[0] > subVolumes[1]) { @@ -806,9 +838,10 @@ float PORT_GetFloatValue(void* controlIDV) { break; default: ERROR1("GetFloatValue requested for non-Float control (control-type == %d)\n", control->type); - return 0; + result = 0; + break; } - + free(subVolumes); TRACE1("<controlCount]; + Float32 *subVolumes = (Float32 *) malloc(sizeof(Float32) * control->controlCount); + if (!subVolumes) { + return; + } Float32 maxVolume; switch (control->type) { case PortControl::Volume: if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return; + break; } // update the values if (maxVolume > 0.001) { @@ -845,7 +881,7 @@ void PORT_SetFloatValue(void* controlIDV, float value) { case PortControl::Balance: // balance control always has 2 volume controls if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) { - return; + break; } // calculate new values if (value < 0.0f) { @@ -861,8 +897,9 @@ void PORT_SetFloatValue(void* controlIDV, float value) { break; default: ERROR1("PORT_SetFloatValue requested for non-Float control (control-type == %d)\n", control->type); - return; + break; } + free(subVolumes); } #endif // USE_PORTS diff --git a/src/java.desktop/share/classes/sun/print/PSPrinterJob.java b/src/java.desktop/share/classes/sun/print/PSPrinterJob.java index 7404c6c3948..03fdb532b71 100644 --- a/src/java.desktop/share/classes/sun/print/PSPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/PSPrinterJob.java @@ -127,24 +127,6 @@ public class PSPrinterJob extends RasterPrinterJob { */ private static final int MAX_PSSTR = (1024 * 64 - 1); - private static final int RED_MASK = 0x00ff0000; - private static final int GREEN_MASK = 0x0000ff00; - private static final int BLUE_MASK = 0x000000ff; - - private static final int RED_SHIFT = 16; - private static final int GREEN_SHIFT = 8; - private static final int BLUE_SHIFT = 0; - - private static final int LOWNIBBLE_MASK = 0x0000000f; - private static final int HINIBBLE_MASK = 0x000000f0; - private static final int HINIBBLE_SHIFT = 4; - private static final byte[] hexDigits = { - (byte)'0', (byte)'1', (byte)'2', (byte)'3', - (byte)'4', (byte)'5', (byte)'6', (byte)'7', - (byte)'8', (byte)'9', (byte)'A', (byte)'B', - (byte)'C', (byte)'D', (byte)'E', (byte)'F' - }; - private static final int PS_XRES = 300; private static final int PS_YRES = 300; @@ -267,14 +249,6 @@ public class PSPrinterJob extends RasterPrinterJob { private double xres = PS_XRES; private double yres = PS_YRES; - /* non-null if printing EPS for Java Plugin */ - private EPSPrinter epsPrinter = null; - - /** - * The metrics for the font currently set. - */ - FontMetrics mCurMetrics; - /** * The output stream to which the generated PostScript * is written. @@ -513,46 +487,43 @@ public class PSPrinterJob extends RasterPrinterJob { OutputStream output = null; - if (epsPrinter == null) { - if (getPrintService() instanceof PSStreamPrintService) { - StreamPrintService sps = (StreamPrintService)getPrintService(); - mDestType = RasterPrinterJob.STREAM; - if (sps.isDisposed()) { - throw new PrinterException("service is disposed"); - } - output = sps.getOutputStream(); - if (output == null) { - throw new PrinterException("Null output stream"); + if (getPrintService() instanceof PSStreamPrintService) { + StreamPrintService sps = (StreamPrintService)getPrintService(); + mDestType = RasterPrinterJob.STREAM; + if (sps.isDisposed()) { + throw new PrinterException("service is disposed"); + } + output = sps.getOutputStream(); + if (output == null) { + throw new PrinterException("Null output stream"); + } + } else { + /* REMIND: This needs to be more maintainable */ + mNoJobSheet = super.noJobSheet; + if (super.destinationAttr != null) { + mDestType = RasterPrinterJob.FILE; + mDestination = super.destinationAttr; + } + if (mDestType == RasterPrinterJob.FILE) { + try { + spoolFile = new File(mDestination); + output = new FileOutputStream(spoolFile); + } catch (IOException ex) { + abortDoc(); + throw new PrinterIOException(ex); } } else { - /* REMIND: This needs to be more maintainable */ - mNoJobSheet = super.noJobSheet; - if (super.destinationAttr != null) { - mDestType = RasterPrinterJob.FILE; - mDestination = super.destinationAttr; - } - if (mDestType == RasterPrinterJob.FILE) { - try { - spoolFile = new File(mDestination); - output = new FileOutputStream(spoolFile); - } catch (IOException ex) { - abortDoc(); - throw new PrinterIOException(ex); - } - } else { - PrinterOpener po = new PrinterOpener(); - po.run(); - if (po.pex != null) { - throw po.pex; - } - output = po.result; + PrinterOpener po = new PrinterOpener(); + po.run(); + if (po.pex != null) { + throw po.pex; } + output = po.result; } - - mPSStream = new PrintStream(new BufferedOutputStream(output)); - mPSStream.println(ADOBE_PS_STR); } + mPSStream = new PrintStream(new BufferedOutputStream(output)); + mPSStream.println(ADOBE_PS_STR); mPSStream.println("%%BeginProlog"); mPSStream.println(READIMAGEPROC); mPSStream.println("/BD {bind def} bind def"); @@ -619,45 +590,43 @@ public class PSPrinterJob extends RasterPrinterJob { mPSStream.println("%%EndProlog"); mPSStream.println("%%BeginSetup"); - if (epsPrinter == null) { - // Set Page Size using first page's format. - PageFormat pageFormat = getPageable().getPageFormat(0); - double paperHeight = pageFormat.getPaper().getHeight(); - double paperWidth = pageFormat.getPaper().getWidth(); + // Set Page Size using first page's format. + PageFormat pageFormat = getPageable().getPageFormat(0); + double paperHeight = pageFormat.getPaper().getHeight(); + double paperWidth = pageFormat.getPaper().getWidth(); - /* PostScript printers can always generate uncollated copies. - */ - mPSStream.print("<< /PageSize [" + - paperWidth + " "+ paperHeight+"]"); + /* PostScript printers can always generate uncollated copies. + */ + mPSStream.print("<< /PageSize [" + + paperWidth + " "+ paperHeight+"]"); - final PrintService pservice = getPrintService(); - Boolean isPS = Boolean.TRUE; - try { - Class psClass = Class.forName("sun.print.IPPPrintService"); - if (psClass.isInstance(pservice)) { - Method isPSMethod = psClass.getMethod("isPostscript", - (Class[])null); - isPS = (Boolean)isPSMethod.invoke(pservice, (Object[])null); - } - } catch (Throwable t) { + final PrintService pservice = getPrintService(); + Boolean isPS = Boolean.TRUE; + try { + Class psClass = Class.forName("sun.print.IPPPrintService"); + if (psClass.isInstance(pservice)) { + Method isPSMethod = psClass.getMethod("isPostscript", + (Class[])null); + isPS = (Boolean)isPSMethod.invoke(pservice, (Object[])null); } - if (isPS) { - mPSStream.print(" /DeferredMediaSelection true"); - } - - mPSStream.print(" /ImagingBBox null /ManualFeed false"); - mPSStream.print(isCollated() ? " /Collate true":""); - mPSStream.print(" /NumCopies " +getCopiesInt()); - - if (sidesAttr != Sides.ONE_SIDED) { - if (sidesAttr == Sides.TWO_SIDED_LONG_EDGE) { - mPSStream.print(" /Duplex true "); - } else if (sidesAttr == Sides.TWO_SIDED_SHORT_EDGE) { - mPSStream.print(" /Duplex true /Tumble true "); - } - } - mPSStream.println(" >> setpagedevice "); + } catch (Throwable t) { } + if (isPS) { + mPSStream.print(" /DeferredMediaSelection true"); + } + + mPSStream.print(" /ImagingBBox null /ManualFeed false"); + mPSStream.print(isCollated() ? " /Collate true":""); + mPSStream.print(" /NumCopies " +getCopiesInt()); + + if (sidesAttr != Sides.ONE_SIDED) { + if (sidesAttr == Sides.TWO_SIDED_LONG_EDGE) { + mPSStream.print(" /Duplex true "); + } else if (sidesAttr == Sides.TWO_SIDED_SHORT_EDGE) { + mPSStream.print(" /Duplex true /Tumble true "); + } + } + mPSStream.println(" >> setpagedevice "); mPSStream.println("%%EndSetup"); } @@ -1175,36 +1144,6 @@ public class PSPrinterJob extends RasterPrinterJob { return psFont; } - - private static String escapeParens(String str) { - if (str.indexOf('(') == -1 && str.indexOf(')') == -1 ) { - return str; - } else { - int count = 0; - int pos = 0; - while ((pos = str.indexOf('(', pos)) != -1) { - count++; - pos++; - } - pos = 0; - while ((pos = str.indexOf(')', pos)) != -1) { - count++; - pos++; - } - char []inArr = str.toCharArray(); - char []outArr = new char[inArr.length+count]; - pos = 0; - for (int i=0;i 0) { - return Printable.NO_SUCH_PAGE; - } else { - // "aware" client code can detect that its been passed a - // PrinterGraphics and could theoretically print - // differently. I think this is more likely useful than - // a problem. - applet.printAll(g); - return Printable.PAGE_EXISTS; - } - } - - } - - /* - * This class can take an application-client supplied printable object - * and send the result to a stream. - * The application does not need to send any postscript to this stream - * unless it needs to specify a translation etc. - * It assumes that its importing application obeys all the conventions - * for importation of EPS. See Appendix H - Encapsulated Postscript File - * Format - of the Adobe Postscript Language Reference Manual, 2nd edition. - * This class could be used as the basis for exposing the ability to - * generate EPSF from 2D graphics as a StreamPrintService. - * In that case a MediaPrintableArea attribute could be used to - * communicate the bounding box. - */ - public static class EPSPrinter implements Pageable { - - private PageFormat pf; - private PSPrinterJob job; - private int llx, lly, urx, ury; - private Printable printable; - private PrintStream stream; - private String epsTitle; - - public EPSPrinter(Printable printable, String title, - PrintStream stream, - int x, int y, int wid, int hgt) { - - this.printable = printable; - this.epsTitle = title; - this.stream = stream; - llx = x; - lly = y; - urx = llx+wid; - ury = lly+hgt; - // construct a PageFormat with zero margins representing the - // exact bounds of the applet. ie construct a theoretical - // paper which happens to exactly match applet panel size. - Paper p = new Paper(); - p.setSize((double)wid, (double)hgt); - p.setImageableArea(0.0,0.0, (double)wid, (double)hgt); - pf = new PageFormat(); - pf.setPaper(p); - } - - public void print() throws PrinterException { - stream.println("%!PS-Adobe-3.0 EPSF-3.0"); - stream.println("%%BoundingBox: " + - llx + " " + lly + " " + urx + " " + ury); - stream.println("%%Title: " + epsTitle); - stream.println("%%Creator: Java Printing"); - stream.println("%%CreationDate: " + new java.util.Date()); - stream.println("%%EndComments"); - stream.println("/pluginSave save def"); - stream.println("mark"); // for restoring stack state on return - - job = new PSPrinterJob(); - job.epsPrinter = this; // modifies the behaviour of PSPrinterJob - job.mPSStream = stream; - job.mDestType = RasterPrinterJob.STREAM; // prevents closure - - job.startDoc(); - try { - job.printPage(this, 0); - } catch (Throwable t) { - if (t instanceof PrinterException) { - throw (PrinterException)t; - } else { - throw new PrinterException(t.toString()); - } - } finally { - stream.println("cleartomark"); // restore stack state - stream.println("pluginSave restore"); - job.endDoc(); - } - stream.flush(); - } - - public int getNumberOfPages() { - return 1; - } - - public PageFormat getPageFormat(int pgIndex) { - if (pgIndex > 0) { - throw new IndexOutOfBoundsException("pgIndex"); - } else { - return pf; - } - } - - public Printable getPrintable(int pgIndex) { - if (pgIndex > 0) { - throw new IndexOutOfBoundsException("pgIndex"); - } else { - return printable; - } - } - - } } diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index 2d0a28ed310..e0c40705ed6 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,4 +1,4 @@ -## Harfbuzz v10.4.0 +## Harfbuzz 11.2.0 ### Harfbuzz License diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh index 43a611a0058..b8f6fa4054b 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh @@ -949,25 +949,25 @@ struct CBDT hb_glyph_extents_t extents; hb_glyph_extents_t pixel_extents; - hb_blob_t *blob = reference_png (font, glyph); - - if (unlikely (blob == hb_blob_get_empty ())) - return false; - - if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents))) + if (unlikely (!font->get_glyph_extents (glyph, &extents, false))) return false; if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) return false; + hb_blob_t *blob = reference_png (font, glyph); + if (unlikely (hb_blob_is_immutable (blob))) + return false; + bool ret = funcs->image (data, blob, pixel_extents.width, -pixel_extents.height, HB_PAINT_IMAGE_FORMAT_PNG, - font->slant_xy, + 0.f, &extents); hb_blob_destroy (blob); + return ret; } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh index 4d6272d05cb..73e6b4c305b 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh @@ -33,6 +33,7 @@ #include "../../../hb-open-type.hh" #include "../../../hb-ot-var-common.hh" #include "../../../hb-paint.hh" +#include "../../../hb-paint-bounded.hh" #include "../../../hb-paint-extents.hh" #include "../CPAL/CPAL.hh" @@ -47,6 +48,12 @@ namespace OT { struct hb_paint_context_t; } +struct hb_colr_scratch_t +{ + hb_paint_bounded_context_t paint_bounded; + hb_paint_extents_context_t paint_extents; +}; + namespace OT { struct COLR; @@ -90,12 +97,27 @@ public: font (font_), palette ( #ifndef HB_NO_COLOR - font->face->table.CPAL->get_palette_colors (palette_) + // https://github.com/harfbuzz/harfbuzz/issues/5116 + font->face->table.CPAL->get_palette_colors (palette_ < font->face->table.CPAL->get_palette_count () ? palette_ : 0) #endif ), foreground (foreground_), instancer (instancer_) - { } + { + if (font->is_synthetic ()) + { + font = hb_font_create_sub_font (font); + hb_font_set_synthetic_bold (font, 0, 0, true); + hb_font_set_synthetic_slant (font, 0); + } + else + hb_font_reference (font); + } + + ~hb_paint_context_t () + { + hb_font_destroy (font); + } hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground) { @@ -932,9 +954,9 @@ struct PaintGlyph void paint_glyph (hb_paint_context_t *c) const { TRACE_PAINT (this); - c->funcs->push_inverse_root_transform (c->data, c->font); + c->funcs->push_inverse_font_transform (c->data, c->font); c->funcs->push_clip_glyph (c->data, gid, c->font); - c->funcs->push_root_transform (c->data, c->font); + c->funcs->push_font_transform (c->data, c->font); c->recurse (this+paint); c->funcs->pop_transform (c->data); c->funcs->pop_clip (c->data); @@ -1511,10 +1533,12 @@ struct PaintComposite void paint_glyph (hb_paint_context_t *c) const { TRACE_PAINT (this); + c->funcs->push_group (c->data); c->recurse (this+backdrop); c->funcs->push_group (c->data); c->recurse (this+src); c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); } HBUINT8 format; /* format = 32 */ @@ -1612,7 +1636,7 @@ struct ClipBox void closurev1 (hb_colrv1_closure_context_t* c) const { switch (u.format) { - case 2: u.format2.closurev1 (c); + case 2: u.format2.closurev1 (c); return; default:return; } } @@ -2079,6 +2103,8 @@ struct COLR { static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; + bool has_data () const { return has_v0_data () || version; } + bool has_v0_data () const { return numBaseGlyphs; } bool has_v1_data () const { @@ -2112,7 +2138,53 @@ struct COLR { accelerator_t (hb_face_t *face) { colr = hb_sanitize_context_t ().reference_table (face); } - ~accelerator_t () { this->colr.destroy (); } + + ~accelerator_t () + { + auto *scratch = cached_scratch.get_relaxed (); + if (scratch) + { + scratch->~hb_colr_scratch_t (); + hb_free (scratch); + } + + colr.destroy (); + } + + + bool has_data () const { return colr->has_data (); } + +#ifndef HB_NO_PAINT + bool + get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + if (unlikely (!has_data ())) return false; + + hb_colr_scratch_t *scratch = acquire_scratch (); + if (unlikely (!scratch)) return true; + bool ret = colr->get_extents (font, glyph, extents, *scratch); + release_scratch (scratch); + return ret; + } + + bool paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *funcs, void *data, + unsigned int palette_index, + hb_color_t foreground, + bool clip = true) const + { + if (unlikely (!has_data ())) return false; + + hb_colr_scratch_t *scratch = acquire_scratch (); + if (unlikely (!scratch)) return true; + bool ret = colr->paint_glyph (font, glyph, funcs, data, palette_index, foreground, clip, *scratch); + release_scratch (scratch); + return ret; + } +#endif bool is_valid () { return colr.get_blob ()->length; } @@ -2148,7 +2220,33 @@ struct COLR { return colr->get_delta_set_index_map_ptr (); } private: + + hb_colr_scratch_t *acquire_scratch () const + { + hb_colr_scratch_t *scratch = cached_scratch.get_acquire (); + + if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr))) + { + scratch = (hb_colr_scratch_t *) hb_calloc (1, sizeof (hb_colr_scratch_t)); + if (unlikely (!scratch)) + return nullptr; + } + + return scratch; + } + void release_scratch (hb_colr_scratch_t *scratch) const + { + if (!cached_scratch.cmpexch (nullptr, scratch)) + { + scratch->~hb_colr_scratch_t (); + hb_free (scratch); + } + } + + public: hb_blob_ptr_t colr; + private: + mutable hb_atomic_t cached_scratch; }; void closure_glyphs (hb_codepoint_t glyph, @@ -2520,7 +2618,10 @@ struct COLR #ifndef HB_NO_PAINT bool - get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + hb_colr_scratch_t &scratch) const { ItemVarStoreInstancer instancer (get_var_store_ptr (), @@ -2534,10 +2635,10 @@ struct COLR } auto *extents_funcs = hb_paint_extents_get_funcs (); - hb_paint_extents_context_t extents_data; - bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0)); + scratch.paint_extents.clear (); + bool ret = paint_glyph (font, glyph, extents_funcs, &scratch.paint_extents, 0, HB_COLOR(0,0,0,0), true, scratch); - hb_extents_t e = extents_data.get_extents (); + auto e = scratch.paint_extents.get_extents (); if (e.is_void ()) { extents->x_bearing = 0; @@ -2583,7 +2684,12 @@ struct COLR #ifndef HB_NO_PAINT bool - paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const + paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *funcs, void *data, + unsigned int palette_index, hb_color_t foreground, + bool clip, + hb_colr_scratch_t &scratch) const { ItemVarStoreInstancer instancer (get_var_store_ptr (), get_delta_set_index_map_ptr (), @@ -2617,26 +2723,26 @@ struct COLR } else { - auto *extents_funcs = hb_paint_extents_get_funcs (); - hb_paint_extents_context_t extents_data; + clip = false; + is_bounded = false; + } + + if (!is_bounded) + { + auto *bounded_funcs = hb_paint_bounded_get_funcs (); + scratch.paint_bounded.clear (); paint_glyph (font, glyph, - extents_funcs, &extents_data, + bounded_funcs, &scratch.paint_bounded, palette_index, foreground, - false); + false, + scratch); - hb_extents_t extents = extents_data.get_extents (); - is_bounded = extents_data.is_bounded (); - - c.funcs->push_clip_rectangle (c.data, - extents.xmin, - extents.ymin, - extents.xmax, - extents.ymax); + is_bounded = scratch.paint_bounded.is_bounded (); } } - c.funcs->push_root_transform (c.data, font); + c.funcs->push_font_transform (c.data, font); if (is_bounded) c.recurse (*paint); @@ -2714,9 +2820,7 @@ void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const return; const Paint &paint = paint_offset_lists.get_paint (i); - c->funcs->push_group (c->data); c->recurse (paint); - c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); } } @@ -2728,7 +2832,7 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const if (unlikely (!node.visit (gid))) return; - c->funcs->push_inverse_root_transform (c->data, c->font); + c->funcs->push_inverse_font_transform (c->data, c->font); if (c->funcs->color_glyph (c->data, gid, c->font)) { c->funcs->pop_transform (c->data); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh index e95d8c13a44..b77486a4340 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh @@ -237,27 +237,28 @@ struct sbix int x_offset = 0, y_offset = 0; unsigned int strike_ppem = 0; - hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); hb_glyph_extents_t extents; hb_glyph_extents_t pixel_extents; - if (blob == hb_blob_get_empty ()) - return false; - - if (!hb_font_get_glyph_extents (font, glyph, &extents)) + if (!font->get_glyph_extents (glyph, &extents, false)) return false; if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) return false; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + if (hb_blob_is_immutable (blob)) + return false; + bool ret = funcs->image (data, blob, pixel_extents.width, -pixel_extents.height, HB_PAINT_IMAGE_FORMAT_PNG, - font->slant_xy, + 0.f, &extents); hb_blob_destroy (blob); + return ret; } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh index 9f676f9689e..9249abf7075 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh @@ -104,15 +104,16 @@ struct SVG if (blob == hb_blob_get_empty ()) return false; - funcs->image (data, - blob, - 0, 0, - HB_PAINT_IMAGE_FORMAT_SVG, - font->slant_xy, - nullptr); + bool ret = funcs->image (data, + blob, + 0, 0, + HB_PAINT_IMAGE_FORMAT_SVG, + 0.f, + nullptr); hb_blob_destroy (blob); - return true; + + return ret; } private: diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh index 4a925763bd7..97ea51089ec 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh @@ -77,7 +77,7 @@ struct CoverageFormat1_3 bool intersects (const hb_set_t *glyphs) const { - if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2) + if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len)) { for (auto g : *glyphs) if (get_coverage (g) != NOT_COVERED) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh index 247b7274b14..0c9bd09cbf7 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh @@ -120,7 +120,7 @@ struct CoverageFormat2_4 bool intersects (const hb_set_t *glyphs) const { - if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len)) { for (auto g : *glyphs) if (get_coverage (g) != NOT_COVERED) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh index 542480d2dd0..ceba37b0619 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh @@ -205,20 +205,19 @@ struct CaretValueFormat3 unsigned varidx = (this+deviceTable).get_variation_index (); hb_pair_t *new_varidx_delta; - if (!c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta)) - return_trace (false); + if (c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta)) { + uint32_t new_varidx = hb_first (*new_varidx_delta); + int delta = hb_second (*new_varidx_delta); + if (delta != 0) + { + if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } - uint32_t new_varidx = hb_first (*new_varidx_delta); - int delta = hb_second (*new_varidx_delta); - if (delta != 0) - { - if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW)) - return_trace (false); + if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); } - if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) - return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); - if (!c->serializer->embed (deviceTable)) return_trace (false); @@ -1015,7 +1014,8 @@ struct GDEF hb_blob_ptr_t table; #ifndef HB_NO_GDEF_CACHE hb_vector_t mark_glyph_set_digests; - mutable hb_cache_t<21, 3, 8> glyph_props_cache; + mutable hb_cache_t<21, 3> glyph_props_cache; + static_assert (sizeof (glyph_props_cache) == 512, ""); #endif }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh index f4af98b25fd..ce3f74d8c3b 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh @@ -152,8 +152,11 @@ GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) for (unsigned i = 0; i < len; i++) propagate_attachment_offsets (pos, len, i, direction); - if (unlikely (font->slant)) + if (unlikely (font->slant_xy) && + HB_DIRECTION_IS_HORIZONTAL (direction)) { + /* Slanting shaping results is only supported for horizontal text, + * as it gets weird otherwise. */ for (unsigned i = 0; i < len; i++) if (unlikely (pos[i].y_offset)) pos[i].x_offset += roundf (font->slant_xy * pos[i].y_offset); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh index 597ff4c088a..2748882f527 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh @@ -54,7 +54,7 @@ struct PairPosFormat1_3 { auto &cov = this+coverage; - if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4) + if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len)) { for (hb_codepoint_t g : glyphs->iter()) { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh index dbbbec9eee0..2ea1b6bca32 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh @@ -21,6 +21,24 @@ namespace OT { #ifndef HB_NO_VAR_COMPOSITES +struct hb_varc_scratch_t +{ + hb_vector_t axisIndices; + hb_vector_t axisValues; + hb_glyf_scratch_t glyf_scratch; +}; + +struct hb_varc_context_t +{ + hb_font_t *font; + hb_draw_session_t *draw_session; + hb_extents_t *extents; + mutable hb_decycler_t decycler; + mutable signed edges_left; + mutable signed depth_left; + hb_varc_scratch_t &scratch; +}; + struct VarComponent { enum class flags_t : uint32_t @@ -44,41 +62,32 @@ struct VarComponent }; HB_INTERNAL hb_ubytes_t - get_path_at (hb_font_t *font, + get_path_at (const hb_varc_context_t &c, hb_codepoint_t parent_gid, - hb_draw_session_t &draw_session, hb_array_t coords, hb_transform_t transform, hb_ubytes_t record, - hb_decycler_t *decycler, - signed *edges_left, - signed depth_left, - hb_glyf_scratch_t &scratch, VarRegionList::cache_t *cache = nullptr) const; }; struct VarCompositeGlyph { static void - get_path_at (hb_font_t *font, - hb_codepoint_t glyph, - hb_draw_session_t &draw_session, + get_path_at (const hb_varc_context_t &c, + hb_codepoint_t gid, hb_array_t coords, hb_transform_t transform, hb_ubytes_t record, - hb_decycler_t *decycler, - signed *edges_left, - signed depth_left, - hb_glyf_scratch_t &scratch, - VarRegionList::cache_t *cache = nullptr) + VarRegionList::cache_t *cache) { while (record) { const VarComponent &comp = * (const VarComponent *) (record.arrayZ); - record = comp.get_path_at (font, glyph, - draw_session, coords, transform, + record = comp.get_path_at (c, + gid, + coords, transform, record, - decycler, edges_left, depth_left, scratch, cache); + cache); } } }; @@ -92,36 +101,47 @@ struct VARC static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C'); HB_INTERNAL bool - get_path_at (hb_font_t *font, - hb_codepoint_t glyph, - hb_draw_session_t &draw_session, + get_path_at (const hb_varc_context_t &c, + hb_codepoint_t gid, hb_array_t coords, - hb_transform_t transform, - hb_codepoint_t parent_glyph, - hb_decycler_t *decycler, - signed *edges_left, - signed depth_left, - hb_glyf_scratch_t &scratch) const; + hb_transform_t transform = HB_TRANSFORM_IDENTITY, + hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID, + VarRegionList::cache_t *parent_cache = nullptr) const; bool get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, - hb_glyf_scratch_t &scratch) const + hb_varc_scratch_t &scratch) const { - hb_decycler_t decycler; - signed edges = HB_MAX_GRAPH_EDGE_COUNT; + hb_varc_context_t c {font, + &draw_session, + nullptr, + hb_decycler_t {}, + HB_MAX_GRAPH_EDGE_COUNT, + HB_MAX_NESTING_LEVEL, + scratch}; - return get_path_at (font, - gid, - draw_session, - hb_array (font->coords, font->num_coords), - HB_TRANSFORM_IDENTITY, - HB_CODEPOINT_INVALID, - &decycler, - &edges, - HB_MAX_NESTING_LEVEL, - scratch); + return get_path_at (c, gid, + hb_array (font->coords, font->num_coords)); + } + + bool + get_extents (hb_font_t *font, + hb_codepoint_t gid, + hb_extents_t *extents, + hb_varc_scratch_t &scratch) const + { + hb_varc_context_t c {font, + nullptr, + extents, + hb_decycler_t {}, + HB_MAX_GRAPH_EDGE_COUNT, + HB_MAX_NESTING_LEVEL, + scratch}; + + return get_path_at (c, gid, + hb_array (font->coords, font->num_coords)); } bool sanitize (hb_sanitize_context_t *c) const @@ -150,7 +170,7 @@ struct VARC auto *scratch = cached_scratch.get_relaxed (); if (scratch) { - scratch->~hb_glyf_scratch_t (); + scratch->~hb_varc_scratch_t (); hb_free (scratch); } @@ -162,34 +182,60 @@ struct VARC { if (!table->has_data ()) return false; - hb_glyf_scratch_t *scratch; - - // Borrow the cached strach buffer. - { - scratch = cached_scratch.get_acquire (); - if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr))) - { - scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t)); - if (unlikely (!scratch)) - return true; - } - } - + auto *scratch = acquire_scratch (); + if (unlikely (!scratch)) return true; bool ret = table->get_path (font, gid, draw_session, *scratch); + release_scratch (scratch); + return ret; + } - // Put it back. - if (!cached_scratch.cmpexch (nullptr, scratch)) - { - scratch->~hb_glyf_scratch_t (); - hb_free (scratch); - } + bool + get_extents (hb_font_t *font, + hb_codepoint_t gid, + hb_glyph_extents_t *extents) const + { + if (!table->has_data ()) return false; + + hb_extents_t f_extents; + + auto *scratch = acquire_scratch (); + if (unlikely (!scratch)) return true; + bool ret = table->get_extents (font, gid, &f_extents, *scratch); + release_scratch (scratch); + + if (ret) + *extents = f_extents.to_glyph_extents (font->x_scale < 0, font->y_scale < 0); return ret; } + private: + + hb_varc_scratch_t *acquire_scratch () const + { + hb_varc_scratch_t *scratch = cached_scratch.get_acquire (); + + if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr))) + { + scratch = (hb_varc_scratch_t *) hb_calloc (1, sizeof (hb_varc_scratch_t)); + if (unlikely (!scratch)) + return nullptr; + } + + return scratch; + } + void release_scratch (hb_varc_scratch_t *scratch) const + { + if (!cached_scratch.cmpexch (nullptr, scratch)) + { + scratch->~hb_varc_scratch_t (); + hb_free (scratch); + } + } + private: hb_blob_ptr_t table; - hb_atomic_ptr_t cached_scratch; + mutable hb_atomic_t cached_scratch; }; bool has_data () const { return version.major != 0; } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh index 681c3b9a007..d9e5fedfa92 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh @@ -429,16 +429,27 @@ struct glyf_accelerator_t } public: - bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + + bool get_extents (hb_font_t *font, + hb_codepoint_t gid, + hb_glyph_extents_t *extents) const + { return get_extents_at (font, gid, extents, hb_array (font->coords, font->num_coords)); } + + bool get_extents_at (hb_font_t *font, + hb_codepoint_t gid, + hb_glyph_extents_t *extents, + hb_array_t coords) const { if (unlikely (gid >= num_glyphs)) return false; #ifndef HB_NO_VAR - if (font->num_coords) + if (coords) { hb_glyf_scratch_t scratch; - return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true), - hb_array (font->coords, font->num_coords), + return get_points (font, + gid, + points_aggregator_t (font, extents, nullptr, true), + coords, scratch); } #endif @@ -532,7 +543,7 @@ struct glyf_accelerator_t unsigned int num_glyphs; hb_blob_ptr_t loca_table; hb_blob_ptr_t glyf_table; - hb_atomic_ptr_t cached_scratch; + mutable hb_atomic_t cached_scratch; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh index 673c7fbe945..d5a44bf473c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh @@ -29,6 +29,8 @@ #include "hb-aat-layout.hh" #include "hb-aat-map.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-layout-gdef-table.hh" #include "hb-open-type.hh" #include "hb-cache.hh" #include "hb-bit-set.hh" @@ -48,6 +50,61 @@ struct ankr; using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>; static_assert (sizeof (hb_aat_class_cache_t) == 256, ""); +struct hb_aat_scratch_t +{ + hb_aat_scratch_t () = default; + hb_aat_scratch_t (const hb_aat_scratch_t &) = delete; + + hb_aat_scratch_t (hb_aat_scratch_t &&o) + { + buffer_glyph_set.set_relaxed (o.buffer_glyph_set.get_relaxed ()); + o.buffer_glyph_set.set_relaxed (nullptr); + } + hb_aat_scratch_t & operator = (hb_aat_scratch_t &&o) + { + buffer_glyph_set.set_relaxed (o.buffer_glyph_set.get_relaxed ()); + o.buffer_glyph_set.set_relaxed (nullptr); + return *this; + } + ~hb_aat_scratch_t () + { + auto *s = buffer_glyph_set.get_relaxed (); + if (unlikely (!s)) + return; + s->fini (); + hb_free (s); + } + + hb_bit_set_t *create_buffer_glyph_set () const + { + hb_bit_set_t *s = buffer_glyph_set.get_acquire (); + if (s && buffer_glyph_set.cmpexch (s, nullptr)) + return s; + + s = (hb_bit_set_t *) hb_calloc (1, sizeof (hb_bit_set_t)); + if (unlikely (!s)) + return nullptr; + s->init (); + + return s; + } + void destroy_buffer_glyph_set (hb_bit_set_t *s) const + { + if (unlikely (!s)) + return; + if (buffer_glyph_set.cmpexch (nullptr, s)) + return; + s->fini (); + hb_free (s); + } + + mutable hb_atomic_t buffer_glyph_set; +}; + +enum { DELETED_GLYPH = 0xFFFF }; + +#define HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED HB_BUFFER_SCRATCH_FLAG_SHAPER0 + struct hb_aat_apply_context_t : hb_dispatch_context_t { @@ -64,10 +121,11 @@ struct hb_aat_apply_context_t : hb_buffer_t *buffer; hb_sanitize_context_t sanitizer; const ankr *ankr_table; - const OT::GDEF *gdef_table; + const OT::GDEF &gdef; + bool has_glyph_classes; const hb_sorted_vector_t *range_flags = nullptr; bool using_buffer_glyph_set = false; - hb_bit_set_t buffer_glyph_set; + hb_bit_set_t *buffer_glyph_set = nullptr; const hb_bit_set_t *left_set = nullptr; const hb_bit_set_t *right_set = nullptr; const hb_bit_set_t *machine_glyph_set = nullptr; @@ -90,15 +148,15 @@ struct hb_aat_apply_context_t : void setup_buffer_glyph_set () { - using_buffer_glyph_set = buffer->len >= 4; + using_buffer_glyph_set = buffer->len >= 4 && buffer_glyph_set; - if (using_buffer_glyph_set) - buffer->collect_codepoints (buffer_glyph_set); + if (likely (using_buffer_glyph_set)) + buffer->collect_codepoints (*buffer_glyph_set); } bool buffer_intersects_machine () const { - if (using_buffer_glyph_set) - return buffer_glyph_set.intersects (*machine_glyph_set); + if (likely (using_buffer_glyph_set)) + return buffer_glyph_set->intersects (*machine_glyph_set); // Faster for shorter buffers. for (unsigned i = 0; i < buffer->len; i++) @@ -106,6 +164,69 @@ struct hb_aat_apply_context_t : return true; return false; } + + template + HB_NODISCARD bool output_glyphs (unsigned int count, + const T *glyphs) + { + if (likely (using_buffer_glyph_set)) + buffer_glyph_set->add_array (glyphs, count); + for (unsigned int i = 0; i < count; i++) + { + if (glyphs[i] == DELETED_GLYPH) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED; + _hb_glyph_info_set_aat_deleted (&buffer->cur()); + } + else + { +#ifndef HB_NO_OT_LAYOUT + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->cur(), + gdef.get_glyph_props (glyphs[i])); +#endif + } + if (unlikely (!buffer->output_glyph (glyphs[i]))) return false; + } + return true; + } + + HB_NODISCARD bool replace_glyph (hb_codepoint_t glyph) + { + if (glyph == DELETED_GLYPH) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED; + _hb_glyph_info_set_aat_deleted (&buffer->cur()); + } + + if (likely (using_buffer_glyph_set)) + buffer_glyph_set->add (glyph); +#ifndef HB_NO_OT_LAYOUT + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->cur(), + gdef.get_glyph_props (glyph)); +#endif + return buffer->replace_glyph (glyph); + } + + HB_NODISCARD bool delete_glyph () + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED; + _hb_glyph_info_set_aat_deleted (&buffer->cur()); + return buffer->replace_glyph (DELETED_GLYPH); + } + + void replace_glyph_inplace (unsigned i, hb_codepoint_t glyph) + { + buffer->info[i].codepoint = glyph; + if (likely (using_buffer_glyph_set)) + buffer_glyph_set->add (glyph); +#ifndef HB_NO_OT_LAYOUT + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->info[i], + gdef.get_glyph_props (glyph)); +#endif + } }; @@ -113,8 +234,6 @@ struct hb_aat_apply_context_t : * Lookup Table */ -enum { DELETED_GLYPH = 0xFFFF }; - template struct Lookup; template @@ -179,6 +298,7 @@ struct LookupSegmentSingle template void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const { + if (first == DELETED_GLYPH) return; if (!filter (value)) return; glyphs.add_range (first, last); } @@ -268,6 +388,7 @@ struct LookupSegmentArray template void collect_glyphs_filtered (set_t &glyphs, const void *base, const filter_t &filter) const { + if (first == DELETED_GLYPH) return; const auto &values = base+valuesZ; for (hb_codepoint_t i = first; i <= last; i++) if (filter (values[i - first])) @@ -368,6 +489,7 @@ struct LookupSingle template void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const { + if (glyph == DELETED_GLYPH) return; if (!filter (value)) return; glyphs.add (glyph); } @@ -746,6 +868,10 @@ struct StateTable } // And glyphs in those classes. + + if (filter (CLASS_DELETED_GLYPH)) + glyphs.add (DELETED_GLYPH); + (this+classTable).collect_glyphs_filtered (glyphs, num_glyphs, filter); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh index a091b05660c..ca4f2243346 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh @@ -185,6 +185,9 @@ struct Format1Entry DEFINE_SIZE_STATIC (2); }; + static bool initiateAction (const Entry &entry) + { return entry.flags & Push; } + static bool performAction (const Entry &entry) { return entry.data.kernActionIndex != 0xFFFF; } @@ -325,8 +328,9 @@ struct KerxSubTableFormat1 } else if (buffer->info[idx].mask & kern_mask) { - o.x_advance += c->font->em_scale_x (v); - o.x_offset += c->font->em_scale_x (v); + auto scaled = c->font->em_scale_x (v); + o.x_advance += scaled; + o.x_offset += scaled; } } else @@ -394,10 +398,8 @@ struct KerxSubTableFormat1 template void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const { - set_t set; - machine.collect_glyphs (set, num_glyphs); - left_set.union_ (set); - right_set.union_ (set); + machine.collect_initial_glyphs (left_set, num_glyphs, *this); + //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning } protected: @@ -671,10 +673,8 @@ struct KerxSubTableFormat4 template void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const { - set_t set; - machine.collect_glyphs (set, num_glyphs); - left_set.union_ (set); - right_set.union_ (set); + machine.collect_initial_glyphs (left_set, num_glyphs, *this); + //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning } protected: @@ -921,7 +921,18 @@ struct KerxSubTable * The 'kerx' Table */ -using kern_accelerator_data_t = hb_vector_t>; +struct kern_subtable_accelerator_data_t +{ + hb_bit_set_t left_set; + hb_bit_set_t right_set; + mutable hb_aat_class_cache_t class_cache; +}; + +struct kern_accelerator_data_t +{ + hb_vector_t subtable_accels; + hb_aat_scratch_t scratch; +}; template struct KerxTable @@ -985,6 +996,8 @@ struct KerxTable { c->buffer->unsafe_to_concat (); + c->setup_buffer_glyph_set (); + typedef typename T::SubTable SubTable; bool ret = false; @@ -996,12 +1009,25 @@ struct KerxTable { bool reverse; + auto &subtable_accel = accel_data.subtable_accels[i]; + if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation)) goto skip; if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ()) goto skip; + c->left_set = &subtable_accel.left_set; + c->right_set = &subtable_accel.right_set; + c->machine_glyph_set = &subtable_accel.left_set; + c->machine_class_cache = &subtable_accel.class_cache; + + if (!c->buffer_intersects_machine ()) + { + (void) c->buffer->message (c->font, "skipped subtable %u because no glyph matches", c->lookup_index); + goto skip; + } + reverse = bool (st->u.header.coverage & st->u.header.Backwards) != HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); @@ -1028,9 +1054,6 @@ struct KerxTable if (reverse) c->buffer->reverse (); - c->left_set = &accel_data[i].first; - c->right_set = &accel_data[i].second; - { /* See comment in sanitize() for conditional here. */ hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr); @@ -1106,9 +1129,13 @@ struct KerxTable unsigned int count = thiz()->tableCount; for (unsigned int i = 0; i < count; i++) { - hb_bit_set_t left_set, right_set; - st->collect_glyphs (left_set, right_set, num_glyphs); - accel_data.push (hb_pair (left_set, right_set)); + auto &subtable_accel = *accel_data.subtable_accels.push (); + if (unlikely (accel_data.subtable_accels.in_error ())) + return accel_data; + + st->collect_glyphs (subtable_accel.left_set, subtable_accel.right_set, num_glyphs); + subtable_accel.class_cache.clear (); + st = &StructAfter (*st); } @@ -1137,6 +1164,7 @@ struct KerxTable hb_blob_ptr_t table; kern_accelerator_data_t accel_data; + hb_aat_scratch_t scratch; }; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh index cabdc0eb555..92b07eec6e5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh @@ -30,8 +30,6 @@ #include "hb-open-type.hh" #include "hb-aat-layout-common.hh" #include "hb-ot-layout.hh" -#include "hb-ot-layout-common.hh" -#include "hb-ot-layout-gdef-table.hh" #include "hb-aat-map.hh" /* @@ -178,12 +176,6 @@ struct RearrangementSubtable StateTableDriver driver (machine, c->face); - if (!c->buffer_intersects_machine ()) - { - (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches"); - return_trace (false); - } - driver.drive (&dc, c); return_trace (dc.ret); @@ -242,9 +234,7 @@ struct ContextualSubtable ret (false), c (c_), table (table_), - gdef (*c->gdef_table), mark_set (false), - has_glyph_classes (gdef.has_glyph_classes ()), mark (0), subs (table+table->substitutionTables) {} @@ -281,12 +271,7 @@ struct ContextualSubtable if (replacement) { buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); - hb_codepoint_t glyph = *replacement; - buffer->info[mark].codepoint = glyph; - c->buffer_glyph_set.add (glyph); - if (has_glyph_classes) - _hb_glyph_info_set_glyph_props (&buffer->info[mark], - gdef.get_glyph_props (*replacement)); + c->replace_glyph_inplace (mark, *replacement); ret = true; } @@ -312,12 +297,7 @@ struct ContextualSubtable } if (replacement) { - hb_codepoint_t glyph = *replacement; - buffer->info[idx].codepoint = glyph; - c->buffer_glyph_set.add (glyph); - if (has_glyph_classes) - _hb_glyph_info_set_glyph_props (&buffer->info[idx], - gdef.get_glyph_props (*replacement)); + c->replace_glyph_inplace (idx, *replacement); ret = true; } @@ -333,9 +313,7 @@ struct ContextualSubtable hb_aat_apply_context_t *c; const ContextualSubtable *table; private: - const OT::GDEF &gdef; bool mark_set; - bool has_glyph_classes; unsigned int mark; const UnsizedListOfOffset16To, HBUINT, void, false> &subs; }; @@ -348,12 +326,6 @@ struct ContextualSubtable StateTableDriver driver (machine, c->face); - if (!c->buffer_intersects_machine ()) - { - (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches"); - return_trace (false); - } - driver.drive (&dc, c); return_trace (dc.ret); @@ -581,7 +553,7 @@ struct LigatureSubtable hb_codepoint_t lig = ligatureData; DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); - if (unlikely (!buffer->replace_glyph (lig))) return; + if (unlikely (!c->replace_glyph (lig))) return; unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; /* Now go and delete all subsequent components. */ @@ -589,8 +561,7 @@ struct LigatureSubtable { DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; - _hb_glyph_info_set_default_ignorable (&buffer->cur()); - if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; + if (!c->delete_glyph ()) return; } if (unlikely (!buffer->move_to (lig_end))) return; @@ -624,12 +595,6 @@ struct LigatureSubtable StateTableDriver driver (machine, c->face); - if (!c->buffer_intersects_machine ()) - { - (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches"); - return_trace (false); - } - driver.drive (&dc, c); return_trace (dc.ret); @@ -665,15 +630,6 @@ struct NoncontextualSubtable { TRACE_APPLY (this); - if (!c->buffer_intersects_machine ()) - { - (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches"); - return_trace (false); - } - - const OT::GDEF &gdef (*c->gdef_table); - bool has_glyph_classes = gdef.has_glyph_classes (); - bool ret = false; unsigned int num_glyphs = c->face->get_num_glyphs (); @@ -703,12 +659,7 @@ struct NoncontextualSubtable const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); if (replacement) { - hb_codepoint_t glyph = *replacement; - info[i].codepoint = glyph; - c->buffer_glyph_set.add (glyph); - if (has_glyph_classes) - _hb_glyph_info_set_glyph_props (&info[i], - gdef.get_glyph_props (*replacement)); + c->replace_glyph_inplace (i, *replacement); ret = true; } } @@ -850,9 +801,7 @@ struct InsertionSubtable if (buffer->idx < buffer->len && !before) if (unlikely (!buffer->copy_glyph ())) return; /* TODO We ignore KashidaLike setting. */ - if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; - for (unsigned int i = 0; i < count; i++) - c->buffer_glyph_set.add (glyphs[i]); + if (unlikely (!c->output_glyphs (count, glyphs))) return; ret = true; if (buffer->idx < buffer->len && !before) buffer->skip_glyph (); @@ -881,7 +830,8 @@ struct InsertionSubtable if (buffer->idx < buffer->len && !before) if (unlikely (!buffer->copy_glyph ())) return; /* TODO We ignore KashidaLike setting. */ - if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; + if (unlikely (!c->output_glyphs (count, glyphs))) return; + ret = true; if (buffer->idx < buffer->len && !before) buffer->skip_glyph (); @@ -921,12 +871,6 @@ struct InsertionSubtable StateTableDriver driver (machine, c->face); - if (!c->buffer_intersects_machine ()) - { - (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches"); - return_trace (false); - } - driver.drive (&dc, c); return_trace (dc.ret); @@ -1224,6 +1168,7 @@ struct Chain if (hb_none (hb_iter (c->range_flags) | hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); }))) goto skip; + c->subtable_flags = subtable_flags; c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t); c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr; @@ -1233,6 +1178,12 @@ struct Chain bool (coverage & ChainSubtable::Vertical)) goto skip; + if (!c->buffer_intersects_machine ()) + { + (void) c->buffer->message (c->font, "skipped chainsubtable %u because no glyph matches", c->lookup_index); + goto skip; + } + /* Buffer contents is always in logical direction. Determine if * we need to reverse before applying this subtable. We reverse * back after if we did reverse indeed. @@ -1376,7 +1327,7 @@ struct mortmorx this->chain_count = table->get_chain_count (); - this->accels = (hb_atomic_ptr_t *) hb_calloc (this->chain_count, sizeof (*accels)); + this->accels = (hb_atomic_t *) hb_calloc (this->chain_count, sizeof (*accels)); if (unlikely (!this->accels)) { this->chain_count = 0; @@ -1423,7 +1374,8 @@ struct mortmorx hb_blob_ptr_t table; unsigned int chain_count; - hb_atomic_ptr_t *accels; + hb_atomic_t *accels; + hb_aat_scratch_t scratch; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh index 0db4035fd8c..4ee73ecee69 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh @@ -31,6 +31,7 @@ #include "hb-aat-layout-common.hh" #include "hb-ot-layout.hh" #include "hb-open-type.hh" +#include "hb-ot-stat-table.hh" /* * trak -- Tracking @@ -115,7 +116,7 @@ struct TrackTableEntry protected: F16DOT16 track; /* Track value for this record. */ - NameID trackNameID; /* The 'name' table index for this track. + OT::NameID trackNameID; /* The 'name' table index for this track. * (a short word or phrase like "loose" * or "very tight") */ NNOffset16To> @@ -142,9 +143,9 @@ struct TrackData unsigned j = count - 1; // Find the two entries that track is between. - while (i + 1 < count && trackTable[i + 1].get_track_value () < track) + while (i + 1 < count && trackTable[i + 1].get_track_value () <= track) i++; - while (j > 0 && trackTable[j - 1].get_track_value () > track) + while (j > 0 && trackTable[j - 1].get_track_value () >= track) j--; // Exact match. @@ -200,6 +201,46 @@ struct trak float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE; return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track)); } + hb_position_t get_tracking (hb_font_t *font, hb_direction_t dir, float track = 0.f) const + { +#ifndef HB_NO_STYLE + if (!font->face->table.STAT->has_data ()) + return 0; + return HB_DIRECTION_IS_HORIZONTAL (dir) ? + get_h_tracking (font, track) : + get_v_tracking (font, track); +#else + return 0; +#endif + } + + bool apply (hb_aat_apply_context_t *c, float track = 0.f) const + { + TRACE_APPLY (this); + + float ptem = c->font->ptem; + if (unlikely (ptem <= 0.f)) + { + /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ + ptem = HB_CORETEXT_DEFAULT_FONT_SIZE; + } + + hb_buffer_t *buffer = c->buffer; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + hb_position_t advance_to_add = get_h_tracking (c->font, track); + foreach_grapheme (buffer, start, end) + buffer->pos[start].x_advance += advance_to_add; + } + else + { + hb_position_t advance_to_add = get_v_tracking (c->font, track); + foreach_grapheme (buffer, start, end) + buffer->pos[start].y_advance += advance_to_add; + } + + return_trace (true); + } bool sanitize (hb_sanitize_context_t *c) const { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc index 2cc94257aa7..b349c3026b6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc @@ -34,7 +34,7 @@ #include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise. #include "hb-aat-layout-kerx-table.hh" #include "hb-aat-layout-morx-table.hh" -#include "hb-aat-layout-trak-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-trak-table.hh" #include "hb-aat-ltag-table.hh" #include "hb-ot-layout-gsub-table.hh" @@ -58,13 +58,14 @@ AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *p buffer (buffer_), sanitizer (), ankr_table (&Null (AAT::ankr)), - gdef_table ( + gdef ( #ifndef HB_NO_OT_LAYOUT - face->table.GDEF->table + *face->table.GDEF->table #else - &Null (GDEF) + Null (GDEF) #endif ), + has_glyph_classes (gdef.has_glyph_classes ()), lookup_index (0) { sanitizer.init (blob); @@ -203,7 +204,7 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag) #endif -#ifndef HB_NO_AAT +#ifndef HB_NO_AAT_SHAPE /* * mort/morx/kerx/trak @@ -287,11 +288,14 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, const hb_feature_t *features, unsigned num_features) { - hb_aat_map_builder_t builder (font->face, plan->props); - for (unsigned i = 0; i < num_features; i++) - builder.add_feature (features[i]); hb_aat_map_t map; - builder.compile (map); + if (num_features) + { + hb_aat_map_builder_t builder (font->face, plan->props); + for (unsigned i = 0; i < num_features; i++) + builder.add_feature (features[i]); + builder.compile (map); + } { auto &accel = *font->face->table.morx; @@ -300,7 +304,10 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, { AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ()); if (!buffer->message (font, "start table morx")) return; - morx.apply (&c, map, accel); + c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set (); + morx.apply (&c, num_features ? map : plan->aat_map, accel); + accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set); + c.buffer_glyph_set = nullptr; (void) buffer->message (font, "end table morx"); return; } @@ -313,34 +320,24 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, { AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ()); if (!buffer->message (font, "start table mort")) return; - mort.apply (&c, map, accel); + mort.apply (&c, num_features ? map : plan->aat_map, accel); (void) buffer->message (font, "end table mort"); return; } } } -void -hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer) -{ - unsigned int count = buffer->len; - hb_glyph_info_t *info = buffer->info; - hb_glyph_position_t *pos = buffer->pos; - for (unsigned int i = 0; i < count; i++) - if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH)) - pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; -} - static bool is_deleted_glyph (const hb_glyph_info_t *info) { - return info->codepoint == AAT::DELETED_GLYPH; + return _hb_glyph_info_is_aat_deleted (info); } void hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) { - buffer->delete_glyphs_inplace (is_deleted_glyph); + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED) + buffer->delete_glyphs_inplace (is_deleted_glyph); } /** @@ -371,8 +368,11 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan, AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ()); if (!buffer->message (font, "start table kerx")) return; + c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set (); c.set_ankr_table (font->face->table.ankr.get ()); accel.apply (&c); + accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set); + c.buffer_glyph_set = nullptr; (void) buffer->message (font, "end table kerx"); } @@ -394,6 +394,17 @@ hb_aat_layout_has_tracking (hb_face_t *face) return face->table.trak->has_data (); } +void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const AAT::trak& trak = *font->face->table.trak; + + AAT::hb_aat_apply_context_t c (plan, font, buffer); + trak.apply (&c); +} + /** * hb_aat_layout_get_feature_types: * @face: #hb_face_t to work upon diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh index ee33223651a..ffa54408ebf 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh @@ -60,9 +60,6 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, const hb_feature_t *features, unsigned num_features); -HB_INTERNAL void -hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer); - HB_INTERNAL void hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer); @@ -71,5 +68,10 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); +HB_INTERNAL void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + #endif /* HB_AAT_LAYOUT_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc index e0e9d14d9b8..306a95b7c4b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc @@ -85,6 +85,11 @@ void hb_aat_map_builder_t::compile (hb_aat_map_t &m) { /* Compute active features per range, and compile each. */ + if (!features.length) + { + hb_aat_layout_compile_map (this, &m); + return; + } /* Sort features by start/end events. */ hb_vector_t feature_events; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh index d2de5f693f0..80bd90e87dc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh @@ -80,15 +80,14 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) #include -#define _hb_memory_barrier() std::atomic_thread_fence(std::memory_order_ack_rel) #define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) #define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) -#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) -#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed)) -#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release)) -#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed)) -#define hb_atomic_int_impl_get(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire)) +#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) +#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire)) #define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) #define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) @@ -149,68 +148,53 @@ static inline void _hb_compiler_memory_r_barrier () {} #define hb_atomic_ptr_impl_get_relaxed(P) (*(P)) #endif #ifndef hb_atomic_int_impl_set -inline void hb_atomic_int_impl_set (int *AI, int v) { _hb_memory_w_barrier (); *AI = v; } -inline void hb_atomic_int_impl_set (short *AI, short v) { _hb_memory_w_barrier (); *AI = v; } +template +inline void hb_atomic_int_impl_set (T *AI, T v) { _hb_memory_w_barrier (); *AI = v; } #endif #ifndef hb_atomic_int_impl_get -inline int hb_atomic_int_impl_get (const int *AI) { int v = *AI; _hb_memory_r_barrier (); return v; } -inline short hb_atomic_int_impl_get (const short *AI) { short v = *AI; _hb_memory_r_barrier (); return v; } +template +inline T hb_atomic_int_impl_get (const T *AI) { T v = *AI; _hb_memory_r_barrier (); return v; } #endif #ifndef hb_atomic_ptr_impl_get inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } #endif -struct hb_atomic_short_t +template +struct hb_atomic_t { - hb_atomic_short_t () = default; - constexpr hb_atomic_short_t (short v) : v (v) {} + hb_atomic_t () = default; + constexpr hb_atomic_t (T v) : v (v) {} - hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; } - operator short () const { return get_relaxed (); } + hb_atomic_t& operator = (T v_) { set_relaxed (v_); return *this; } + operator T () const { return get_relaxed (); } - void set_relaxed (short v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } - void set_release (short v_) { hb_atomic_int_impl_set (&v, v_); } - short get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } - short get_acquire () const { return hb_atomic_int_impl_get (&v); } - short inc () { return hb_atomic_int_impl_add (&v, 1); } - short dec () { return hb_atomic_int_impl_add (&v, -1); } + void set_relaxed (T v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set_release (T v_) { hb_atomic_int_impl_set (&v, v_); } + T get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + T get_acquire () const { return hb_atomic_int_impl_get (&v); } + T inc () { return hb_atomic_int_impl_add (&v, 1); } + T dec () { return hb_atomic_int_impl_add (&v, -1); } - short v = 0; + int operator ++ (int) { return inc (); } + int operator -- (int) { return dec (); } + long operator |= (long v_) { set_relaxed (get_relaxed () | v_); return *this; } + + T v = 0; }; -struct hb_atomic_int_t +template +struct hb_atomic_t { - hb_atomic_int_t () = default; - constexpr hb_atomic_int_t (int v) : v (v) {} - - hb_atomic_int_t& operator = (int v_) { set_relaxed (v_); return *this; } - operator int () const { return get_relaxed (); } - - void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } - void set_release (int v_) { hb_atomic_int_impl_set (&v, v_); } - int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } - int get_acquire () const { return hb_atomic_int_impl_get (&v); } - int inc () { return hb_atomic_int_impl_add (&v, 1); } - int dec () { return hb_atomic_int_impl_add (&v, -1); } - - int v = 0; -}; - -template -struct hb_atomic_ptr_t -{ - typedef hb_remove_pointer

T; - - hb_atomic_ptr_t () = default; - constexpr hb_atomic_ptr_t (T* v) : v (v) {} - hb_atomic_ptr_t (const hb_atomic_ptr_t &other) = delete; + hb_atomic_t () = default; + constexpr hb_atomic_t (T* v) : v (v) {} + hb_atomic_t (const hb_atomic_t &other) = delete; void init (T* v_ = nullptr) { set_relaxed (v_); } void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } - bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + bool cmpexch (const T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } operator bool () const { return get_acquire () != nullptr; } T * operator -> () const { return get_acquire (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh index c42e617f67a..799c3607599 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh @@ -77,7 +77,7 @@ struct hb_bit_set_t bool successful = true; /* Allocations successful */ mutable unsigned int population = 0; - mutable hb_atomic_int_t last_page_lookup = 0; + mutable hb_atomic_t last_page_lookup = 0; hb_sorted_vector_t page_map; hb_vector_t pages; @@ -88,7 +88,7 @@ struct hb_bit_set_t { if (unlikely (!successful)) return false; - if (pages.length < count && count <= 2) + if (pages.length < count && (unsigned) pages.allocated < count && count <= 2) exact_size = true; // Most sets are small and local if (unlikely (!pages.resize (count, clear, exact_size) || diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh new file mode 100644 index 00000000000..2a0ec3a0710 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh @@ -0,0 +1,195 @@ +/* + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_BIT_VECTOR_HH +#define HB_BIT_VECTOR_HH + +#include "hb.hh" + +#include "hb-atomic.hh" + +struct hb_min_max_t +{ + void add (hb_codepoint_t v) { min_v = hb_min (min_v, v); max_v = hb_max (max_v, v); } + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + min_v = hb_min (min_v, a); + max_v = hb_max (max_v, b); + } + + template + void union_ (const set_t &set) + { + hb_codepoint_t set_min = set.get_min (); + if (unlikely (set_min == HB_CODEPOINT_INVALID)) + return; + hb_codepoint_t set_max = set.get_max (); + min_v = hb_min (min_v, set_min); + max_v = hb_max (max_v, set_max); + } + + hb_codepoint_t get_min () const { return min_v; } + hb_codepoint_t get_max () const { return max_v; } + + private: + hb_codepoint_t min_v = HB_CODEPOINT_INVALID; + hb_codepoint_t max_v = 0; +}; + +template +struct hb_bit_vector_t +{ + using int_t = uint64_t; + using elt_t = typename std::conditional, int_t>::type; + + hb_bit_vector_t () = delete; + hb_bit_vector_t (const hb_bit_vector_t &other) = delete; + hb_bit_vector_t &operator= (const hb_bit_vector_t &other) = delete; + + // Move + hb_bit_vector_t (hb_bit_vector_t &&other) + : min_v (other.min_v), max_v (other.max_v), count (other.count), elts (other.elts) + { + other.min_v = other.max_v = other.count = 0; + other.elts = nullptr; + } + hb_bit_vector_t &operator= (hb_bit_vector_t &&other) + { + hb_swap (min_v, other.min_v); + hb_swap (max_v, other.max_v); + hb_swap (count, other.count); + hb_swap (elts, other.elts); + return *this; + } + + hb_bit_vector_t (unsigned min_v, unsigned max_v) + : min_v (min_v), max_v (max_v) + { + if (unlikely (min_v >= max_v)) + { + min_v = max_v = count = 0; + return; + } + + unsigned num = (max_v - min_v + sizeof (int_t) * 8) / (sizeof (int_t) * 8); + elts = (elt_t *) hb_calloc (num, sizeof (int_t)); + if (unlikely (!elts)) + { + min_v = max_v = count = 0; + return; + } + + count = max_v - min_v + 1; + } + ~hb_bit_vector_t () + { + hb_free (elts); + } + + void add (hb_codepoint_t g) { elt (g) |= mask (g); } + void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } + void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } + bool has (hb_codepoint_t g) const { return get (g); } + bool may_have (hb_codepoint_t g) const { return get (g); } + + bool operator [] (hb_codepoint_t g) const { return get (g); } + bool operator () (hb_codepoint_t g) const { return get (g); } + + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!count || a > b || a < min_v || b > max_v)) + return; + + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la |= (mask (b) << 1) - mask(a); + else + { + *la |= ~(mask (a) - 1llu); + la++; + + hb_memset (la, 0xff, (char *) lb - (char *) la); + + *lb |= ((mask (b) << 1) - 1llu); + } + } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!count || a > b || a < min_v || b > max_v)) + return; + + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1llu) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + hb_memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1llu); + } + } + void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v) + { if (v) add_range (a, b); else del_range (a, b); } + + template + void union_ (const set_t &set) + { + for (hb_codepoint_t g : set) + add (g); + } + + static const unsigned int ELT_BITS = sizeof (elt_t) * 8; + static constexpr unsigned ELT_MASK = ELT_BITS - 1; + + static constexpr elt_t zero = 0; + + elt_t &elt (hb_codepoint_t g) + { + g -= min_v; + if (unlikely (g >= count)) + return Crap(elt_t); + return elts[g / ELT_BITS]; + } + const elt_t& elt (hb_codepoint_t g) const + { + g -= min_v; + if (unlikely (g >= count)) + return Null(elt_t); + return elts[g / ELT_BITS]; + } + + static constexpr int_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); } + + hb_codepoint_t min_v = 0, max_v = 0, count = 0; + elt_t *elts = nullptr; +}; + + +#endif /* HB_BIT_VECTOR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh index 69cc1640230..255497b652d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh @@ -32,96 +32,83 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-json.hh" +#line 33 "hb-buffer-deserialize-json.hh" static const unsigned char _deserialize_json_trans_keys[] = { - 0u, 0u, 9u, 123u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, - 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, - 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, - 9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, - 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, - 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, - 9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, - 9u, 93u, 9u, 123u, 0u, 0u, 0 + 0u, 0u, 9u, 34u, 97u, 121u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, + 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, + 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, + 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 9u, 125u, + 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, + 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 98u, 98u, 9u, 123u, 9u, 123u, 9u, 123u, + 0 }; static const char _deserialize_json_key_spans[] = { - 0, 115, 115, 26, 21, 2, 1, 50, - 49, 10, 117, 117, 117, 1, 50, 49, - 10, 117, 117, 1, 1, 50, 49, 117, - 117, 2, 1, 50, 49, 10, 117, 117, - 1, 50, 49, 10, 117, 117, 1, 1, - 50, 49, 117, 117, 1, 50, 49, 59, - 117, 59, 117, 117, 1, 50, 49, 117, - 85, 115, 0 + 0, 26, 25, 2, 1, 50, 49, 10, + 117, 117, 85, 117, 1, 50, 49, 10, + 117, 117, 1, 1, 50, 49, 117, 117, + 2, 1, 50, 49, 10, 117, 117, 1, + 50, 49, 10, 117, 117, 1, 1, 50, + 49, 117, 117, 1, 50, 49, 59, 117, + 59, 117, 117, 1, 50, 49, 10, 117, + 1, 50, 49, 117, 1, 115, 115, 115 }; static const short _deserialize_json_index_offsets[] = { - 0, 0, 116, 232, 259, 281, 284, 286, - 337, 387, 398, 516, 634, 752, 754, 805, - 855, 866, 984, 1102, 1104, 1106, 1157, 1207, - 1325, 1443, 1446, 1448, 1499, 1549, 1560, 1678, - 1796, 1798, 1849, 1899, 1910, 2028, 2146, 2148, - 2150, 2201, 2251, 2369, 2487, 2489, 2540, 2590, - 2650, 2768, 2828, 2946, 3064, 3066, 3117, 3167, - 3285, 3371, 3487 + 0, 0, 27, 53, 56, 58, 109, 159, + 170, 288, 406, 492, 610, 612, 663, 713, + 724, 842, 960, 962, 964, 1015, 1065, 1183, + 1301, 1304, 1306, 1357, 1407, 1418, 1536, 1654, + 1656, 1707, 1757, 1768, 1886, 2004, 2006, 2008, + 2059, 2109, 2227, 2345, 2347, 2398, 2448, 2508, + 2626, 2686, 2804, 2922, 2924, 2975, 3025, 3036, + 3154, 3156, 3207, 3257, 3375, 3377, 3493, 3609 }; static const char _deserialize_json_indicies[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 1, 3, 1, 4, 5, + 1, 6, 7, 8, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9, 1, 8, 10, 10, 1, 11, 12, + 1, 13, 1, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 14, 1, 14, 14, + 14, 14, 14, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 14, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 1, 1, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 1, + 18, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 1, 20, 20, 20, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 3, 1, 2, 2, 2, - 2, 2, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 3, - 1, 4, 4, 4, 4, 4, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 4, 1, 5, 1, 6, 1, 7, 8, - 1, 9, 10, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 11, 1, 12, 13, 1, 14, 1, 14, - 14, 14, 14, 14, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 14, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 15, 1, 15, 15, 15, 15, 15, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 15, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 16, 1, - 1, 17, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 1, 19, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 1, 21, - 21, 21, 21, 21, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 21, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 22, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 22, + 1, 23, 23, 23, 23, 23, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 23, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -131,12 +118,52 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 23, 1, 24, 24, 24, - 24, 24, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 24, 1, 24, + 24, 24, 24, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 24, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 25, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 25, 1, 20, 20, 20, + 20, 20, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 20, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 21, 1, 1, 1, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 22, 1, 26, 1, 26, 26, 26, + 26, 26, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 26, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 27, 1, + 27, 27, 27, 27, 27, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 27, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 28, 1, 1, 29, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 1, 31, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 1, 33, 33, 33, + 33, 33, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 33, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -146,42 +173,13 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 25, 1, 21, 21, 21, 21, 21, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 21, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 22, 1, - 1, 1, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 23, - 1, 26, 1, 26, 26, 26, 26, 26, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 26, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 27, 1, 27, 27, - 27, 27, 27, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 27, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 28, 1, 1, 29, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 1, - 31, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 1, 33, 33, 33, 33, 33, + 1, 35, 1, 33, 33, 33, 33, 33, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 33, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 34, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -190,13 +188,24 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 35, - 1, 33, 33, 33, 33, 33, 1, 1, + 1, 36, 1, 37, 1, 37, 37, 37, + 37, 37, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 33, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 34, 1, 1, 1, - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 38, 1, + 38, 38, 38, 38, 38, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 38, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 40, 40, 40, 40, 40, 40, 40, 40, + 40, 1, 41, 41, 41, 41, 41, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 41, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 42, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -204,25 +213,43 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 35, 1, 36, - 1, 37, 1, 37, 37, 37, 37, 37, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 37, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 38, 1, 38, 38, - 38, 38, 38, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 38, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 39, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 1, + 1, 1, 1, 1, 1, 1, 43, 1, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 42, 1, 1, 1, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 43, 1, 45, 46, + 1, 47, 1, 47, 47, 47, 47, 47, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 48, 1, 48, 48, + 48, 48, 48, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 48, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 49, 1, 1, 50, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 1, + 52, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 1, 54, 54, 54, 54, 54, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 54, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -232,57 +259,41 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 43, 1, 41, 41, - 41, 41, 41, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 41, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 42, 1, 1, 1, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 43, 1, 45, 46, 1, 47, - 1, 47, 47, 47, 47, 47, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 47, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 48, 1, 48, 48, 48, 48, - 48, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 48, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 49, 1, 1, 50, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 1, 52, 53, - 53, 53, 53, 53, 53, 53, 53, 53, + 1, 1, 1, 1, 1, 1, 1, 56, 1, 54, 54, 54, 54, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 55, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 56, 1, 54, - 54, 54, 54, 54, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 54, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 55, 1, 1, 1, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 56, 1, 57, + 1, 57, 57, 57, 57, 57, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 57, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 58, 1, 58, 58, 58, 58, + 58, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 58, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 59, 1, 1, 60, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 1, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, + 1, 64, 64, 64, 64, 64, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 64, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -291,26 +302,41 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 56, 1, 57, 1, 57, - 57, 57, 57, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 57, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 58, 1, 58, 58, 58, 58, 58, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 58, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 59, 1, - 1, 60, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 1, 62, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 1, 64, + 1, 1, 1, 1, 1, 66, 1, 64, 64, 64, 64, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 65, 1, 1, 1, 1, 1, + 1, 1, 65, 1, 1, 1, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 66, 1, 67, 1, 68, + 1, 68, 68, 68, 68, 68, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 68, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 69, 1, 69, 69, 69, 69, + 69, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 69, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 70, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 1, 72, 72, + 72, 72, 72, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 72, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 73, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -320,39 +346,13 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 66, 1, 64, 64, 64, - 64, 64, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 64, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 65, 1, 1, 1, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 66, 1, 67, 1, 68, 1, 68, - 68, 68, 68, 68, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 68, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 69, 1, 69, 69, 69, 69, 69, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 69, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 70, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 1, 72, 72, 72, 72, + 1, 1, 74, 1, 72, 72, 72, 72, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 73, + 1, 1, 1, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -361,48 +361,32 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 74, 1, 76, 1, 76, 76, 76, 76, + 76, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 74, 1, 72, 72, 72, 72, 72, 1, + 1, 1, 1, 1, 1, 77, 1, 77, + 77, 77, 77, 77, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 72, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 73, 1, 1, - 1, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 74, 1, - 76, 1, 76, 76, 76, 76, 76, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 76, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 77, 1, 77, 77, 77, - 77, 77, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 77, 1, 78, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 79, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 1, 82, + 1, 1, 1, 1, 1, 1, 77, 1, + 78, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 79, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 1, 82, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, - 81, 83, 81, 84, 84, 84, 84, 84, + 81, 81, 81, 83, 81, 84, 84, 84, + 84, 84, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 84, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 84, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 85, 1, + 85, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -412,20 +396,35 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 86, - 1, 81, 1, 1, 1, 1, 1, 1, + 1, 86, 1, 81, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 81, 1, 87, 87, 87, + 1, 1, 1, 1, 1, 81, 1, 87, + 87, 87, 87, 87, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 87, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 88, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 89, 1, 87, 87, 87, 87, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 88, 1, 1, 1, 1, 1, 1, 1, + 88, 1, 1, 1, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -434,42 +433,27 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 89, 1, 91, 1, 91, 91, 91, + 91, 91, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 89, 1, 87, 87, 87, 87, 87, + 1, 1, 1, 1, 91, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 87, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 88, 1, - 1, 1, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 92, 1, + 92, 92, 92, 92, 92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 89, - 1, 91, 1, 91, 91, 91, 91, 91, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 91, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 92, 1, 92, 92, - 92, 92, 92, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 92, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 93, 94, 94, - 94, 94, 94, 94, 94, 94, 94, 1, - 87, 87, 87, 87, 87, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 87, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 88, 1, 1, 1, 95, + 1, 1, 1, 1, 93, 1, 1, 94, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 1, 1, 1, 1, 1, 1, 1, + 95, 1, 23, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 1, 23, 23, 23, + 23, 23, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 23, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -477,21 +461,53 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 89, 1, 96, 96, - 96, 96, 96, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 96, 1, 1, + 1, 24, 1, 97, 1, 97, 97, 97, + 97, 97, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 97, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 97, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 98, 1, + 98, 98, 98, 98, 98, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 98, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 99, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 1, 87, 87, 87, 87, 87, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 87, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 88, 1, 1, + 1, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 98, 1, 2, 2, 2, 2, - 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 89, 1, + 8, 1, 102, 102, 102, 102, 102, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 102, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 103, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 104, 1, 103, 103, + 103, 103, 103, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 103, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -502,50 +518,66 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 3, 1, - 1, 0 + 1, 1, 1, 1, 1, 1, 1, 1, + 104, 1, 25, 25, 25, 25, 25, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 25, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 104, 1, 0 }; static const char _deserialize_json_trans_targs[] = { - 1, 0, 2, 3, 3, 4, 5, 19, - 25, 38, 44, 52, 6, 13, 7, 8, - 9, 10, 12, 10, 12, 11, 3, 56, - 11, 56, 14, 15, 16, 17, 18, 17, - 18, 11, 3, 56, 20, 21, 22, 23, - 24, 11, 3, 56, 24, 26, 32, 27, - 28, 29, 30, 31, 30, 31, 11, 3, - 56, 33, 34, 35, 36, 37, 36, 37, - 11, 3, 56, 39, 40, 41, 42, 43, - 11, 3, 56, 43, 45, 46, 47, 50, - 51, 47, 48, 49, 11, 3, 56, 11, - 3, 56, 51, 53, 54, 50, 55, 55, - 56, 57, 58 + 1, 0, 2, 3, 18, 24, 37, 43, + 51, 56, 60, 4, 12, 5, 6, 7, + 8, 11, 8, 11, 9, 1, 10, 9, + 10, 63, 13, 14, 15, 16, 17, 16, + 17, 9, 1, 10, 19, 20, 21, 22, + 23, 9, 1, 10, 23, 25, 31, 26, + 27, 28, 29, 30, 29, 30, 9, 1, + 10, 32, 33, 34, 35, 36, 35, 36, + 9, 1, 10, 38, 39, 40, 41, 42, + 9, 1, 10, 42, 44, 45, 46, 49, + 50, 46, 47, 48, 9, 1, 10, 9, + 1, 10, 50, 52, 53, 54, 9, 55, + 55, 57, 58, 49, 59, 59, 61, 62, + 1 }; static const char _deserialize_json_trans_actions[] = { - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 0, 0, 3, 3, 4, - 0, 5, 0, 0, 2, 2, 2, 0, - 0, 6, 6, 7, 0, 0, 0, 2, - 2, 8, 8, 9, 0, 0, 0, 0, - 0, 2, 2, 2, 0, 0, 10, 10, - 11, 0, 0, 2, 2, 2, 0, 0, - 12, 12, 13, 0, 0, 0, 2, 2, - 14, 14, 15, 0, 0, 0, 2, 16, - 16, 0, 17, 0, 18, 18, 19, 20, - 20, 21, 17, 0, 0, 22, 22, 23, - 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 0, 0, 2, 2, 2, 0, + 0, 3, 0, 0, 1, 1, 1, 0, + 0, 4, 4, 4, 0, 0, 0, 1, + 1, 5, 5, 5, 0, 0, 0, 0, + 0, 1, 1, 1, 0, 0, 6, 6, + 6, 0, 0, 1, 1, 1, 0, 0, + 7, 7, 7, 0, 0, 0, 1, 1, + 8, 8, 8, 0, 0, 0, 1, 9, + 9, 0, 10, 0, 11, 11, 11, 12, + 12, 12, 10, 0, 0, 1, 1, 1, + 0, 0, 0, 13, 13, 14, 0, 0, + 15 }; -static const int deserialize_json_start = 1; -static const int deserialize_json_first_final = 56; +static const int deserialize_json_start = 61; +static const int deserialize_json_first_final = 61; static const int deserialize_json_error = 0; -static const int deserialize_json_en_main = 1; +static const int deserialize_json_en_main = 61; -#line 111 "hb-buffer-deserialize-json.rl" +#line 115 "hb-buffer-deserialize-json.rl" static hb_bool_t @@ -565,12 +597,12 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 569 "hb-buffer-deserialize-json.hh" +#line 594 "hb-buffer-deserialize-json.hh" { cs = deserialize_json_start; } -#line 574 "hb-buffer-deserialize-json.hh" +#line 597 "hb-buffer-deserialize-json.hh" { int _slen; int _trans; @@ -595,14 +627,14 @@ _resume: goto _again; switch ( _deserialize_json_trans_actions[_trans] ) { - case 1: + case 15: #line 38 "hb-buffer-deserialize-json.rl" { hb_memset (&info, 0, sizeof (info)); hb_memset (&pos , 0, sizeof (pos )); } break; - case 5: + case 3: #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); @@ -612,75 +644,21 @@ _resume: *end_ptr = p; } break; - case 2: + case 1: #line 51 "hb-buffer-deserialize-json.rl" { tok = p; } - break; - case 17: -#line 55 "hb-buffer-deserialize-json.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } - break; - case 23: -#line 56 "hb-buffer-deserialize-json.rl" - { if (unlikely (!buffer->ensure_unicode ())) return false; } - break; - case 18: -#line 58 "hb-buffer-deserialize-json.rl" - { - /* TODO Unescape \" and \\ if found. */ - if (!hb_font_glyph_from_string (font, - tok+1, p - tok - 2, /* Skip "" */ - &info.codepoint)) - return false; -} - break; - case 20: -#line 66 "hb-buffer-deserialize-json.rl" - { if (!parse_uint (tok, p, &info.codepoint)) return false; } - break; - case 8: -#line 67 "hb-buffer-deserialize-json.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } break; case 10: -#line 68 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.x_offset )) return false; } - break; - case 12: -#line 69 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } - break; - case 3: -#line 70 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } - break; - case 6: -#line 71 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } - break; - case 14: -#line 72 "hb-buffer-deserialize-json.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } - break; - case 16: -#line 51 "hb-buffer-deserialize-json.rl" - { - tok = p; -} #line 55 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_glyphs ())) return false; } break; - case 22: -#line 51 "hb-buffer-deserialize-json.rl" - { - tok = p; -} + case 14: #line 56 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; - case 19: + case 11: #line 58 "hb-buffer-deserialize-json.rl" { /* TODO Unescape \" and \\ if found. */ @@ -688,101 +666,53 @@ _resume: tok+1, p - tok - 2, /* Skip "" */ &info.codepoint)) return false; -} -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; } break; - case 21: + case 12: #line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} break; - case 9: + case 5: #line 67 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} break; - case 11: + case 6: #line 68 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_offset )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 13: -#line 69 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 4: -#line 70 "hb-buffer-deserialize-json.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} break; case 7: +#line 69 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 2: +#line 70 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 4: #line 71 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} break; - case 15: + case 8: #line 72 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-json.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} break; -#line 786 "hb-buffer-deserialize-json.hh" + case 9: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 13: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 56 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; +#line 689 "hb-buffer-deserialize-json.hh" } _again: @@ -794,12 +724,12 @@ _again: _out: {} } -#line 132 "hb-buffer-deserialize-json.rl" +#line 136 "hb-buffer-deserialize-json.rl" *end_ptr = p; - return p == pe && *(p-1) != ']'; + return p == pe; } #endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh index 8e526ce4e2a..78570486376 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh @@ -32,287 +32,344 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-text-glyphs.hh" +#line 33 "hb-buffer-deserialize-text-glyphs.hh" static const unsigned char _deserialize_text_glyphs_trans_keys[] = { - 0u, 0u, 48u, 57u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, - 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 0 + 0u, 0u, 35u, 124u, 48u, 57u, 60u, 124u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 62u, 62u, + 93u, 124u, 45u, 57u, 48u, 57u, 35u, 124u, 45u, 57u, 48u, 57u, 35u, 124u, 35u, 124u, + 35u, 124u, 35u, 124u, 35u, 124u, 35u, 124u, 48u, 57u, 35u, 124u, 45u, 57u, 48u, 57u, + 44u, 44u, 45u, 57u, 48u, 57u, 35u, 124u, 35u, 124u, 44u, 57u, 35u, 124u, 43u, 124u, + 35u, 124u, 48u, 62u, 44u, 57u, 44u, 57u, 44u, 57u, 48u, 124u, 35u, 124u, 35u, 124u, + 35u, 124u, 0 }; static const char _deserialize_text_glyphs_key_spans[] = { - 0, 10, 13, 10, 13, 10, 10, 13, - 10, 1, 13, 10, 14, 82, 116, 116, - 116, 116, 116, 116, 116, 116, 116, 116, - 116, 116, 116 + 0, 90, 10, 65, 13, 10, 1, 13, + 10, 1, 13, 10, 1, 13, 10, 1, + 32, 13, 10, 90, 13, 10, 90, 90, + 90, 90, 90, 90, 10, 90, 13, 10, + 1, 13, 10, 90, 90, 14, 90, 82, + 90, 15, 14, 14, 14, 77, 90, 90, + 90 }; static const short _deserialize_text_glyphs_index_offsets[] = { - 0, 0, 11, 25, 36, 50, 61, 72, - 86, 97, 99, 113, 124, 139, 222, 339, - 456, 573, 690, 807, 924, 1041, 1158, 1275, - 1392, 1509, 1626 + 0, 0, 91, 102, 168, 182, 193, 195, + 209, 220, 222, 236, 247, 249, 263, 274, + 276, 309, 323, 334, 425, 439, 450, 541, + 632, 723, 814, 905, 996, 1007, 1098, 1112, + 1123, 1125, 1139, 1150, 1241, 1332, 1347, 1438, + 1521, 1612, 1628, 1643, 1658, 1673, 1751, 1842, + 1933 }; static const char _deserialize_text_glyphs_indicies[] = { - 0, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 1, 3, 1, 1, 4, - 5, 5, 5, 5, 5, 5, 5, 5, - 5, 1, 6, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 1, 8, 1, 1, - 9, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 1, 11, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 1, 13, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 1, 15, 1, 1, 16, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 1, 18, + 1, 0, 0, 0, 0, 0, 0, + 0, 2, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 0, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 9, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 3, 11, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 12, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 12, + 3, 13, 3, 3, 14, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 3, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 3, 16, 3, 17, 3, 3, 18, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 1, 20, 1, 21, 1, 1, 22, - 23, 23, 23, 23, 23, 23, 23, 23, - 23, 1, 24, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 1, 20, 1, 1, - 1, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 1, 26, 26, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 26, 1, - 1, 26, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 26, 26, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 26, 1, 28, - 28, 28, 28, 28, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 28, 27, - 27, 29, 27, 27, 27, 27, 27, 27, - 27, 30, 1, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 31, 27, 27, 32, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 33, 1, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 28, 27, 34, 34, 34, 34, - 34, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 34, 26, 26, 35, 26, - 26, 26, 26, 26, 26, 26, 36, 1, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 37, 26, 26, 38, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 39, - 1, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 40, - 26, 41, 41, 41, 41, 41, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 41, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 42, 1, 43, 43, - 43, 43, 43, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 43, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 44, 1, 41, 41, 41, 41, 41, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 41, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 42, 1, - 46, 46, 46, 46, 46, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 46, - 1, 1, 47, 1, 1, 1, 1, 1, - 1, 1, 1, 48, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 49, 1, 50, 50, 50, - 50, 50, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 50, 1, 1, 51, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 52, 1, 50, 50, 50, 50, 50, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 50, 1, 1, 51, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 52, 1, 46, - 46, 46, 46, 46, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 46, 1, - 1, 47, 1, 1, 1, 1, 1, 1, - 1, 1, 48, 1, 1, 1, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 49, 1, 53, 53, 53, 53, - 53, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 53, 1, 1, 54, 1, - 1, 1, 1, 1, 1, 1, 55, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 56, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 57, - 1, 58, 58, 58, 58, 58, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 58, 1, 1, 59, 1, 1, 1, 1, - 1, 1, 1, 60, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 61, 1, 58, 58, - 58, 58, 58, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 58, 1, 1, - 59, 1, 1, 1, 1, 1, 1, 1, - 60, 1, 1, 1, 1, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 61, 1, 53, 53, 53, 53, 53, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 53, 1, 1, 54, 1, 1, - 1, 1, 1, 1, 1, 55, 1, 1, - 1, 1, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 1, 1, 1, 1, - 1, 1, 56, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 57, 1, - 0 + 19, 3, 18, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 3, 20, 3, 21, + 3, 3, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 3, 22, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 3, + 24, 3, 25, 3, 3, 26, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 3, + 26, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 3, 28, 3, 29, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 29, 3, 30, 3, + 3, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 3, 33, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 3, 35, + 3, 3, 3, 3, 3, 3, 3, 3, + 36, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 37, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 38, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 38, 3, 39, 3, 3, 40, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 3, + 42, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 3, 44, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 45, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 46, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 46, 3, 44, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 3, 3, 45, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 46, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 46, + 3, 35, 3, 3, 3, 3, 3, 3, + 3, 3, 36, 3, 3, 3, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, + 3, 3, 37, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 38, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 38, 3, 1, 0, 0, 0, + 0, 0, 0, 0, 2, 3, 47, 0, + 0, 48, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 0, 0, 4, 5, 0, + 0, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 2, + 3, 0, 0, 0, 48, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 0, 0, + 4, 5, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 2, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 5, 0, 0, 6, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 0, 50, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 3, + 52, 3, 3, 3, 3, 3, 3, 3, + 53, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 54, 3, 3, 3, 55, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 56, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 56, 3, 57, 3, 3, 58, 59, + 59, 59, 59, 59, 59, 59, 59, 59, + 3, 60, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 3, 62, 3, 63, 3, + 3, 64, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 3, 66, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 3, 68, + 3, 3, 3, 3, 3, 3, 3, 69, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 70, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 71, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 71, 3, 68, 3, 3, 3, 3, 3, + 3, 3, 69, 3, 3, 3, 3, 67, + 67, 67, 67, 67, 67, 67, 67, 67, + 67, 3, 3, 70, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 71, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 71, 3, 62, 3, 3, + 3, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 3, 52, 3, 3, 3, + 3, 3, 3, 3, 53, 3, 3, 3, + 3, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 3, 3, 54, 3, 3, + 3, 55, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 56, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 56, 3, 0, + 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 3, 0, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 0, 3, 1, 0, 0, 0, 0, 0, + 0, 0, 2, 16, 0, 0, 0, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 0, 0, 4, 5, 0, 0, 6, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 0, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 3, + 3, 3, 3, 28, 3, 24, 3, 3, + 3, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 3, 20, 3, 3, 3, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 3, 16, 3, 3, 3, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 3, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 3, 3, 11, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 12, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 12, 3, + 75, 74, 74, 74, 74, 74, 74, 74, + 76, 3, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 77, 78, 74, 74, 79, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 80, 81, 82, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 82, 74, 84, 83, 83, 83, 83, + 83, 83, 83, 85, 3, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 86, 87, 83, 83, + 88, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 89, 90, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 90, 83, 91, 74, + 74, 74, 74, 74, 74, 74, 92, 3, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 93, + 94, 74, 74, 95, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 81, + 96, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 96, + 74, 0 }; static const char _deserialize_text_glyphs_trans_targs[] = { - 16, 0, 18, 3, 19, 22, 19, 22, - 5, 20, 21, 20, 21, 23, 26, 8, - 9, 12, 9, 12, 10, 11, 24, 25, - 24, 25, 15, 15, 14, 1, 2, 6, - 7, 13, 15, 1, 2, 6, 7, 13, - 14, 17, 14, 17, 14, 18, 17, 1, - 4, 14, 17, 1, 14, 17, 1, 2, - 7, 14, 17, 1, 2, 14, 26 + 1, 2, 17, 0, 25, 28, 30, 39, + 47, 3, 45, 4, 47, 5, 6, 44, + 7, 8, 9, 43, 10, 11, 12, 42, + 13, 14, 15, 41, 16, 47, 18, 19, + 24, 19, 24, 2, 20, 4, 47, 21, + 22, 23, 22, 23, 2, 4, 47, 26, + 27, 40, 29, 38, 2, 17, 4, 30, + 47, 31, 32, 37, 32, 37, 33, 34, + 35, 36, 35, 36, 2, 17, 4, 47, + 38, 45, 1, 2, 17, 25, 28, 30, + 48, 39, 47, 1, 2, 17, 25, 28, + 30, 39, 47, 2, 17, 25, 28, 30, + 47 }; static const char _deserialize_text_glyphs_trans_actions[] = { - 1, 0, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 1, - 1, 1, 0, 0, 2, 1, 1, 1, - 0, 0, 0, 4, 3, 5, 5, 5, - 5, 4, 6, 7, 7, 7, 7, 0, - 6, 8, 8, 0, 0, 0, 9, 10, - 10, 9, 11, 12, 11, 13, 14, 14, - 14, 13, 15, 16, 16, 15, 0 + 0, 1, 1, 0, 1, 1, 1, 0, + 1, 2, 2, 3, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 4, 4, 4, 4, 2, + 2, 2, 0, 0, 5, 5, 5, 0, + 0, 0, 2, 2, 6, 6, 6, 6, + 6, 2, 2, 2, 0, 0, 7, 2, + 2, 2, 0, 0, 8, 8, 8, 8, + 0, 0, 9, 10, 10, 10, 10, 10, + 9, 9, 10, 12, 13, 13, 13, 13, + 13, 12, 13, 14, 14, 14, 14, 14, + 14 }; static const char _deserialize_text_glyphs_eof_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 6, - 8, 0, 8, 9, 11, 11, 9, 13, - 15, 15, 13 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, + 0 }; -static const int deserialize_text_glyphs_start = 14; -static const int deserialize_text_glyphs_first_final = 14; +static const int deserialize_text_glyphs_start = 46; +static const int deserialize_text_glyphs_first_final = 46; static const int deserialize_text_glyphs_error = 0; -static const int deserialize_text_glyphs_en_main = 14; +static const int deserialize_text_glyphs_en_main = 46; -#line 98 "hb-buffer-deserialize-text-glyphs.rl" +#line 101 "hb-buffer-deserialize-text-glyphs.rl" static hb_bool_t @@ -322,39 +379,22 @@ _hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer, const char **end_ptr, hb_font_t *font) { - const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + const char *p = buf, *pe = buf + buf_len, *eof = pe; /* Ensure we have positions. */ (void) hb_buffer_get_glyph_positions (buffer, nullptr); - while (p < pe && ISSPACE (*p)) - p++; - if (p < pe && *p == (buffer->len ? '|' : '[')) - *end_ptr = ++p; - - const char *end = strchr ((char *) p, ']'); - if (end) - pe = eof = end; - else - { - end = strrchr ((char *) p, '|'); - if (end) - pe = eof = end; - else - pe = eof = p; - } - const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 353 "hb-buffer-deserialize-text-glyphs.hh" +#line 386 "hb-buffer-deserialize-text-glyphs.hh" { cs = deserialize_text_glyphs_start; } -#line 358 "hb-buffer-deserialize-text-glyphs.hh" +#line 389 "hb-buffer-deserialize-text-glyphs.hh" { int _slen; int _trans; @@ -379,55 +419,68 @@ _resume: goto _again; switch ( _deserialize_text_glyphs_trans_actions[_trans] ) { - case 1: -#line 51 "hb-buffer-deserialize-text-glyphs.rl" + case 2: +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { tok = p; } break; - case 7: -#line 55 "hb-buffer-deserialize-text-glyphs.rl" + case 1: +#line 54 "hb-buffer-deserialize-text-glyphs.rl" { /* TODO Unescape delimiters. */ if (!hb_font_glyph_from_string (font, tok, p - tok, &info.codepoint)) return false; -} - break; - case 14: -#line 63 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } - break; - case 2: -#line 64 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.x_offset )) return false; } - break; - case 16: -#line 65 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } - break; - case 10: -#line 66 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } - break; - case 12: -#line 67 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } - break; - case 4: -#line 38 "hb-buffer-deserialize-text-glyphs.rl" - { - hb_memset (&info, 0, sizeof (info)); - hb_memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text-glyphs.rl" - { - tok = p; } break; case 6: -#line 55 "hb-buffer-deserialize-text-glyphs.rl" +#line 62 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 7: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 8: +#line 64 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 4: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 5: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 3: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } + break; + case 9: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 50 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 10: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 50 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 54 "hb-buffer-deserialize-text-glyphs.rl" { /* TODO Unescape delimiters. */ if (!hb_font_glyph_from_string (font, @@ -435,86 +488,62 @@ _resume: &info.codepoint)) return false; } + break; + case 12: #line 43 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); + buffer->add_info_and_pos (info, pos); if (unlikely (!buffer->successful)) return false; - buffer->pos[buffer->len - 1] = pos; *end_ptr = p; +} +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 50 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 14: +#line 54 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 50 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; } break; case 13: -#line 63 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } #line 43 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); + buffer->add_info_and_pos (info, pos); if (unlikely (!buffer->successful)) return false; - buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } - break; - case 15: -#line 65 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 66 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 67 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 8: -#line 68 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 5: #line 38 "hb-buffer-deserialize-text-glyphs.rl" { hb_memset (&info, 0, sizeof (info)); hb_memset (&pos , 0, sizeof (pos )); } -#line 51 "hb-buffer-deserialize-text-glyphs.rl" +#line 50 "hb-buffer-deserialize-text-glyphs.rl" { tok = p; } -#line 55 "hb-buffer-deserialize-text-glyphs.rl" +#line 54 "hb-buffer-deserialize-text-glyphs.rl" { /* TODO Unescape delimiters. */ if (!hb_font_glyph_from_string (font, @@ -523,34 +552,7 @@ _resume: return false; } break; - case 3: -#line 38 "hb-buffer-deserialize-text-glyphs.rl" - { - hb_memset (&info, 0, sizeof (info)); - hb_memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text-glyphs.rl" - { - tok = p; -} -#line 55 "hb-buffer-deserialize-text-glyphs.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 554 "hb-buffer-deserialize-text-glyphs.hh" +#line 523 "hb-buffer-deserialize-text-glyphs.hh" } _again: @@ -562,128 +564,25 @@ _again: if ( p == eof ) { switch ( _deserialize_text_glyphs_eof_actions[cs] ) { - case 6: -#line 55 "hb-buffer-deserialize-text-glyphs.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 13: -#line 63 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 15: -#line 65 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 66 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; case 11: -#line 67 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } #line 43 "hb-buffer-deserialize-text-glyphs.rl" { - buffer->add_info (info); + buffer->add_info_and_pos (info, pos); if (unlikely (!buffer->successful)) return false; - buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; - case 8: -#line 68 "hb-buffer-deserialize-text-glyphs.rl" - { if (!parse_uint (tok, p, &info.mask )) return false; } -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 3: -#line 38 "hb-buffer-deserialize-text-glyphs.rl" - { - hb_memset (&info, 0, sizeof (info)); - hb_memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text-glyphs.rl" - { - tok = p; -} -#line 55 "hb-buffer-deserialize-text-glyphs.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text-glyphs.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 671 "hb-buffer-deserialize-text-glyphs.hh" +#line 542 "hb-buffer-deserialize-text-glyphs.hh" } } _out: {} } -#line 136 "hb-buffer-deserialize-text-glyphs.rl" +#line 122 "hb-buffer-deserialize-text-glyphs.rl" - if (pe < orig_pe && *pe == ']') - { - pe++; - if (p == pe) - p++; - } - *end_ptr = p; return p == pe; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh index 6a1706ccb72..9e21beebd34 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh @@ -34,135 +34,106 @@ #line 36 "hb-buffer-deserialize-text-unicode.hh" static const unsigned char _deserialize_text_unicode_trans_keys[] = { - 0u, 0u, 9u, 117u, 43u, 102u, 48u, 102u, 48u, 57u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 0 + 0u, 0u, 43u, 102u, 48u, 102u, 48u, 124u, 48u, 57u, 62u, 124u, 48u, 124u, 60u, 117u, + 85u, 117u, 85u, 117u, 0 }; static const char _deserialize_text_unicode_key_spans[] = { - 0, 109, 60, 55, 10, 116, 116, 116, - 116 + 0, 60, 55, 77, 10, 63, 77, 58, + 33, 33 }; static const short _deserialize_text_unicode_index_offsets[] = { - 0, 0, 110, 171, 227, 238, 355, 472, - 589 + 0, 0, 61, 117, 195, 206, 270, 348, + 407, 441 }; static const char _deserialize_text_unicode_indicies[] = { - 0, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 1, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 4, 5, 1, 1, 3, + 3, 3, 3, 3, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, + 3, 3, 3, 3, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 1, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 8, 1, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 1, 3, - 1, 1, 1, 1, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 4, 4, 4, - 4, 4, 4, 1, 5, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 1, 7, - 7, 7, 7, 7, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 7, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 1, 1, 1, 9, 1, 1, 1, 8, - 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 8, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 8, - 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 10, 1, 11, 11, 11, 11, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 11, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 11, 1, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, - 1, 12, 12, 12, 12, 12, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 12, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 13, 1, 12, 12, - 12, 12, 12, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 12, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 13, 1, 0 + 1, 1, 12, 1, 0 }; static const char _deserialize_text_unicode_trans_targs[] = { - 1, 0, 2, 3, 5, 7, 8, 6, - 5, 4, 1, 6, 6, 1, 8 + 2, 0, 3, 3, 4, 9, 5, 6, + 9, 6, 8, 1, 1 }; static const char _deserialize_text_unicode_trans_actions[] = { - 0, 0, 1, 0, 2, 2, 2, 3, - 0, 4, 3, 0, 5, 5, 0 + 0, 0, 1, 0, 2, 2, 1, 1, + 3, 0, 0, 4, 6 }; static const char _deserialize_text_unicode_eof_actions[] = { - 0, 0, 0, 0, 0, 3, 0, 5, - 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5 }; -static const int deserialize_text_unicode_start = 1; -static const int deserialize_text_unicode_first_final = 5; +static const int deserialize_text_unicode_start = 7; +static const int deserialize_text_unicode_first_final = 7; static const int deserialize_text_unicode_error = 0; -static const int deserialize_text_unicode_en_main = 1; +static const int deserialize_text_unicode_en_main = 7; -#line 79 "hb-buffer-deserialize-text-unicode.rl" +#line 80 "hb-buffer-deserialize-text-unicode.rl" static hb_bool_t @@ -172,37 +143,19 @@ _hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, const char **end_ptr, hb_font_t *font) { - const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; - - while (p < pe && ISSPACE (*p)) - p++; - if (p < pe && *p == (buffer->len ? '|' : '<')) - *end_ptr = ++p; - - const char *end = strchr ((char *) p, '>'); - if (end) - pe = eof = end; - else - { - end = strrchr ((char *) p, '|'); - if (end) - pe = eof = end; - else - pe = eof = p; - } - + const char *p = buf, *pe = buf + buf_len, *eof = pe; const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; const hb_glyph_position_t pos = {0}; -#line 201 "hb-buffer-deserialize-text-unicode.hh" +#line 154 "hb-buffer-deserialize-text-unicode.hh" { cs = deserialize_text_unicode_start; } -#line 206 "hb-buffer-deserialize-text-unicode.hh" +#line 159 "hb-buffer-deserialize-text-unicode.hh" { int _slen; int _trans; @@ -227,38 +180,27 @@ _resume: goto _again; switch ( _deserialize_text_unicode_trans_actions[_trans] ) { - case 1: + case 4: #line 38 "hb-buffer-deserialize-text-unicode.rl" { hb_memset (&info, 0, sizeof (info)); } break; - case 2: + case 1: #line 51 "hb-buffer-deserialize-text-unicode.rl" { tok = p; } break; - case 4: + case 2: #line 55 "hb-buffer-deserialize-text-unicode.rl" {if (!parse_hex (tok, p, &info.codepoint )) return false; } break; case 3: -#line 55 "hb-buffer-deserialize-text-unicode.rl" - {if (!parse_hex (tok, p, &info.codepoint )) return false; } -#line 42 "hb-buffer-deserialize-text-unicode.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - if (buffer->have_positions) - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 5: #line 57 "hb-buffer-deserialize-text-unicode.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 6: #line 42 "hb-buffer-deserialize-text-unicode.rl" { buffer->add_info (info); @@ -267,9 +209,13 @@ _resume: if (buffer->have_positions) buffer->pos[buffer->len - 1] = pos; *end_ptr = p; +} +#line 38 "hb-buffer-deserialize-text-unicode.rl" + { + hb_memset (&info, 0, sizeof (info)); } break; -#line 273 "hb-buffer-deserialize-text-unicode.hh" +#line 219 "hb-buffer-deserialize-text-unicode.hh" } _again: @@ -281,22 +227,7 @@ _again: if ( p == eof ) { switch ( _deserialize_text_unicode_eof_actions[cs] ) { - case 3: -#line 55 "hb-buffer-deserialize-text-unicode.rl" - {if (!parse_hex (tok, p, &info.codepoint )) return false; } -#line 42 "hb-buffer-deserialize-text-unicode.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - if (buffer->have_positions) - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; case 5: -#line 57 "hb-buffer-deserialize-text-unicode.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } #line 42 "hb-buffer-deserialize-text-unicode.rl" { buffer->add_info (info); @@ -307,23 +238,16 @@ _again: *end_ptr = p; } break; -#line 311 "hb-buffer-deserialize-text-unicode.hh" +#line 242 "hb-buffer-deserialize-text-unicode.hh" } } _out: {} } -#line 115 "hb-buffer-deserialize-text-unicode.rl" +#line 98 "hb-buffer-deserialize-text-unicode.rl" - if (pe < orig_pe && *pe == '>') - { - pe++; - if (p == pe) - p++; - } - *end_ptr = p; return p == pe; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc index bb2cddcb655..763172818c4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc @@ -169,11 +169,13 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; - hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", - extents.x_bearing, extents.y_bearing)); - p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", - extents.width, extents.height)); + if (hb_font_get_glyph_extents(font, info[i].codepoint, &extents)) + { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); + } } *p++ = '}'; @@ -318,8 +320,8 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; - hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + if (hb_font_get_glyph_extents(font, info[i].codepoint, &extents)) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); } if (i == end-1) { @@ -737,8 +739,7 @@ parse_hex (const char *pp, const char *end, uint32_t *pv) * Deserializes glyphs @buffer from textual representation in the format * produced by hb_buffer_serialize_glyphs(). * - * Return value: `true` if parse was successful, `false` if an error - * occurred. + * Return value: `true` if the full string was parsed, `false` otherwise. * * Since: 0.9.7 **/ @@ -810,8 +811,7 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, * Deserializes Unicode @buffer from textual representation in the format * produced by hb_buffer_serialize_unicode(). * - * Return value: `true` if parse was successful, `false` if an error - * occurred. + * Return value: `true` if the full string was parsed, `false` otherwise. * * Since: 2.7.3 **/ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc index 3a4c9c9f317..56299d9b7e0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc @@ -63,24 +63,25 @@ static bool buffer_verify_monotone (hb_buffer_t *buffer, hb_font_t *font) { - /* Check that clusters are monotone. */ - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES || - buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (buffer->cluster_level)) { - bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); - - unsigned int num_glyphs; - hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); - - for (unsigned int i = 1; i < num_glyphs; i++) - if (info[i-1].cluster != info[i].cluster && - (info[i-1].cluster < info[i].cluster) != is_forward) - { - buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "clusters are not monotone."); - return false; - } + /* Cannot perform this check without monotone clusters. */ + return true; } + bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + for (unsigned int i = 1; i < num_glyphs; i++) + if (info[i-1].cluster != info[i].cluster && + (info[i-1].cluster < info[i].cluster) != is_forward) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "clusters are not monotone."); + return false; + } + return true; } @@ -92,8 +93,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, unsigned int num_features, const char * const *shapers) { - if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && - buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (buffer->cluster_level)) { /* Cannot perform this check without monotone clusters. */ return true; @@ -207,8 +207,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, unsigned int num_features, const char * const *shapers) { - if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && - buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (buffer->cluster_level)) { /* Cannot perform this check without monotone clusters. */ return true; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc index 72970c9c2f7..c0600fd839b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc @@ -370,6 +370,18 @@ hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) len++; } +void +hb_buffer_t::add_info_and_pos (const hb_glyph_info_t &glyph_info, + const hb_glyph_position_t &glyph_pos) +{ + if (unlikely (!ensure (len + 1))) return; + + info[len] = glyph_info; + assert (have_positions); + pos[len] = glyph_pos; + + len++; +} void @@ -518,7 +530,7 @@ void hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int end) { - if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) { unsafe_to_break (start, end); return; @@ -551,7 +563,7 @@ void hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int end) { - if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) return; if (unlikely (end - start < 2)) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h index d2258634ff4..332001ef171 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h @@ -422,6 +422,7 @@ hb_buffer_get_flags (const hb_buffer_t *buffer); * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values. * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level, * equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + * @HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES: Only group clusters, but don't enforce monotone order. * * Data type for holding HarfBuzz's clustering behavior options. The cluster level * dictates one aspect of how HarfBuzz will treat non-base characters @@ -429,11 +430,26 @@ hb_buffer_get_flags (const hb_buffer_t *buffer); * * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, non-base * characters are merged into the cluster of the base character that precedes them. + * There is also cluster merging every time the clusters will otherwise become non-monotone. * * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, non-base characters are initially * assigned their own cluster values, which are not merged into preceding base * clusters. This allows HarfBuzz to perform additional operations like reorder - * sequences of adjacent marks. + * sequences of adjacent marks. The output is still monotone, but the cluster + * values are more granular. + * + * In @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS, non-base characters are assigned their + * own cluster values, which are not merged into preceding base clusters. Moreover, + * the cluster values are not merged into monotone order. This is the most granular + * cluster level, and it is useful for clients that need to know the exact cluster + * values of each character, but is harder to use for clients, since clusters + * might appear in any order. + * + * In @HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES, non-base characters are merged into the + * cluster of the base character that precedes them. This is similar to the Unicode + * Grapheme Cluster algorithm, but it is not exactly the same. The output is + * not forced to be monotone. This is useful for clients that want to use HarfBuzz + * as a cheap implementation of the Unicode Grapheme Cluster algorithm. * * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES is the default, because it maintains * backward compatibility with older versions of HarfBuzz. New client programs that @@ -446,9 +462,52 @@ typedef enum { HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = 0, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = 1, HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = 2, + HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES = 3, HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES } hb_buffer_cluster_level_t; +/** + * HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE: + * @level: #hb_buffer_cluster_level_t to test + * + * Tests whether a cluster level groups cluster values into monotone order. + * Requires that the level be valid. + * + * Since: 11.0.0 + */ +#define HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE(level) \ + ((bool) ((1u << (unsigned) (level)) & \ + ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) | \ + (1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)))) + +/** + * HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES: + * @level: #hb_buffer_cluster_level_t to test + * + * Tests whether a cluster level groups cluster values by graphemes. Requires + * that the level be valid. + * + * Since: 11.0.0 + */ +#define HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES(level) \ + ((bool) ((1u << (unsigned) (level)) & \ + ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) | \ + (1u << HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES)))) + +/** + * HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS + * @level: #hb_buffer_cluster_level_t to test + * + * Tests whether a cluster level does not group cluster values by graphemes. + * Requires that the level be valid. + * + * Since: 11.0.0 + */ +#define HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS(level) \ + ((bool) ((1u << (unsigned) (level)) & \ + ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARCATERS) | \ + (1u << HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)))) + HB_EXTERN void hb_buffer_set_cluster_level (hb_buffer_t *buffer, hb_buffer_cluster_level_t cluster_level); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh index c92e7df320e..81c0f4a72e3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh @@ -229,6 +229,8 @@ struct hb_buffer_t HB_INTERNAL void add (hb_codepoint_t codepoint, unsigned int cluster); HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info); + HB_INTERNAL void add_info_and_pos (const hb_glyph_info_t &glyph_info, + const hb_glyph_position_t &glyph_pos); void reverse_range (unsigned start, unsigned end) { @@ -410,6 +412,9 @@ struct hb_buffer_t { end = hb_min (end, len); + if (unlikely (end - start > 255)) + return; + if (interior && !from_out_buffer && end - start < 2) return; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh index 8c7e0c655e6..7d09ca89d7f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh @@ -30,18 +30,31 @@ #include "hb.hh" -/* Implements a lockfree cache for int->int functions. +/* Implements a lockfree and thread-safe cache for int->int functions, + * using (optionally) _relaxed_ atomic integer operations. * - * The cache is a fixed-size array of 16-bit or 32-bit integers. - * The key is split into two parts: the cache index and the rest. + * The cache is a fixed-size array of 16-bit or 32-bit integers, + * typically 256 elements. * - * The cache index is used to index into the array. The rest is used - * to store the key and the value. + * The key is split into two parts: the cache index (high bits) + * and the rest (low bits). + * + * The cache index is used to index into the array. The array + * member is a 16-bit or 32-bit integer that is used *both* + * to store the low bits of the key, and the value. * * The value is stored in the least significant bits of the integer. - * The key is stored in the most significant bits of the integer. - * The key is shifted by cache_bits to the left to make room for the - * value. + * The low bits of the key are stored in the most significant bits + * of the integer. + * + * A cache hit is detected by comparing the low bits of the key + * with the high bits of the integer at the array position indexed + * by the high bits of the key. If they match, the value is extracted + * from the least significant bits of the integer and returned. + * Otherwise, a cache miss is reported. + * + * Cache operations (storage and retrieval) involve just a few + * arithmetic operations and a single memory access. */ template ::type, + hb_atomic_t, + hb_atomic_t>::type, typename std::conditional::type + unsigned short, + unsigned int>::type >::type; static_assert ((key_bits >= cache_bits), ""); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh index ab17d68318b..0eacdd92ab8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh @@ -71,7 +71,8 @@ struct cff2_cs_interp_env_t : cs_interp_env_t template cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, const int *coords_=nullptr, unsigned int num_coords_=0) - : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs), + cached_scalars_vector (&acc.cached_scalars_vector) { coords = coords_; num_coords = num_coords_; @@ -80,9 +81,39 @@ struct cff2_cs_interp_env_t : cs_interp_env_t set_ivs (acc.privateDicts[fd].ivs); } - void fini () + ~cff2_cs_interp_env_t () { - SUPER::fini (); + release_scalars_vector (scalars); + } + + hb_vector_t *acquire_scalars_vector () const + { + hb_vector_t *scalars = cached_scalars_vector->get_acquire (); + + if (!scalars || !cached_scalars_vector->cmpexch (scalars, nullptr)) + { + scalars = (hb_vector_t *) hb_calloc (1, sizeof (hb_vector_t)); + if (unlikely (!scalars)) + return nullptr; + scalars->init (); + } + + return scalars; + } + + void release_scalars_vector (hb_vector_t *scalars) const + { + if (!scalars) + return; + + scalars->clear (); + + if (!cached_scalars_vector->cmpexch (nullptr, scalars)) + { + scalars->fini (); + hb_free (scalars); + } + scalars = nullptr; } op_code_t fetch_op () @@ -111,14 +142,20 @@ struct cff2_cs_interp_env_t : cs_interp_env_t { if (!seen_blend) { - region_count = varStore->varStore.get_region_index_count (get_ivs ()); - if (do_blend) + scalars = acquire_scalars_vector (); + if (unlikely (!scalars)) + SUPER::set_error (); + else { - if (unlikely (!scalars.resize_exact (region_count))) - SUPER::set_error (); - else - varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, - &scalars[0], region_count); + region_count = varStore->varStore.get_region_index_count (get_ivs ()); + if (do_blend) + { + if (unlikely (!scalars->resize_exact (region_count))) + SUPER::set_error (); + else + varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, + &(*scalars)[0], region_count); + } } seen_blend = true; } @@ -149,11 +186,11 @@ struct cff2_cs_interp_env_t : cs_interp_env_t double v = 0; if (do_blend) { - if (likely (scalars.length == deltas.length)) + if (likely (scalars && scalars->length == deltas.length)) { - unsigned count = scalars.length; + unsigned count = scalars->length; for (unsigned i = 0; i < count; i++) - v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real (); + v += (double) scalars->arrayZ[i] * deltas.arrayZ[i].to_real (); } } return v; @@ -167,7 +204,8 @@ struct cff2_cs_interp_env_t : cs_interp_env_t const CFF2ItemVariationStore *varStore; unsigned int region_count; unsigned int ivs; - hb_vector_t scalars; + hb_vector_t *scalars = nullptr; + hb_atomic_t *> *cached_scalars_vector = nullptr; bool do_blend; bool seen_vsindex_ = false; bool seen_blend = false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc index bce0d09d20a..5c8546a378c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc @@ -42,7 +42,7 @@ /* hb_options_t */ -hb_atomic_int_t _hb_options; +hb_atomic_t _hb_options; void _hb_options_init () @@ -273,7 +273,7 @@ struct hb_language_item_t { /* Thread-safe lockfree language list */ -static hb_atomic_ptr_t langs; +static hb_atomic_t langs; static inline void free_langs () @@ -403,7 +403,7 @@ hb_language_to_string (hb_language_t language) hb_language_t hb_language_get_default () { - static hb_atomic_ptr_t default_language; + static hb_atomic_t default_language; hb_language_t language = default_language; if (unlikely (language == HB_LANGUAGE_INVALID)) @@ -968,6 +968,9 @@ hb_feature_from_string (const char *str, int len, * understood by hb_feature_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * + * Note that the feature value will be omitted if it is '1', but the + * string won't include any whitespace. + * * Since: 0.9.5 **/ void @@ -1121,6 +1124,8 @@ get_C_locale () * understood by hb_variation_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * + * Note that the string won't include any whitespace. + * * Since: 1.4.2 */ void @@ -1212,6 +1217,58 @@ uint8_t return hb_color_get_blue (color); } +/** + * hb_malloc: + * @size: The size of the memory to allocate. + * + * Allocates @size bytes of memory, using the allocator set at + * compile-time. Typically just malloc(). + * + * Return value: A pointer to the allocated memory. + * + * Since: 11.0.0 + **/ +void* hb_malloc(size_t size) { return hb_malloc_impl (size); } + +/** + * hb_calloc: + * @nmemb: The number of elements to allocate. + * @size: The size of each element. + * + * Allocates @nmemb elements of @size bytes each, initialized to zero, + * using the allocator set at compile-time. Typically just calloc(). + * + * Return value: A pointer to the allocated memory. + * + * Since: 11.0.0 + **/ +void* hb_calloc(size_t nmemb, size_t size) { return hb_calloc_impl (nmemb, size); } + +/** + * hb_realloc: + * @ptr: The pointer to the memory to reallocate. + * @size: The new size of the memory. + * + * Reallocates the memory pointed to by @ptr to @size bytes, using the + * allocator set at compile-time. Typically just realloc(). + * + * Return value: A pointer to the reallocated memory. + * + * Since: 11.0.0 + **/ +void* hb_realloc(void *ptr, size_t size) { return hb_realloc_impl (ptr, size); } + +/** + * hb_free: + * @ptr: The pointer to the memory to free. + * + * Frees the memory pointed to by @ptr, using the allocator set at + * compile-time. Typically just free(). + * + * Since: 11.0.0 + **/ +void hb_free(void *ptr) { hb_free_impl (ptr); } + /* If there is no visibility control, then hb-static.cc will NOT * define anything. Instead, we get it to define one set in here diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.h b/src/java.desktop/share/native/libharfbuzz/hb-common.h index a8bae366f7e..5a0ef65392d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.h @@ -65,6 +65,7 @@ typedef unsigned __int64 uint64_t; #else # include #endif +#include #if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #define HB_DEPRECATED __attribute__((__deprecated__)) @@ -337,437 +338,7 @@ HB_EXTERN hb_bool_t hb_language_matches (hb_language_t language, hb_language_t specific); -/** - * hb_script_t: - * @HB_SCRIPT_COMMON: `Zyyy` - * @HB_SCRIPT_INHERITED: `Zinh` - * @HB_SCRIPT_UNKNOWN: `Zzzz` - * @HB_SCRIPT_ARABIC: `Arab` - * @HB_SCRIPT_ARMENIAN: `Armn` - * @HB_SCRIPT_BENGALI: `Beng` - * @HB_SCRIPT_CYRILLIC: `Cyrl` - * @HB_SCRIPT_DEVANAGARI: `Deva` - * @HB_SCRIPT_GEORGIAN: `Geor` - * @HB_SCRIPT_GREEK: `Grek` - * @HB_SCRIPT_GUJARATI: `Gujr` - * @HB_SCRIPT_GURMUKHI: `Guru` - * @HB_SCRIPT_HANGUL: `Hang` - * @HB_SCRIPT_HAN: `Hani` - * @HB_SCRIPT_HEBREW: `Hebr` - * @HB_SCRIPT_HIRAGANA: `Hira` - * @HB_SCRIPT_KANNADA: `Knda` - * @HB_SCRIPT_KATAKANA: `Kana` - * @HB_SCRIPT_LAO: `Laoo` - * @HB_SCRIPT_LATIN: `Latn` - * @HB_SCRIPT_MALAYALAM: `Mlym` - * @HB_SCRIPT_ORIYA: `Orya` - * @HB_SCRIPT_TAMIL: `Taml` - * @HB_SCRIPT_TELUGU: `Telu` - * @HB_SCRIPT_THAI: `Thai` - * @HB_SCRIPT_TIBETAN: `Tibt` - * @HB_SCRIPT_BOPOMOFO: `Bopo` - * @HB_SCRIPT_BRAILLE: `Brai` - * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans` - * @HB_SCRIPT_CHEROKEE: `Cher` - * @HB_SCRIPT_ETHIOPIC: `Ethi` - * @HB_SCRIPT_KHMER: `Khmr` - * @HB_SCRIPT_MONGOLIAN: `Mong` - * @HB_SCRIPT_MYANMAR: `Mymr` - * @HB_SCRIPT_OGHAM: `Ogam` - * @HB_SCRIPT_RUNIC: `Runr` - * @HB_SCRIPT_SINHALA: `Sinh` - * @HB_SCRIPT_SYRIAC: `Syrc` - * @HB_SCRIPT_THAANA: `Thaa` - * @HB_SCRIPT_YI: `Yiii` - * @HB_SCRIPT_DESERET: `Dsrt` - * @HB_SCRIPT_GOTHIC: `Goth` - * @HB_SCRIPT_OLD_ITALIC: `Ital` - * @HB_SCRIPT_BUHID: `Buhd` - * @HB_SCRIPT_HANUNOO: `Hano` - * @HB_SCRIPT_TAGALOG: `Tglg` - * @HB_SCRIPT_TAGBANWA: `Tagb` - * @HB_SCRIPT_CYPRIOT: `Cprt` - * @HB_SCRIPT_LIMBU: `Limb` - * @HB_SCRIPT_LINEAR_B: `Linb` - * @HB_SCRIPT_OSMANYA: `Osma` - * @HB_SCRIPT_SHAVIAN: `Shaw` - * @HB_SCRIPT_TAI_LE: `Tale` - * @HB_SCRIPT_UGARITIC: `Ugar` - * @HB_SCRIPT_BUGINESE: `Bugi` - * @HB_SCRIPT_COPTIC: `Copt` - * @HB_SCRIPT_GLAGOLITIC: `Glag` - * @HB_SCRIPT_KHAROSHTHI: `Khar` - * @HB_SCRIPT_NEW_TAI_LUE: `Talu` - * @HB_SCRIPT_OLD_PERSIAN: `Xpeo` - * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo` - * @HB_SCRIPT_TIFINAGH: `Tfng` - * @HB_SCRIPT_BALINESE: `Bali` - * @HB_SCRIPT_CUNEIFORM: `Xsux` - * @HB_SCRIPT_NKO: `Nkoo` - * @HB_SCRIPT_PHAGS_PA: `Phag` - * @HB_SCRIPT_PHOENICIAN: `Phnx` - * @HB_SCRIPT_CARIAN: `Cari` - * @HB_SCRIPT_CHAM: `Cham` - * @HB_SCRIPT_KAYAH_LI: `Kali` - * @HB_SCRIPT_LEPCHA: `Lepc` - * @HB_SCRIPT_LYCIAN: `Lyci` - * @HB_SCRIPT_LYDIAN: `Lydi` - * @HB_SCRIPT_OL_CHIKI: `Olck` - * @HB_SCRIPT_REJANG: `Rjng` - * @HB_SCRIPT_SAURASHTRA: `Saur` - * @HB_SCRIPT_SUNDANESE: `Sund` - * @HB_SCRIPT_VAI: `Vaii` - * @HB_SCRIPT_AVESTAN: `Avst` - * @HB_SCRIPT_BAMUM: `Bamu` - * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp` - * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi` - * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli` - * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti` - * @HB_SCRIPT_JAVANESE: `Java` - * @HB_SCRIPT_KAITHI: `Kthi` - * @HB_SCRIPT_LISU: `Lisu` - * @HB_SCRIPT_MEETEI_MAYEK: `Mtei` - * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb` - * @HB_SCRIPT_OLD_TURKIC: `Orkh` - * @HB_SCRIPT_SAMARITAN: `Samr` - * @HB_SCRIPT_TAI_THAM: `Lana` - * @HB_SCRIPT_TAI_VIET: `Tavt` - * @HB_SCRIPT_BATAK: `Batk` - * @HB_SCRIPT_BRAHMI: `Brah` - * @HB_SCRIPT_MANDAIC: `Mand` - * @HB_SCRIPT_CHAKMA: `Cakm` - * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc` - * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero` - * @HB_SCRIPT_MIAO: `Plrd` - * @HB_SCRIPT_SHARADA: `Shrd` - * @HB_SCRIPT_SORA_SOMPENG: `Sora` - * @HB_SCRIPT_TAKRI: `Takr` - * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30 - * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30 - * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30 - * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30 - * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30 - * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30 - * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30 - * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30 - * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30 - * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30 - * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30 - * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30 - * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30 - * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30 - * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30 - * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30 - * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30 - * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30 - * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30 - * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30 - * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30 - * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30 - * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30 - * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30 - * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30 - * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30 - * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30 - * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30 - * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30 - * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0 - * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0 - * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0 - * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0 - * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0 - * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0 - * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0 - * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0 - * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0 - * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0 - * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0 - * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0 - * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0 - * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0 - * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0 - * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0 - * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0 - * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0 - * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0 - * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0 - * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0 - * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7 - * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7 - * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7 - * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7 - * @HB_SCRIPT_CYPRO_MINOAN: `Cpmn`, Since: 3.0.0 - * @HB_SCRIPT_OLD_UYGHUR: `Ougr`, Since: 3.0.0 - * @HB_SCRIPT_TANGSA: `Tnsa`, Since: 3.0.0 - * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 - * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 - * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 - * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 - * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 - * @HB_SCRIPT_GARAY: `Gara`, Since: 10.0.0 - * @HB_SCRIPT_GURUNG_KHEMA: `Gukh`, Since: 10.0.0 - * @HB_SCRIPT_KIRAT_RAI: `Krai`, Since: 10.0.0 - * @HB_SCRIPT_OL_ONAL: `Onao`, Since: 10.0.0 - * @HB_SCRIPT_SUNUWAR: `Sunu`, Since: 10.0.0 - * @HB_SCRIPT_TODHRI: `Todr`, Since: 10.0.0 - * @HB_SCRIPT_TULU_TIGALARI: `Tutg`, Since: 10.0.0 - * @HB_SCRIPT_INVALID: No script set - * - * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding - * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/). - * - * See also the Script (sc) property of the Unicode Character Database. - * - **/ - -/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ -typedef enum -{ - HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), /*1.1*/ - HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), /*1.1*/ - HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /*5.0*/ - - HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), /*1.1*/ - HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), /*1.1*/ - HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), /*1.1*/ - HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), /*1.1*/ - HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), /*1.1*/ - HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), /*1.1*/ - HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), /*1.1*/ - HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), /*1.1*/ - HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), /*1.1*/ - HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), /*1.1*/ - HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), /*1.1*/ - HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), /*1.1*/ - HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), /*1.1*/ - HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), /*1.1*/ - HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), /*1.1*/ - HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), /*1.1*/ - HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), /*1.1*/ - HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), /*1.1*/ - HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), /*1.1*/ - HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), /*1.1*/ - HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), /*1.1*/ - HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), /*1.1*/ - - HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.0*/ - - HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), /*3.0*/ - HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), /*3.0*/ - HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), /*3.0*/ - HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), /*3.0*/ - HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), /*3.0*/ - HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), /*3.0*/ - HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), /*3.0*/ - HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), /*3.0*/ - HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), /*3.0*/ - HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), /*3.0*/ - HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), /*3.0*/ - HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), /*3.0*/ - HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), /*3.0*/ - HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), /*3.0*/ - - HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), /*3.1*/ - HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), /*3.1*/ - HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), /*3.1*/ - - HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), /*3.2*/ - HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), /*3.2*/ - HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), /*3.2*/ - HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /*3.2*/ - - HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), /*4.0*/ - HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), /*4.0*/ - HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), /*4.0*/ - HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), /*4.0*/ - HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), /*4.0*/ - HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), /*4.0*/ - HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /*4.0*/ - - HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), /*4.1*/ - HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), /*4.1*/ - HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), /*4.1*/ - HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), /*4.1*/ - HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), /*4.1*/ - HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), /*4.1*/ - HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), /*4.1*/ - HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /*4.1*/ - - HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), /*5.0*/ - HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), /*5.0*/ - HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), /*5.0*/ - HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), /*5.0*/ - HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), /*5.0*/ - - HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), /*5.1*/ - HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), /*5.1*/ - HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), /*5.1*/ - HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), /*5.1*/ - HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), /*5.1*/ - HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), /*5.1*/ - HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), /*5.1*/ - HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), /*5.1*/ - HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), /*5.1*/ - HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), /*5.1*/ - HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /*5.1*/ - - HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), /*5.2*/ - HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), /*5.2*/ - HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), /*5.2*/ - HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), /*5.2*/ - HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), /*5.2*/ - HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), /*5.2*/ - HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), /*5.2*/ - HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), /*5.2*/ - HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), /*5.2*/ - HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), /*5.2*/ - HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), /*5.2*/ - HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), /*5.2*/ - HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), /*5.2*/ - HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), /*5.2*/ - HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /*5.2*/ - - HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), /*6.0*/ - HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), /*6.0*/ - HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), /*6.0*/ - - HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), /*6.1*/ - HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), /*6.1*/ - HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), /*6.1*/ - HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), /*6.1*/ - HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), /*6.1*/ - HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), /*6.1*/ - HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /*6.1*/ - - /* - * Since: 0.9.30 - */ - HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), /*7.0*/ - HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), /*7.0*/ - HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), /*7.0*/ - HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), /*7.0*/ - HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), /*7.0*/ - HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), /*7.0*/ - HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), /*7.0*/ - HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), /*7.0*/ - HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), /*7.0*/ - HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), /*7.0*/ - HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), /*7.0*/ - HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), /*7.0*/ - HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), /*7.0*/ - HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), /*7.0*/ - HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), /*7.0*/ - HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), /*7.0*/ - HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), /*7.0*/ - HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), /*7.0*/ - HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), /*7.0*/ - HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), /*7.0*/ - HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), /*7.0*/ - HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), /*7.0*/ - HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), /*7.0*/ - - HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), /*8.0*/ - HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), /*8.0*/ - HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), /*8.0*/ - HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), /*8.0*/ - HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), /*8.0*/ - HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), /*8.0*/ - - /* - * Since 1.3.0 - */ - HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), /*9.0*/ - HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), /*9.0*/ - HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), /*9.0*/ - HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), /*9.0*/ - HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ - HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), /*9.0*/ - - /* - * Since 1.6.0 - */ - HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), /*10.0*/ - HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), /*10.0*/ - HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), /*10.0*/ - HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), /*10.0*/ - - /* - * Since 1.8.0 - */ - HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), /*11.0*/ - HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), /*11.0*/ - HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), /*11.0*/ - HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), /*11.0*/ - HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), /*11.0*/ - HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/ - HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), /*11.0*/ - - /* - * Since 2.4.0 - */ - HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), /*12.0*/ - HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), /*12.0*/ - HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), /*12.0*/ - HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), /*12.0*/ - - /* - * Since 2.6.7 - */ - HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), /*13.0*/ - HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), /*13.0*/ - HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), /*13.0*/ - HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), /*13.0*/ - - /* - * Since 3.0.0 - */ - HB_SCRIPT_CYPRO_MINOAN = HB_TAG ('C','p','m','n'), /*14.0*/ - HB_SCRIPT_OLD_UYGHUR = HB_TAG ('O','u','g','r'), /*14.0*/ - HB_SCRIPT_TANGSA = HB_TAG ('T','n','s','a'), /*14.0*/ - HB_SCRIPT_TOTO = HB_TAG ('T','o','t','o'), /*14.0*/ - HB_SCRIPT_VITHKUQI = HB_TAG ('V','i','t','h'), /*14.0*/ - - /* - * Since 3.4.0 - */ - HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), - - /* - * Since 5.2.0 - */ - HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ - HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ - - /* - * Since 10.0.0 - */ - HB_SCRIPT_GARAY = HB_TAG ('G','a','r','a'), /*16.0*/ - HB_SCRIPT_GURUNG_KHEMA = HB_TAG ('G','u','k','h'), /*16.0*/ - HB_SCRIPT_KIRAT_RAI = HB_TAG ('K','r','a','i'), /*16.0*/ - HB_SCRIPT_OL_ONAL = HB_TAG ('O','n','a','o'), /*16.0*/ - HB_SCRIPT_SUNUWAR = HB_TAG ('S','u','n','u'), /*16.0*/ - HB_SCRIPT_TODHRI = HB_TAG ('T','o','d','r'), /*16.0*/ - HB_SCRIPT_TULU_TIGALARI = HB_TAG ('T','u','t','g'), /*16.0*/ - - /* No script set. */ - HB_SCRIPT_INVALID = HB_TAG_NONE, - - /*< private >*/ - - /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t - * without risking undefined behavior. We have two, for historical reasons. - * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed - * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. - * - * See this thread for technicalities: - * - * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html - */ - _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ - _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ - -} hb_script_t; - +#include "hb-script-list.h" /* Script functions */ @@ -948,6 +519,16 @@ typedef struct hb_glyph_extents_t { */ typedef struct hb_font_t hb_font_t; +/* Not of much use to clients. */ +HB_EXTERN void* +hb_malloc (size_t size); +HB_EXTERN void* +hb_calloc (size_t nmemb, size_t size); +HB_EXTERN void* +hb_realloc (void *ptr, size_t size); +HB_EXTERN void +hb_free (void *ptr); + HB_END_DECLS #endif /* HB_COMMON_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-config.hh b/src/java.desktop/share/native/libharfbuzz/hb-config.hh index 40cc2403c18..3956690da35 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-config.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh @@ -146,6 +146,7 @@ #ifdef HB_NO_DRAW #define HB_NO_OUTLINE +#define HB_NO_PAINT #endif #ifdef HB_NO_GETENV @@ -191,7 +192,6 @@ #ifdef HB_MINIMIZE_MEMORY_USAGE #define HB_NO_GDEF_CACHE #define HB_NO_OT_LAYOUT_LOOKUP_CACHE -#define HB_NO_OT_FONT_ADVANCE_CACHE #define HB_NO_OT_FONT_CMAP_CACHE #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh index 341e61e57c6..1e9a18b9326 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh @@ -49,15 +49,15 @@ struct hb_options_t }; union hb_options_union_t { - int i; + unsigned i; hb_options_t opts; }; -static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), ""); +static_assert ((sizeof (hb_atomic_t) >= sizeof (hb_options_union_t)), ""); HB_INTERNAL void _hb_options_init (); -extern HB_INTERNAL hb_atomic_int_t _hb_options; +extern HB_INTERNAL hb_atomic_t _hb_options; static inline hb_options_t hb_options () diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h index d854a2b16f5..927563c4c40 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h @@ -275,6 +275,48 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data); +/** + * hb_font_draw_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_draw_glyph_func_or_fail_t instead. + **/ +typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_paint_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @paint_funcs: The paint functions to use + * @paint_data: The data accompanying the paint functions + * @palette_index: The color palette to use + * @foreground: The foreground color + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_paint_glyph_or_fail_func_t instead. + */ +typedef hb_bool_t (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data); + /** * hb_font_funcs_set_glyph_shape_func: * @ffuncs: A font-function structure @@ -288,13 +330,49 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data * Since: 4.0.0 * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead **/ -HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_func) +HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func) HB_EXTERN void hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_shape_func_t func, void *user_data, hb_destroy_func_t destroy); -HB_DEPRECATED_FOR (hb_font_draw_glyph) +/** + * hb_font_funcs_set_draw_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_draw_glyph_func_t. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_funcs_set_draw_glyph_or_fail_func instead. + **/ +HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func) +HB_EXTERN void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_paint_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is no longer needed + * + * Sets the implementation function for #hb_font_paint_glyph_func_t. + * + * Since: 7.0.0 + * XDeprecated: REPLACEME: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead. + */ +HB_DEPRECATED_FOR (hb_font_funcs_set_paint_glyph_or_fail_func) +HB_EXTERN void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_DEPRECATED_FOR (hb_font_draw_glyph_or_fail) HB_EXTERN void hb_font_get_glyph_shape (hb_font_t *font, hb_codepoint_t glyph, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc index f2821578b65..8764ffe7129 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc @@ -28,6 +28,11 @@ #include "hb-draw.hh" +#include "hb-geometry.hh" + +#include "hb-machinery.hh" + + /** * SECTION:hb-draw * @title: hb-draw @@ -455,4 +460,92 @@ hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, } +static void +hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (control_x, control_y); + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (control1_x, control1_y); + extents->add_point (control2_x, control2_y); + extents->add_point (to_x, to_y); +} + +static inline void free_static_draw_extents_funcs (); + +static struct hb_draw_extents_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_draw_extents_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_draw_extents_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_draw_extents_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_draw_extents_cubic_to, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_draw_extents_funcs); + + return funcs; + } +} static_draw_extents_funcs; + +static inline +void free_static_draw_extents_funcs () +{ + static_draw_extents_funcs.free_instance (); +} + +hb_draw_funcs_t * +hb_draw_extents_get_funcs () +{ + return static_draw_extents_funcs.get_unconst (); +} + + #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.h b/src/java.desktop/share/native/libharfbuzz/hb-draw.h index 4556b082d60..06299e17a78 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.h @@ -70,7 +70,7 @@ typedef struct hb_draw_state_t { * * The default #hb_draw_state_t at the start of glyph drawing. */ -#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}, {0.}, {0.}, {0.}, {0.}} +#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0}, {0}, {0}, {0}, {0}, {0}, {0}} /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.hh b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh index 2c30843de64..224e10939fe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh @@ -99,6 +99,7 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (st.path_open)) close_path (draw_data, st); + st.current_x = to_x; st.current_y = to_y; } @@ -109,7 +110,9 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_line_to (draw_data, st, to_x, to_y); + st.current_x = to_x; st.current_y = to_y; } @@ -121,7 +124,9 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y); + st.current_x = to_x; st.current_y = to_y; } @@ -134,7 +139,9 @@ struct hb_draw_funcs_t float to_x, float to_y) { if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); + st.current_x = to_x; st.current_y = to_y; } @@ -168,9 +175,8 @@ DECLARE_NULL_INSTANCE (hb_draw_funcs_t); struct hb_draw_session_t { - hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_, float slant_ = 0.f) - : slant {slant_}, not_slanted {slant == 0.f}, - funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT + hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_) + : funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT {} ~hb_draw_session_t () { close_path (); } @@ -178,36 +184,23 @@ struct hb_draw_session_t HB_ALWAYS_INLINE void move_to (float to_x, float to_y) { - if (likely (not_slanted)) - funcs->move_to (draw_data, st, - to_x, to_y); - else - funcs->move_to (draw_data, st, - to_x + to_y * slant, to_y); + funcs->move_to (draw_data, st, + to_x, to_y); } HB_ALWAYS_INLINE void line_to (float to_x, float to_y) { - if (likely (not_slanted)) - funcs->line_to (draw_data, st, - to_x, to_y); - else - funcs->line_to (draw_data, st, - to_x + to_y * slant, to_y); + funcs->line_to (draw_data, st, + to_x, to_y); } void HB_ALWAYS_INLINE quadratic_to (float control_x, float control_y, float to_x, float to_y) { - if (likely (not_slanted)) - funcs->quadratic_to (draw_data, st, - control_x, control_y, - to_x, to_y); - else - funcs->quadratic_to (draw_data, st, - control_x + control_y * slant, control_y, - to_x + to_y * slant, to_y); + funcs->quadratic_to (draw_data, st, + control_x, control_y, + to_x, to_y); } void HB_ALWAYS_INLINE @@ -215,16 +208,10 @@ struct hb_draw_session_t float control2_x, float control2_y, float to_x, float to_y) { - if (likely (not_slanted)) - funcs->cubic_to (draw_data, st, - control1_x, control1_y, - control2_x, control2_y, - to_x, to_y); - else - funcs->cubic_to (draw_data, st, - control1_x + control1_y * slant, control1_y, - control2_x + control2_y * slant, control2_y, - to_x + to_y * slant, to_y); + funcs->cubic_to (draw_data, st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); } HB_ALWAYS_INLINE void close_path () @@ -233,11 +220,14 @@ struct hb_draw_session_t } public: - float slant; - bool not_slanted; hb_draw_funcs_t *funcs; void *draw_data; hb_draw_state_t st; }; + +HB_INTERNAL hb_draw_funcs_t * +hb_draw_extents_get_funcs (); + + #endif /* HB_DRAW_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc index 7967ae3241b..9d59d7c9468 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc @@ -34,6 +34,16 @@ #include "hb-ot-face.hh" #include "hb-ot-cmap-table.hh" +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif +#ifdef HAVE_CORETEXT +#include "hb-coretext.h" +#endif +#ifdef HAVE_DIRECTWRITE +#include "hb-directwrite.h" +#endif + /** * SECTION:hb-face @@ -72,14 +82,14 @@ hb_face_count (hb_blob_t *blob) if (unlikely (!blob)) return 0; - /* TODO We shouldn't be sanitizing blob. Port to run sanitizer and return if not sane. */ - /* Make API signature const after. */ - hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); - const OT::OpenTypeFontFile& ot = *sanitized->as (); - unsigned int ret = ot.get_face_count (); - hb_blob_destroy (sanitized); + hb_sanitize_context_t c (blob); - return ret; + const char *start = hb_blob_get_data (blob, nullptr); + auto *ot = reinterpret_cast (const_cast (start)); + if (unlikely (!ot->sanitize (&c))) + return 0; + + return ot->get_face_count (); } /* @@ -318,7 +328,209 @@ hb_face_create_from_file_or_fail (const char *file_name, return face; } + +static struct supported_face_loaders_t { + char name[16]; + hb_face_t * (*from_file) (const char *font_file, unsigned face_index); + hb_face_t * (*from_blob) (hb_blob_t *blob, unsigned face_index); +} supported_face_loaders[] = +{ + {"ot", +#ifndef HB_NO_OPEN + hb_face_create_from_file_or_fail, +#else + nullptr, #endif + hb_face_create_or_fail + }, +#ifdef HAVE_FREETYPE + {"ft", + hb_ft_face_create_from_file_or_fail, + hb_ft_face_create_from_blob_or_fail + }, +#endif +#ifdef HAVE_CORETEXT + {"coretext", + hb_coretext_face_create_from_file_or_fail, + hb_coretext_face_create_from_blob_or_fail + }, +#endif +#ifdef HAVE_DIRECTWRITE + {"directwrite", + hb_directwrite_face_create_from_file_or_fail, + hb_directwrite_face_create_from_blob_or_fail + }, +#endif +}; + +static const char *get_default_loader_name () +{ + static hb_atomic_t static_loader_name; + const char *loader_name = static_loader_name.get_acquire (); + if (!loader_name) + { + loader_name = getenv ("HB_FACE_LOADER"); + if (!loader_name) + loader_name = ""; + if (!static_loader_name.cmpexch (nullptr, loader_name)) + loader_name = static_loader_name.get_acquire (); + } + return loader_name; +} + +/** + * hb_face_create_from_file_or_fail_using: + * @file_name: A font filename + * @index: The index of the face within the file + * @loader_name: (nullable): The name of the loader to use, or `NULL` + * + * A thin wrapper around the face loader functions registered with HarfBuzz. + * If @loader_name is `NULL` or the empty string, the first available loader + * is used. + * + * For example, the FreeType ("ft") loader might be able to load + * WOFF and WOFF2 files if FreeType is built with those features, + * whereas the OpenType ("ot") loader will not. + * + * Return value: (transfer full): The new face object, or `NULL` if + * the file cannot be read or the loader fails to load the face. + * + * Since: 11.0.0 + **/ +hb_face_t * +hb_face_create_from_file_or_fail_using (const char *file_name, + unsigned int index, + const char *loader_name) +{ + // Duplicated in hb_face_create_or_fail_using + bool retry = false; + if (!loader_name || !*loader_name) + { + loader_name = get_default_loader_name (); + retry = true; + } + if (loader_name && !*loader_name) loader_name = nullptr; + +retry: + for (unsigned i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++) + { + if (!loader_name || (supported_face_loaders[i].from_file && !strcmp (supported_face_loaders[i].name, loader_name))) + return supported_face_loaders[i].from_file (file_name, index); + } + + if (retry) + { + retry = false; + loader_name = nullptr; + goto retry; + } + + return nullptr; +} + +/** + * hb_face_create_or_fail_using: + * @blob: #hb_blob_t to work upon + * @index: The index of the face within @blob + * @loader_name: (nullable): The name of the loader to use, or `NULL` + * + * A thin wrapper around the face loader functions registered with HarfBuzz. + * If @loader_name is `NULL` or the empty string, the first available loader + * is used. + * + * For example, the FreeType ("ft") loader might be able to load + * WOFF and WOFF2 files if FreeType is built with those features, + * whereas the OpenType ("ot") loader will not. + * + * Return value: (transfer full): The new face object, or `NULL` if + * the loader fails to load the face. + * + * Since: 11.0.0 + **/ +hb_face_t * +hb_face_create_or_fail_using (hb_blob_t *blob, + unsigned int index, + const char *loader_name) +{ + // Duplicated in hb_face_create_from_file_or_fail_using + bool retry = false; + if (!loader_name || !*loader_name) + { + loader_name = get_default_loader_name (); + retry = true; + } + if (loader_name && !*loader_name) loader_name = nullptr; + +retry: + for (unsigned i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++) + { + if (!loader_name || (supported_face_loaders[i].from_blob && !strcmp (supported_face_loaders[i].name, loader_name))) + return supported_face_loaders[i].from_blob (blob, index); + } + + if (retry) + { + retry = false; + loader_name = nullptr; + goto retry; + } + + return nullptr; +} + +static inline void free_static_face_loader_list (); + +static const char * const nil_face_loader_list[] = {nullptr}; + +static struct hb_face_loader_list_lazy_loader_t : hb_lazy_loader_t +{ + static const char ** create () + { + const char **face_loader_list = (const char **) hb_calloc (1 + ARRAY_LENGTH (supported_face_loaders), sizeof (const char *)); + if (unlikely (!face_loader_list)) + return nullptr; + + unsigned i; + for (i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++) + face_loader_list[i] = supported_face_loaders[i].name; + face_loader_list[i] = nullptr; + + hb_atexit (free_static_face_loader_list); + + return face_loader_list; + } + static void destroy (const char **l) + { hb_free (l); } + static const char * const * get_null () + { return nil_face_loader_list; } +} static_face_loader_list; + +static inline +void free_static_face_loader_list () +{ + static_face_loader_list.free_instance (); +} + +/** + * hb_face_list_loaders: + * + * Retrieves the list of face loaders supported by HarfBuzz. + * + * Return value: (transfer none) (array zero-terminated=1): a + * `NULL`-terminated array of supported face loaders + * constant strings. The returned array is owned by HarfBuzz + * and should not be modified or freed. + * + * Since: 11.0.0 + **/ +const char ** +hb_face_list_loaders () +{ + return static_face_loader_list.get_unconst (); +} +#endif + /** * hb_face_get_empty: @@ -460,7 +672,7 @@ hb_face_make_immutable (hb_face_t *face) * Since: 0.9.2 **/ hb_bool_t -hb_face_is_immutable (const hb_face_t *face) +hb_face_is_immutable (hb_face_t *face) { return hb_object_is_immutable (face); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.h b/src/java.desktop/share/native/libharfbuzz/hb-face.h index 953298fa50b..2c130f00fee 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.h @@ -63,10 +63,24 @@ HB_EXTERN hb_face_t * hb_face_create_or_fail (hb_blob_t *blob, unsigned int index); +HB_EXTERN hb_face_t * +hb_face_create_or_fail_using (hb_blob_t *blob, + unsigned int index, + const char *loader_name); + HB_EXTERN hb_face_t * hb_face_create_from_file_or_fail (const char *file_name, unsigned int index); +HB_EXTERN hb_face_t * +hb_face_create_from_file_or_fail_using (const char *file_name, + unsigned int index, + const char *loader_name); + +HB_EXTERN const char ** +hb_face_list_loaders (void); + + /** * hb_reference_table_func_t: * @face: an #hb_face_t to reference table for @@ -117,7 +131,7 @@ HB_EXTERN void hb_face_make_immutable (hb_face_t *face); HB_EXTERN hb_bool_t -hb_face_is_immutable (const hb_face_t *face); +hb_face_is_immutable (hb_face_t *face); HB_EXTERN hb_blob_t * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-face.hh index 4d12030f187..e58e8f9b94c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.hh @@ -49,8 +49,8 @@ struct hb_face_t hb_object_header_t header; unsigned int index; /* Face index in a collection, zero-based. */ - mutable hb_atomic_int_t upem; /* Units-per-EM. */ - mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */ + mutable hb_atomic_t upem; /* Units-per-EM. */ + mutable hb_atomic_t num_glyphs;/* Number of glyphs. */ hb_reference_table_func_t reference_table_func; void *user_data; @@ -70,7 +70,7 @@ struct hb_face_t plan_node_t *next; }; #ifndef HB_NO_SHAPER - hb_atomic_ptr_t shape_plans; + hb_atomic_t shape_plans; #endif hb_blob_t *reference_table (hb_tag_t tag) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc index 0c9084c574f..849855e2c22 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc @@ -38,6 +38,22 @@ #include "hb-ot-var-avar-table.hh" #include "hb-ot-var-fvar-table.hh" +#ifndef HB_NO_OT_FONT +#include "hb-ot.h" +#endif +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif +#ifdef HAVE_FONTATIONS +#include "hb-fontations.h" +#endif +#ifdef HAVE_CORETEXT +#include "hb-coretext.h" +#endif +#ifdef HAVE_DIRECTWRITE +#include "hb-directwrite.h" +#endif + /** * SECTION:hb-font @@ -87,7 +103,7 @@ hb_font_get_font_h_extents_default (hb_font_t *font, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_h_extents (extents); + hb_bool_t ret = font->parent->get_font_h_extents (extents, false); if (ret) { extents->ascender = font->parent_scale_y_distance (extents->ascender); extents->descender = font->parent_scale_y_distance (extents->descender); @@ -112,7 +128,7 @@ hb_font_get_font_v_extents_default (hb_font_t *font, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_v_extents (extents); + hb_bool_t ret = font->parent->get_font_v_extents (extents, false); if (ret) { extents->ascender = font->parent_scale_x_distance (extents->ascender); extents->descender = font->parent_scale_x_distance (extents->descender); @@ -218,10 +234,10 @@ hb_font_get_glyph_h_advance_default (hb_font_t *font, if (font->has_glyph_h_advances_func_set ()) { hb_position_t ret; - font->get_glyph_h_advances (1, &glyph, 0, &ret, 0); + font->get_glyph_h_advances (1, &glyph, 0, &ret, 0, false); return ret; } - return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); + return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph, false)); } static hb_position_t @@ -243,10 +259,10 @@ hb_font_get_glyph_v_advance_default (hb_font_t *font, if (font->has_glyph_v_advances_func_set ()) { hb_position_t ret; - font->get_glyph_v_advances (1, &glyph, 0, &ret, 0); + font->get_glyph_v_advances (1, &glyph, 0, &ret, 0, false); return ret; } - return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); + return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph, false)); } #define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default @@ -265,7 +281,7 @@ hb_font_get_glyph_h_advances_default (hb_font_t* font, { for (unsigned int i = 0; i < count; i++) { - *first_advance = font->get_glyph_h_advance (*first_glyph); + *first_advance = font->get_glyph_h_advance (*first_glyph, false); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } @@ -274,7 +290,8 @@ hb_font_get_glyph_h_advances_default (hb_font_t* font, font->parent->get_glyph_h_advances (count, first_glyph, glyph_stride, - first_advance, advance_stride); + first_advance, advance_stride, + false); for (unsigned int i = 0; i < count; i++) { *first_advance = font->parent_scale_x_distance (*first_advance); @@ -297,7 +314,7 @@ hb_font_get_glyph_v_advances_default (hb_font_t* font, { for (unsigned int i = 0; i < count; i++) { - *first_advance = font->get_glyph_v_advance (*first_glyph); + *first_advance = font->get_glyph_v_advance (*first_glyph, false); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } @@ -306,7 +323,8 @@ hb_font_get_glyph_v_advances_default (hb_font_t* font, font->parent->get_glyph_v_advances (count, first_glyph, glyph_stride, - first_advance, advance_stride); + first_advance, advance_stride, + false); for (unsigned int i = 0; i < count; i++) { *first_advance = font->parent_scale_y_distance (*first_advance); @@ -426,7 +444,7 @@ hb_font_get_glyph_extents_default (hb_font_t *font, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); + hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents, false); if (ret) { font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); font->parent_scale_distance (&extents->width, &extents->height); @@ -456,7 +474,7 @@ hb_font_get_glyph_contour_point_default (hb_font_t *font, hb_position_t *y, void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); + hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y, false); if (ret) font->parent_scale_position (x, y); return ret; @@ -508,26 +526,28 @@ hb_font_get_glyph_from_name_default (hb_font_t *font, return font->parent->get_glyph_from_name (name, len, glyph); } -static void -hb_font_draw_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, - void *draw_data, - void *user_data HB_UNUSED) +static hb_bool_t +hb_font_draw_glyph_or_fail_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) { + return false; } -static void -hb_font_paint_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph HB_UNUSED, - hb_paint_funcs_t *paint_funcs HB_UNUSED, - void *paint_data HB_UNUSED, - unsigned int palette HB_UNUSED, - hb_color_t foreground HB_UNUSED, - void *user_data HB_UNUSED) +static hb_bool_t +hb_font_paint_glyph_or_fail_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_paint_funcs_t *paint_funcs HB_UNUSED, + void *paint_data HB_UNUSED, + unsigned int palette HB_UNUSED, + hb_color_t foreground HB_UNUSED, + void *user_data HB_UNUSED) { + return false; } typedef struct hb_font_draw_glyph_default_adaptor_t { @@ -535,7 +555,6 @@ typedef struct hb_font_draw_glyph_default_adaptor_t { void *draw_data; float x_scale; float y_scale; - float slant; } hb_font_draw_glyph_default_adaptor_t; static void @@ -548,10 +567,9 @@ hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * to_x, y_scale * to_y); } static void @@ -563,13 +581,12 @@ hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; - st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_x = st->current_x * x_scale; st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * to_x, y_scale * to_y); } static void @@ -582,14 +599,13 @@ hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; - st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_x = st->current_x * x_scale; st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st, - x_scale * control_x + slant * control_y, y_scale * control_y, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * control_x, y_scale * control_y, + x_scale * to_x, y_scale * to_y); } static void @@ -603,15 +619,14 @@ hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; - float slant = adaptor->slant; - st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_x = st->current_x * x_scale; st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st, - x_scale * control1_x + slant * control1_y, y_scale * control1_y, - x_scale * control2_x + slant * control2_y, y_scale * control2_y, - x_scale * to_x + slant * to_y, y_scale * to_y); + x_scale * control1_x, y_scale * control1_y, + x_scale * control2_x, y_scale * control2_y, + x_scale * to_x, y_scale * to_y); } static void @@ -634,49 +649,47 @@ static const hb_draw_funcs_t _hb_draw_funcs_default = { } }; -static void -hb_font_draw_glyph_default (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, - void *draw_data, - void *user_data HB_UNUSED) +static hb_bool_t +hb_font_draw_glyph_or_fail_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) { hb_font_draw_glyph_default_adaptor_t adaptor = { draw_funcs, draw_data, font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, - font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, - font->parent->y_scale ? (font->slant - font->parent->slant) * - (float) font->x_scale / (float) font->parent->y_scale : 0.f + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f }; - font->parent->draw_glyph (glyph, - const_cast (&_hb_draw_funcs_default), - &adaptor); + return font->parent->draw_glyph_or_fail (glyph, + const_cast (&_hb_draw_funcs_default), + &adaptor, + false); } -static void -hb_font_paint_glyph_default (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, - void *paint_data, - unsigned int palette, - hb_color_t foreground, - void *user_data) +static hb_bool_t +hb_font_paint_glyph_or_fail_default (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) { paint_funcs->push_transform (paint_data, - font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, - font->parent->y_scale ? (font->slant - font->parent->slant) * - (float) font->x_scale / (float) font->parent->y_scale : 0.f, - 0.f, - font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, - 0.f, 0.f); + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0, 0, + 0, font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0, + 0, 0); - font->parent->paint_glyph (glyph, paint_funcs, paint_data, palette, foreground); + bool ret = font->parent->paint_glyph_or_fail (glyph, paint_funcs, paint_data, palette, foreground); paint_funcs->pop_transform (paint_data); + + return ret; } DEFINE_NULL_INSTANCE (hb_font_funcs_t) = @@ -1413,6 +1426,92 @@ hb_font_get_glyph_shape (hb_font_t *font, } #endif +/** + * hb_font_draw_glyph_or_fail: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Draws the outline that corresponds to a glyph in the specified @font. + * + * This is a newer name for hb_font_draw_glyph(), that returns `false` + * if the font has no outlines for the glyph. + * + * The outline is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Return value: `true` if glyph was drawn, `false` otherwise + * + * XSince: REPLACEME + **/ +hb_bool_t +hb_font_draw_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + return font->draw_glyph_or_fail (glyph, dfuncs, draw_data); +} + +/** + * hb_font_paint_glyph_or_fail: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @pfuncs: #hb_paint_funcs_t to paint with + * @paint_data: User data to pass to paint callbacks + * @palette_index: The index of the font's color palette to use + * @foreground: The foreground color, unpremultipled + * + * Paints a color glyph. + * + * This function is similar to, but lower-level than, + * hb_font_paint_glyph(). It is suitable for clients that + * need more control. If there are no color glyphs available, + * it will return `false`. The client can then fall back to + * hb_font_draw_glyph_or_fail() for the monochrome outline glyph. + * + * The painting instructions are returned by way of calls to + * the callbacks of the @funcs object, with @paint_data passed + * to them. + * + * If the font has color palettes (see hb_ot_color_has_palettes()), + * then @palette_index selects the palette to use. If the font only + * has one palette, this will be 0. + * + * Return value: `true` if glyph was painted, `false` otherwise + * + * XSince: REPLACEME + */ +hb_bool_t +hb_font_paint_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground) +{ + return font->paint_glyph_or_fail (glyph, pfuncs, paint_data, palette_index, foreground); +} + +/* A bit higher-level, and with fallback */ + +void +hb_font_t::paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground) +{ + if (paint_glyph_or_fail (glyph, + paint_funcs, paint_data, + palette, foreground)) + return; + + /* Fallback for outline glyph. */ + paint_funcs->push_clip_glyph (paint_data, glyph, this); + paint_funcs->color (paint_data, true, foreground); + paint_funcs->pop_clip (paint_data); +} + + /** * hb_font_draw_glyph: * @font: #hb_font_t to work upon @@ -1422,6 +1521,9 @@ hb_font_get_glyph_shape (hb_font_t *font, * * Draws the outline that corresponds to a glyph in the specified @font. * + * This is an older name for hb_font_draw_glyph_or_fail(), with no + * return value. + * * The outline is returned by way of calls to the callbacks of the @dfuncs * objects, with @draw_data passed to them. * @@ -1429,10 +1531,10 @@ hb_font_get_glyph_shape (hb_font_t *font, **/ void hb_font_draw_glyph (hb_font_t *font, - hb_codepoint_t glyph, - hb_draw_funcs_t *dfuncs, void *draw_data) + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) { - font->draw_glyph (glyph, dfuncs, draw_data); + (void) hb_font_draw_glyph_or_fail (font, glyph, dfuncs, draw_data); } /** @@ -1444,7 +1546,10 @@ hb_font_draw_glyph (hb_font_t *font, * @palette_index: The index of the font's color palette to use * @foreground: The foreground color, unpremultipled * - * Paints the glyph. + * Paints the glyph. This function is similar to + * hb_font_paint_glyph_or_fail(), but if painting a color glyph + * failed, it will fall back to painting an outline monochrome + * glyph. * * The painting instructions are returned by way of calls to * the callbacks of the @funcs object, with @paint_data passed @@ -1466,8 +1571,6 @@ hb_font_paint_glyph (hb_font_t *font, font->paint_glyph (glyph, pfuncs, paint_data, palette_index, foreground); } -/* A bit higher-level, and with fallback */ - /** * hb_font_get_extents_for_direction: * @font: #hb_font_t to work upon @@ -1854,10 +1957,7 @@ hb_font_create (hb_face_t *face) { hb_font_t *font = _hb_font_create (face); -#ifndef HB_NO_OT_FONT - /* Install our in-house, very lightweight, funcs. */ - hb_ot_font_set_funcs (font); -#endif + hb_font_set_funcs_using (font, nullptr); #ifndef HB_NO_VAR if (face && face->index >> 16) @@ -1880,7 +1980,8 @@ _hb_font_adopt_var_coords (hb_font_t *font, font->design_coords = design_coords; font->num_coords = coords_length; - font->mults_changed (); // Easiest to call this to drop cached data + font->changed (); + font->serial_coords = font->serial; } /** @@ -1935,7 +2036,8 @@ hb_font_create_sub_font (hb_font_t *parent) } } - font->mults_changed (); + font->changed (); + font->serial_coords = font->serial; return font; } @@ -2023,7 +2125,7 @@ hb_font_set_user_data (hb_font_t *font, hb_bool_t replace) { if (!hb_object_is_immutable (font)) - font->serial++; + font->changed (); return hb_object_set_user_data (font, key, data, destroy, replace); } @@ -2098,7 +2200,7 @@ hb_font_is_immutable (hb_font_t *font) unsigned int hb_font_get_serial (hb_font_t *font) { - return font->serial; + return font->serial.get_acquire (); } /** @@ -2117,9 +2219,7 @@ hb_font_changed (hb_font_t *font) if (hb_object_is_immutable (font)) return; - font->serial++; - - font->mults_changed (); + font->changed (); } /** @@ -2141,8 +2241,6 @@ hb_font_set_parent (hb_font_t *font, if (parent == font->parent) return; - font->serial++; - if (!parent) parent = hb_font_get_empty (); @@ -2151,6 +2249,8 @@ hb_font_set_parent (hb_font_t *font, font->parent = hb_font_reference (parent); hb_font_destroy (old); + + font->changed (); } /** @@ -2188,8 +2288,6 @@ hb_font_set_face (hb_font_t *font, if (face == font->face) return; - font->serial++; - if (unlikely (!face)) face = hb_face_get_empty (); @@ -2197,9 +2295,12 @@ hb_font_set_face (hb_font_t *font, hb_face_make_immutable (face); font->face = hb_face_reference (face); - font->mults_changed (); + font->changed (); hb_face_destroy (old); + + font->changed (); + font->serial_coords = font->serial; } /** @@ -2244,8 +2345,6 @@ hb_font_set_funcs (hb_font_t *font, return; } - font->serial++; - if (font->destroy) font->destroy (font->user_data); @@ -2257,6 +2356,8 @@ hb_font_set_funcs (hb_font_t *font, font->klass = klass; font->user_data = font_data; font->destroy = destroy; + + font->changed (); } /** @@ -2283,15 +2384,151 @@ hb_font_set_funcs_data (hb_font_t *font, return; } - font->serial++; - if (font->destroy) font->destroy (font->user_data); font->user_data = font_data; font->destroy = destroy; + + font->changed (); } +static struct supported_font_funcs_t { + char name[16]; + void (*func) (hb_font_t *); +} supported_font_funcs[] = +{ +#ifndef HB_NO_OT_FONT + {"ot", hb_ot_font_set_funcs}, +#endif +#ifdef HAVE_FREETYPE + {"ft", hb_ft_font_set_funcs}, +#endif +#ifdef HAVE_FONTATIONS + {"fontations",hb_fontations_font_set_funcs}, +#endif +#ifdef HAVE_CORETEXT + {"coretext", hb_coretext_font_set_funcs}, +#endif +#ifdef HAVE_DIRECTWRITE + {"directwrite",hb_directwrite_font_set_funcs}, +#endif +}; + +static const char *get_default_funcs_name () +{ + static hb_atomic_t static_funcs_name; + const char *name = static_funcs_name.get_acquire (); + if (!name) + { + name = getenv ("HB_FONT_FUNCS"); + if (!name) + name = ""; + if (!static_funcs_name.cmpexch (nullptr, name)) + name = static_funcs_name.get_acquire (); + } + return name; +} + +/** + * hb_font_set_funcs_using: + * @font: #hb_font_t to work upon + * @name: The name of the font-functions structure to use, or `NULL` + * + * Sets the font-functions structure to use for a font, based on the + * specified name. + * + * If @name is `NULL` or the empty string, the default (first) functioning font-functions + * are used. This default can be changed by setting the `HB_FONT_FUNCS` environment + * variable to the name of the desired font-functions. + * + * Return value: `true` if the font-functions was found and set, `false` otherwise + * + * Since: 11.0.0 + **/ +hb_bool_t +hb_font_set_funcs_using (hb_font_t *font, + const char *name) +{ + bool retry = false; + + if (!name || !*name) + { + name = get_default_funcs_name (); + retry = true; + } + if (name && !*name) name = nullptr; + +retry: + for (unsigned i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + if (!name || strcmp (supported_font_funcs[i].name, name) == 0) + { + supported_font_funcs[i].func (font); + if (name || font->klass != hb_font_funcs_get_empty ()) + return true; + } + + if (retry) + { + retry = false; + name = nullptr; + goto retry; + } + + return false; +} + +static inline void free_static_font_funcs_list (); + +static const char * const nil_font_funcs_list[] = {nullptr}; + +static struct hb_font_funcs_list_lazy_loader_t : hb_lazy_loader_t +{ + static const char ** create () + { + const char **font_funcs_list = (const char **) hb_calloc (1 + ARRAY_LENGTH (supported_font_funcs), sizeof (const char *)); + if (unlikely (!font_funcs_list)) + return nullptr; + + unsigned i; + for (i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + font_funcs_list[i] = supported_font_funcs[i].name; + font_funcs_list[i] = nullptr; + + hb_atexit (free_static_font_funcs_list); + + return font_funcs_list; + } + static void destroy (const char **l) + { hb_free (l); } + static const char * const * get_null () + { return nil_font_funcs_list; } +} static_font_funcs_list; + +static inline +void free_static_font_funcs_list () +{ + static_font_funcs_list.free_instance (); +} + +/** + * hb_font_list_funcs: + * + * Retrieves the list of font functions supported by HarfBuzz. + * + * Return value: (transfer none) (array zero-terminated=1): a + * `NULL`-terminated array of supported font functions + * constant strings. The returned array is owned by HarfBuzz + * and should not be modified or freed. + * + * Since: 11.0.0 + **/ +const char ** +hb_font_list_funcs () +{ + return static_font_funcs_list.get_unconst (); +} /** * hb_font_set_scale: @@ -2339,11 +2576,10 @@ hb_font_set_scale (hb_font_t *font, if (font->x_scale == x_scale && font->y_scale == y_scale) return; - font->serial++; - font->x_scale = x_scale; font->y_scale = y_scale; - font->mults_changed (); + + font->changed (); } /** @@ -2390,10 +2626,10 @@ hb_font_set_ppem (hb_font_t *font, if (font->x_ppem == x_ppem && font->y_ppem == y_ppem) return; - font->serial++; - font->x_ppem = x_ppem; font->y_ppem = y_ppem; + + font->changed (); } /** @@ -2437,9 +2673,9 @@ hb_font_set_ptem (hb_font_t *font, if (font->ptem == ptem) return; - font->serial++; - font->ptem = ptem; + + font->changed (); } /** @@ -2459,6 +2695,23 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +/** + * hb_font_is_synthetic: + * @font: #hb_font_t to work upon + * + * Tests whether a font is synthetic. A synthetic font is one + * that has either synthetic slant or synthetic bold set on it. + * + * Return value: `true` if the font is synthetic, `false` otherwise. + * + * XSince: REPLACEME + */ +hb_bool_t +hb_font_is_synthetic (hb_font_t *font) +{ + return font->is_synthetic (); +} + /** * hb_font_set_synthetic_bold: * @font: #hb_font_t to work upon @@ -2476,7 +2729,7 @@ hb_font_get_ptem (hb_font_t *font) * points of the glyph shape. * * Synthetic boldness is applied when rendering a glyph via - * hb_font_draw_glyph(). + * hb_font_draw_glyph_or_fail(). * * If @in_place is `false`, then glyph advance-widths are also * adjusted, otherwise they are not. The in-place mode is @@ -2499,12 +2752,11 @@ hb_font_set_synthetic_bold (hb_font_t *font, font->embolden_in_place == (bool) in_place) return; - font->serial++; - font->x_embolden = x_embolden; font->y_embolden = y_embolden; font->embolden_in_place = in_place; - font->mults_changed (); + + font->changed (); } /** @@ -2541,7 +2793,7 @@ hb_font_get_synthetic_bold (hb_font_t *font, * HarfBuzz needs to know this value to adjust shaping results, * metrics, and style values to match the slanted rendering. * - * Note: The glyph shape fetched via the hb_font_draw_glyph() + * Note: The glyph shape fetched via the hb_font_draw_glyph_or_fail() * function is slanted to reflect this value as well. * * Note: The slant value is a ratio. For example, a @@ -2558,10 +2810,9 @@ hb_font_set_synthetic_slant (hb_font_t *font, float slant) if (font->slant == slant) return; - font->serial++; - font->slant = slant; - font->mults_changed (); + + font->changed (); } /** @@ -2607,8 +2858,6 @@ hb_font_set_variations (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE) { hb_font_set_var_coords_normalized (font, nullptr, 0); @@ -2677,8 +2926,6 @@ hb_font_set_variation (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - // TODO Share some of this code with set_variations() const OT::fvar &fvar = *font->face->table.fvar; @@ -2749,8 +2996,6 @@ hb_font_set_var_coords_design (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; @@ -2787,8 +3032,6 @@ hb_font_set_var_named_instance (hb_font_t *font, if (font->instance_index == instance_index) return; - font->serial_coords = ++font->serial; - font->instance_index = instance_index; hb_font_set_variations (font, nullptr, 0); } @@ -2834,8 +3077,6 @@ hb_font_set_var_coords_normalized (hb_font_t *font, if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; - int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; @@ -3058,12 +3299,134 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, #ifndef HB_DISABLE_DEPRECATED + +struct hb_draw_glyph_closure_t +{ + hb_font_draw_glyph_func_t func; + void *user_data; + hb_destroy_func_t destroy; +}; +static hb_bool_t +hb_font_draw_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data) +{ + hb_draw_glyph_closure_t *closure = (hb_draw_glyph_closure_t *) user_data; + closure->func (font, font_data, glyph, draw_funcs, draw_data, closure->user_data); + return true; +} +static void +hb_font_draw_glyph_closure_destroy (void *user_data) +{ + hb_draw_glyph_closure_t *closure = (hb_draw_glyph_closure_t *) user_data; + + if (closure->destroy) + closure->destroy (closure->user_data); + hb_free (closure); +} +static void +_hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_draw_glyph_closure_t *closure = (hb_draw_glyph_closure_t *) hb_calloc (1, sizeof (hb_draw_glyph_closure_t)); + if (unlikely (!closure)) + { + if (destroy) + destroy (user_data); + return; + } + closure->func = func; + closure->user_data = user_data; + closure->destroy = destroy; + + hb_font_funcs_set_draw_glyph_or_fail_func (ffuncs, + hb_font_draw_glyph_trampoline, + closure, + hb_font_draw_glyph_closure_destroy); +} +void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + _hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} void hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_shape_func_t func, void *user_data, hb_destroy_func_t destroy /* May be NULL. */) { - hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); + _hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} + +struct hb_paint_glyph_closure_t +{ + hb_font_paint_glyph_func_t func; + void *user_data; + hb_destroy_func_t destroy; +}; +static hb_bool_t +hb_font_paint_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ + hb_paint_glyph_closure_t *closure = (hb_paint_glyph_closure_t *) user_data; + closure->func (font, font_data, glyph, paint_funcs, paint_data, palette, foreground, closure->user_data); + return true; +} +static void +hb_font_paint_glyph_closure_destroy (void *user_data) +{ + hb_paint_glyph_closure_t *closure = (hb_paint_glyph_closure_t *) user_data; + + if (closure->destroy) + closure->destroy (closure->user_data); + hb_free (closure); +} +void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_paint_glyph_closure_t *closure = (hb_paint_glyph_closure_t *) hb_calloc (1, sizeof (hb_paint_glyph_closure_t)); + if (unlikely (!closure)) + { + if (destroy) + destroy (user_data); + return; + } + closure->func = func; + closure->user_data = user_data; + closure->destroy = destroy; + + hb_font_funcs_set_paint_glyph_or_fail_func (ffuncs, + hb_font_paint_glyph_trampoline, + closure, + hb_font_paint_glyph_closure_destroy); } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h index f16658d9d88..4e267ba6a80 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h @@ -486,7 +486,7 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * void *user_data); /** - * hb_font_draw_glyph_func_t: + * hb_font_draw_glyph_or_fail_func_t: * @font: #hb_font_t to work upon * @font_data: @font user data pointer * @glyph: The glyph ID to query @@ -496,16 +496,17 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * Since: 7.0.0 + * Return value: `true` if glyph was drawn, `false` otherwise * + * XSince: REPLACEME **/ -typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data); +typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); /** - * hb_font_paint_glyph_func_t: + * hb_font_paint_glyph_or_fail_func_t: * @font: #hb_font_t to work upon * @font_data: @font user data pointer * @glyph: The glyph ID to query @@ -517,14 +518,16 @@ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * Since: 7.0.0 + * Return value: `true` if glyph was painted, `false` otherwise + * + * XSince: REPLACEME */ -typedef void (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, - hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette_index, - hb_color_t foreground, - void *user_data); +typedef hb_bool_t (*hb_font_paint_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data); /* func setters */ @@ -785,36 +788,36 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_draw_glyph_func: + * hb_font_funcs_set_draw_glyph_or_fail_func: * @ffuncs: A font-function structure * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign * @user_data: Data to pass to @func * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * Sets the implementation function for #hb_font_draw_glyph_func_t. + * Sets the implementation function for #hb_font_draw_glyph_or_fail_func_t. * - * Since: 7.0.0 + * XSince: REPLACEME **/ HB_EXTERN void -hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, - hb_font_draw_glyph_func_t func, - void *user_data, hb_destroy_func_t destroy); +hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_or_fail_func_t func, + void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_paint_glyph_func: + * hb_font_funcs_set_paint_glyph_or_fail_func: * @ffuncs: A font-function structure * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign * @user_data: Data to pass to @func * @destroy: (nullable): The function to call when @user_data is no longer needed * - * Sets the implementation function for #hb_font_paint_glyph_func_t. + * Sets the implementation function for #hb_font_paint_glyph_or_fail_func_t. * - * Since: 7.0.0 + * XSince: REPLACEME */ HB_EXTERN void -hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, - hb_font_paint_glyph_func_t func, - void *user_data, hb_destroy_func_t destroy); +hb_font_funcs_set_paint_glyph_or_fail_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_or_fail_func_t func, + void *user_data, hb_destroy_func_t destroy); /* func dispatch */ @@ -896,17 +899,17 @@ hb_font_get_glyph_from_name (hb_font_t *font, const char *name, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph); -HB_EXTERN void -hb_font_draw_glyph (hb_font_t *font, - hb_codepoint_t glyph, - hb_draw_funcs_t *dfuncs, void *draw_data); +HB_EXTERN hb_bool_t +hb_font_draw_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); -HB_EXTERN void -hb_font_paint_glyph (hb_font_t *font, - hb_codepoint_t glyph, - hb_paint_funcs_t *pfuncs, void *paint_data, - unsigned int palette_index, - hb_color_t foreground); +HB_EXTERN hb_bool_t +hb_font_paint_glyph_or_fail (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground); /* high-level funcs, with fallback */ @@ -979,6 +982,19 @@ hb_font_glyph_from_string (hb_font_t *font, const char *s, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph); +/* Older alias for hb_font_draw_glyph_or_fail() with no return value. */ +HB_EXTERN void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + +/* Paints color glyph; if failed, draws outline glyph. */ +HB_EXTERN void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground); /* * hb_font_t @@ -1052,6 +1068,12 @@ hb_font_set_funcs_data (hb_font_t *font, void *font_data, hb_destroy_func_t destroy); +HB_EXTERN hb_bool_t +hb_font_set_funcs_using (hb_font_t *font, + const char *name); + +HB_EXTERN const char ** +hb_font_list_funcs (void); HB_EXTERN void hb_font_set_scale (hb_font_t *font, @@ -1086,6 +1108,9 @@ hb_font_set_ptem (hb_font_t *font, float ptem); HB_EXTERN float hb_font_get_ptem (hb_font_t *font); +HB_EXTERN hb_bool_t +hb_font_is_synthetic (hb_font_t *font); + HB_EXTERN void hb_font_set_synthetic_bold (hb_font_t *font, float x_embolden, float y_embolden, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh index 8273f347562..69d8d4b09df 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh @@ -32,7 +32,11 @@ #include "hb.hh" #include "hb-face.hh" +#include "hb-atomic.hh" +#include "hb-draw.hh" +#include "hb-paint-extents.hh" #include "hb-shaper.hh" +#include "hb-outline.hh" /* @@ -57,8 +61,8 @@ HB_FONT_FUNC_IMPLEMENT (get_,glyph_contour_point) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_name) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_from_name) \ - HB_FONT_FUNC_IMPLEMENT (,draw_glyph) \ - HB_FONT_FUNC_IMPLEMENT (,paint_glyph) \ + HB_FONT_FUNC_IMPLEMENT (,draw_glyph_or_fail) \ + HB_FONT_FUNC_IMPLEMENT (,paint_glyph_or_fail) \ /* ^--- Add new callbacks here */ struct hb_font_funcs_t @@ -105,8 +109,8 @@ DECLARE_NULL_INSTANCE (hb_font_funcs_t); struct hb_font_t { hb_object_header_t header; - unsigned int serial; - unsigned int serial_coords; + hb_atomic_t serial; + hb_atomic_t serial_coords; hb_font_t *parent; hb_face_t *face; @@ -191,23 +195,35 @@ struct hb_font_t void scale_glyph_extents (hb_glyph_extents_t *extents) { - float x1 = em_fscale_x (extents->x_bearing); - float y1 = em_fscale_y (extents->y_bearing); - float x2 = em_fscale_x (extents->x_bearing + extents->width); - float y2 = em_fscale_y (extents->y_bearing + extents->height); + float x1 = em_scale_x (extents->x_bearing); + float y1 = em_scale_y (extents->y_bearing); + float x2 = em_scale_x (extents->x_bearing + extents->width); + float y2 = em_scale_y (extents->y_bearing + extents->height); - /* Apply slant. */ + extents->x_bearing = roundf (x1); + extents->y_bearing = roundf (y1); + extents->width = roundf (x2) - extents->x_bearing; + extents->height = roundf (y2) - extents->y_bearing; + } + + void synthetic_glyph_extents (hb_glyph_extents_t *extents) + { + /* Slant. */ if (slant_xy) { - x1 += hb_min (y1 * slant_xy, y2 * slant_xy); - x2 += hb_max (y1 * slant_xy, y2 * slant_xy); + hb_position_t x1 = extents->x_bearing; + hb_position_t y1 = extents->y_bearing; + hb_position_t x2 = extents->x_bearing + extents->width; + hb_position_t y2 = extents->y_bearing + extents->height; + + x1 += floorf (hb_min (y1 * slant_xy, y2 * slant_xy)); + x2 += ceilf (hb_max (y1 * slant_xy, y2 * slant_xy)); + + extents->x_bearing = x1; + extents->width = x2 - extents->x_bearing; } - extents->x_bearing = floorf (x1); - extents->y_bearing = floorf (y1); - extents->width = ceilf (x2) - extents->x_bearing; - extents->height = ceilf (y2) - extents->y_bearing; - + /* Embolden. */ if (x_strength || y_strength) { /* Y */ @@ -250,19 +266,45 @@ struct hb_font_t HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT - hb_bool_t get_font_h_extents (hb_font_extents_t *extents) + hb_bool_t get_font_h_extents (hb_font_extents_t *extents, + bool synthetic = true) { hb_memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_h_extents (this, user_data, - extents, - !klass->user_data ? nullptr : klass->user_data->font_h_extents); + bool ret = klass->get.f.font_h_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_h_extents); + + if (synthetic && ret) + { + /* Embolden */ + int y_shift = y_scale < 0 ? -y_strength : y_strength; + extents->ascender += y_shift; + } + + return ret; } - hb_bool_t get_font_v_extents (hb_font_extents_t *extents) + hb_bool_t get_font_v_extents (hb_font_extents_t *extents, + bool synthetic = true) { hb_memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_v_extents (this, user_data, - extents, - !klass->user_data ? nullptr : klass->user_data->font_v_extents); + bool ret = klass->get.f.font_v_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_v_extents); + + if (synthetic && ret) + { + /* Embolden */ + int x_shift = x_scale < 0 ? -x_strength : x_strength; + if (embolden_in_place) + { + extents->ascender += x_shift / 2; + extents->descender -= x_shift - x_shift / 2; + } + else + extents->ascender += x_shift; + } + + return ret; } bool has_glyph (hb_codepoint_t unicode) @@ -303,44 +345,88 @@ struct hb_font_t !klass->user_data ? nullptr : klass->user_data->variation_glyph); } - hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + hb_position_t get_glyph_h_advance (hb_codepoint_t glyph, + bool synthetic = true) { - return klass->get.f.glyph_h_advance (this, user_data, - glyph, - !klass->user_data ? nullptr : klass->user_data->glyph_h_advance); + hb_position_t advance = klass->get.f.glyph_h_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advance); + + if (synthetic && x_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = x_scale >= 0 ? x_strength : -x_strength; + advance += advance ? strength : 0; + } + + return advance; } - hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + hb_position_t get_glyph_v_advance (hb_codepoint_t glyph, + bool synthetic = true) { - return klass->get.f.glyph_v_advance (this, user_data, - glyph, - !klass->user_data ? nullptr : klass->user_data->glyph_v_advance); + hb_position_t advance = klass->get.f.glyph_v_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advance); + + if (synthetic && y_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = y_scale >= 0 ? y_strength : -y_strength; + advance += advance ? strength : 0; + } + + return advance; } void get_glyph_h_advances (unsigned int count, const hb_codepoint_t *first_glyph, unsigned int glyph_stride, hb_position_t *first_advance, - unsigned int advance_stride) + unsigned int advance_stride, + bool synthetic = true) { - return klass->get.f.glyph_h_advances (this, user_data, - count, - first_glyph, glyph_stride, - first_advance, advance_stride, - !klass->user_data ? nullptr : klass->user_data->glyph_h_advances); + klass->get.f.glyph_h_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advances); + + if (synthetic && x_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = x_scale >= 0 ? x_strength : -x_strength; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } void get_glyph_v_advances (unsigned int count, const hb_codepoint_t *first_glyph, unsigned int glyph_stride, hb_position_t *first_advance, - unsigned int advance_stride) + unsigned int advance_stride, + bool synthetic = true) { - return klass->get.f.glyph_v_advances (this, user_data, - count, - first_glyph, glyph_stride, - first_advance, advance_stride, - !klass->user_data ? nullptr : klass->user_data->glyph_v_advances); + klass->get.f.glyph_v_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advances); + + if (synthetic && y_strength && !embolden_in_place) + { + /* Embolden */ + hb_position_t strength = y_scale >= 0 ? y_strength : -y_strength; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, @@ -386,23 +472,82 @@ struct hb_font_t } hb_bool_t get_glyph_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) + hb_glyph_extents_t *extents, + bool synthetic = true) { hb_memset (extents, 0, sizeof (*extents)); - return klass->get.f.glyph_extents (this, user_data, - glyph, - extents, - !klass->user_data ? nullptr : klass->user_data->glyph_extents); + + /* This is rather messy, but necessary. */ + + if (!synthetic) + { + return klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents); + } + if (!is_synthetic () && + klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents)) + return true; + + /* Try getting extents from paint(), then draw(), *then* get_extents() + * and apply synthetic settings in the last case. */ + + hb_paint_extents_context_t paint_extents; + if (paint_glyph_or_fail (glyph, + hb_paint_extents_get_funcs (), &paint_extents, + 0, 0)) + { + *extents = paint_extents.get_extents ().to_glyph_extents (); + return true; + } + + hb_extents_t draw_extents; + if (draw_glyph_or_fail (glyph, + hb_draw_extents_get_funcs (), &draw_extents)) + { + *extents = draw_extents.to_glyph_extents (); + return true; + } + + bool ret = klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents); + if (ret) + synthetic_glyph_extents (extents); + + return ret; } hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y, + bool synthetic = true) { *x = *y = 0; - return klass->get.f.glyph_contour_point (this, user_data, - glyph, point_index, - x, y, - !klass->user_data ? nullptr : klass->user_data->glyph_contour_point); + bool ret = klass->get.f.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_contour_point); + + if (synthetic && ret) + { + /* Slant */ + if (slant_xy) + *x += roundf (*y * slant_xy); + + /* Embolden */ + if (!embolden_in_place) + { + int x_shift = x_scale < 0 ? -x_strength : x_strength; + *x += x_shift; + } + } + + return ret; } hb_bool_t get_glyph_name (hb_codepoint_t glyph, @@ -426,29 +571,88 @@ struct hb_font_t !klass->user_data ? nullptr : klass->user_data->glyph_from_name); } - void draw_glyph (hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data) + bool draw_glyph_or_fail (hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + bool synthetic = true) { - klass->get.f.draw_glyph (this, user_data, - glyph, - draw_funcs, draw_data, - !klass->user_data ? nullptr : klass->user_data->draw_glyph); +#ifndef HB_NO_OUTLINE + bool embolden = x_strength || y_strength; + bool slanted = slant_xy; + synthetic = synthetic && (embolden || slanted); +#else + synthetic = false; +#endif + + if (!synthetic) + { + return klass->get.f.draw_glyph_or_fail (this, user_data, + glyph, + draw_funcs, draw_data, + !klass->user_data ? nullptr : klass->user_data->draw_glyph_or_fail); + } + +#ifndef HB_NO_OUTLINE + + hb_outline_t outline; + if (!klass->get.f.draw_glyph_or_fail (this, user_data, + glyph, + hb_outline_recording_pen_get_funcs (), &outline, + !klass->user_data ? nullptr : klass->user_data->draw_glyph_or_fail)) + return false; + + // Slant before embolden; produces nicer results. + + if (slanted) + outline.slant (slant_xy); + + if (embolden) + { + float x_shift = embolden_in_place ? 0 : (float) x_strength / 2; + float y_shift = (float) y_strength / 2; + if (x_scale < 0) x_shift = -x_shift; + if (y_scale < 0) y_shift = -y_shift; + outline.embolden (x_strength, y_strength, x_shift, y_shift); + } + + outline.replay (draw_funcs, draw_data); + + return true; +#endif } - void paint_glyph (hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette, - hb_color_t foreground) + bool paint_glyph_or_fail (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + bool synthetic = true) { - klass->get.f.paint_glyph (this, user_data, - glyph, - paint_funcs, paint_data, - palette, foreground, - !klass->user_data ? nullptr : klass->user_data->paint_glyph); + /* Slant */ + if (synthetic && slant_xy) + hb_paint_push_transform (paint_funcs, paint_data, + 1.f, 0.f, + slant_xy, 1.f, + 0.f, 0.f); + + bool ret = klass->get.f.paint_glyph_or_fail (this, user_data, + glyph, + paint_funcs, paint_data, + palette, foreground, + !klass->user_data ? nullptr : klass->user_data->paint_glyph_or_fail); + + if (synthetic && slant_xy) + hb_paint_pop_transform (paint_funcs, paint_data); + + return ret; } /* A bit higher-level, and with fallback */ + HB_INTERNAL + void paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground); + void get_h_extents_with_fallback (hb_font_extents_t *extents) { if (!get_font_h_extents (extents)) @@ -686,7 +890,12 @@ struct hb_font_t return false; } - void mults_changed () + bool is_synthetic () const + { + return x_embolden || y_embolden || slant; + } + + void changed () { float upem = face->get_upem (); @@ -697,12 +906,14 @@ struct hb_font_t bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; - x_strength = fabsf (roundf (x_scale * x_embolden)); - y_strength = fabsf (roundf (y_scale * y_embolden)); + x_strength = roundf (abs (x_scale) * x_embolden); + y_strength = roundf (abs (y_scale) * y_embolden); slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; data.fini (); + + serial++; } hb_position_t em_mult (int16_t v, int64_t mult) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc index b126d2022d1..e9c0e734a53 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc @@ -37,11 +37,7 @@ #include "hb-draw.hh" #include "hb-font.hh" #include "hb-machinery.hh" -#ifndef HB_NO_AAT -#include "hb-aat-layout-trak-table.hh" -#endif #include "hb-ot-os2-table.hh" -#include "hb-ot-stat-table.hh" #include "hb-ot-shaper-arabic-pua.hh" #include "hb-paint.hh" @@ -101,7 +97,7 @@ struct hb_ft_font_t mutable hb_mutex_t lock; /* Protects members below. */ FT_Face ft_face; - mutable unsigned cached_serial; + mutable hb_atomic_t cached_serial; mutable hb_ft_advance_cache_t advance_cache; }; @@ -118,7 +114,7 @@ _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; - ft_font->cached_serial = (unsigned) -1; + ft_font->cached_serial = UINT_MAX; new (&ft_font->advance_cache) hb_ft_advance_cache_t; return ft_font; @@ -213,9 +209,10 @@ _hb_ft_hb_font_check_changed (hb_font_t *font, { if (font->serial != ft_font->cached_serial) { + hb_lock_t lock (ft_font->lock); _hb_ft_hb_font_changed (font, ft_font->ft_face); ft_font->advance_cache.clear (); - ft_font->cached_serial = font->serial; + ft_font->cached_serial.set_release (font->serial.get_acquire ()); return true; } return false; @@ -478,7 +475,8 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; - hb_position_t *orig_first_advance = first_advance; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; int load_flags = ft_font->load_flags; @@ -519,38 +517,6 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } - - if (font->x_strength && !font->embolden_in_place) - { - /* Emboldening. */ - hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += *first_advance ? x_strength : 0; - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } - -#ifndef HB_NO_AAT - /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */ -#ifndef HB_NO_STYLE - bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data (); -#else - bool apply_trak = false; -#endif - if (apply_trak) - { - hb_position_t tracking = font->face->table.trak->get_h_tracking (font); - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += tracking; - first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } -#endif } #ifndef HB_NO_VERTICAL @@ -561,6 +527,8 @@ hb_ft_get_glyph_v_advance (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Fixed v; float y_mult; @@ -581,26 +549,11 @@ hb_ft_get_glyph_v_advance (hb_font_t *font, if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) return 0; - v = (int) (y_mult * v); - /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates * have a Y growing upward. Hence the extra negation. */ + v = ((-v + (1<<9)) >> 10); - hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; - v = ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength); - -#ifndef HB_NO_AAT - /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */ -#ifndef HB_NO_STYLE - bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data (); -#else - bool apply_trak = false; -#endif - if (apply_trak) - v += font->face->table.trak->get_v_tracking (font); -#endif - - return v; + return (hb_position_t) (y_mult * v); } #endif @@ -614,6 +567,8 @@ hb_ft_get_glyph_v_origin (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; float x_mult, y_mult; @@ -658,6 +613,8 @@ hb_ft_get_glyph_h_kerning (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Vector kerningv; @@ -669,6 +626,41 @@ hb_ft_get_glyph_h_kerning (hb_font_t *font, } #endif +static bool +hb_ft_is_colr_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t gid) +{ +#ifndef HB_NO_PAINT +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + + /* COLRv1 */ + FT_OpaquePaint paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, gid, + FT_COLOR_NO_ROOT_TRANSFORM, + &paint)) + return true; + + /* COLRv0 */ + FT_LayerIterator iterator; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + iterator.p = NULL; + if (FT_Get_Color_Glyph_Layer (ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator)) + return true; +#endif +#endif + + return false; +} + static hb_bool_t hb_ft_get_glyph_extents (hb_font_t *font, void *font_data, @@ -676,11 +668,17 @@ hb_ft_get_glyph_extents (hb_font_t *font, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { + // FreeType doesn't return COLR glyph extents. + if (hb_ft_is_colr_glyph (font, font_data, glyph)) + return false; + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; float x_mult, y_mult; - float slant_xy = font->slant_xy; + #ifdef HAVE_FT_GET_TRANSFORM if (ft_font->transform) { @@ -708,33 +706,10 @@ hb_ft_get_glyph_extents (hb_font_t *font, float x2 = x1 + x_mult * ft_face->glyph->metrics.width; float y2 = y1 + y_mult * -ft_face->glyph->metrics.height; - /* Apply slant. */ - if (slant_xy) - { - x1 += hb_min (y1 * slant_xy, y2 * slant_xy); - x2 += hb_max (y1 * slant_xy, y2 * slant_xy); - } - - extents->x_bearing = floorf (x1); - extents->y_bearing = floorf (y1); - extents->width = ceilf (x2) - extents->x_bearing; - extents->height = ceilf (y2) - extents->y_bearing; - - if (font->x_strength || font->y_strength) - { - /* Y */ - int y_shift = font->y_strength; - if (font->y_scale < 0) y_shift = -y_shift; - extents->y_bearing += y_shift; - extents->height -= y_shift; - - /* X */ - int x_shift = font->x_strength; - if (font->x_scale < 0) x_shift = -x_shift; - if (font->embolden_in_place) - extents->x_bearing -= x_shift / 2; - extents->width += x_shift; - } + extents->x_bearing = roundf (x1); + extents->y_bearing = roundf (y1); + extents->width = roundf (x2) - extents->x_bearing; + extents->height = roundf (y2) - extents->y_bearing; return true; } @@ -749,6 +724,8 @@ hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; @@ -826,6 +803,8 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; float y_mult; @@ -857,7 +836,7 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, metrics->line_gap = ft_face->size->metrics.height - (metrics->ascender - metrics->descender); } - metrics->ascender = (hb_position_t) (y_mult * (metrics->ascender + font->y_strength)); + metrics->ascender = (hb_position_t) (y_mult * metrics->ascender); metrics->descender = (hb_position_t) (y_mult * metrics->descender); metrics->line_gap = (hb_position_t) (y_mult * metrics->line_gap); @@ -908,23 +887,25 @@ _hb_ft_cubic_to (const FT_Vector *control1, return FT_Err_Ok; } -static void -hb_ft_draw_glyph (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data HB_UNUSED) +static hb_bool_t +hb_ft_draw_glyph_or_fail (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; if (unlikely (FT_Load_Glyph (ft_face, glyph, FT_LOAD_NO_BITMAP | ft_font->load_flags))) - return; + return false; if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) - return; + return false; const FT_Outline_Funcs outline_funcs = { _hb_ft_move_to, @@ -935,43 +916,13 @@ hb_ft_draw_glyph (hb_font_t *font, 0, /* delta */ }; - hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); - - /* Embolden */ - if (font->x_strength || font->y_strength) - { - FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_strength, font->y_strength); - - int x_shift = 0; - int y_shift = 0; - if (font->embolden_in_place) - { - /* Undo the FreeType shift. */ - x_shift = -font->x_strength / 2; - y_shift = 0; - if (font->y_scale < 0) y_shift = -font->y_strength; - } - else - { - /* FreeType applied things in the wrong direction for negative scale; fix up. */ - if (font->x_scale < 0) x_shift = -font->x_strength; - if (font->y_scale < 0) y_shift = -font->y_strength; - } - if (x_shift || y_shift) - { - auto &outline = ft_face->glyph->outline; - for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1] + 1)) - { - point.x += x_shift; - point.y += y_shift; - } - } - } - + hb_draw_session_t draw_session {draw_funcs, draw_data}; FT_Outline_Decompose (&ft_face->glyph->outline, &outline_funcs, &draw_session); + + return true; } #endif @@ -980,20 +931,22 @@ hb_ft_draw_glyph (hb_font_t *font, #include "hb-ft-colr.hh" -static void -hb_ft_paint_glyph (hb_font_t *font, - void *font_data, - hb_codepoint_t gid, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette_index, - hb_color_t foreground, - void *user_data) +static hb_bool_t +hb_ft_paint_glyph_or_fail (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + _hb_ft_hb_font_check_changed (font, ft_font); + hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; - FT_Long load_flags = ft_font->load_flags | FT_LOAD_NO_BITMAP | FT_LOAD_COLOR; + FT_Long load_flags = ft_font->load_flags | FT_LOAD_COLOR; #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21301 load_flags |= FT_LOAD_NO_SVG; #endif @@ -1002,7 +955,7 @@ hb_ft_paint_glyph (hb_font_t *font, * eg. draw API can call back into the face.*/ if (unlikely (FT_Load_Glyph (ft_face, gid, load_flags))) - return; + return false; if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { @@ -1010,26 +963,21 @@ hb_ft_paint_glyph (hb_font_t *font, paint_funcs, paint_data, palette_index, foreground, user_data)) - return; + return true; - /* Simple outline. */ - ft_font->lock.unlock (); - paint_funcs->push_clip_glyph (paint_data, gid, font); - ft_font->lock.lock (); - paint_funcs->color (paint_data, true, foreground); - paint_funcs->pop_clip (paint_data); - - return; + // Outline glyph + return false; } auto *glyph = ft_face->glyph; if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + bool ret = false; auto &bitmap = glyph->bitmap; if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { if (bitmap.pitch != (signed) bitmap.width * 4) - return; + return ret; ft_font->lock.unlock (); @@ -1039,27 +987,26 @@ hb_ft_paint_glyph (hb_font_t *font, nullptr, nullptr); hb_glyph_extents_t extents; - if (!hb_font_get_glyph_extents (font, gid, &extents)) + if (!font->get_glyph_extents (gid, &extents, false)) goto out; - if (!paint_funcs->image (paint_data, - blob, - bitmap.width, - bitmap.rows, - HB_PAINT_IMAGE_FORMAT_BGRA, - font->slant_xy, - &extents)) - { - /* TODO Try a forced outline load and paint? */ - } + if (paint_funcs->image (paint_data, + blob, + bitmap.width, + bitmap.rows, + HB_PAINT_IMAGE_FORMAT_BGRA, + 0.f, + &extents)) + ret = true; out: hb_blob_destroy (blob); ft_font->lock.lock (); } - return; + return ret; } + return false; } #endif #endif @@ -1079,10 +1026,8 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t= 21300 - hb_font_funcs_set_paint_glyph_func (funcs, hb_ft_paint_glyph, nullptr, nullptr); + hb_font_funcs_set_paint_glyph_or_fail_func (funcs, hb_ft_paint_glyph_or_fail, nullptr, nullptr); #endif #endif @@ -1456,6 +1400,10 @@ hb_ft_font_changed (hb_font_t *font) * variation-axis settings on the @font. * This call is fast if nothing has changed on @font. * + * Note that as of version 11.0.0, calling this function is not necessary, + * as HarfBuzz will automatically detect changes to the font and update + * the underlying FT_Face as needed. + * * Return value: true if changed, false otherwise * * Since: 4.4.0 @@ -1587,7 +1535,8 @@ destroy_ft_library (void *arg) * font file and face index. * * This is similar in functionality to hb_face_create_from_file_or_fail(), - * but uses the FreeType library for loading the font file. + * but uses the FreeType library for loading the font file. This can + * be useful, for example, to load WOFF and WOFF2 font data. * * Return value: (transfer full): The new face object, or `NULL` if * no face is found at the specified index or the file cannot be read. @@ -1624,6 +1573,75 @@ hb_ft_face_create_from_file_or_fail (const char *file_name, return face; } +static hb_user_data_key_t ft_blob_key = {0}; + +static void +_destroy_blob (void *p) +{ + hb_blob_destroy ((hb_blob_t *) p); +} + +/** + * hb_ft_face_create_from_blob_or_fail: + * @blob: A blob + * @index: The index of the face within the blob + * + * Creates an #hb_face_t face object from the specified + * font blob and face index. + * + * This is similar in functionality to hb_face_create_from_blob_or_fail(), + * but uses the FreeType library for loading the font blob. This can + * be useful, for example, to load WOFF and WOFF2 font data. + * + * Return value: (transfer full): The new face object, or `NULL` if + * loading fails (eg. blob does not contain valid font data). + * + * Since: 11.0.0 + */ +hb_face_t * +hb_ft_face_create_from_blob_or_fail (hb_blob_t *blob, + unsigned int index) +{ + FT_Library ft_library = reference_ft_library (); + if (unlikely (!ft_library)) + { + DEBUG_MSG (FT, ft_library, "reference_ft_library failed"); + return nullptr; + } + + hb_blob_make_immutable (blob); + unsigned blob_size; + const char *blob_data = hb_blob_get_data (blob, &blob_size); + + FT_Face ft_face; + if (unlikely (FT_New_Memory_Face (ft_library, + (const FT_Byte *) blob_data, + blob_size, + index, + &ft_face))) + return nullptr; + + hb_face_t *face = hb_ft_face_create_referenced (ft_face); + FT_Done_Face (ft_face); + + ft_face->generic.data = ft_library; + ft_face->generic.finalizer = finalize_ft_library; + + if (hb_face_is_immutable (face)) + return nullptr; + + // Hook the blob to the hb_face_t, since FT_Face still needs it. + hb_blob_reference (blob); + if (!hb_face_set_user_data (face, &ft_blob_key, blob, _destroy_blob, true)) + { + hb_blob_destroy (blob); + hb_face_destroy (face); + return nullptr; + } + + return face; +} + static void _release_blob (void *arg) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.h b/src/java.desktop/share/native/libharfbuzz/hb-ft.h index 94952f48a05..9f83b91abb6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.h @@ -88,6 +88,10 @@ HB_EXTERN hb_face_t * hb_ft_face_create_from_file_or_fail (const char *file_name, unsigned int index); +HB_EXTERN hb_face_t * +hb_ft_face_create_from_blob_or_fail (hb_blob_t *blob, + unsigned int index); + /* * hb-font from ft-face. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh index e91e371dc0c..795f29f6ab0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh @@ -30,6 +30,11 @@ struct hb_extents_t { hb_extents_t () {} + hb_extents_t (const hb_glyph_extents_t &extents) : + xmin (hb_min (extents.x_bearing, extents.x_bearing + extents.width)), + ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)), + xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)), + ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {} hb_extents_t (float xmin, float ymin, float xmax, float ymax) : xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} @@ -38,6 +43,12 @@ struct hb_extents_t void union_ (const hb_extents_t &o) { + if (o.is_empty ()) return; + if (is_empty ()) + { + *this = o; + return; + } xmin = hb_min (xmin, o.xmin); ymin = hb_min (ymin, o.ymin); xmax = hb_max (xmax, o.xmax); @@ -46,6 +57,11 @@ struct hb_extents_t void intersect (const hb_extents_t &o) { + if (o.is_empty () || is_empty ()) + { + *this = hb_extents_t {}; + return; + } xmin = hb_max (xmin, o.xmin); ymin = hb_max (ymin, o.ymin); xmax = hb_min (xmax, o.xmax); @@ -69,6 +85,18 @@ struct hb_extents_t } } + hb_glyph_extents_t to_glyph_extents (bool xneg = false, bool yneg = false) const + { + hb_position_t x0 = (hb_position_t) roundf (xmin); + hb_position_t y0 = (hb_position_t) roundf (ymin); + hb_position_t x1 = (hb_position_t) roundf (xmax); + hb_position_t y1 = (hb_position_t) roundf (ymax); + return hb_glyph_extents_t {xneg ? x1 : x0, + yneg ? y0 : y1, + xneg ? x0 - x1 : x1 - x0, + yneg ? y1 - y0 : y0 - y1}; + } + float xmin = 0.f; float ymin = 0.f; float xmax = -1.f; @@ -218,7 +246,7 @@ struct hb_bounds_t EMPTY, }; - hb_bounds_t (status_t status) : status (status) {} + hb_bounds_t (status_t status = UNBOUNDED) : status (status) {} hb_bounds_t (const hb_extents_t &extents) : status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh index 580c9f0c785..ffbb8f3d301 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh @@ -273,7 +273,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t private: /* Must only have one pointer. */ - hb_atomic_ptr_t instance; + mutable hb_atomic_t instance; }; /* Specializations. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh index 72012bd2e64..4b6c5a118ae 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh @@ -99,6 +99,8 @@ struct hb_mutex_t hb_mutex_t () { init (); } ~hb_mutex_t () { fini (); } + hb_mutex_t (const hb_mutex_t &) = delete; + hb_mutex_t &operator= (const hb_mutex_t &) = delete; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" @@ -114,6 +116,10 @@ struct hb_lock_t hb_lock_t (hb_mutex_t &mutex_) : mutex (&mutex_) { mutex->lock (); } hb_lock_t (hb_mutex_t *mutex_) : mutex (mutex_) { if (mutex) mutex->lock (); } ~hb_lock_t () { if (mutex) mutex->unlock (); } + + hb_lock_t (const hb_lock_t &) = delete; + hb_lock_t &operator= (const hb_lock_t &) = delete; + private: hb_mutex_t *mutex; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-object.hh b/src/java.desktop/share/native/libharfbuzz/hb-object.hh index c02ec8bfacf..fa9a249b2e2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-object.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-object.hh @@ -142,7 +142,7 @@ struct hb_lockable_set_t struct hb_reference_count_t { - mutable hb_atomic_int_t ref_count; + mutable hb_atomic_t ref_count; void init (int v = 1) { ref_count = v; } int get_relaxed () const { return ref_count; } @@ -213,8 +213,8 @@ struct hb_user_data_array_t struct hb_object_header_t { hb_reference_count_t ref_count; - mutable hb_atomic_int_t writable = 0; - hb_atomic_ptr_t user_data; + mutable hb_atomic_t writable = false; + hb_atomic_t user_data; bool is_inert () const { return !ref_count.get_relaxed (); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh index 89da9c7bc95..75d87f11ea5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh @@ -1888,7 +1888,7 @@ struct TupleValues bool ensure_run () { - if (likely (run_count > 0)) return true; + if (run_count > 0) return true; if (unlikely (p >= end)) { @@ -1943,6 +1943,11 @@ struct TupleValues unsigned count = hb_min (n - i, (unsigned) run_count); switch (width) { + case 0: + { + arrayZ += count; + break; + } case 1: { const auto *pp = (const HBINT8 *) p; @@ -1958,6 +1963,8 @@ struct TupleValues #endif for (; j < count; j++) *arrayZ++ += scaled ? *pp++ * scale : *pp++; + + p = (const unsigned char *) pp; } break; case 2: @@ -1975,6 +1982,8 @@ struct TupleValues #endif for (; j < count; j++) *arrayZ++ += scaled ? *pp++ * scale : *pp++; + + p = (const unsigned char *) pp; } break; case 4: @@ -1982,10 +1991,11 @@ struct TupleValues const auto *pp = (const HBINT32 *) p; for (unsigned j = 0; j < count; j++) *arrayZ++ += scaled ? *pp++ * scale : *pp++; + + p = (const unsigned char *) pp; } break; } - p += count * width; run_count -= count; i += count; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh index 0116a0c9192..27c4d31b9ce 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh @@ -1073,7 +1073,7 @@ struct cff1 this->blob = sc.reference_table (face); - /* setup for run-time santization */ + /* setup for run-time sanitization */ sc.init (this->blob); sc.start_processing (); @@ -1176,7 +1176,8 @@ struct cff1 if (unlikely (!font_interp.interpret (*font))) goto fail; PRIVDICTVAL *priv = &privateDicts[i]; const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); - if (unlikely (privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; + if (unlikely (font->privateDictInfo.size && + privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; num_interp_env_t env2 (privDictStr); dict_interpreter_t priv_interp (env2); priv->init (); @@ -1191,7 +1192,8 @@ struct cff1 PRIVDICTVAL *priv = &privateDicts[0]; const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); - if (unlikely (privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; + if (font->privateDictInfo.size && + unlikely (privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; num_interp_env_t env (privDictStr); dict_interpreter_t priv_interp (env); priv->init (); @@ -1483,7 +1485,7 @@ struct cff1 int cmp (const gname_t &a) const { return cmp (&a, this); } }; - mutable hb_atomic_ptr_t> glyph_names; + mutable hb_atomic_t *> glyph_names; typedef accelerator_templ_t SUPER; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc index fce39da21ce..b2702106ecc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc @@ -102,6 +102,14 @@ struct cff2_cs_opset_extents_t : cff2_cs_opset_tcoords, font->num_coords)); +} + +bool OT::cff2::accelerator_t::get_extents_at (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + hb_array_t coords) const { #ifdef HB_NO_OT_FONT_CFF /* XXX Remove check when this code moves to .hh file. */ @@ -112,7 +120,7 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, unsigned int fd = fdSelect->get_fd (glyph); const hb_ubytes_t str = (*charStrings)[glyph]; - cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interp_env_t env (str, *this, fd, coords.arrayZ, coords.length); cff2_cs_interpreter_t interp (env); cff2_extents_param_t param; if (unlikely (!interp.interpret (param))) return false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh index 81d30f57a28..7e94e38e115 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh @@ -405,7 +405,7 @@ struct cff2 this->blob = sc.reference_table (face); - /* setup for run-time santization */ + /* setup for run-time sanitization */ sc.init (this->blob); sc.start_processing (); @@ -458,7 +458,8 @@ struct cff2 if (unlikely (!font_interp.interpret (*font))) goto fail; const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff2, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size); - if (unlikely (privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; + if (unlikely (font->privateDictInfo.size && + privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail; cff2_priv_dict_interp_env_t env2 (privDictStr); dict_interpreter_t priv_interp (env2); privateDicts[i].init (); @@ -481,6 +482,13 @@ struct cff2 privateDicts.fini (); hb_blob_destroy (blob); blob = nullptr; + + auto *scalars = cached_scalars_vector.get_acquire (); + if (scalars && cached_scalars_vector.cmpexch (scalars, nullptr)) + { + scalars->fini (); + hb_free (scalars); + } } hb_vector_t *create_glyph_to_sid_map () const @@ -508,6 +516,8 @@ struct cff2 hb_vector_t fontDicts; hb_vector_t privateDicts; + mutable hb_atomic_t *> cached_scalars_vector; + unsigned int num_glyphs = 0; }; @@ -518,6 +528,10 @@ struct cff2 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool get_extents_at (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + hb_array_t coords) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t coords) const; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh index 249d4cac5db..6bf37c062fc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh @@ -2014,7 +2014,8 @@ struct cmap struct accelerator_t { - using cache_t = hb_cache_t<21, 16, 8, true>; + using cache_t = hb_cache_t<21, 19>; + static_assert (sizeof (cache_t) == 1024, ""); accelerator_t (hb_face_t *face) { @@ -2028,6 +2029,14 @@ struct cmap subtable_uvs = &st->u.format14; } +#ifndef HB_NO_OT_FONT_CMAP_CACHE + cache = (cache_t *) hb_malloc (sizeof (cache_t)); + if (cache) + new (cache) cache_t (); + else + return; // Such that get_glyph_funcZ remains null. +#endif + this->get_glyph_data = subtable; #ifndef HB_NO_CMAP_LEGACY_SUBTABLES if (unlikely (symbol)) @@ -2061,62 +2070,71 @@ struct cmap #endif { switch (subtable->u.format) { - /* Accelerate format 4 and format 12. */ - default: - this->get_glyph_funcZ = get_glyph_from; - break; - case 12: - this->get_glyph_funcZ = get_glyph_from; - break; - case 4: - { - this->format4_accel.init (&subtable->u.format4); - this->get_glyph_data = &this->format4_accel; - this->get_glyph_funcZ = this->format4_accel.get_glyph_func; - break; - } + /* Accelerate format 4 and format 12. */ + default: + this->get_glyph_funcZ = get_glyph_from; + break; + case 12: + this->get_glyph_funcZ = get_glyph_from; + break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; + break; + } } } } - ~accelerator_t () { this->table.destroy (); } + ~accelerator_t () + { +#ifndef HB_NO_OT_FONT_CMAP_CACHE + hb_free (cache); +#endif + table.destroy (); + } inline bool _cached_get (hb_codepoint_t unicode, - hb_codepoint_t *glyph, - cache_t *cache) const + hb_codepoint_t *glyph) const { +#ifndef HB_NO_OT_FONT_CMAP_CACHE + // cache is always non-null if we have a get_glyph_funcZ unsigned v; - if (cache && cache->get (unicode, &v)) + if (cache->get (unicode, &v)) { *glyph = v; return true; } +#endif bool ret = this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); - if (cache && ret) +#ifndef HB_NO_OT_FONT_CMAP_CACHE + if (ret) cache->set (unicode, *glyph); +#endif + return ret; } bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph, - cache_t *cache = nullptr) const + hb_codepoint_t *glyph) const { if (unlikely (!this->get_glyph_funcZ)) return false; - return _cached_get (unicode, glyph, cache); + return _cached_get (unicode, glyph); } unsigned int get_nominal_glyphs (unsigned int count, const hb_codepoint_t *first_unicode, unsigned int unicode_stride, hb_codepoint_t *first_glyph, - unsigned int glyph_stride, - cache_t *cache = nullptr) const + unsigned int glyph_stride) const { if (unlikely (!this->get_glyph_funcZ)) return 0; unsigned int done; for (done = 0; - done < count && _cached_get (*first_unicode, first_glyph, cache); + done < count && _cached_get (*first_unicode, first_glyph); done++) { first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); @@ -2127,8 +2145,7 @@ struct cmap bool get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, - hb_codepoint_t *glyph, - cache_t *cache = nullptr) const + hb_codepoint_t *glyph) const { switch (this->subtable_uvs->get_glyph_variant (unicode, variation_selector, @@ -2139,7 +2156,7 @@ struct cmap case GLYPH_VARIANT_USE_DEFAULT: break; } - return get_nominal_glyph (unicode, glyph, cache); + return get_nominal_glyph (unicode, glyph); } void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const @@ -2209,11 +2226,15 @@ struct cmap hb_nonnull_ptr_t subtable; hb_nonnull_ptr_t subtable_uvs; - hb_cmap_get_glyph_func_t get_glyph_funcZ; - const void *get_glyph_data; + hb_cmap_get_glyph_func_t get_glyph_funcZ = nullptr; + const void *get_glyph_data = nullptr; CmapSubtableFormat4::accelerator_t format4_accel; +#ifndef HB_NO_OT_FONT_CMAP_CACHE + cache_t *cache = nullptr; +#endif + public: hb_blob_ptr_t table; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc index ef46fe5429a..2d9b8e7cad2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc @@ -204,7 +204,7 @@ hb_ot_color_palette_get_colors (hb_face_t *face, hb_bool_t hb_ot_color_has_layers (hb_face_t *face) { - return face->table.COLR->has_v0_data (); + return face->table.COLR->colr->has_v0_data (); } /** @@ -221,7 +221,7 @@ hb_ot_color_has_layers (hb_face_t *face) hb_bool_t hb_ot_color_has_paint (hb_face_t *face) { - return face->table.COLR->has_v1_data (); + return face->table.COLR->colr->has_v1_data (); } /** @@ -240,7 +240,7 @@ hb_bool_t hb_ot_color_glyph_has_paint (hb_face_t *face, hb_codepoint_t glyph) { - return face->table.COLR->has_paint_for_glyph (glyph); + return face->table.COLR->colr->has_paint_for_glyph (glyph); } /** @@ -266,7 +266,7 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */) { - return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); + return face->table.COLR->colr->get_glyph_layers (glyph, start_offset, layer_count, layers); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh index 97825f4d19c..afc8aa476e2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh @@ -136,7 +136,7 @@ HB_OT_TABLE (AAT, feat) /* OpenType color fonts. */ #ifndef HB_NO_COLOR -HB_OT_CORE_TABLE (OT, COLR) +HB_OT_ACCELERATOR (OT, COLR) HB_OT_CORE_TABLE (OT, CPAL) HB_OT_ACCELERATOR (OT, CBDT) HB_OT_ACCELERATOR (OT, sbix) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc index 1cf14f3ebc6..d17f088073c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc @@ -36,6 +36,7 @@ #include "hb-ot-name-table.hh" #include "hb-ot-post-table.hh" #include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" #include "OT/Color/sbix/sbix.hh" #include "OT/Color/svg/svg.hh" #include "hb-ot-layout-gdef-table.hh" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc index 2a633c77816..0238bb346a9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc @@ -34,11 +34,7 @@ #include "hb-font.hh" #include "hb-machinery.hh" #include "hb-ot-face.hh" -#include "hb-outline.hh" -#ifndef HB_NO_AAT -#include "hb-aat-layout-trak-table.hh" -#endif #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" #include "hb-ot-cff2-table.hh" @@ -65,28 +61,111 @@ * never need to call these functions directly. **/ -using hb_ot_font_cmap_cache_t = hb_cache_t<21, 16, 8, true>; -using hb_ot_font_advance_cache_t = hb_cache_t<24, 16, 8, true>; - -#ifndef HB_NO_OT_FONT_CMAP_CACHE -static hb_user_data_key_t hb_ot_font_cmap_cache_user_data_key; -#endif +using hb_ot_font_advance_cache_t = hb_cache_t<24, 16>; +static_assert (sizeof (hb_ot_font_advance_cache_t) == 1024, ""); struct hb_ot_font_t { const hb_ot_face_t *ot_face; -#ifndef HB_NO_AAT - bool apply_trak; -#endif - -#ifndef HB_NO_OT_FONT_CMAP_CACHE - hb_ot_font_cmap_cache_t *cmap_cache; -#endif - /* h_advance caching */ - mutable hb_atomic_int_t cached_coords_serial; - mutable hb_atomic_ptr_t advance_cache; + mutable hb_atomic_t cached_coords_serial; + struct advance_cache_t + { + mutable hb_atomic_t advance_cache; + mutable hb_atomic_t varStore_cache; + + ~advance_cache_t () + { + clear (); + } + + hb_ot_font_advance_cache_t *acquire_advance_cache () const + { + retry: + auto *cache = advance_cache.get_acquire (); + if (!cache) + { + cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); + if (!cache) + return nullptr; + new (cache) hb_ot_font_advance_cache_t; + return cache; + } + if (advance_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_advance_cache (hb_ot_font_advance_cache_t *cache) const + { + if (!cache) + return; + if (!advance_cache.cmpexch (nullptr, cache)) + hb_free (cache); + } + void clear_advance_cache () const + { + retry: + auto *cache = advance_cache.get_acquire (); + if (!cache) + return; + if (advance_cache.cmpexch (cache, nullptr)) + hb_free (cache); + else + goto retry; + } + + OT::ItemVariationStore::cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return varStore.create_cache (); + if (varStore_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_varStore_cache (OT::ItemVariationStore::cache_t *cache) const + { + if (!cache) + return; + if (!varStore_cache.cmpexch (nullptr, cache)) + OT::ItemVariationStore::destroy_cache (cache); + } + void clear_varStore_cache () const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return; + if (varStore_cache.cmpexch (cache, nullptr)) + OT::ItemVariationStore::destroy_cache (cache); + else + goto retry; + } + + void clear () const + { + clear_advance_cache (); + clear_varStore_cache (); + } + + } h, v; + + void check_serial (hb_font_t *font) const + { + int font_serial = font->serial_coords.get_acquire (); + + if (cached_coords_serial.get_acquire () == font_serial) + return; + + h.clear (); + v.clear (); + + cached_coords_serial.set_release (font_serial); + } }; static hb_ot_font_t * @@ -98,44 +177,6 @@ _hb_ot_font_create (hb_font_t *font) ot_font->ot_face = &font->face->table; -#ifndef HB_NO_AAT - /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */ -#ifndef HB_NO_STYLE - ot_font->apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data (); -#else - ot_font->apply_trak = false; -#endif -#endif - -#ifndef HB_NO_OT_FONT_CMAP_CACHE - // retry: - auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face, - &hb_ot_font_cmap_cache_user_data_key); - if (!cmap_cache) - { - cmap_cache = (hb_ot_font_cmap_cache_t *) hb_malloc (sizeof (hb_ot_font_cmap_cache_t)); - if (unlikely (!cmap_cache)) goto out; - new (cmap_cache) hb_ot_font_cmap_cache_t (); - if (unlikely (!hb_face_set_user_data (font->face, - &hb_ot_font_cmap_cache_user_data_key, - cmap_cache, - hb_free, - false))) - { - hb_free (cmap_cache); - cmap_cache = nullptr; - /* Normally we would retry here, but that would - * infinite-loop if the face is the empty-face. - * Just let it go and this font will be uncached if it - * happened to collide with another thread creating the - * cache at the same time. */ - // goto retry; - } - } - out: - ot_font->cmap_cache = cmap_cache; -#endif - return ot_font; } @@ -144,8 +185,7 @@ _hb_ot_font_destroy (void *font_data) { hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; - auto *cache = ot_font->advance_cache.get_relaxed (); - hb_free (cache); + ot_font->~hb_ot_font_t (); hb_free (ot_font); } @@ -159,11 +199,7 @@ hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - hb_ot_font_cmap_cache_t *cmap_cache = nullptr; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - cmap_cache = ot_font->cmap_cache; -#endif - return ot_face->cmap->get_nominal_glyph (unicode, glyph, cmap_cache); + return ot_face->cmap->get_nominal_glyph (unicode, glyph); } static unsigned int @@ -178,14 +214,9 @@ hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - hb_ot_font_cmap_cache_t *cmap_cache = nullptr; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - cmap_cache = ot_font->cmap_cache; -#endif return ot_face->cmap->get_nominal_glyphs (count, first_unicode, unicode_stride, - first_glyph, glyph_stride, - cmap_cache); + first_glyph, glyph_stride); } static hb_bool_t @@ -198,13 +229,8 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - hb_ot_font_cmap_cache_t *cmap_cache = nullptr; -#ifndef HB_NO_OT_FONT_CMAP_CACHE - cmap_cache = ot_font->cmap_cache; -#endif return ot_face->cmap->get_variation_glyph (unicode, - variation_selector, glyph, - cmap_cache); + variation_selector, glyph); } static void @@ -216,47 +242,25 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, unsigned advance_stride, void *user_data HB_UNUSED) { + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; - hb_position_t *orig_first_advance = first_advance; - -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + ot_font->check_serial (font); const OT::HVAR &HVAR = *hmtx.var_table; const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore; - OT::ItemVariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; + OT::ItemVariationStore::cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore); + + hb_ot_font_advance_cache_t *advance_cache = nullptr; bool use_cache = font->num_coords; -#else - OT::ItemVariationStore::cache_t *varStore_cache = nullptr; - bool use_cache = false; -#endif - - hb_ot_font_advance_cache_t *cache = nullptr; if (use_cache) { - retry: - cache = ot_font->advance_cache.get_acquire (); - if (unlikely (!cache)) - { - cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); - if (unlikely (!cache)) - { - use_cache = false; - goto out; - } - new (cache) hb_ot_font_advance_cache_t; - - if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache))) - { - hb_free (cache); - goto retry; - } - ot_font->cached_coords_serial.set_release (font->serial_coords); - } + advance_cache = ot_font->h.acquire_advance_cache (); + if (!advance_cache) + use_cache = false; } - out: if (!use_cache) { @@ -269,58 +273,26 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, } else { /* Use cache. */ - if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords) - { - ot_font->advance_cache->clear (); - ot_font->cached_coords_serial.set_release (font->serial_coords); - } - for (unsigned int i = 0; i < count; i++) { hb_position_t v; unsigned cv; - if (ot_font->advance_cache->get (*first_glyph, &cv)) + if (advance_cache->get (*first_glyph, &cv)) v = cv; else { v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); - ot_font->advance_cache->set (*first_glyph, v); + advance_cache->set (*first_glyph, v); } *first_advance = font->em_scale_x (v); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + + ot_font->h.release_advance_cache (advance_cache); } -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) - OT::ItemVariationStore::destroy_cache (varStore_cache); -#endif - - if (font->x_strength && !font->embolden_in_place) - { - /* Emboldening. */ - hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += *first_advance ? x_strength : 0; - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } - -#ifndef HB_NO_AAT - if (ot_font->apply_trak) - { - hb_position_t tracking = font->face->table.trak->get_h_tracking (font); - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += tracking; - first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } -#endif + ot_font->h.release_varStore_cache (varStore_cache); } #ifndef HB_NO_VERTICAL @@ -337,17 +309,13 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - hb_position_t *orig_first_advance = first_advance; - if (vmtx.has_data ()) { -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + ot_font->check_serial (font); const OT::VVAR &VVAR = *vmtx.var_table; const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore; - OT::ItemVariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; -#else - OT::ItemVariationStore::cache_t *varStore_cache = nullptr; -#endif + OT::ItemVariationStore::cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore); + // TODO Use advance_cache. for (unsigned int i = 0; i < count; i++) { @@ -356,9 +324,7 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } -#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) - OT::ItemVariationStore::destroy_cache (varStore_cache); -#endif + ot_font->v.release_varStore_cache (varStore_cache); } else { @@ -373,32 +339,6 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } } - - if (font->y_strength && !font->embolden_in_place) - { - /* Emboldening. */ - hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += *first_advance ? y_strength : 0; - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } - -#ifndef HB_NO_AAT - if (ot_font->apply_trak) - { - hb_position_t tracking = font->face->table.trak->get_v_tracking (font); - first_advance = orig_first_advance; - for (unsigned int i = 0; i < count; i++) - { - *first_advance += tracking; - first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); - first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); - } - } -#endif } #endif @@ -435,7 +375,8 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, } hb_glyph_extents_t extents = {0}; - if (ot_face->glyf->get_extents (font, glyph, &extents)) + + if (hb_font_get_glyph_extents (font, glyph, &extents)) { const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; int tsb = 0; @@ -448,7 +389,7 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, hb_font_extents_t font_extents; font->get_h_extents_with_fallback (&font_extents); hb_position_t advance = font_extents.ascender - font_extents.descender; - int diff = advance - -extents.height; + hb_position_t diff = advance - -extents.height; *y = extents.y_bearing + (diff >> 1); return true; } @@ -477,6 +418,9 @@ hb_ot_get_glyph_extents (hb_font_t *font, #endif #if !defined(HB_NO_COLOR) && !defined(HB_NO_PAINT) if (ot_face->COLR->get_extents (font, glyph, extents)) return true; +#endif +#ifndef HB_NO_VAR_COMPOSITES + if (ot_face->VARC->get_extents (font, glyph, extents)) return true; #endif if (ot_face->glyf->get_extents (font, glyph, extents)) return true; #ifndef HB_NO_OT_FONT_CFF @@ -528,16 +472,9 @@ hb_ot_get_font_h_extents (hb_font_t *font, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - bool ret = _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); - - /* Embolden */ - int y_shift = font->y_strength; - if (font->y_scale < 0) y_shift = -y_shift; - metrics->ascender += y_shift; - - return ret; + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); } #ifndef HB_NO_VERTICAL @@ -554,68 +491,46 @@ hb_ot_get_font_v_extents (hb_font_t *font, #endif #ifndef HB_NO_DRAW -static void -hb_ot_draw_glyph (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data) +static hb_bool_t +hb_ot_draw_glyph_or_fail (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data) { - bool embolden = font->x_strength || font->y_strength; - hb_outline_t outline; - - { // Need draw_session to be destructed before emboldening. - hb_draw_session_t draw_session (embolden ? hb_outline_recording_pen_get_funcs () : draw_funcs, - embolden ? &outline : draw_data, font->slant_xy); + hb_draw_session_t draw_session {draw_funcs, draw_data}; #ifndef HB_NO_VAR_COMPOSITES - if (!font->face->table.VARC->get_path (font, glyph, draw_session)) + if (font->face->table.VARC->get_path (font, glyph, draw_session)) return true; #endif - // Keep the following in synch with VARC::get_path_at() - if (!font->face->table.glyf->get_path (font, glyph, draw_session)) + // Keep the following in synch with VARC::get_path_at() + if (font->face->table.glyf->get_path (font, glyph, draw_session)) return true; #ifndef HB_NO_CFF - if (!font->face->table.cff2->get_path (font, glyph, draw_session)) - if (!font->face->table.cff1->get_path (font, glyph, draw_session)) + if (font->face->table.cff2->get_path (font, glyph, draw_session)) return true; + if (font->face->table.cff1->get_path (font, glyph, draw_session)) return true; #endif - {} - } - - if (embolden) - { - float x_shift = font->embolden_in_place ? 0 : (float) font->x_strength / 2; - float y_shift = (float) font->y_strength / 2; - if (font->x_scale < 0) x_shift = -x_shift; - if (font->y_scale < 0) y_shift = -y_shift; - outline.embolden (font->x_strength, font->y_strength, - x_shift, y_shift); - - outline.replay (draw_funcs, draw_data); - } + return false; } #endif #ifndef HB_NO_PAINT -static void -hb_ot_paint_glyph (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_paint_funcs_t *paint_funcs, void *paint_data, - unsigned int palette, - hb_color_t foreground, - void *user_data) +static hb_bool_t +hb_ot_paint_glyph_or_fail (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) { #ifndef HB_NO_COLOR - if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return; - if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return true; + if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; #ifndef HB_NO_OT_FONT_BITMAP - if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return; - if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; + if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; #endif #endif - - // Outline glyph - paint_funcs->push_clip_glyph (paint_data, glyph, font); - paint_funcs->color (paint_data, true, foreground); - paint_funcs->pop_clip (paint_data); + return false; } #endif @@ -642,11 +557,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t table; AAT::kern_accelerator_data_t accel_data; + AAT::hb_aat_scratch_t scratch; }; protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh index 5a3679e4bfa..04a79a93a89 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh @@ -182,7 +182,7 @@ struct BaseCoord void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const { switch (u.format) { - case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set); + case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set); return; default:return; } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh index f14675295f6..7ee34185575 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh @@ -1850,7 +1850,7 @@ struct ClassDefFormat2_4 hb_sorted_vector_t glyph_and_klass; hb_set_t orig_klasses; - if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2 + if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) < get_population ()) { for (hb_codepoint_t g : glyph_set) @@ -1931,7 +1931,7 @@ struct ClassDefFormat2_4 bool intersects (const hb_set_t *glyphs) const { - if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len)) { for (auto g : *glyphs) if (get_class (g)) @@ -2000,7 +2000,7 @@ struct ClassDefFormat2_4 } unsigned count = rangeRecord.len; - if (count > glyphs->get_population () * hb_bit_storage (count) * 8) + if (count > glyphs->get_population () * hb_bit_storage (count)) { for (auto g : *glyphs) { @@ -2548,11 +2548,13 @@ struct SparseVarRegionAxis DEFINE_SIZE_STATIC (8); }; -#define REGION_CACHE_ITEM_CACHE_INVALID 2.f +#define REGION_CACHE_ITEM_CACHE_INVALID INT_MIN +#define REGION_CACHE_ITEM_MULTIPLIER (float (1 << ((sizeof (int) * 8) - 2))) +#define REGION_CACHE_ITEM_DIVISOR (1.f / float (1 << ((sizeof (int) * 8) - 2))) struct VarRegionList { - using cache_t = float; + using cache_t = hb_atomic_t; float evaluate (unsigned int region_index, const int *coords, unsigned int coord_len, @@ -2561,12 +2563,12 @@ struct VarRegionList if (unlikely (region_index >= regionCount)) return 0.; - float *cached_value = nullptr; + cache_t *cached_value = nullptr; if (cache) { cached_value = &(cache[region_index]); - if (likely (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)) - return *cached_value; + if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) + return *cached_value * REGION_CACHE_ITEM_DIVISOR; } const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount); @@ -2587,7 +2589,7 @@ struct VarRegionList } if (cache) - *cached_value = v; + *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; return v; } @@ -2730,7 +2732,7 @@ struct SparseVariationRegion : Array16Of struct SparseVarRegionList { - using cache_t = float; + using cache_t = hb_atomic_t; float evaluate (unsigned int region_index, const int *coords, unsigned int coord_len, @@ -2739,12 +2741,12 @@ struct SparseVarRegionList if (unlikely (region_index >= regions.len)) return 0.; - float *cached_value = nullptr; + cache_t *cached_value = nullptr; if (cache) { cached_value = &(cache[region_index]); - if (likely (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)) - return *cached_value; + if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) + return *cached_value * REGION_CACHE_ITEM_DIVISOR; } const SparseVariationRegion ®ion = this+regions[region_index]; @@ -2752,7 +2754,7 @@ struct SparseVarRegionList float v = region.evaluate (coords, coord_len); if (cache) - *cached_value = v; + *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; return v; } @@ -2861,8 +2863,13 @@ struct VarData const hb_vector_t*>& rows) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (this))) return_trace (false); unsigned row_count = rows.length; + if (!row_count) { + // Nothing to serialize, will be empty. + return false; + } + + if (unlikely (!c->extend_min (this))) return_trace (false); itemCount = row_count; int min_threshold = has_long ? -65536 : -128; @@ -3187,10 +3194,10 @@ struct ItemVariationStore #ifdef HB_NO_VAR return nullptr; #endif - auto &r = this+regions; - unsigned count = r.regionCount; + unsigned count = (this+regions).regionCount; + if (!count) return nullptr; - float *cache = (float *) hb_malloc (sizeof (float) * count); + cache_t *cache = (cache_t *) hb_malloc (sizeof (float) * count); if (unlikely (!cache)) return nullptr; for (unsigned i = 0; i < count; i++) @@ -3440,7 +3447,7 @@ struct MultiItemVariationStore { using cache_t = SparseVarRegionList::cache_t; - cache_t *create_cache (hb_array_t static_cache = hb_array_t ()) const + cache_t *create_cache (hb_array_t static_cache = hb_array_t ()) const { #ifdef HB_NO_VAR return nullptr; @@ -3448,12 +3455,12 @@ struct MultiItemVariationStore auto &r = this+regions; unsigned count = r.regions.len; - float *cache; + cache_t *cache; if (count <= static_cache.length) cache = static_cache.arrayZ; else { - cache = (float *) hb_malloc (sizeof (float) * count); + cache = (cache_t *) hb_malloc (sizeof (float) * count); if (unlikely (!cache)) return nullptr; } @@ -3464,7 +3471,7 @@ struct MultiItemVariationStore } static void destroy_cache (cache_t *cache, - hb_array_t static_cache = hb_array_t ()) + hb_array_t static_cache = hb_array_t ()) { if (cache != static_cache.arrayZ) hb_free (cache); @@ -4777,12 +4784,12 @@ struct VariationDevice hb_position_t get_x_delta (hb_font_t *font, const ItemVariationStore &store, ItemVariationStore::cache_t *store_cache = nullptr) const - { return font->em_scalef_x (get_delta (font, store, store_cache)); } + { return !font->num_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); } hb_position_t get_y_delta (hb_font_t *font, const ItemVariationStore &store, ItemVariationStore::cache_t *store_cache = nullptr) const - { return font->em_scalef_y (get_delta (font, store, store_cache)); } + { return !font->num_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); } VariationDevice* copy (hb_serialize_context_t *c, const hb_hashmap_t> *layout_variation_idx_delta_map) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh index 1a719391b8e..072e5cdba26 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh @@ -737,7 +737,8 @@ struct hb_ot_apply_context_t : hb_ot_apply_context_t (unsigned int table_index_, hb_font_t *font_, hb_buffer_t *buffer_, - hb_blob_t *table_blob_) : + hb_blob_t *table_blob_, + ItemVariationStore::cache_t *var_store_cache_ = nullptr) : table_index (table_index_), font (font_), face (font->face), buffer (buffer_), sanitizer (table_blob_), @@ -756,13 +757,7 @@ struct hb_ot_apply_context_t : #endif ), var_store (gdef.get_var_store ()), - var_store_cache ( -#ifndef HB_NO_VAR - table_index == 1 && font->num_coords ? var_store.create_cache () : nullptr -#else - nullptr -#endif - ), + var_store_cache (var_store_cache_), direction (buffer_->props.direction), has_glyph_classes (gdef.has_glyph_classes ()) { @@ -770,13 +765,6 @@ struct hb_ot_apply_context_t : buffer->collect_codepoints (digest); } - ~hb_ot_apply_context_t () - { -#ifndef HB_NO_VAR - ItemVariationStore::destroy_cache (var_store_cache); -#endif - } - void init_iters () { iter_input.init (this, false); @@ -4900,7 +4888,7 @@ struct GSUBGPOS this->lookup_count = table->get_lookup_count (); - this->accels = (hb_atomic_ptr_t *) hb_calloc (this->lookup_count, sizeof (*accels)); + this->accels = (hb_atomic_t *) hb_calloc (this->lookup_count, sizeof (*accels)); if (unlikely (!this->accels)) { this->lookup_count = 0; @@ -4948,7 +4936,7 @@ struct GSUBGPOS hb_blob_ptr_t table; unsigned int lookup_count; - hb_atomic_ptr_t *accels; + hb_atomic_t *accels; }; protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc index 787119dc62b..afe52963201 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc @@ -131,13 +131,15 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { - hb_blob_t *blob = font->face->table.kern.get_blob (); - const auto& kern = *font->face->table.kern; + auto &accel = *font->face->table.kern; + hb_blob_t *blob = accel.get_blob (); AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); if (!buffer->message (font, "start table kern")) return; - kern.apply (&c); + c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set (); + accel.apply (&c); + accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set); (void) buffer->message (font, "end table kern"); } #endif @@ -2013,7 +2015,11 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, { const unsigned int table_index = proxy.table_index; unsigned int i = 0; - OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob ()); + + auto *font_data = font->data.ot.get (); + auto *var_store_cache = font_data == HB_SHAPER_DATA_SUCCEEDED ? nullptr : (OT::ItemVariationStore::cache_t *) font_data; + + OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache); c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func); for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) @@ -2626,7 +2632,8 @@ struct hb_get_glyph_alternates_dispatch_t : * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. * Alternate glyphs associated with the glyph id. * - * Fetches alternates of a glyph from a given GSUB lookup index. + * Fetches alternates of a glyph from a given GSUB lookup index. Note that for one-to-one GSUB + * glyph substitutions, this function fetches the substituted glyph. * * Return value: Total number of alternates found in the specific lookup index for the given glyph id. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh index 71074a87401..a68c8421e4c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh @@ -202,7 +202,8 @@ enum hb_unicode_props_flags_t { /* If GEN_CAT=FORMAT, top byte masks: */ UPROPS_MASK_Cf_ZWJ = 0x0100u, UPROPS_MASK_Cf_ZWNJ = 0x0200u, - UPROPS_MASK_Cf_VS = 0x0400u + UPROPS_MASK_Cf_VS = 0x0400u, + UPROPS_MASK_Cf_AAT_DELETED = 0x0800u }; HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t); @@ -386,6 +387,8 @@ _hb_grapheme_group_func (const hb_glyph_info_t& a HB_UNUSED, static inline void _hb_ot_layout_reverse_graphemes (hb_buffer_t *buffer) { + // MONOTONE_GRAPHEMES was already applied and is taken care of by _hb_grapheme_group_func. + // So we just check for MONOTONE_CHARACTERS here. buffer->reverse_groups (_hb_grapheme_group_func, buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); } @@ -418,6 +421,18 @@ _hb_glyph_info_flip_joiners (hb_glyph_info_t *info) return; info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ; } +static inline bool +_hb_glyph_info_is_aat_deleted (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_AAT_DELETED); +} +static inline void +_hb_glyph_info_set_aat_deleted (hb_glyph_info_t *info) +{ + _hb_glyph_info_set_general_category (info, HB_UNICODE_GENERAL_CATEGORY_FORMAT); + info->unicode_props() |= UPROPS_MASK_Cf_AAT_DELETED; + info->unicode_props() |= UPROPS_MASK_HIDDEN; +} /* lig_props: aka lig_id / lig_comp * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh index c1b2a27b21d..b33e01fd6c9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh @@ -1104,6 +1104,24 @@ struct MATH mathVariants.sanitize (c, this)); } + // https://github.com/harfbuzz/harfbuzz/issues/4653 + HB_INTERNAL bool is_bad_cambria (hb_font_t *font) const + { +#ifndef HB_NO_MATH + switch HB_CODEPOINT_ENCODE3 (font->face->table.MATH.get_blob ()->length, + get_constant (HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT, font), + get_constant (HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT, font)) + { + /* sha1sum:ab4a4fe054d23061f3c039493d6f665cfda2ecf5 cambria.ttc + * sha1sum:086855301bff644f9d8827b88491fcf73a6d4cb9 cambria.ttc + * sha1sum:b1e5a3feaca2ea3dfcf79ccb377de749ecf60343 cambria.ttc */ + case HB_CODEPOINT_ENCODE3 (25722, 2500, 3000): + return true; + } +#endif + return false; + } + hb_position_t get_constant (hb_ot_math_constant_t constant, hb_font_t *font) const { return (this+mathConstants).get_value (constant, font); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc index 191149964f9..ab1f50c8eb8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc @@ -87,6 +87,20 @@ hb_position_t hb_ot_math_get_constant (hb_font_t *font, hb_ot_math_constant_t constant) { + /* https://github.com/harfbuzz/harfbuzz/issues/4653 + * Cambria Math has incorrect value for displayOperatorMinHeight, and + * apparently Microsoft implementation swaps displayOperatorMinHeight and + * delimitedSubFormulaMinHeight, so we do the same if we detect Cambria Math + * with the swapped values. */ + if ((constant == HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT || + constant == HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT) && + font->face->table.MATH->is_bad_cambria (font)) + { + if (constant == HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT) + constant = HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT; + else + constant = HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT; + } return font->face->table.MATH->get_constant(constant, font); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh index 83d310e16a0..2c4c6c04663 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh @@ -290,7 +290,7 @@ struct post const Array16Of *glyphNameIndex = nullptr; hb_vector_t index_to_offset; const uint8_t *pool = nullptr; - hb_atomic_ptr_t gids_sorted_by_name; + mutable hb_atomic_t gids_sorted_by_name; }; bool has_data () const { return version.to_int (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc index b74d3104a7e..2401e18f0e6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc @@ -427,8 +427,13 @@ position_cluster (const hb_ot_shape_plan_t *plan, /* Find mark glyphs */ unsigned int j; for (j = i + 1; j < end; j++) + { + if (_hb_glyph_info_is_hidden (&info[j]) || + _hb_glyph_info_is_default_ignorable (&info[j])) + continue; if (!_hb_glyph_info_is_unicode_mark (&info[j])) break; + } position_around_base (plan, font, buffer, i, j, adjust_offsets_when_zeroing); @@ -455,7 +460,9 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 1; i < count; i++) - if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]))) { + if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]) && + !_hb_glyph_info_is_hidden (&info[i]) && + !_hb_glyph_info_is_default_ignorable (&info[i]))) { position_cluster (plan, font, buffer, start, i, adjust_offsets_when_zeroing); start = i; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc index 6265f0eb17d..477a7c7fc72 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc @@ -46,6 +46,7 @@ #include "hb-set.hh" #include "hb-aat-layout.hh" +#include "hb-ot-layout-gdef-table.hh" #include "hb-ot-stat-table.hh" @@ -84,6 +85,7 @@ hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *fac props (props), map (face, props) #ifndef HB_NO_AAT_SHAPE + , aat_map (face, props) , apply_morx (_hb_apply_morx (face, props)) #endif { @@ -106,6 +108,10 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, plan.props = props; plan.shaper = shaper; map.compile (plan.map, key); +#ifndef HB_NO_AAT_SHAPE + if (apply_morx) + aat_map.compile (plan.aat_map); +#endif #ifndef HB_NO_OT_SHAPE_FRACTIONS plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); @@ -205,6 +211,14 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, https://github.com/harfbuzz/harfbuzz/issues/2967. */ if (plan.apply_morx) plan.adjust_mark_positioning_when_zeroing = false; + + /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */ +#ifndef HB_NO_STYLE + plan.apply_trak = hb_aat_layout_has_tracking (face) && face->table.STAT->has_data (); +#else + plan.apply_trak = false; +#endif + #endif } @@ -269,6 +283,11 @@ hb_ot_shape_plan_t::position (hb_font_t *font, #endif else if (this->apply_fallback_kern) _hb_ot_shape_fallback_kern (this, font, buffer); + +#ifndef HB_NO_AAT_SHAPE + if (this->apply_trak) + hb_aat_layout_track (this, font, buffer); +#endif } @@ -405,17 +424,26 @@ _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) * shaper font data */ -struct hb_ot_font_data_t {}; +struct hb_ot_font_data_t { + OT::ItemVariationStore::cache_t unused; // Just for alignment +}; hb_ot_font_data_t * -_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) +_hb_ot_shaper_font_data_create (hb_font_t *font) { - return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + if (!font->num_coords) + return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + + const OT::ItemVariationStore &var_store = font->face->table.GDEF->table->get_var_store (); + auto *cache = (hb_ot_font_data_t *) var_store.create_cache (); + return cache ? cache : (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED) +_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data) { + if (data == HB_SHAPER_DATA_SUCCEEDED) return; + OT::ItemVariationStore::destroy_cache ((OT::ItemVariationStore::cache_t *) data); } @@ -551,7 +579,7 @@ hb_form_clusters (hb_buffer_t *buffer) if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) return; - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level)) foreach_grapheme (buffer, start, end) buffer->merge_clusters (start, end); else @@ -609,7 +637,7 @@ hb_ensure_native_direction (hb_buffer_t *buffer) * Ogham fonts are supposed to be implemented BTT or not. Need to research that * first. */ if ((HB_DIRECTION_IS_HORIZONTAL (direction) && - direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || + direction != horiz_dir && HB_DIRECTION_IS_VALID (horiz_dir)) || (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) { @@ -1109,10 +1137,6 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c) /* Finish off. Has to follow a certain order. */ hb_ot_layout_position_finish_advances (c->font, c->buffer); hb_ot_zero_width_default_ignorables (c->buffer); -#ifndef HB_NO_AAT_SHAPE - if (c->plan->apply_morx) - hb_aat_layout_zero_width_deleted_glyphs (c->buffer); -#endif hb_ot_layout_position_finish_offsets (c->font, c->buffer); /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh index d5ee82d4c0b..068d7192d0d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh @@ -66,6 +66,7 @@ struct hb_ot_shape_plan_t hb_segment_properties_t props; const struct hb_ot_shaper_t *shaper; hb_ot_map_t map; + hb_aat_map_t aat_map; const void *data; #ifndef HB_NO_OT_SHAPE_FRACTIONS hb_mask_t frac_mask, numr_mask, dnom_mask; @@ -108,9 +109,11 @@ struct hb_ot_shape_plan_t #ifndef HB_NO_AAT_SHAPE bool apply_kerx : 1; bool apply_morx : 1; + bool apply_trak : 1; #else static constexpr bool apply_kerx = false; static constexpr bool apply_morx = false; + static constexpr bool apply_trak = false; #endif void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const @@ -141,6 +144,7 @@ struct hb_ot_shape_planner_t hb_segment_properties_t props; hb_ot_map_builder_t map; #ifndef HB_NO_AAT_SHAPE + hb_aat_map_builder_t aat_map; bool apply_morx : 1; #else static constexpr bool apply_morx = false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc index 67db6cb26c4..7379bb7f15b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc @@ -260,7 +260,7 @@ struct arabic_shape_plan_t * mask_array[NONE] == 0. */ hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; - hb_atomic_ptr_t fallback_plan; + mutable hb_atomic_t fallback_plan; unsigned int do_fallback : 1; unsigned int has_stch : 1; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc index 8b41e2219b1..5c46ebbc281 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc @@ -298,8 +298,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, end = start + 2; if (unlikely (!buffer->successful)) break; - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) - buffer->merge_out_clusters (start, end); + buffer->merge_out_clusters (start, end); continue; } } @@ -372,8 +371,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, if (i < end) info[i++].hangul_shaping_feature() = TJMO; - if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) - buffer->merge_out_clusters (start, end); + buffer->merge_out_clusters (start, end); continue; } else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint))) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc index 20007df8963..d78b5670f61 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc @@ -301,7 +301,7 @@ struct indic_shape_plan_t #else static constexpr bool uniscribe_bug_compatible = false; #endif - mutable hb_atomic_int_t virama_glyph; + mutable hb_atomic_t virama_glyph; hb_indic_would_substitute_feature_t rphf; hb_indic_would_substitute_feature_t pref; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc index 68642a5c178..a0ea464e775 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc @@ -360,7 +360,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan, { /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the * previous cluster. */ - if (start && buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + if (start) buffer->merge_out_clusters (start - 1, end); } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc index 3cdb703203b..786a220b6ec 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc @@ -326,7 +326,7 @@ hb_ot_tags_from_language (const char *lang_str, hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len); - static hb_atomic_int_t last_tag_idx; /* Poor man's cache. */ + static hb_atomic_t last_tag_idx = 0; /* Poor man's cache. */ unsigned tag_idx = last_tag_idx; if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) || diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh index c3040187f6e..9f9b5be3cbe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh @@ -53,10 +53,6 @@ struct hb_glyf_scratch_t contour_point_vector_t deltas; hb_vector_t shared_indices; hb_vector_t private_indices; - - // VARC - hb_vector_t axisIndices; - hb_vector_t axisValues; }; namespace OT { @@ -594,9 +590,9 @@ struct gvar_GVAR /* If sanitize failed, set glyphCount to 0. */ glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0; - /* For shared tuples that only have one axis active, shared the index of - * that axis as a cache. This will speed up caclulate_scalar() a lot - * for fonts with lots of axes and many "monovar" tuples. */ + /* For shared tuples that only have one or two axes active, shared the index + * of that axis as a cache. This will speed up caclulate_scalar() a lot + * for fonts with lots of axes and many "monovar" or "duovar" tuples. */ hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); unsigned count = table->sharedTupleCount; if (unlikely (!shared_tuple_active_idx.resize (count, false))) return; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc index 82bbbb0b1f2..8c695c41e24 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc @@ -117,7 +117,7 @@ hb_ot_var_get_axes (hb_face_t *face, * in the specified face. * * Since: 1.4.2 - * Deprecated: 2.2.0 - use hb_ot_var_find_axis_info() instead + * Deprecated: 2.2.0: use hb_ot_var_find_axis_info() instead **/ hb_bool_t hb_ot_var_find_axis (hb_face_t *face, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc index a0a6ea156c3..85ffc793678 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc @@ -84,6 +84,12 @@ void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const } } +void hb_outline_t::slant (float slant_xy) +{ + for (auto &p : points) + p.x += slant_xy * p.y; +} + float hb_outline_t::control_area () const { float a = 0; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh index c44625da2e1..c5b68c05dd7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh @@ -69,6 +69,7 @@ struct hb_outline_t HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; HB_INTERNAL float control_area () const; + HB_INTERNAL void slant (float slant_xy); HB_INTERNAL void embolden (float x_strength, float y_strength, float x_shift, float y_shift); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.cc new file mode 100644 index 00000000000..bc7e962bb46 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.cc @@ -0,0 +1,207 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_PAINT + +#include "hb-paint-bounded.hh" + +#include "hb-machinery.hh" + + +/* + * This file implements boundedness computation of COLRv1 fonts as described in: + * + * https://learn.microsoft.com/en-us/typography/opentype/spec/colr#glyph-metrics-and-boundedness + */ + +static void +hb_paint_bounded_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_clip (); +} + +static void +hb_paint_bounded_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_clip (); +} + +static void +hb_paint_bounded_pop_clip (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->pop_clip (); +} + +static void +hb_paint_bounded_push_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_group (); +} + +static void +hb_paint_bounded_pop_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->pop_group (mode); +} + +static hb_bool_t +hb_paint_bounded_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_blob_t *blob HB_UNUSED, + unsigned int width HB_UNUSED, + unsigned int height HB_UNUSED, + hb_tag_t format HB_UNUSED, + float slant HB_UNUSED, + hb_glyph_extents_t *glyph_extents, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->push_clip (); + c->paint (); + c->pop_clip (); + + return true; +} + +static void +hb_paint_bounded_paint_color (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_bool_t use_foreground HB_UNUSED, + hb_color_t color HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_bounded_paint_linear_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, + float x2 HB_UNUSED, float y2 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_bounded_paint_radial_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, float r0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, float r1 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_bounded_paint_sweep_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float cx HB_UNUSED, float cy HB_UNUSED, + float start_angle HB_UNUSED, + float end_angle HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_bounded_context_t *c = (hb_paint_bounded_context_t *) paint_data; + + c->paint (); +} + +static inline void free_static_paint_bounded_funcs (); + +static struct hb_paint_bounded_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t +{ + static hb_paint_funcs_t *create () + { + hb_paint_funcs_t *funcs = hb_paint_funcs_create (); + + hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_paint_bounded_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_paint_bounded_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, hb_paint_bounded_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, hb_paint_bounded_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, hb_paint_bounded_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, hb_paint_bounded_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, hb_paint_bounded_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, hb_paint_bounded_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, hb_paint_bounded_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, hb_paint_bounded_paint_sweep_gradient, nullptr, nullptr); + + hb_paint_funcs_make_immutable (funcs); + + hb_atexit (free_static_paint_bounded_funcs); + + return funcs; + } +} static_paint_bounded_funcs; + +static inline +void free_static_paint_bounded_funcs () +{ + static_paint_bounded_funcs.free_instance (); +} + +hb_paint_funcs_t * +hb_paint_bounded_get_funcs () +{ + return static_paint_bounded_funcs.get_unconst (); +} + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.hh new file mode 100644 index 00000000000..bdcdba76397 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-bounded.hh @@ -0,0 +1,117 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_PAINT_BOUNDED_HH +#define HB_PAINT_BOUNDED_HH + +#include "hb.hh" +#include "hb-paint.h" + +#include "hb-geometry.hh" + + +typedef struct hb_paint_bounded_context_t hb_paint_bounded_context_t; + +struct hb_paint_bounded_context_t +{ + void clear () + { + clips = 0; + bounded = true; + groups.clear (); + } + + hb_paint_bounded_context_t () + { + clear (); + } + + bool is_bounded () + { + return bounded; + } + + void push_clip () + { + clips++; + } + + void pop_clip () + { + if (clips == 0) return; + clips--; + } + + void push_group () + { + groups.push (bounded); + bounded = true; + } + + void pop_group (hb_paint_composite_mode_t mode) + { + const bool src_bounded = bounded; + bounded = groups.pop (); + bool &backdrop_bounded = bounded; + + // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite + switch ((int) mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: + backdrop_bounded = true; + break; + case HB_PAINT_COMPOSITE_MODE_SRC: + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: + backdrop_bounded = src_bounded; + break; + case HB_PAINT_COMPOSITE_MODE_DEST: + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: + break; + case HB_PAINT_COMPOSITE_MODE_SRC_IN: + case HB_PAINT_COMPOSITE_MODE_DEST_IN: + backdrop_bounded = backdrop_bounded && src_bounded; + break; + default: + backdrop_bounded = backdrop_bounded || src_bounded; + break; + } + } + + void paint () + { + if (!clips) + bounded = false; + } + + protected: + bool bounded; // true if current drawing bounded + unsigned clips; // number of active clips + hb_vector_t groups; // true if group bounded +}; + +HB_INTERNAL hb_paint_funcs_t * +hb_paint_bounded_get_funcs (); + + +#endif /* HB_PAINT_BOUNDED_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc index 3bbe6351725..bc08c5a9e14 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc @@ -28,14 +28,13 @@ #include "hb-paint-extents.hh" -#include "hb-draw.h" +#include "hb-draw.hh" #include "hb-machinery.hh" /* - * This file implements bounds-extraction as well as boundedness - * computation of COLRv1 fonts as described in: + * This file implements bounds-extraction computation of COLRv1 fonts as described in: * * https://learn.microsoft.com/en-us/typography/opentype/spec/colr#glyph-metrics-and-boundedness */ @@ -63,93 +62,6 @@ hb_paint_extents_pop_transform (hb_paint_funcs_t *funcs HB_UNUSED, c->pop_transform (); } -static void -hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (to_x, to_y); -} - -static void -hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (to_x, to_y); -} - -static void -hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control_x, float control_y, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (control_x, control_y); - extents->add_point (to_x, to_y); -} - -static void -hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, - void *data, - hb_draw_state_t *st, - float control1_x, float control1_y, - float control2_x, float control2_y, - float to_x, float to_y, - void *user_data HB_UNUSED) -{ - hb_extents_t *extents = (hb_extents_t *) data; - - extents->add_point (control1_x, control1_y); - extents->add_point (control2_x, control2_y); - extents->add_point (to_x, to_y); -} - -static inline void free_static_draw_extents_funcs (); - -static struct hb_draw_extents_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t -{ - static hb_draw_funcs_t *create () - { - hb_draw_funcs_t *funcs = hb_draw_funcs_create (); - - hb_draw_funcs_set_move_to_func (funcs, hb_draw_extents_move_to, nullptr, nullptr); - hb_draw_funcs_set_line_to_func (funcs, hb_draw_extents_line_to, nullptr, nullptr); - hb_draw_funcs_set_quadratic_to_func (funcs, hb_draw_extents_quadratic_to, nullptr, nullptr); - hb_draw_funcs_set_cubic_to_func (funcs, hb_draw_extents_cubic_to, nullptr, nullptr); - - hb_draw_funcs_make_immutable (funcs); - - hb_atexit (free_static_draw_extents_funcs); - - return funcs; - } -} static_draw_extents_funcs; - -static inline -void free_static_draw_extents_funcs () -{ - static_draw_extents_funcs.free_instance (); -} - -static hb_draw_funcs_t * -hb_draw_extents_get_funcs () -{ - return static_draw_extents_funcs.get_unconst (); -} - static void hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, void *paint_data, @@ -221,6 +133,9 @@ hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + if (!glyph_extents) + return false; // Happens with SVG images. + hb_extents_t extents = {(float) glyph_extents->x_bearing, (float) glyph_extents->y_bearing + glyph_extents->height, (float) glyph_extents->x_bearing + glyph_extents->width, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh index 18aa638c7ad..60374eaa2d2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh @@ -35,13 +35,22 @@ typedef struct hb_paint_extents_context_t hb_paint_extents_context_t; struct hb_paint_extents_context_t { - hb_paint_extents_context_t () + void clear () { + transforms.clear (); + clips.clear (); + groups.clear (); + transforms.push (hb_transform_t{}); clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); } + hb_paint_extents_context_t () + { + clear (); + } + hb_extents_t get_extents () { return groups.tail().extents; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint.cc index 6f2a296744f..094597d6457 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.cc @@ -87,7 +87,7 @@ hb_paint_image_nil (hb_paint_funcs_t *funcs, void *paint_data, unsigned int width, unsigned int height, hb_tag_t format, - float slant_xy, + float slant_xy_deprecated, hb_glyph_extents_t *extents, void *user_data) { return false; } @@ -464,6 +464,42 @@ hb_paint_push_transform (hb_paint_funcs_t *funcs, void *paint_data, funcs->push_transform (paint_data, xx, yx, xy, yy, dx, dy); } +/** + * hb_paint_push_font_transform: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @font: a font + * + * Push the transform reflecting the font's scale and slant + * settings onto the paint functions. + * + * Since: 11.0.0 + */ +void +hb_paint_push_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font) +{ + funcs->push_font_transform (paint_data, font); +} + +/** + * hb_paint_push_inverse_font_transform: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @font: a font + * + * Push the inverse of the transform reflecting the font's + * scale and slant settings onto the paint functions. + * + * Since: 11.0.0 + */ +void +hb_paint_push_inverse_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font) +{ + funcs->push_inverse_font_transform (paint_data, font); +} + /** * hb_paint_pop_transform: * @funcs: paint functions @@ -579,7 +615,7 @@ hb_paint_color (hb_paint_funcs_t *funcs, void *paint_data, * @width: width of the raster image in pixels, or 0 * @height: height of the raster image in pixels, or 0 * @format: the image format as a tag - * @slant: the synthetic slant ratio to be applied to the image during rendering + * @slant: Deprecated. set to 0.0 * @extents: (nullable): the extents of the glyph * * Perform a "image" paint operation. @@ -592,10 +628,10 @@ hb_paint_image (hb_paint_funcs_t *funcs, void *paint_data, unsigned int width, unsigned int height, hb_tag_t format, - float slant, + HB_UNUSED float slant, hb_glyph_extents_t *extents) { - funcs->image (paint_data, image, width, height, format, slant, extents); + funcs->image (paint_data, image, width, height, format, 0.f, extents); } /** @@ -646,7 +682,7 @@ hb_paint_radial_gradient (hb_paint_funcs_t *funcs, void *paint_data, float x0, float y0, float r0, float x1, float y1, float r1) { - funcs->radial_gradient (paint_data, color_line, x0, y0, r0, y1, x1, r1); + funcs->radial_gradient (paint_data, color_line, x0, y0, r0, x1, y1, r1); } /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.h b/src/java.desktop/share/native/libharfbuzz/hb-paint.h index f0d9438506d..9827a02887b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.h @@ -167,8 +167,10 @@ typedef hb_bool_t (*hb_paint_color_glyph_func_t) (hb_paint_funcs_t *funcs, * A virtual method for the #hb_paint_funcs_t to clip * subsequent paint calls to the outline of a glyph. * - * The coordinates of the glyph outline are interpreted according - * to the current transform. + * The coordinates of the glyph outline are expected in the + * current @font scale (ie. the results of calling + * hb_font_draw_glyph() with @font). The outline is + * transformed by the current transform. * * This clip is applied in addition to the current clip, * and remains in effect until a matching call to @@ -281,7 +283,7 @@ typedef void (*hb_paint_color_func_t) (hb_paint_funcs_t *funcs, * @width: width of the raster image in pixels, or 0 * @height: height of the raster image in pixels, or 0 * @format: the image format as a tag - * @slant: the synthetic slant ratio to be applied to the image during rendering + * @slant: Deprecated. Always set to 0.0. * @extents: (nullable): glyph extents for desired rendering * @user_data: User data pointer passed to hb_paint_funcs_set_image_func() * @@ -956,6 +958,14 @@ hb_paint_push_transform (hb_paint_funcs_t *funcs, void *paint_data, float xy, float yy, float dx, float dy); +HB_EXTERN void +hb_paint_push_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font); + +HB_EXTERN void +hb_paint_push_inverse_font_transform (hb_paint_funcs_t *funcs, void *paint_data, + const hb_font_t *font); + HB_EXTERN void hb_paint_pop_transform (hb_paint_funcs_t *funcs, void *paint_data); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh index fbf4a08cfff..2abadebab3c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh @@ -157,27 +157,29 @@ struct hb_paint_funcs_t /* Internal specializations. */ - void push_root_transform (void *paint_data, + void push_font_transform (void *paint_data, const hb_font_t *font) { float upem = font->face->get_upem (); int xscale = font->x_scale, yscale = font->y_scale; - float slant = font->slant_xy; push_transform (paint_data, - xscale/upem, 0, slant * yscale/upem, yscale/upem, 0, 0); + xscale/upem, 0, + 0, yscale/upem, + 0, 0); } - void push_inverse_root_transform (void *paint_data, - hb_font_t *font) + void push_inverse_font_transform (void *paint_data, + const hb_font_t *font) { float upem = font->face->get_upem (); int xscale = font->x_scale ? font->x_scale : upem; int yscale = font->y_scale ? font->y_scale : upem; - float slant = font->slant_xy; push_transform (paint_data, - upem/xscale, 0, -slant * upem/xscale, upem/yscale, 0, 0); + upem/xscale, 0, + 0, upem/yscale, + 0, 0); } HB_NODISCARD diff --git a/src/java.desktop/share/native/libharfbuzz/hb-script-list.h b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h new file mode 100644 index 00000000000..f811dbc3408 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h @@ -0,0 +1,484 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_SCRIPT_LIST_H +#define HB_SCRIPT_LIST_H + +/* This file belongs to the middle of hb-common.h. + * The reason it has been surgically extracted is because + * FreeType imports types and enums from hb-common.h, + * and since this enum is large and growing, we want to + * make it easy to just copy the file over to FreeType. + * https://github.com/harfbuzz/harfbuzz/issues/5271 + */ + +/* Dummy lines to make our checks happy. */ +#if 0 +#include "hb-common.h" +HB_BEGIN_DECLS +HB_END_DECLS +#endif + + +/** + * hb_script_t: + * @HB_SCRIPT_COMMON: `Zyyy` + * @HB_SCRIPT_INHERITED: `Zinh` + * @HB_SCRIPT_UNKNOWN: `Zzzz` + * @HB_SCRIPT_ARABIC: `Arab` + * @HB_SCRIPT_ARMENIAN: `Armn` + * @HB_SCRIPT_BENGALI: `Beng` + * @HB_SCRIPT_CYRILLIC: `Cyrl` + * @HB_SCRIPT_DEVANAGARI: `Deva` + * @HB_SCRIPT_GEORGIAN: `Geor` + * @HB_SCRIPT_GREEK: `Grek` + * @HB_SCRIPT_GUJARATI: `Gujr` + * @HB_SCRIPT_GURMUKHI: `Guru` + * @HB_SCRIPT_HANGUL: `Hang` + * @HB_SCRIPT_HAN: `Hani` + * @HB_SCRIPT_HEBREW: `Hebr` + * @HB_SCRIPT_HIRAGANA: `Hira` + * @HB_SCRIPT_KANNADA: `Knda` + * @HB_SCRIPT_KATAKANA: `Kana` + * @HB_SCRIPT_LAO: `Laoo` + * @HB_SCRIPT_LATIN: `Latn` + * @HB_SCRIPT_MALAYALAM: `Mlym` + * @HB_SCRIPT_ORIYA: `Orya` + * @HB_SCRIPT_TAMIL: `Taml` + * @HB_SCRIPT_TELUGU: `Telu` + * @HB_SCRIPT_THAI: `Thai` + * @HB_SCRIPT_TIBETAN: `Tibt` + * @HB_SCRIPT_BOPOMOFO: `Bopo` + * @HB_SCRIPT_BRAILLE: `Brai` + * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans` + * @HB_SCRIPT_CHEROKEE: `Cher` + * @HB_SCRIPT_ETHIOPIC: `Ethi` + * @HB_SCRIPT_KHMER: `Khmr` + * @HB_SCRIPT_MONGOLIAN: `Mong` + * @HB_SCRIPT_MYANMAR: `Mymr` + * @HB_SCRIPT_OGHAM: `Ogam` + * @HB_SCRIPT_RUNIC: `Runr` + * @HB_SCRIPT_SINHALA: `Sinh` + * @HB_SCRIPT_SYRIAC: `Syrc` + * @HB_SCRIPT_THAANA: `Thaa` + * @HB_SCRIPT_YI: `Yiii` + * @HB_SCRIPT_DESERET: `Dsrt` + * @HB_SCRIPT_GOTHIC: `Goth` + * @HB_SCRIPT_OLD_ITALIC: `Ital` + * @HB_SCRIPT_BUHID: `Buhd` + * @HB_SCRIPT_HANUNOO: `Hano` + * @HB_SCRIPT_TAGALOG: `Tglg` + * @HB_SCRIPT_TAGBANWA: `Tagb` + * @HB_SCRIPT_CYPRIOT: `Cprt` + * @HB_SCRIPT_LIMBU: `Limb` + * @HB_SCRIPT_LINEAR_B: `Linb` + * @HB_SCRIPT_OSMANYA: `Osma` + * @HB_SCRIPT_SHAVIAN: `Shaw` + * @HB_SCRIPT_TAI_LE: `Tale` + * @HB_SCRIPT_UGARITIC: `Ugar` + * @HB_SCRIPT_BUGINESE: `Bugi` + * @HB_SCRIPT_COPTIC: `Copt` + * @HB_SCRIPT_GLAGOLITIC: `Glag` + * @HB_SCRIPT_KHAROSHTHI: `Khar` + * @HB_SCRIPT_NEW_TAI_LUE: `Talu` + * @HB_SCRIPT_OLD_PERSIAN: `Xpeo` + * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo` + * @HB_SCRIPT_TIFINAGH: `Tfng` + * @HB_SCRIPT_BALINESE: `Bali` + * @HB_SCRIPT_CUNEIFORM: `Xsux` + * @HB_SCRIPT_NKO: `Nkoo` + * @HB_SCRIPT_PHAGS_PA: `Phag` + * @HB_SCRIPT_PHOENICIAN: `Phnx` + * @HB_SCRIPT_CARIAN: `Cari` + * @HB_SCRIPT_CHAM: `Cham` + * @HB_SCRIPT_KAYAH_LI: `Kali` + * @HB_SCRIPT_LEPCHA: `Lepc` + * @HB_SCRIPT_LYCIAN: `Lyci` + * @HB_SCRIPT_LYDIAN: `Lydi` + * @HB_SCRIPT_OL_CHIKI: `Olck` + * @HB_SCRIPT_REJANG: `Rjng` + * @HB_SCRIPT_SAURASHTRA: `Saur` + * @HB_SCRIPT_SUNDANESE: `Sund` + * @HB_SCRIPT_VAI: `Vaii` + * @HB_SCRIPT_AVESTAN: `Avst` + * @HB_SCRIPT_BAMUM: `Bamu` + * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp` + * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi` + * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli` + * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti` + * @HB_SCRIPT_JAVANESE: `Java` + * @HB_SCRIPT_KAITHI: `Kthi` + * @HB_SCRIPT_LISU: `Lisu` + * @HB_SCRIPT_MEETEI_MAYEK: `Mtei` + * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb` + * @HB_SCRIPT_OLD_TURKIC: `Orkh` + * @HB_SCRIPT_SAMARITAN: `Samr` + * @HB_SCRIPT_TAI_THAM: `Lana` + * @HB_SCRIPT_TAI_VIET: `Tavt` + * @HB_SCRIPT_BATAK: `Batk` + * @HB_SCRIPT_BRAHMI: `Brah` + * @HB_SCRIPT_MANDAIC: `Mand` + * @HB_SCRIPT_CHAKMA: `Cakm` + * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc` + * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero` + * @HB_SCRIPT_MIAO: `Plrd` + * @HB_SCRIPT_SHARADA: `Shrd` + * @HB_SCRIPT_SORA_SOMPENG: `Sora` + * @HB_SCRIPT_TAKRI: `Takr` + * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30 + * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30 + * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30 + * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30 + * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30 + * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30 + * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30 + * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30 + * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30 + * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30 + * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30 + * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30 + * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30 + * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30 + * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30 + * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30 + * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30 + * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30 + * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30 + * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30 + * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30 + * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30 + * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30 + * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30 + * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30 + * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30 + * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30 + * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30 + * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30 + * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0 + * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0 + * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0 + * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0 + * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0 + * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0 + * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0 + * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0 + * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0 + * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0 + * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0 + * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0 + * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0 + * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0 + * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0 + * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0 + * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0 + * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0 + * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0 + * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0 + * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0 + * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7 + * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7 + * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7 + * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7 + * @HB_SCRIPT_CYPRO_MINOAN: `Cpmn`, Since: 3.0.0 + * @HB_SCRIPT_OLD_UYGHUR: `Ougr`, Since: 3.0.0 + * @HB_SCRIPT_TANGSA: `Tnsa`, Since: 3.0.0 + * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 + * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 + * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 + * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 + * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 + * @HB_SCRIPT_GARAY: `Gara`, Since: 10.0.0 + * @HB_SCRIPT_GURUNG_KHEMA: `Gukh`, Since: 10.0.0 + * @HB_SCRIPT_KIRAT_RAI: `Krai`, Since: 10.0.0 + * @HB_SCRIPT_OL_ONAL: `Onao`, Since: 10.0.0 + * @HB_SCRIPT_SUNUWAR: `Sunu`, Since: 10.0.0 + * @HB_SCRIPT_TODHRI: `Todr`, Since: 10.0.0 + * @HB_SCRIPT_TULU_TIGALARI: `Tutg`, Since: 10.0.0 + * @HB_SCRIPT_INVALID: No script set + * + * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding + * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/). + * + * See also the Script (sc) property of the Unicode Character Database. + * + **/ + +/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ +typedef enum +{ + HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), /*1.1*/ + HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), /*1.1*/ + HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /*5.0*/ + + HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), /*1.1*/ + HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), /*1.1*/ + HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), /*1.1*/ + HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), /*1.1*/ + HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), /*1.1*/ + HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), /*1.1*/ + HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), /*1.1*/ + HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), /*1.1*/ + HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), /*1.1*/ + HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), /*1.1*/ + HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), /*1.1*/ + HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), /*1.1*/ + HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), /*1.1*/ + HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), /*1.1*/ + HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), /*1.1*/ + HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), /*1.1*/ + HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), /*1.1*/ + HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), /*1.1*/ + HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), /*1.1*/ + HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), /*1.1*/ + HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), /*1.1*/ + HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), /*1.1*/ + + HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.0*/ + + HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), /*3.0*/ + HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), /*3.0*/ + HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), /*3.0*/ + HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), /*3.0*/ + HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), /*3.0*/ + HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), /*3.0*/ + HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), /*3.0*/ + HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), /*3.0*/ + HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), /*3.0*/ + HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), /*3.0*/ + HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), /*3.0*/ + HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), /*3.0*/ + HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), /*3.0*/ + HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), /*3.0*/ + + HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), /*3.1*/ + HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), /*3.1*/ + HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), /*3.1*/ + + HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), /*3.2*/ + HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), /*3.2*/ + HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), /*3.2*/ + HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /*3.2*/ + + HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), /*4.0*/ + HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), /*4.0*/ + HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), /*4.0*/ + HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), /*4.0*/ + HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), /*4.0*/ + HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), /*4.0*/ + HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /*4.0*/ + + HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), /*4.1*/ + HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), /*4.1*/ + HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), /*4.1*/ + HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), /*4.1*/ + HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), /*4.1*/ + HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), /*4.1*/ + HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), /*4.1*/ + HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /*4.1*/ + + HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), /*5.0*/ + HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), /*5.0*/ + HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), /*5.0*/ + HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), /*5.0*/ + HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), /*5.0*/ + + HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), /*5.1*/ + HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), /*5.1*/ + HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), /*5.1*/ + HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), /*5.1*/ + HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), /*5.1*/ + HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), /*5.1*/ + HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), /*5.1*/ + HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), /*5.1*/ + HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), /*5.1*/ + HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), /*5.1*/ + HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /*5.1*/ + + HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), /*5.2*/ + HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), /*5.2*/ + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), /*5.2*/ + HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), /*5.2*/ + HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), /*5.2*/ + HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), /*5.2*/ + HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), /*5.2*/ + HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), /*5.2*/ + HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), /*5.2*/ + HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), /*5.2*/ + HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), /*5.2*/ + HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), /*5.2*/ + HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /*5.2*/ + + HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), /*6.0*/ + HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), /*6.0*/ + HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), /*6.0*/ + + HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), /*6.1*/ + HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), /*6.1*/ + HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), /*6.1*/ + HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), /*6.1*/ + HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), /*6.1*/ + HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), /*6.1*/ + HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /*6.1*/ + + /* + * Since: 0.9.30 + */ + HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), /*7.0*/ + HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), /*7.0*/ + HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), /*7.0*/ + HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), /*7.0*/ + HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), /*7.0*/ + HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), /*7.0*/ + HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), /*7.0*/ + HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), /*7.0*/ + HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), /*7.0*/ + HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), /*7.0*/ + HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), /*7.0*/ + HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), /*7.0*/ + HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), /*7.0*/ + HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), /*7.0*/ + HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), /*7.0*/ + HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), /*7.0*/ + HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), /*7.0*/ + HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), /*7.0*/ + HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), /*7.0*/ + HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), /*7.0*/ + HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), /*7.0*/ + HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), /*7.0*/ + HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), /*7.0*/ + + HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), /*8.0*/ + HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), /*8.0*/ + HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), /*8.0*/ + HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), /*8.0*/ + HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), /*8.0*/ + HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), /*8.0*/ + + /* + * Since 1.3.0 + */ + HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), /*9.0*/ + HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), /*9.0*/ + HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), /*9.0*/ + HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), /*9.0*/ + HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ + HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), /*9.0*/ + + /* + * Since 1.6.0 + */ + HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), /*10.0*/ + HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), /*10.0*/ + HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), /*10.0*/ + HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), /*10.0*/ + + /* + * Since 1.8.0 + */ + HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), /*11.0*/ + HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), /*11.0*/ + HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), /*11.0*/ + HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), /*11.0*/ + HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), /*11.0*/ + HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/ + HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), /*11.0*/ + + /* + * Since 2.4.0 + */ + HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), /*12.0*/ + HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), /*12.0*/ + HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), /*12.0*/ + HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), /*12.0*/ + + /* + * Since 2.6.7 + */ + HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), /*13.0*/ + HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), /*13.0*/ + HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), /*13.0*/ + HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), /*13.0*/ + + /* + * Since 3.0.0 + */ + HB_SCRIPT_CYPRO_MINOAN = HB_TAG ('C','p','m','n'), /*14.0*/ + HB_SCRIPT_OLD_UYGHUR = HB_TAG ('O','u','g','r'), /*14.0*/ + HB_SCRIPT_TANGSA = HB_TAG ('T','n','s','a'), /*14.0*/ + HB_SCRIPT_TOTO = HB_TAG ('T','o','t','o'), /*14.0*/ + HB_SCRIPT_VITHKUQI = HB_TAG ('V','i','t','h'), /*14.0*/ + + /* + * Since 3.4.0 + */ + HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), + + /* + * Since 5.2.0 + */ + HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ + HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ + + /* + * Since 10.0.0 + */ + HB_SCRIPT_GARAY = HB_TAG ('G','a','r','a'), /*16.0*/ + HB_SCRIPT_GURUNG_KHEMA = HB_TAG ('G','u','k','h'), /*16.0*/ + HB_SCRIPT_KIRAT_RAI = HB_TAG ('K','r','a','i'), /*16.0*/ + HB_SCRIPT_OL_ONAL = HB_TAG ('O','n','a','o'), /*16.0*/ + HB_SCRIPT_SUNUWAR = HB_TAG ('S','u','n','u'), /*16.0*/ + HB_SCRIPT_TODHRI = HB_TAG ('T','o','d','r'), /*16.0*/ + HB_SCRIPT_TULU_TIGALARI = HB_TAG ('T','u','t','g'), /*16.0*/ + + /* No script set. */ + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /*< private >*/ + + /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t + * without risking undefined behavior. We have two, for historical reasons. + * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed + * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. + * + * See this thread for technicalities: + * + * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + */ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ + +} hb_script_t; + + +#endif /* HB_SCRIPT_LIST_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh index 7bd14a28193..f0416d5fa1d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh @@ -31,11 +31,10 @@ #include "hb-machinery.hh" /* - * The set-digests here implement various "filters" that support - * "approximate member query". Conceptually these are like Bloom - * Filter and Quotient Filter, however, much smaller, faster, and - * designed to fit the requirements of our uses for glyph coverage - * queries. + * The set-digests implement "filters" that support "approximate + * member query". Conceptually these are like Bloom Filter and + * Quotient Filter, however, much smaller, faster, and designed + * to fit the requirements of our uses for glyph coverage queries. * * Our filters are highly accurate if the lookup covers fairly local * set of glyphs, but fully flooded and ineffective if coverage is diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-set.hh index eaa892ab9f5..c5df9b8aec4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set.hh @@ -30,6 +30,7 @@ #include "hb.hh" #include "hb-bit-set-invertible.hh" +#include "hb-bit-vector.hh" // Just to include template diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc index 2f7d335ddce..d14f577be00 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc @@ -91,8 +91,10 @@ void free_static_shaper_list () * * Retrieves the list of shapers supported by HarfBuzz. * - * Return value: (transfer none) (array zero-terminated=1): an array of - * constant strings + * Return value: (transfer none) (array zero-terminated=1): a + * `NULL`-terminated array of supported shapers constant string. + * The returned array is owned by HarfBuzz and should not be + * modified or freed. * * Since: 0.9.2 **/ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-static.cc b/src/java.desktop/share/native/libharfbuzz/hb-static.cc index abdfda9a756..4e70e413651 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-static.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-static.cc @@ -41,6 +41,7 @@ #include "hb-ot-maxp-table.hh" #ifndef HB_NO_VISIBILITY + #include "hb-ot-name-language-static.hh" uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh index 62206e0f5aa..e7bddaef723 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh @@ -570,7 +570,7 @@ struct cff_subset_accelerator_t parsed_cs_str_vec_t parsed_charstrings; parsed_cs_str_vec_t parsed_global_subrs; hb_vector_t parsed_local_subrs; - mutable hb_atomic_ptr_t glyph_to_sid_map; + mutable hb_atomic_t glyph_to_sid_map; private: hb_blob_t* original_blob; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc index bbe50b1fb36..5de29081cc1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc @@ -868,7 +868,7 @@ hb_subset_input_override_name_table (hb_subset_input_t *input, src = hb_utf8_t::next (src, src_end, &unicode, replacement); if (unicode >= 0x0080u) { - printf ("Non-ascii character detected, ignored...This API supports ascii characters only for mac platform\n"); + // Non-ascii character detected, ignored... return false; } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc index fb5282916b9..3ba726d925e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc @@ -29,27 +29,21 @@ #include "hb-map.hh" #include "hb-multimap.hh" #include "hb-set.hh" +#include "hb-subset.h" +#include "hb-unicode.h" #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" #include "hb-ot-layout-base-table.hh" -#include "hb-ot-layout-gdef-table.hh" -#include "hb-ot-layout-gpos-table.hh" -#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "OT/Color/COLR/COLR.hh" #include "OT/Color/COLR/colrv1-closure.hh" #include "OT/Color/CPAL/CPAL.hh" #include "hb-ot-var-fvar-table.hh" -#include "hb-ot-var-avar-table.hh" #include "hb-ot-stat-table.hh" #include "hb-ot-math-table.hh" -using OT::Layout::GSUB; -using OT::Layout::GPOS; - - hb_subset_accelerator_t::~hb_subset_accelerator_t () { if (cmap_cache && destroy_cmap_cache) @@ -63,7 +57,6 @@ hb_subset_accelerator_t::~hb_subset_accelerator_t () } -typedef hb_hashmap_t> script_langsys_map; #ifndef HB_NO_SUBSET_CFF static inline bool _add_cff_seac_components (const OT::cff1::accelerator_subset_t &cff, @@ -98,414 +91,14 @@ _remap_palette_indexes (const hb_set_t *palette_indexes, } } -static void -_remap_indexes (const hb_set_t *indexes, - hb_map_t *mapping /* OUT */) +void +remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */) { for (auto _ : + hb_enumerate (indexes->iter ())) mapping->set (_.second, _.first); - } -#ifndef HB_NO_SUBSET_LAYOUT - -/* - * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates. - * Returns true if anything was removed (not including duplicates). - */ -static bool _filter_tag_list(hb_vector_t* tags, /* IN/OUT */ - const hb_set_t* filter) -{ - hb_vector_t out; - out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator. - - bool removed = false; - hb_set_t visited; - - for (hb_tag_t tag : *tags) - { - if (!tag) continue; - if (visited.has (tag)) continue; - - if (!filter->has (tag)) - { - removed = true; - continue; - } - - visited.add (tag); - out.push (tag); - } - - // The collect function needs a null element to signal end of the array. - out.push (HB_TAG_NONE); - - hb_swap (out, *tags); - return removed; -} - -template -static void _collect_layout_indices (hb_subset_plan_t *plan, - const T& table, - hb_set_t *lookup_indices, /* OUT */ - hb_set_t *feature_indices, /* OUT */ - hb_hashmap_t> *feature_record_cond_idx_map, /* OUT */ - hb_hashmap_t *feature_substitutes_map, /* OUT */ - hb_set_t& catch_all_record_feature_idxes, /* OUT */ - hb_hashmap_t>& catch_all_record_idx_feature_map /* OUT */) -{ - unsigned num_features = table.get_feature_count (); - hb_vector_t features; - if (!plan->check_success (features.resize (num_features))) return; - table.get_feature_tags (0, &num_features, features.arrayZ); - bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features); - - unsigned num_scripts = table.get_script_count (); - hb_vector_t scripts; - if (!plan->check_success (scripts.resize (num_scripts))) return; - table.get_script_tags (0, &num_scripts, scripts.arrayZ); - bool retain_all_scripts = !_filter_tag_list (&scripts, &plan->layout_scripts); - - if (!plan->check_success (!features.in_error ()) || !features - || !plan->check_success (!scripts.in_error ()) || !scripts) - return; - - hb_ot_layout_collect_features (plan->source, - T::tableTag, - retain_all_scripts ? nullptr : scripts.arrayZ, - nullptr, - retain_all_features ? nullptr : features.arrayZ, - feature_indices); - -#ifndef HB_NO_VAR - // collect feature substitutes with variations - if (!plan->user_axes_location.is_empty ()) - { - hb_hashmap_t, unsigned> conditionset_map; - OT::hb_collect_feature_substitutes_with_var_context_t c = - { - &plan->axes_old_index_tag_map, - &plan->axes_location, - feature_record_cond_idx_map, - feature_substitutes_map, - catch_all_record_feature_idxes, - feature_indices, - false, - false, - false, - 0, - &conditionset_map - }; - table.collect_feature_substitutes_with_variations (&c); - } -#endif - - for (unsigned feature_index : *feature_indices) - { - const OT::Feature* f = &(table.get_feature (feature_index)); - const OT::Feature **p = nullptr; - if (feature_substitutes_map->has (feature_index, &p)) - f = *p; - - f->add_lookup_indexes_to (lookup_indices); - } - -#ifndef HB_NO_VAR - if (catch_all_record_feature_idxes) - { - for (unsigned feature_index : catch_all_record_feature_idxes) - { - const OT::Feature& f = table.get_feature (feature_index); - f.add_lookup_indexes_to (lookup_indices); - const void *tag = reinterpret_cast (&(table.get_feature_list ().get_tag (feature_index))); - catch_all_record_idx_feature_map.set (feature_index, hb_pair (&f, tag)); - } - } - - // If all axes are pinned then all feature variations will be dropped so there's no need - // to collect lookups from them. - if (!plan->all_axes_pinned) - table.feature_variation_collect_lookups (feature_indices, - plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map, - lookup_indices); -#endif -} - - -static inline void -_GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g, - const hb_map_t *lookup_indices, - const hb_set_t *feature_indices, - const hb_hashmap_t *feature_substitutes_map, - hb_map_t *duplicate_feature_map /* OUT */) -{ - if (feature_indices->is_empty ()) return; - hb_hashmap_t> unique_features; - //find out duplicate features after subset - for (unsigned i : feature_indices->iter ()) - { - hb_tag_t t = g.get_feature_tag (i); - if (t == HB_MAP_VALUE_INVALID) continue; - if (!unique_features.has (t)) - { - if (unlikely (!unique_features.set (t, hb::unique_ptr {hb_set_create ()}))) - return; - if (unique_features.has (t)) - unique_features.get (t)->add (i); - duplicate_feature_map->set (i, i); - continue; - } - - bool found = false; - - hb_set_t* same_tag_features = unique_features.get (t); - for (unsigned other_f_index : same_tag_features->iter ()) - { - const OT::Feature* f = &(g.get_feature (i)); - const OT::Feature **p = nullptr; - if (feature_substitutes_map->has (i, &p)) - f = *p; - - const OT::Feature* other_f = &(g.get_feature (other_f_index)); - if (feature_substitutes_map->has (other_f_index, &p)) - other_f = *p; - - auto f_iter = - + hb_iter (f->lookupIndex) - | hb_filter (lookup_indices) - ; - - auto other_f_iter = - + hb_iter (other_f->lookupIndex) - | hb_filter (lookup_indices) - ; - - bool is_equal = true; - for (; f_iter && other_f_iter; f_iter++, other_f_iter++) - { - unsigned a = *f_iter; - unsigned b = *other_f_iter; - if (a != b) { is_equal = false; break; } - } - - if (is_equal == false || f_iter || other_f_iter) continue; - - found = true; - duplicate_feature_map->set (i, other_f_index); - break; - } - - if (found == false) - { - same_tag_features->add (i); - duplicate_feature_map->set (i, i); - } - } -} - -template -static inline void -_closure_glyphs_lookups_features (hb_subset_plan_t *plan, - hb_set_t *gids_to_retain, - hb_map_t *lookups, - hb_map_t *features, - script_langsys_map *langsys_map, - hb_hashmap_t> *feature_record_cond_idx_map, - hb_hashmap_t *feature_substitutes_map, - hb_set_t &catch_all_record_feature_idxes, - hb_hashmap_t>& catch_all_record_idx_feature_map) -{ - hb_blob_ptr_t table = plan->source_table (); - hb_tag_t table_tag = table->tableTag; - hb_set_t lookup_indices, feature_indices; - _collect_layout_indices (plan, - *table, - &lookup_indices, - &feature_indices, - feature_record_cond_idx_map, - feature_substitutes_map, - catch_all_record_feature_idxes, - catch_all_record_idx_feature_map); - - if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE)) - hb_ot_layout_lookups_substitute_closure (plan->source, - &lookup_indices, - gids_to_retain); - table->closure_lookups (plan->source, - gids_to_retain, - &lookup_indices); - _remap_indexes (&lookup_indices, lookups); - - // prune features - table->prune_features (lookups, - plan->user_axes_location.is_empty () ? nullptr : feature_record_cond_idx_map, - feature_substitutes_map, - &feature_indices); - hb_map_t duplicate_feature_map; - _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map); - - feature_indices.clear (); - table->prune_langsys (&duplicate_feature_map, &plan->layout_scripts, langsys_map, &feature_indices); - _remap_indexes (&feature_indices, features); - - table.destroy (); -} - -#endif - -#ifndef HB_NO_VAR -static inline void -_generate_varstore_inner_maps (const hb_set_t& varidx_set, - unsigned subtable_count, - hb_vector_t &inner_maps /* OUT */) -{ - if (varidx_set.is_empty () || subtable_count == 0) return; - - if (unlikely (!inner_maps.resize (subtable_count))) return; - for (unsigned idx : varidx_set) - { - uint16_t major = idx >> 16; - uint16_t minor = idx & 0xFFFF; - - if (major >= subtable_count) - continue; - inner_maps[major].add (minor); - } -} - -static inline hb_font_t* -_get_hb_font_with_variations (const hb_subset_plan_t *plan) -{ - hb_font_t *font = hb_font_create (plan->source); - - hb_vector_t vars; - if (!vars.alloc (plan->user_axes_location.get_population ())) { - hb_font_destroy (font); - return nullptr; - } - - for (auto _ : plan->user_axes_location) - { - hb_variation_t var; - var.tag = _.first; - var.value = _.second.middle; - vars.push (var); - } - -#ifndef HB_NO_VAR - hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ()); -#endif - return font; -} - -static inline void -_remap_variation_indices (const OT::ItemVariationStore &var_store, - const hb_set_t &variation_indices, - const hb_vector_t& normalized_coords, - bool calculate_delta, /* not pinned at default */ - bool no_variations, /* all axes pinned */ - hb_hashmap_t> &variation_idx_delta_map /* OUT */) -{ - if (&var_store == &Null (OT::ItemVariationStore)) return; - unsigned subtable_count = var_store.get_sub_table_count (); - float *store_cache = var_store.create_cache (); - - unsigned new_major = 0, new_minor = 0; - unsigned last_major = (variation_indices.get_min ()) >> 16; - for (unsigned idx : variation_indices) - { - int delta = 0; - if (calculate_delta) - delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ, - normalized_coords.length, store_cache)); - - if (no_variations) - { - variation_idx_delta_map.set (idx, hb_pair_t (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta)); - continue; - } - - uint16_t major = idx >> 16; - if (major >= subtable_count) break; - if (major != last_major) - { - new_minor = 0; - ++new_major; - } - - unsigned new_idx = (new_major << 16) + new_minor; - variation_idx_delta_map.set (idx, hb_pair_t (new_idx, delta)); - ++new_minor; - last_major = major; - } - var_store.destroy_cache (store_cache); -} - -static inline void -_collect_layout_variation_indices (hb_subset_plan_t* plan) -{ - hb_blob_ptr_t gdef = plan->source_table (); - hb_blob_ptr_t gpos = plan->source_table (); - - if (!gdef->has_data () || !gdef->has_var_store ()) - { - gdef.destroy (); - gpos.destroy (); - return; - } - - hb_set_t varidx_set; - OT::hb_collect_variation_indices_context_t c (&varidx_set, - &plan->_glyphset_gsub, - &plan->gpos_lookups); - gdef->collect_variation_indices (&c); - - if (hb_ot_layout_has_positioning (plan->source)) - gpos->collect_variation_indices (&c); - - _remap_variation_indices (gdef->get_var_store (), - varidx_set, plan->normalized_coords, - !plan->pinned_at_default, - plan->all_axes_pinned, - plan->layout_variation_idx_delta_map); - - unsigned subtable_count = gdef->get_var_store ().get_sub_table_count (); - _generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps); - - gdef.destroy (); - gpos.destroy (); -} - -#ifndef HB_NO_BASE -static inline void -_collect_base_variation_indices (hb_subset_plan_t* plan) -{ - hb_blob_ptr_t base = plan->source_table (); - if (!base->has_var_store ()) - { - base.destroy (); - return; - } - - hb_set_t varidx_set; - base->collect_variation_indices (plan, varidx_set); - const OT::ItemVariationStore &var_store = base->get_var_store (); - unsigned subtable_count = var_store.get_sub_table_count (); - - - _remap_variation_indices (var_store, varidx_set, - plan->normalized_coords, - !plan->pinned_at_default, - plan->all_axes_pinned, - plan->base_variation_idx_map); - _generate_varstore_inner_maps (varidx_set, subtable_count, plan->base_varstore_inner_maps); - - base.destroy (); -} - -#endif -#endif - static inline void _cmap_closure (hb_face_t *face, const hb_set_t *unicodes, @@ -515,41 +108,6 @@ _cmap_closure (hb_face_t *face, cmap.table->closure_glyphs (unicodes, glyphset); } -#ifndef HB_NO_VAR -static void -_remap_colrv1_delta_set_index_indices (const OT::DeltaSetIndexMap &index_map, - const hb_set_t &delta_set_idxes, - hb_hashmap_t> &variation_idx_delta_map, /* IN/OUT */ - hb_map_t &new_deltaset_idx_varidx_map /* OUT */) -{ - if (!index_map.get_map_count ()) - return; - - hb_hashmap_t> delta_set_idx_delta_map; - unsigned new_delta_set_idx = 0; - for (unsigned delta_set_idx : delta_set_idxes) - { - unsigned var_idx = index_map.map (delta_set_idx); - unsigned new_varidx = HB_OT_LAYOUT_NO_VARIATIONS_INDEX; - int delta = 0; - - if (var_idx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX) - { - hb_pair_t *new_varidx_delta; - if (!variation_idx_delta_map.has (var_idx, &new_varidx_delta)) continue; - - new_varidx = hb_first (*new_varidx_delta); - delta = hb_second (*new_varidx_delta); - } - - new_deltaset_idx_varidx_map.set (new_delta_set_idx, new_varidx); - delta_set_idx_delta_map.set (delta_set_idx, hb_pair_t (new_delta_set_idx, delta)); - new_delta_set_idx++; - } - variation_idx_delta_map = std::move (delta_set_idx_delta_map); -} -#endif - static void _colr_closure (hb_subset_plan_t* plan, hb_set_t *glyphs_colred) { @@ -569,7 +127,7 @@ static void _colr_closure (hb_subset_plan_t* plan, colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices, &variation_indices, &delta_set_indices); colr.closure_V0palette_indices (glyphs_colred, &palette_indices); - _remap_indexes (&layer_indices, &plan->colrv1_layers); + remap_indexes (&layer_indices, &plan->colrv1_layers); _remap_palette_indexes (&palette_indices, &plan->colr_palettes); #ifndef HB_NO_VAR @@ -578,7 +136,7 @@ static void _colr_closure (hb_subset_plan_t* plan, const OT::ItemVariationStore &var_store = colr.get_var_store (); // generated inner_maps is used by ItemVariationStore serialize(), which is subset only unsigned subtable_count = var_store.get_sub_table_count (); - _generate_varstore_inner_maps (variation_indices, subtable_count, plan->colrv1_varstore_inner_maps); + generate_varstore_inner_maps (variation_indices, subtable_count, plan->colrv1_varstore_inner_maps); /* colr variation indices mapping during planning phase: * generate colrv1_variation_idx_delta_map. When delta set index map is not @@ -590,7 +148,7 @@ static void _colr_closure (hb_subset_plan_t* plan, * instancing. */ if (!plan->all_axes_pinned) { - _remap_variation_indices (var_store, + remap_variation_indices (var_store, variation_indices, plan->normalized_coords, false, /* no need to calculate delta for COLR during planning */ @@ -598,7 +156,7 @@ static void _colr_closure (hb_subset_plan_t* plan, plan->colrv1_variation_idx_delta_map); if (colr.has_delta_set_index_map ()) - _remap_colrv1_delta_set_index_indices (colr.get_delta_set_index_map (), + remap_colrv1_delta_set_index_indices (colr.get_delta_set_index_map (), delta_set_indices, plan->colrv1_variation_idx_delta_map, plan->colrv1_new_deltaset_idx_varidx_map); @@ -616,25 +174,6 @@ _math_closure (hb_subset_plan_t *plan, math.destroy (); } -static inline void -_remap_used_mark_sets (hb_subset_plan_t *plan, - hb_map_t& used_mark_sets_map) -{ - hb_blob_ptr_t gdef = plan->source_table (); - - if (!gdef->has_data () || !gdef->has_mark_glyph_sets ()) - { - gdef.destroy (); - return; - } - - hb_set_t used_mark_sets; - gdef->get_mark_glyph_sets ().collect_used_mark_sets (plan->_glyphset_gsub, used_mark_sets); - gdef.destroy (); - - _remap_indexes (&used_mark_sets, &used_mark_sets_map); -} - static inline void _remove_invalid_gids (hb_set_t *glyphs, unsigned int num_glyphs) @@ -672,15 +211,46 @@ _fill_unicode_and_glyph_map(hb_subset_plan_t *plan, _fill_unicode_and_glyph_map(plan, unicode_iterator, unicode_to_gid_for_iterator, unicode_to_gid_for_iterator); } +/* + * Finds additional unicode codepoints which are reachable from the input unicode set. + * Currently this adds in mirrored variants (needed for bidi) of any input unicodes. + */ +static hb_set_t +_unicode_closure (const hb_set_t* unicodes, bool bidi_closure) { + // TODO: we may want to also consider pulling in reachable unicode composition and decompositions. + // see: https://github.com/harfbuzz/harfbuzz/issues/2283 + hb_set_t out = *unicodes; + if (!bidi_closure) return out; + + if (out.is_inverted()) { + // don't closure inverted sets, they are asking to specifically exclude certain codepoints. + // otherwise everything is already included. + return out; + } + + auto unicode_funcs = hb_unicode_funcs_get_default (); + for (hb_codepoint_t cp : *unicodes) { + hb_codepoint_t mirror = hb_unicode_mirroring(unicode_funcs, cp); + if (unlikely (mirror != cp)) { + out.add(mirror); + } + } + + return out; +} + static void -_populate_unicodes_to_retain (const hb_set_t *unicodes, +_populate_unicodes_to_retain (const hb_set_t *unicodes_in, const hb_set_t *glyphs, hb_subset_plan_t *plan) { + hb_set_t unicodes = _unicode_closure(unicodes_in, + !(plan->flags & HB_SUBSET_FLAGS_NO_BIDI_CLOSURE)); + OT::cmap::accelerator_t cmap (plan->source); unsigned size_threshold = plan->source->get_num_glyphs (); - if (glyphs->is_empty () && unicodes->get_population () < size_threshold) + if (glyphs->is_empty () && unicodes.get_population () < size_threshold) { const hb_map_t* unicode_to_gid = nullptr; @@ -690,9 +260,9 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, // This is approach to collection is faster, but can only be used if glyphs // are not being explicitly added to the subset and the input unicodes set is // not excessively large (eg. an inverted set). - plan->unicode_to_new_gid_list.alloc (unicodes->get_population ()); + plan->unicode_to_new_gid_list.alloc (unicodes.get_population ()); if (!unicode_to_gid) { - _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) { + _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) { hb_codepoint_t gid; if (!cmap.get_nominal_glyph (cp, &gid)) { return HB_MAP_VALUE_INVALID; @@ -704,7 +274,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, // the map. This code is mostly duplicated from above to avoid doing // conditionals on the presence of the unicode_to_gid map each // iteration. - _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) { + _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) { return unicode_to_gid->get (cp); }); } @@ -721,7 +291,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, if (!plan->accelerator) { cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage); - plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population () + plan->unicode_to_new_gid_list.alloc (hb_min(unicodes.get_population () + glyphs->get_population (), cmap_unicodes->get_population ())); } else { @@ -730,10 +300,10 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, } if (plan->accelerator && - unicodes->get_population () < cmap_unicodes->get_population () && + unicodes.get_population () < cmap_unicodes->get_population () && glyphs->get_population () < cmap_unicodes->get_population ()) { - plan->codepoint_to_glyph->alloc (unicodes->get_population () + glyphs->get_population ()); + plan->codepoint_to_glyph->alloc (unicodes.get_population () + glyphs->get_population ()); auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes; @@ -748,7 +318,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, }); } - _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) { + _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) { /* Don't double-add entry. */ if (plan->codepoint_to_glyph->has (cp)) return HB_MAP_VALUE_INVALID; @@ -769,7 +339,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes, { _fill_unicode_and_glyph_map(plan, hb_range(first, last + 1), [&] (hb_codepoint_t cp) { hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; - if (!unicodes->has (cp) && !glyphs->has (gid)) + if (!unicodes.has (cp) && !glyphs->has (gid)) return HB_MAP_VALUE_INVALID; return gid; }, @@ -860,18 +430,7 @@ _nameid_closure (hb_subset_plan_t* plan, #endif #ifndef HB_NO_SUBSET_LAYOUT - if (!drop_tables->has (HB_OT_TAG_GPOS)) - { - hb_blob_ptr_t gpos = plan->source_table (); - gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids); - gpos.destroy (); - } - if (!drop_tables->has (HB_OT_TAG_GSUB)) - { - hb_blob_ptr_t gsub = plan->source_table (); - gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids); - gsub.destroy (); - } + layout_nameid_closure(plan, drop_tables); #endif } @@ -893,31 +452,9 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, _cmap_closure (plan->source, &plan->unicodes, &plan->_glyphset_gsub); #ifndef HB_NO_SUBSET_LAYOUT - if (!drop_tables->has (HB_OT_TAG_GSUB)) - // closure all glyphs/lookups/features needed for GSUB substitutions. - _closure_glyphs_lookups_features ( - plan, - &plan->_glyphset_gsub, - &plan->gsub_lookups, - &plan->gsub_features, - &plan->gsub_langsys, - &plan->gsub_feature_record_cond_idx_map, - &plan->gsub_feature_substitutes_map, - plan->gsub_old_features, - plan->gsub_old_feature_idx_tag_map); - - if (!drop_tables->has (HB_OT_TAG_GPOS)) - _closure_glyphs_lookups_features ( - plan, - &plan->_glyphset_gsub, - &plan->gpos_lookups, - &plan->gpos_features, - &plan->gpos_langsys, - &plan->gpos_feature_record_cond_idx_map, - &plan->gpos_feature_substitutes_map, - plan->gpos_old_features, - plan->gpos_old_feature_idx_tag_map); + layout_populate_gids_to_retain(plan, drop_tables); #endif + _remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ()); plan->_glyphset_mathed = plan->_glyphset_gsub; @@ -962,8 +499,10 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, _remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ()); #ifndef HB_NO_VAR +#ifndef HB_NO_SUBSET_LAYOUT if (!drop_tables->has (HB_OT_TAG_GDEF)) - _collect_layout_variation_indices (plan); + collect_layout_variation_indices (plan); +#endif #endif } @@ -1077,193 +616,6 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face, return true; } -#ifndef HB_NO_VAR -static void -_normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan) -{ - if (plan->user_axes_location.is_empty ()) - return; - - hb_array_t axes = face->table.fvar->get_axes (); - plan->normalized_coords.resize (axes.length); - - bool has_avar = face->table.avar->has_data (); - const OT::SegmentMaps *seg_maps = nullptr; - unsigned avar_axis_count = 0; - if (has_avar) - { - seg_maps = face->table.avar->get_segment_maps (); - avar_axis_count = face->table.avar->get_axis_count(); - } - - bool axis_not_pinned = false; - unsigned old_axis_idx = 0, new_axis_idx = 0; - for (const auto& axis : axes) - { - hb_tag_t axis_tag = axis.get_axis_tag (); - plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag); - - if (!plan->user_axes_location.has (axis_tag) || - !plan->user_axes_location.get (axis_tag).is_point ()) - { - axis_not_pinned = true; - plan->axes_index_map.set (old_axis_idx, new_axis_idx); - plan->axis_tags.push (axis_tag); - new_axis_idx++; - } - - Triple *axis_range; - if (plan->user_axes_location.has (axis_tag, &axis_range)) - { - plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ()); - - int normalized_min = axis.normalize_axis_value (axis_range->minimum); - int normalized_default = axis.normalize_axis_value (axis_range->middle); - int normalized_max = axis.normalize_axis_value (axis_range->maximum); - - if (has_avar && old_axis_idx < avar_axis_count) - { - normalized_min = seg_maps->map (normalized_min); - normalized_default = seg_maps->map (normalized_default); - normalized_max = seg_maps->map (normalized_max); - } - plan->axes_location.set (axis_tag, Triple (static_cast (normalized_min / 16384.0), - static_cast (normalized_default / 16384.0), - static_cast (normalized_max / 16384.0))); - - if (normalized_default != 0) - plan->pinned_at_default = false; - - plan->normalized_coords[old_axis_idx] = normalized_default; - } - - old_axis_idx++; - - if (has_avar && old_axis_idx < avar_axis_count) - seg_maps = &StructAfter (*seg_maps); - } - plan->all_axes_pinned = !axis_not_pinned; -} - -static void -_update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan) -{ - if (!plan->normalized_coords) return; - OT::cff2::accelerator_t cff2 (plan->source); - if (!cff2.is_valid ()) return; - - hb_font_t *font = _get_hb_font_with_variations (plan); - if (unlikely (!plan->check_success (font != nullptr))) - { - hb_font_destroy (font); - return; - } - - hb_glyph_extents_t extents = {0x7FFF, -0x7FFF}; - OT::hmtx_accelerator_t _hmtx (plan->source); - float *hvar_store_cache = nullptr; - if (_hmtx.has_data () && _hmtx.var_table.get_length ()) - hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache (); - - OT::vmtx_accelerator_t _vmtx (plan->source); - float *vvar_store_cache = nullptr; - if (_vmtx.has_data () && _vmtx.var_table.get_length ()) - vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache (); - - for (auto p : *plan->glyph_map) - { - hb_codepoint_t old_gid = p.first; - hb_codepoint_t new_gid = p.second; - if (!cff2.get_extents (font, old_gid, &extents)) continue; - bool has_bounds_info = true; - if (extents.x_bearing == 0 && extents.width == 0 && - extents.height == 0 && extents.y_bearing == 0) - has_bounds_info = false; - - if (has_bounds_info) - { - plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, extents.x_bearing); - plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, extents.x_bearing + extents.width); - plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, extents.y_bearing); - plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, extents.y_bearing + extents.height); - } - - if (_hmtx.has_data ()) - { - int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid); - if (_hmtx.var_table.get_length ()) - hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords, - hvar_store_cache)); - int lsb = extents.x_bearing; - if (!has_bounds_info) - { - if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) - continue; - } - plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb)); - plan->bounds_width_vec[new_gid] = extents.width; - } - - if (_vmtx.has_data ()) - { - int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid); - if (_vmtx.var_table.get_length ()) - vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords, - vvar_store_cache)); - - int tsb = extents.y_bearing; - if (!has_bounds_info) - { - if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb)) - continue; - } - plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb)); - plan->bounds_height_vec[new_gid] = extents.height; - } - } - hb_font_destroy (font); - if (hvar_store_cache) - _hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache); - if (vvar_store_cache) - _vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache); -} - -static bool -_get_instance_glyphs_contour_points (hb_subset_plan_t *plan) -{ - /* contour_points vector only needed for updating gvar table (infer delta and - * iup delta optimization) during partial instancing */ - if (plan->user_axes_location.is_empty () || plan->all_axes_pinned) - return true; - - OT::glyf_accelerator_t glyf (plan->source); - - for (auto &_ : plan->new_to_old_gid_list) - { - hb_codepoint_t new_gid = _.first; - contour_point_vector_t all_points; - if (new_gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) - { - if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) - return false; - continue; - } - - hb_codepoint_t old_gid = _.second; - auto glyph = glyf.glyph_for_gid (old_gid); - if (unlikely (!glyph.get_all_points_without_var (plan->source, all_points))) - return false; - if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) - return false; - - /* composite new gids are only needed by iup delta optimization */ - if ((plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS) && glyph.is_composite ()) - plan->composite_new_gids.add (new_gid); - } - return true; -} -#endif - hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, const hb_subset_input_t *input) { @@ -1324,7 +676,7 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; #ifndef HB_NO_VAR - _normalize_axes_location (face, this); + normalize_axes_location (face, this); #endif _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this); @@ -1365,13 +717,15 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, for (auto &v : bounds_height_vec) v = 0xFFFFFFFF; +#ifndef HB_NO_SUBSET_LAYOUT if (!drop_tables.has (HB_OT_TAG_GDEF)) - _remap_used_mark_sets (this, used_mark_sets_map); + remap_used_mark_sets (this, used_mark_sets_map); +#endif #ifndef HB_NO_VAR #ifndef HB_NO_BASE if (!drop_tables.has (HB_OT_TAG_BASE)) - _collect_base_variation_indices (this); + collect_base_variation_indices (this); #endif #endif @@ -1379,8 +733,8 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; #ifndef HB_NO_VAR - _update_instance_metrics_map_from_cff2 (this); - if (!check_success (_get_instance_glyphs_contour_points (this))) + update_instance_metrics_map_from_cff2 (this); + if (!check_success (get_instance_glyphs_contour_points (this))) return; #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh index a6bae23966a..8a6574365ed 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh @@ -296,5 +296,75 @@ struct hb_subset_plan_t } }; +// hb-subset-plan implementation is split into multiple files to keep +// compile times more reasonable: +// - hb-subset-plan.cc +// - hb-subset-plan-layout.cc +// +// The functions below are those needed to connect the split files +// above together. +HB_INTERNAL void +remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */); + + +#ifndef HB_NO_VAR +template +HB_INTERNAL void +remap_variation_indices (const ItemVarStore &var_store, + const hb_set_t &variation_indices, + const hb_vector_t& normalized_coords, + bool calculate_delta, /* not pinned at default */ + bool no_variations, /* all axes pinned */ + hb_hashmap_t> &variation_idx_delta_map /* OUT */); + + +template +HB_INTERNAL void +remap_colrv1_delta_set_index_indices (const DeltaSetIndexMap &index_map, + const hb_set_t &delta_set_idxes, + hb_hashmap_t> &variation_idx_delta_map, /* IN/OUT */ + hb_map_t &new_deltaset_idx_varidx_map /* OUT */); + + +HB_INTERNAL void +generate_varstore_inner_maps (const hb_set_t& varidx_set, + unsigned subtable_count, + hb_vector_t &inner_maps /* OUT */); + +HB_INTERNAL void +normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan); + +HB_INTERNAL void +update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan); + +HB_INTERNAL bool +get_instance_glyphs_contour_points (hb_subset_plan_t *plan); + +#ifndef HB_NO_BASE +HB_INTERNAL void +collect_base_variation_indices (hb_subset_plan_t* plan); +#endif +#endif + +#ifndef HB_NO_SUBSET_LAYOUT +typedef hb_hashmap_t> script_langsys_map; + +HB_INTERNAL void +remap_used_mark_sets (hb_subset_plan_t *plan, + hb_map_t& used_mark_sets_map); + +HB_INTERNAL void +layout_nameid_closure (hb_subset_plan_t* plan, + hb_set_t* drop_tables); + +HB_INTERNAL void +layout_populate_gids_to_retain (hb_subset_plan_t* plan, + hb_set_t* drop_tables); + +HB_INTERNAL void +collect_layout_variation_indices (hb_subset_plan_t* plan); +#endif + #endif /* HB_SUBSET_PLAN_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc index 595cafc283e..079c94eddfe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc @@ -708,3 +708,107 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) end: return success ? hb_face_reference (plan->dest) : nullptr; } + + +#ifdef HB_EXPERIMENTAL_API + +#include "hb-ot-cff1-table.hh" + +template +static hb_blob_t* get_charstrings_data(accel_t& accel, hb_codepoint_t glyph_index) { + if (!accel.is_valid()) { + return hb_blob_get_empty (); + } + + hb_ubytes_t bytes = (*accel.charStrings)[glyph_index]; + if (!bytes) { + return hb_blob_get_empty (); + } + + hb_blob_t* cff_blob = accel.get_blob(); + uint32_t length; + const char* cff_data = hb_blob_get_data(cff_blob, &length) ; + + long int offset = (const char*) bytes.arrayZ - cff_data; + if (offset < 0 || offset > INT32_MAX) { + return hb_blob_get_empty (); + } + + return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, bytes.length); +} + +template +static hb_blob_t* get_charstrings_index(accel_t& accel) { + if (!accel.is_valid()) { + return hb_blob_get_empty (); + } + + const char* charstrings_start = (const char*) accel.charStrings; + unsigned charstrings_length = accel.charStrings->get_size(); + + hb_blob_t* cff_blob = accel.get_blob(); + uint32_t length; + const char* cff_data = hb_blob_get_data(cff_blob, &length) ; + + long int offset = charstrings_start - cff_data; + if (offset < 0 || offset > INT32_MAX) { + return hb_blob_get_empty (); + } + + return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, charstrings_length); +} + +/** + * hb_subset_cff_get_charstring_data: + * @face: A face object + * @glyph_index: Glyph index to get data for. + * + * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { + return get_charstrings_data(*face->table.cff1, glyph_index); +} + +/** + * hb_subset_cff_get_charstrings_index: + * @face: A face object + * + * Returns the raw CFF CharStrings INDEX from the CFF table. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstrings_index (hb_face_t* face) { + return get_charstrings_index (*face->table.cff1); +} + +/** + * hb_subset_cff2_get_charstring_data: + * @face: A face object + * @glyph_index: Glyph index to get data for. + * + * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { + return get_charstrings_data(*face->table.cff2, glyph_index); +} + +/** + * hb_subset_cff2_get_charstrings_index: + * @face: A face object + * + * Returns the raw CFF2 CharStrings INDEX from the CFF2 table. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstrings_index (hb_face_t* face) { + return get_charstrings_index (*face->table.cff2); +} +#endif \ No newline at end of file diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.h b/src/java.desktop/share/native/libharfbuzz/hb-subset.h index 13fcd5332e6..3eccd25738e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.h @@ -71,10 +71,12 @@ typedef struct hb_subset_plan_t hb_subset_plan_t; * in the final subset. * @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in * OS/2 will not be recalculated. - * @HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE: If set don't perform glyph closure on layout + * @HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE: If set do not perform glyph closure on layout * substitution rules (GSUB). Since: 7.2.0. * @HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS: If set perform IUP delta optimization on the * remaining gvar table's deltas. Since: 8.5.0 + * @HB_SUBSET_FLAGS_NO_BIDI_CLOSURE: If set do not pull mirrored versions of input + * codepoints into the subset. Since: 11.1.0 * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset * to allow it to be used with incremental font transfer IFTB patches. Primarily, * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL @@ -96,8 +98,9 @@ typedef enum { /*< flags >*/ HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u, HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u, HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS = 0x00000400u, + HB_SUBSET_FLAGS_NO_BIDI_CLOSURE = 0x00000800u, #ifdef HB_EXPERIMENTAL_API - HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00000800u, + HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00001000u, #endif } hb_subset_flags_t; @@ -224,6 +227,23 @@ hb_subset_input_override_name_table (hb_subset_input_t *input, unsigned language_id, const char *name_str, int str_len); + + +/* +* Raw outline data access +*/ + +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstring_data (hb_face_t* face, hb_codepoint_t glyph_index); + +HB_EXTERN hb_blob_t* +hb_subset_cff_get_charstrings_index (hb_face_t* face); + +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstring_data (hb_face_t* face, hb_codepoint_t glyph_index); + +HB_EXTERN hb_blob_t* +hb_subset_cff2_get_charstrings_index (hb_face_t* face); #endif HB_EXTERN hb_face_t * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh index 7e755818ddd..cd27da96648 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh @@ -90,7 +90,13 @@ struct hb_vector_t { auto iter = hb_iter (o); if (iter.is_random_access_iterator || iter.has_fast_len) - alloc (hb_len (iter), true); + { + if (unlikely (!alloc (hb_len (iter), true))) + return; + unsigned count = hb_len (iter); + for (unsigned i = 0; i < count; i++) + push_has_room (*iter++); + } while (iter) { if (unlikely (!alloc (length + 1))) @@ -436,7 +442,6 @@ struct hb_vector_t new_allocated += (new_allocated >> 1) + 8; } - /* Reallocate */ bool overflows = diff --git a/src/java.desktop/share/native/libharfbuzz/hb-version.h b/src/java.desktop/share/native/libharfbuzz/hb-version.h index 01edf174350..e41286d2d8c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-version.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-version.h @@ -41,13 +41,13 @@ HB_BEGIN_DECLS * * The major component of the library version available at compile-time. */ -#define HB_VERSION_MAJOR 10 +#define HB_VERSION_MAJOR 11 /** * HB_VERSION_MINOR: * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 4 +#define HB_VERSION_MINOR 2 /** * HB_VERSION_MICRO: * @@ -60,7 +60,7 @@ HB_BEGIN_DECLS * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "10.4.0" +#define HB_VERSION_STRING "11.2.0" /** * HB_VERSION_ATLEAST: diff --git a/src/java.desktop/share/native/libharfbuzz/hb.hh b/src/java.desktop/share/native/libharfbuzz/hb.hh index fee1c9cd663..8c430e5770d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb.hh @@ -89,6 +89,7 @@ #pragma GCC diagnostic error "-Wstring-conversion" #pragma GCC diagnostic error "-Wswitch-enum" #pragma GCC diagnostic error "-Wtautological-overlap-compare" +#pragma GCC diagnostic error "-Wuninitialized" #pragma GCC diagnostic error "-Wunneeded-internal-declaration" #pragma GCC diagnostic error "-Wunused" #pragma GCC diagnostic error "-Wunused-local-typedefs" @@ -230,33 +231,6 @@ #define HB_PASTE(a,b) HB_PASTE1(a,b) -/* Compile-time custom allocator support. */ - -#if !defined(HB_CUSTOM_MALLOC) \ - && defined(hb_malloc_impl) \ - && defined(hb_calloc_impl) \ - && defined(hb_realloc_impl) \ - && defined(hb_free_impl) -#define HB_CUSTOM_MALLOC -#endif - -#ifdef HB_CUSTOM_MALLOC -extern "C" void* hb_malloc_impl(size_t size); -extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); -extern "C" void* hb_realloc_impl(void *ptr, size_t size); -extern "C" void hb_free_impl(void *ptr); -#define hb_malloc hb_malloc_impl -#define hb_calloc hb_calloc_impl -#define hb_realloc hb_realloc_impl -#define hb_free hb_free_impl -#else -#define hb_malloc malloc -#define hb_calloc calloc -#define hb_realloc realloc -#define hb_free free -#endif - - /* * Compiler attributes */ @@ -282,7 +256,7 @@ extern "C" void hb_free_impl(void *ptr); #define __attribute__(x) #endif -#if defined(__MINGW32__) && (__GNUC__ >= 3) +#if defined(__MINGW32__) && (__GNUC__ >= 3) && !defined(__clang__) #define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (gnu_printf, format_idx, arg_idx))) #elif defined(__GNUC__) && (__GNUC__ >= 3) #define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx))) @@ -491,7 +465,7 @@ static int HB_UNUSED _hb_errno = 0; # define hb_atexit atexit # else template struct hb_atexit_t { ~hb_atexit_t () { function (); } }; -# define hb_atexit(f) static hb_atexit_t _hb_atexit_##__LINE__; +# define hb_atexit(f) static hb_atexit_t _hb_atexit_##__LINE__ # endif #endif #endif @@ -539,6 +513,29 @@ static_assert ((sizeof (hb_var_int_t) == 4), ""); #define HB_PI 3.14159265358979f #define HB_2_PI (2.f * HB_PI) +/* Compile-time custom allocator support. */ + +#if !defined(HB_CUSTOM_MALLOC) \ + && defined(hb_malloc_impl) \ + && defined(hb_calloc_impl) \ + && defined(hb_realloc_impl) \ + && defined(hb_free_impl) +#define HB_CUSTOM_MALLOC +#endif + +#ifdef HB_CUSTOM_MALLOC +extern "C" void* hb_malloc_impl(size_t size); +extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); +extern "C" void* hb_realloc_impl(void *ptr, size_t size); +extern "C" void hb_free_impl(void *ptr); +#else +#define hb_malloc_impl malloc +#define hb_calloc_impl calloc +#define hb_realloc_impl realloc +#define hb_free_impl free +#endif + + /* Headers we include for everyone. Keep topologically sorted by dependency. * They express dependency amongst themselves, but no other file should include diff --git a/src/java.xml/share/legal/xhtml11.md b/src/java.xml/share/legal/xhtml11.md index b447a5ac9e4..eafd55a9c16 100644 --- a/src/java.xml/share/legal/xhtml11.md +++ b/src/java.xml/share/legal/xhtml11.md @@ -47,7 +47,7 @@ The notice is: "Copyright © 2023 W3C®. This software or document includes material copied from or derived from [title and URI of the W3C document]." -Disclaimers §anchor +Disclaimers THIS DOCUMENT IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF diff --git a/src/java.xml/share/legal/xmlxsd.md b/src/java.xml/share/legal/xmlxsd.md index 163e26e9f77..b752b10da29 100644 --- a/src/java.xml/share/legal/xmlxsd.md +++ b/src/java.xml/share/legal/xmlxsd.md @@ -26,7 +26,7 @@ modifications: [$year-of-document] World Wide Web Consortium. https://www.w3.org/copyright/software-license-2023/" -Disclaimers §anchor +Disclaimers THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index 44d90a7e59b..e0fdd30c242 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -146,13 +146,7 @@ public enum Option { } }, - // -nowarn is retained for command-line backward compatibility - NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC) { - @Override - public void process(OptionHelper helper, String option) { - helper.put("-Xlint:none", option); - } - }, + NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC), VERBOSE("-verbose", "opt.verbose", STANDARD, BASIC), diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index 28a89ee3072..1a8be506e7f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ javac.opt.g.none=\ javac.opt.g.lines.vars.source=\ Generate only some debugging info javac.opt.nowarn=\ - Generate no warnings + Generate only mandatory warnings javac.opt.verbose=\ Output messages about what the compiler is doing javac.opt.deprecation=\ @@ -168,16 +168,16 @@ javac.opt.Xbootclasspath.p=\ javac.opt.Xbootclasspath.a=\ Append to the bootstrap class path javac.opt.Xlint=\ - Enable recommended warnings + Enable recommended warning categories javac.opt.Xlint.all=\ - Enable all warnings + Enable all warning categories javac.opt.Xlint.none=\ - Disable all warnings + Disable all warning categories #L10N: do not localize: -Xlint javac.opt.arg.Xlint=\ (,)* javac.opt.Xlint.custom=\ - Warnings to enable or disable, separated by comma.\n\ + Warning categories to enable or disable, separated by comma.\n\ Precede a key by ''-'' to disable the specified warning.\n\ Use --help-lint to see the supported keys. javac.opt.Xlint.desc.auxiliaryclass=\ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 66b8ead65e7..c7627d6e45b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -510,7 +510,7 @@ public class Log extends AbstractLog { private void initOptions(Options options) { this.dumpOnError = options.isSet(DOE); this.promptOnError = options.isSet(PROMPT); - this.emitWarnings = options.isUnset(XLINT_CUSTOM, "none"); + this.emitWarnings = options.isUnset(NOWARN); this.suppressNotes = options.isSet("suppressNotes"); this.MaxErrors = getIntOption(options, XMAXERRS, getDefaultMaxErrors()); this.MaxWarnings = getIntOption(options, XMAXWARNS, getDefaultMaxWarnings()); diff --git a/src/jdk.compiler/share/man/javac.md b/src/jdk.compiler/share/man/javac.md index 3edbeda6eca..f951ea0def3 100644 --- a/src/jdk.compiler/share/man/javac.md +++ b/src/jdk.compiler/share/man/javac.md @@ -325,8 +325,7 @@ file system locations may be directories, JAR files or JMOD files. : Specifies the version of modules that are being compiled. `-nowarn` -: Disables warning messages. This option operates the same as the - `-Xlint:none` option. +: Generate only mandatory warnings. `-parameters` : Generates metadata for reflection on method parameters. Stores formal @@ -562,12 +561,14 @@ file system locations may be directories, JAR files or JMOD files. warnings is recommended. `-Xlint:`\[`-`\]*key*(`,`\[`-`\]*key*)\* -: Supplies warnings to enable or disable, separated by comma. Precede a key - by a hyphen (`-`) to disable the specified warning. +: Enables and/or disables warning categories using the one or more of the keys described + below separated by commas. The keys `all` and `none` enable or disable all categories + (respectively); other keys enable the corresponding category, or disable it if preceded + by a hyphen (`-`). Supported values for *key* are: - - `all`: Enables all warnings. + - `all`: Enables all warning categories. - `auxiliaryclass`: Warns about an auxiliary class that is hidden in a source file, and is used from other files. @@ -659,7 +660,7 @@ file system locations may be directories, JAR files or JMOD files. - `varargs`: Warns about the potentially unsafe `vararg` methods. - - `none`: Disables all warnings. + - `none`: Disables all warning categories. With the exception of `all` and `none`, the keys can be used with the `@SuppressWarnings` annotation to suppress warnings in a part 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 0c54fc1e29c..2b6bfa77951 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 @@ -352,7 +352,6 @@ public class Head extends Content { if (syntaxHighlight) { addStylesheet(head, DocPaths.RESOURCE_FILES.resolve(DocPaths.HIGHLIGHT_CSS)); - addScriptElement(head, DocPaths.HIGHLIGHT_JS); } for (DocPath path : localStylesheets) { @@ -367,6 +366,9 @@ public class Head extends Content { } private void addScripts(HtmlTree head) { + if (syntaxHighlight) { + addScriptElement(head, DocPaths.HIGHLIGHT_JS); + } if (addDefaultScript) { addScriptElement(head, DocPaths.SCRIPT_JS); } 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 1b4a5021a08..d3b59f4f40b 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 @@ -21,8 +21,12 @@ var activeTableTab = "active-table-tab"; const linkIcon = "##REPLACE:doclet.Link_icon##"; const linkToSection = "##REPLACE:doclet.Link_to_section##"; -if (hljs) { - hljs.highlightAll(); +if (typeof hljs !== "undefined") { + try { + hljs.highlightAll(); + } catch (err) { + console.error(err) + } } function loadScripts(doc, tag) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationLayout.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationLayout.java index c89131b2d4b..230e4beb49b 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationLayout.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationLayout.java @@ -26,14 +26,16 @@ package jdk.jpackage.internal; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.PathGroup; +import java.io.IOException; import java.nio.file.Path; +import java.util.List; import java.util.Map; /** * Application directory layout. */ -public final class ApplicationLayout implements PathGroup.Facade { +public final class ApplicationLayout { enum PathRole { /** * Java run-time directory. @@ -84,16 +86,34 @@ public final class ApplicationLayout implements PathGroup.Facade roots() { + return data.roots(); + } + + public long sizeInBytes() throws IOException { + return data.sizeInBytes(); + } + + public void copy(ApplicationLayout other) throws IOException { + data.copy(other.data); + } + + public void move(ApplicationLayout other) throws IOException { + data.move(other.data); + } + + public void transform(ApplicationLayout other, PathGroup.TransformHandler handler) throws IOException { + data.transform(other.data, handler); + } + /** * Path to launchers directory. */ diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java index c6938b6b986..bad4bc3069f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java @@ -24,36 +24,69 @@ */ package jdk.jpackage.internal.util; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; + import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.CopyOption; +import java.nio.file.FileVisitOption; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.BiFunction; -import java.util.stream.Collectors; +import java.util.Objects; +import java.util.Set; import java.util.stream.Stream; - /** - * Group of paths. - * Each path in the group is assigned a unique id. + * Group of paths. Each path in the group is assigned a unique id. */ public final class PathGroup { + + /** + * Creates path group with the initial paths. + * + * @param paths the initial paths + */ public PathGroup(Map paths) { + paths.keySet().forEach(Objects::requireNonNull); + paths.values().forEach(Objects::requireNonNull); entries = new HashMap<>(paths); } + /** + * Returns a path associated with the given identifier in this path group. + * + * @param id the identifier + * @return the path corresponding to the given identifier in this path group or + * null if there is no such path + */ public Path getPath(Object id) { - if (id == null) { - throw new NullPointerException(); - } + Objects.requireNonNull(id); return entries.get(id); } + /** + * Assigns the specified path value to the given identifier in this path group. + * If the given identifier doesn't exist in this path group, it is added, + * otherwise, the current value associated with the identifier is replaced with + * the given path value. If the path value is null the given + * identifier is removed from this path group if it existed; otherwise, no + * action is taken. + * + * @param id the identifier + * @param path the path to associate with the identifier or null + */ public void setPath(Object id, Path path) { + Objects.requireNonNull(id); if (path != null) { entries.put(id, path); } else { @@ -62,196 +95,379 @@ public final class PathGroup { } /** - * All configured entries. + * Adds a path associated with the new unique identifier to this path group. + * + * @param path the path to associate the new unique identifier in this path + * group + */ + public void ghostPath(Path path) { + Objects.requireNonNull(path); + setPath(new Object(), path); + } + + /** + * Gets all identifiers of this path group. + *

+ * The order of identifiers in the returned list is undefined. + * + * @return all identifiers of this path group + */ + public Set keys() { + return entries.keySet(); + } + + /** + * Gets paths associated with all identifiers in this path group. + *

+ * The order of paths in the returned list is undefined. + * + * @return paths associated with all identifiers in this path group */ public List paths() { return entries.values().stream().toList(); } /** - * Root entries. + * Gets root paths in this path group. + *

+ * If multiple identifiers are associated with the same path value in the group, + * the path value is added to the returned list only once. Paths that are + * descendants of other paths in the group are not added to the returned list. + *

+ * The order of paths in the returned list is undefined. + * + * @return unique root paths in this path group */ public List roots() { - // Sort by the number of path components in ascending order. - List> sorted = normalizedPaths().stream().sorted( - (a, b) -> a.getKey().getNameCount() - b.getKey().getNameCount()).toList(); + if (entries.isEmpty()) { + return List.of(); + } - // Returns `true` if `a` is a parent of `b` - BiFunction, Map.Entry, Boolean> isParentOrSelf = (a, b) -> { - return a == b || b.getKey().startsWith(a.getKey()); - }; + // Sort by the number of path components in descending order. + final var sorted = entries.entrySet().stream().map(e -> { + return Map.entry(e.getValue().normalize(), e.getValue()); + }).sorted(Comparator.comparingInt(e -> e.getValue().getNameCount() * -1)).distinct().toList(); - return sorted.stream().filter( - v -> v == sorted.stream().sequential().filter( - v2 -> isParentOrSelf.apply(v2, v)).findFirst().get()).map( - v -> v.getValue()).toList(); + final var shortestNormalizedPath = sorted.getLast().getKey(); + if (shortestNormalizedPath.getNameCount() == 1 && shortestNormalizedPath.getFileName().toString().isEmpty()) { + return List.of(sorted.getLast().getValue()); + } + + final List roots = new ArrayList<>(); + + for (int i = 0; i < sorted.size(); ++i) { + final var path = sorted.get(i).getKey(); + boolean pathIsRoot = true; + for (int j = i + 1; j < sorted.size(); ++j) { + final var maybeParent = sorted.get(j).getKey(); + if (path.getNameCount() > maybeParent.getNameCount() && path.startsWith(maybeParent)) { + pathIsRoot = false; + break; + } + } + + if (pathIsRoot) { + roots.add(sorted.get(i).getValue()); + } + } + + return roots; } + /** + * Gets the number of bytes in root paths of this path group. The method sums + * the size of all root path entries in the group. If the path entry is a + * directory it calculates the total size of the files in the directory. If the + * path entry is a file, it takes its size. + * + * @return the total number of bytes in root paths of this path group + * @throws IOException If an I/O error occurs + */ public long sizeInBytes() throws IOException { long reply = 0; - for (Path dir : roots().stream().filter(f -> Files.isDirectory(f)).collect( - Collectors.toList())) { - try (Stream stream = Files.walk(dir)) { - reply += stream.filter(p -> Files.isRegularFile(p)).mapToLong( - f -> f.toFile().length()).sum(); + final var roots = roots(); + try { + for (Path dir : roots.stream().filter(Files::isDirectory).toList()) { + try (Stream stream = Files.walk(dir)) { + reply += stream.mapToLong(PathGroup::sizeInBytes).sum(); + } } + reply += roots.stream().mapToLong(PathGroup::sizeInBytes).sum(); + } catch (UncheckedIOException ex) { + throw ex.getCause(); } return reply; } + private static long sizeInBytes(Path path) throws UncheckedIOException { + if (Files.isRegularFile(path)) { + try { + return Files.size(path); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } else { + return 0; + } + } + + /** + * Creates a copy of this path group with all paths resolved against the given + * root. Taken action is equivalent to creating a copy of this path group and + * calling root.resolve() on every path in the copy. + * + * @param root the root against which to resolve paths + * + * @return a new path group resolved against the given root path + */ public PathGroup resolveAt(Path root) { - return new PathGroup(entries.entrySet().stream().collect( - Collectors.toMap(e -> e.getKey(), - e -> root.resolve(e.getValue())))); + Objects.requireNonNull(root); + return new PathGroup(entries.entrySet().stream() + .collect(toMap(Map.Entry::getKey, e -> root.resolve(e.getValue())))); } - public void copy(PathGroup dst) throws IOException { - copy(this, dst, null, false); + /** + * Copies files/directories from the locations in the path group into the + * locations of the given path group. For every identifier found in this and the + * given group, copy the associated file or directory from the location + * specified by the path value associated with the identifier in this group into + * the location associated with the identifier in the given group. + * + * @param dst the destination path group + * @throws IOException If an I/O error occurs + */ + public void copy(PathGroup dst, CopyOption ...options) throws IOException { + final var handler = new Copy(false, options); + copy(this, dst, handler, handler.followSymlinks()); } - public void move(PathGroup dst) throws IOException { - copy(this, dst, null, true); + /** + * Similar to {@link #copy(PathGroup)} but moves files/directories instead of + * copying. + * + * @param dst the destination path group + * @throws IOException If an I/O error occurs + */ + public void move(PathGroup dst, CopyOption ...options) throws IOException { + final var handler = new Copy(true, options); + copy(this, dst, handler, handler.followSymlinks()); + deleteEntries(); } + /** + * Similar to {@link #copy(PathGroup)} but uses the given handler to transform + * paths instead of coping. + * + * @param dst the destination path group + * @param handler the path transformation handler + * @throws IOException If an I/O error occurs + */ public void transform(PathGroup dst, TransformHandler handler) throws IOException { copy(this, dst, handler, false); } - public static interface Facade { - PathGroup pathGroup(); - - default Collection paths() { - return pathGroup().paths(); - } - - default List roots() { - return pathGroup().roots(); - } - - default long sizeInBytes() throws IOException { - return pathGroup().sizeInBytes(); - } - - T resolveAt(Path root); - - default void copy(Facade dst) throws IOException { - pathGroup().copy(dst.pathGroup()); - } - - default void move(Facade dst) throws IOException { - pathGroup().move(dst.pathGroup()); - } - - default void transform(Facade dst, TransformHandler handler) throws - IOException { - pathGroup().transform(dst.pathGroup(), handler); - } - } - + /** + * Handler of file copying and directory creating. + * + * @see #transform + */ public static interface TransformHandler { - void copyFile(Path src, Path dst) throws IOException; - void createDirectory(Path dir) throws IOException; + + /** + * Request to copy a file from the given source location into the given + * destination location. + * + * @implNote Default implementation takes no action + * + * @param src the source file location + * @param dst the destination file location + * @throws IOException If an I/O error occurs + */ + default void copyFile(Path src, Path dst) throws IOException { + + } + + /** + * Request to create a directory at the given location. + * + * @implNote Default implementation takes no action + * + * @param dir the path where the directory is requested to be created + * @throws IOException + */ + default void createDirectory(Path dir) throws IOException { + + } + + /** + * Request to copy a symbolic link from the given source location into the given + * destination location. + * + * @implNote Default implementation calls {@link #copyFile}. + * + * @param src the source symbolic link location + * @param dst the destination symbolic link location + * @throws IOException If an I/O error occurs + */ + default void copySymbolicLink(Path src, Path dst) throws IOException { + copyFile(src, dst); + } } - private static void copy(PathGroup src, PathGroup dst, - TransformHandler handler, boolean move) throws IOException { - List> copyItems = new ArrayList<>(); - List excludeItems = new ArrayList<>(); - - for (var id: src.entries.keySet()) { - Path srcPath = src.entries.get(id); - if (dst.entries.containsKey(id)) { - copyItems.add(Map.entry(srcPath, dst.entries.get(id))); + private void deleteEntries() throws IOException { + for (final var file : entries.values()) { + if (Files.isDirectory(file)) { + FileUtils.deleteRecursive(file); } else { - excludeItems.add(srcPath); + Files.deleteIfExists(file); + } + } + } + + private record CopySpec(Path basepath, Path from, Path to) { + CopySpec { + Objects.requireNonNull(basepath); + Objects.requireNonNull(to); + if (!from.startsWith(basepath)) { + throw new IllegalArgumentException(); } } - copy(move, copyItems, excludeItems, handler); + @Override + public int hashCode() { + return Objects.hash(from, to); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + CopySpec other = (CopySpec) obj; + return Objects.equals(from, other.from) && Objects.equals(to, other.to); + } + + Path fromNormalized() { + return from().normalize(); + } + + Path toNormalized() { + return to().normalize(); + } + + CopySpec(Path from, Path to) { + this(from, from, to); + } } - private static void copy(boolean move, List> entries, - List excludePaths, TransformHandler handler) throws - IOException { + private static void copy(PathGroup src, PathGroup dst, TransformHandler handler, boolean followSymlinks) throws IOException { + List copySpecs = new ArrayList<>(); + List excludePaths = new ArrayList<>(); - if (handler == null) { - handler = new TransformHandler() { - @Override - public void copyFile(Path src, Path dst) throws IOException { - Files.createDirectories(dst.getParent()); - if (move) { - Files.move(src, dst); - } else { - Files.copy(src, dst); + for (final var e : src.entries.entrySet()) { + final var srcPath = e.getValue(); + final var dstPath = dst.entries.get(e.getKey()); + if (dstPath != null) { + copySpecs.add(new CopySpec(srcPath, dstPath)); + } else { + excludePaths.add(srcPath.normalize()); + } + } + + copy(copySpecs, excludePaths, handler, followSymlinks); + } + + private record Copy(boolean move, boolean followSymlinks, CopyOption ... options) implements TransformHandler { + + Copy(boolean move, CopyOption ... options) { + this(move, !Set.of(options).contains(LinkOption.NOFOLLOW_LINKS), options); + } + + @Override + public void copyFile(Path src, Path dst) throws IOException { + Files.createDirectories(dst.getParent()); + if (move) { + Files.move(src, dst, options); + } else { + Files.copy(src, dst, options); + } + } + + @Override + public void createDirectory(Path dir) throws IOException { + Files.createDirectories(dir); + } + } + + private static boolean match(Path what, List paths) { + return paths.stream().anyMatch(what::startsWith); + } + + private static void copy(List copySpecs, List excludePaths, + TransformHandler handler, boolean followSymlinks) throws IOException { + Objects.requireNonNull(excludePaths); + Objects.requireNonNull(handler); + + + final var filteredCopySpecs = copySpecs.stream().mapMulti((copySpec, consumer) -> { + final var src = copySpec.from(); + + if (!Files.exists(src) || match(src, excludePaths)) { + return; + } + + if (Files.isDirectory(copySpec.from())) { + final var dst = copySpec.to; + final var walkMode = followSymlinks ? new FileVisitOption[] { FileVisitOption.FOLLOW_LINKS } : new FileVisitOption[0]; + try (final var files = Files.walk(src, walkMode)) { + files.filter(file -> { + return !match(file, excludePaths); + }).map(file -> { + return new CopySpec(src, file, dst.resolve(src.relativize(file))); + }).toList().forEach(consumer::accept); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } else { + consumer.accept(copySpec); + } + }).collect(groupingBy(CopySpec::fromNormalized, collectingAndThen(toSet(), copySpecGroup -> { + return copySpecGroup.stream().filter(copySpec -> { + for (final var otherCopySpec : copySpecGroup) { + if (otherCopySpec != copySpec && !otherCopySpec.basepath().equals(copySpec.basepath()) + && otherCopySpec.basepath().startsWith(copySpec.basepath())) { + return false; } } + return true; + }).toList(); + }))).values().stream().flatMap(Collection::stream).toList(); - @Override - public void createDirectory(Path dir) throws IOException { - Files.createDirectories(dir); + filteredCopySpecs.stream().collect(toMap(CopySpec::toNormalized, x -> x, (x, y) -> { + throw new IllegalStateException(String.format( + "Duplicate source files [%s] and [%s] for [%s] destination file", x.from(), y.from(), x.to())); + })); + + try { + filteredCopySpecs.stream().forEach(copySpec -> { + try { + if (Files.isSymbolicLink(copySpec.from())) { + handler.copySymbolicLink(copySpec.from(), copySpec.to()); + } else if (Files.isDirectory(copySpec.from())) { + handler.createDirectory(copySpec.to()); + } else { + handler.copyFile(copySpec.from(), copySpec.to()); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); } - }; + }); + } catch (UncheckedIOException ex) { + throw ex.getCause(); } - - // destination -> source file mapping - Map actions = new HashMap<>(); - for (var action: entries) { - Path src = action.getKey(); - Path dst = action.getValue(); - if (Files.isDirectory(src)) { - try (Stream stream = Files.walk(src)) { - stream.sequential().forEach(path -> actions.put(dst.resolve( - src.relativize(path)).normalize(), path)); - } - } else { - actions.put(dst.normalize(), src); - } - } - - for (var action : actions.entrySet()) { - Path dst = action.getKey(); - Path src = action.getValue(); - - if (excludePaths.stream().anyMatch(src::startsWith)) { - continue; - } - - if (src.equals(dst) || !src.toFile().exists()) { - continue; - } - - if (Files.isDirectory(src)) { - handler.createDirectory(dst); - } else { - handler.copyFile(src, dst); - } - } - - if (move) { - // Delete source dirs. - for (var entry: entries) { - Path srcFile = entry.getKey(); - if (Files.isDirectory(srcFile)) { - FileUtils.deleteRecursive(srcFile); - } - } - } - } - - private static Map.Entry normalizedPath(Path v) { - final Path normalized; - if (!v.isAbsolute()) { - normalized = Path.of("./").resolve(v.normalize()); - } else { - normalized = v.normalize(); - } - - return Map.entry(normalized, v); - } - - private List> normalizedPaths() { - return entries.values().stream().map(PathGroup::normalizedPath).collect( - Collectors.toList()); } private final Map entries; diff --git a/src/utils/IdealGraphVisualizer/README.md b/src/utils/IdealGraphVisualizer/README.md index 904c7b9eaf6..195878a5b99 100644 --- a/src/utils/IdealGraphVisualizer/README.md +++ b/src/utils/IdealGraphVisualizer/README.md @@ -22,6 +22,8 @@ for building and running IGV. # Usage +## Regular JVM Execution + The JVM support is controlled by the flag `-XX:PrintIdealGraphLevel=N`, where Ideal graphs are dumped at the following points: @@ -48,6 +50,59 @@ Alternatively the output can be sent to a file using with unique names being generated by adding a number onto the provided file name. +## Dumping Graphs From a Debugger + +The JVM provides some entry functions to dump graphs from a debugger such as +`gdb` or `rr`, see the different variants of `igv_print` and `igv_append` in +[`compile.cpp`](/src/hotspot/share/opto/compile.cpp). In combination with the +IGV network interface, these functions enable a powerful interactive workflow +where the user can simultaneously step through C2's code and visualize the +evolving Ideal graph. Note that, to produce and print meaningful C2 stack +traces, these functions take the stack pointer, frame pointer, and program +counter registers as arguments. These are usually `$sp`, `$fp`, and `$pc`: + +``` +(gdb) p igv_print(true, $sp, $fp, $pc) +Method printed over network stream to IGV +``` + +but might be given different names on different platforms, see the output of +`p help()` for more information. A tip to further simplify the workflow in +`gdb` or `rr` is to create a user-defined command such as e.g.: + +``` +define igv + p igv_print(true, $sp, $fp, $pc) +end +``` + +On `lldb`, it might be necessary to explicitly cast the registers to `void*` to + ensure their values are correctly passed as arguments: + +``` +(lldb) p igv_print(true, (void*)$sp, (void*)$fp, (void*)$pc) +``` + +Another way to dump graphs interactively is through the `Node::dump_bfs` +functionality with the option `!` (run `p find_node(0)->dump_bfs(0,0,"H")` to +see the complete list of options). One of the versions of this function also +takes the three stack management registers to produce a C2 stack trace: + +``` +(gdb) p find_node(3)->dump_bfs(0, 0, "!", $sp, $fp, $pc) +(...) +Method printed over network stream to IGV +``` + +Again, user-defined debugger commands can be created for simplicity. For +example, to dump the current graph with a given node highlighted: + +``` +define igv_node + p find_node($arg0)->dump_bfs(0, 0, "!", $sp, $fp, $pc) +end +``` + ## Defining Custom Filters IGV has a powerful filter mechanism with which nodes and blocks can be colored, diff --git a/test/failure_handler/src/share/conf/windows.properties b/test/failure_handler/src/share/conf/windows.properties index 6d11553ebbf..69b9ed4f47d 100644 --- a/test/failure_handler/src/share/conf/windows.properties +++ b/test/failure_handler/src/share/conf/windows.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -22,10 +22,10 @@ # config.execSuffix=.exe -config.getChildren.app=bash +config.getChildren.app=powershell config.getChildren.pattern=%p -config.getChildren.args=-c\0wmic process where ParentProcessId=%p get ProcessId | tail -n+2 config.getChildren.args.delimiter=\0 +config.getChildren.args=-NoLogo\0-Command\0"Get-CimInstance Win32_Process -Filter \\\"ParentProcessId = %p\\\" | Select-Object ProcessId" | tail -n+4 ################################################################################ # process info to gather ################################################################################ @@ -39,8 +39,9 @@ native.pattern=%p native.javaOnly=false native.args=%p -native.info.app=wmic -native.info.args=process where processId=%p list full +native.info.app=powershell +native.info.delimiter=\0 +native.info.args=-NoLogo\0-Command\0"Get-WmiObject Win32_Process -Filter \\\"ProcessId = %p\\\" | Format-List -Property *" native.pmap.app=pmap native.pmap.normal.args=%p @@ -96,8 +97,9 @@ system.events.delimiter=\0 system.events.system.args=-NoLogo\0-Command\0Get-EventLog System -After (Get-Date).AddDays(-1) | Format-List system.events.application.args=-NoLogo\0-Command\0Get-EventLog Application -After (Get-Date).AddDays(-1) | Format-List -system.os.app=wmic -system.os.args=os get /format:list +system.os.app=powershell +system.os.delimiter=\0 +system.os.args=-NoLogo\0-Command\0Get-WmiObject Win32_OperatingSystem | Format-List -Property * process.top.app=top process.top.args=-b -n 1 diff --git a/test/hotspot/gtest/gc/z/test_zIntrusiveRBTree.cpp b/test/hotspot/gtest/gc/z/test_zIntrusiveRBTree.cpp index 45fa2033a7f..ca38201e53e 100644 --- a/test/hotspot/gtest/gc/z/test_zIntrusiveRBTree.cpp +++ b/test/hotspot/gtest/gc/z/test_zIntrusiveRBTree.cpp @@ -32,22 +32,22 @@ #include -struct ZTestEntryCompare { +struct ZRBTestEntryCompare { int operator()(const ZIntrusiveRBTreeNode* a, const ZIntrusiveRBTreeNode* b); int operator()(int key, const ZIntrusiveRBTreeNode* entry); }; -class ZTestEntry : public ArenaObj { - friend class ZIntrusiveRBTree; +class ZRBTestEntry : public ArenaObj { + friend class ZIntrusiveRBTree; public: - using ZTree = ZIntrusiveRBTree; + using ZTree = ZIntrusiveRBTree; private: const int _id; ZIntrusiveRBTreeNode _node; public: - ZTestEntry(int id) + ZRBTestEntry(int id) : _id(id), _node() {} @@ -55,26 +55,26 @@ public: return _id; } - static ZIntrusiveRBTreeNode* cast_to_inner(ZTestEntry* element) { + static ZIntrusiveRBTreeNode* cast_to_inner(ZRBTestEntry* element) { return &element->_node; } - static const ZTestEntry* cast_to_outer(const ZIntrusiveRBTreeNode* node) { - return (ZTestEntry*)((uintptr_t)node - offset_of(ZTestEntry, _node)); + static const ZRBTestEntry* cast_to_outer(const ZIntrusiveRBTreeNode* node) { + return (ZRBTestEntry*)((uintptr_t)node - offset_of(ZRBTestEntry, _node)); } }; -int ZTestEntryCompare::operator()(const ZIntrusiveRBTreeNode* a, const ZIntrusiveRBTreeNode* b) { - return ZTestEntry::cast_to_outer(a)->id() - ZTestEntry::cast_to_outer(b)->id(); +int ZRBTestEntryCompare::operator()(const ZIntrusiveRBTreeNode* a, const ZIntrusiveRBTreeNode* b) { + return ZRBTestEntry::cast_to_outer(a)->id() - ZRBTestEntry::cast_to_outer(b)->id(); } -int ZTestEntryCompare::operator()(int key, const ZIntrusiveRBTreeNode* entry) { - return key - ZTestEntry::cast_to_outer(entry)->id(); +int ZRBTestEntryCompare::operator()(int key, const ZIntrusiveRBTreeNode* entry) { + return key - ZRBTestEntry::cast_to_outer(entry)->id(); } class ZTreeTest : public ZTest { public: - void shuffle_array(ZTestEntry** beg, ZTestEntry** end); - void reverse_array(ZTestEntry** beg, ZTestEntry** end); + void shuffle_array(ZRBTestEntry** beg, ZRBTestEntry** end); + void reverse_array(ZRBTestEntry** beg, ZRBTestEntry** end); }; class ResettableArena : public Arena { @@ -96,10 +96,10 @@ TEST_F(ZTreeTest, test_random) { constexpr size_t sizes[] = {1, 2, 4, 8, 16, 1024, 1024 * 1024}; constexpr size_t num_sizes = ARRAY_SIZE(sizes); constexpr size_t iterations_multiplier = 4; - constexpr size_t max_allocation_size = sizes[num_sizes - 1] * iterations_multiplier * sizeof(ZTestEntry); + constexpr size_t max_allocation_size = sizes[num_sizes - 1] * iterations_multiplier * sizeof(ZRBTestEntry); ResettableArena arena{MemTag::mtTest, Arena::Tag::tag_other, max_allocation_size}; for (size_t s : sizes) { - ZTestEntry::ZTree tree; + ZRBTestEntry::ZTree tree; const size_t num_iterations = s * iterations_multiplier; for (size_t i = 0; i < num_iterations; i++) { if (i % s == 0) { @@ -113,7 +113,7 @@ TEST_F(ZTreeTest, test_random) { // Replace if (i % 4 == 0) { // Replace with new - tree.replace(ZTestEntry::cast_to_inner(new (&arena) ZTestEntry(id)), cursor); + tree.replace(ZRBTestEntry::cast_to_inner(new (&arena) ZRBTestEntry(id)), cursor); } else { // Replace with same tree.replace(cursor.node(), cursor); @@ -124,7 +124,7 @@ TEST_F(ZTreeTest, test_random) { } } else { // Insert - tree.insert(ZTestEntry::cast_to_inner(new (&arena) ZTestEntry(id)), cursor); + tree.insert(ZRBTestEntry::cast_to_inner(new (&arena) ZRBTestEntry(id)), cursor); } } tree.verify_tree(); @@ -132,13 +132,13 @@ TEST_F(ZTreeTest, test_random) { } } -void ZTreeTest::reverse_array(ZTestEntry** beg, ZTestEntry** end) { +void ZTreeTest::reverse_array(ZRBTestEntry** beg, ZRBTestEntry** end) { if (beg == end) { return; } - ZTestEntry** first = beg; - ZTestEntry** last = end - 1; + ZRBTestEntry** first = beg; + ZRBTestEntry** last = end - 1; while (first < last) { ::swap(*first, *last); first++; @@ -146,12 +146,12 @@ void ZTreeTest::reverse_array(ZTestEntry** beg, ZTestEntry** end) { } } -void ZTreeTest::shuffle_array(ZTestEntry** beg, ZTestEntry** end) { +void ZTreeTest::shuffle_array(ZRBTestEntry** beg, ZRBTestEntry** end) { if (beg == end) { return; } - for (ZTestEntry** first = beg + 1; first != end; first++) { + for (ZRBTestEntry** first = beg + 1; first != end; first++) { const ptrdiff_t distance = first - beg; ASSERT_GE(distance, 0); const ptrdiff_t random_index = random() % (distance + 1); @@ -162,51 +162,51 @@ void ZTreeTest::shuffle_array(ZTestEntry** beg, ZTestEntry** end) { TEST_F(ZTreeTest, test_insert) { Arena arena(MemTag::mtTest); constexpr size_t num_entries = 1024; - ZTestEntry* forward[num_entries]{}; - ZTestEntry* reverse[num_entries]{}; - ZTestEntry* shuffle[num_entries]{}; + ZRBTestEntry* forward[num_entries]{}; + ZRBTestEntry* reverse[num_entries]{}; + ZRBTestEntry* shuffle[num_entries]{}; for (size_t i = 0; i < num_entries; i++) { const int id = static_cast(i); - forward[i] = new (&arena) ZTestEntry(id); - reverse[i] = new (&arena) ZTestEntry(id); - shuffle[i] = new (&arena) ZTestEntry(id); + forward[i] = new (&arena) ZRBTestEntry(id); + reverse[i] = new (&arena) ZRBTestEntry(id); + shuffle[i] = new (&arena) ZRBTestEntry(id); } reverse_array(reverse, reverse + num_entries); shuffle_array(shuffle, shuffle + num_entries); - ZTestEntry::ZTree forward_tree; + ZRBTestEntry::ZTree forward_tree; auto cursor = forward_tree.root_cursor(); for (size_t i = 0; i < num_entries; i++) { ASSERT_TRUE(cursor.is_valid()); ASSERT_FALSE(cursor.found()); - ZIntrusiveRBTreeNode* const new_node = ZTestEntry::cast_to_inner(forward[i]); + ZIntrusiveRBTreeNode* const new_node = ZRBTestEntry::cast_to_inner(forward[i]); forward_tree.insert(new_node, cursor); cursor = forward_tree.next_cursor(new_node); } forward_tree.verify_tree(); - ZTestEntry::ZTree reverse_tree; + ZRBTestEntry::ZTree reverse_tree; cursor = reverse_tree.root_cursor(); for (size_t i = 0; i < num_entries; i++) { ASSERT_TRUE(cursor.is_valid()); ASSERT_FALSE(cursor.found()); - ZIntrusiveRBTreeNode* const new_node = ZTestEntry::cast_to_inner(reverse[i]); + ZIntrusiveRBTreeNode* const new_node = ZRBTestEntry::cast_to_inner(reverse[i]); reverse_tree.insert(new_node, cursor); cursor = reverse_tree.prev_cursor(new_node); } reverse_tree.verify_tree(); - ZTestEntry::ZTree shuffle_tree; + ZRBTestEntry::ZTree shuffle_tree; for (size_t i = 0; i < num_entries; i++) { cursor = shuffle_tree.find(reverse[i]->id()); ASSERT_TRUE(cursor.is_valid()); ASSERT_FALSE(cursor.found()); - ZIntrusiveRBTreeNode* const new_node = ZTestEntry::cast_to_inner(reverse[i]); + ZIntrusiveRBTreeNode* const new_node = ZRBTestEntry::cast_to_inner(reverse[i]); shuffle_tree.insert(new_node, cursor); } shuffle_tree.verify_tree(); - ZTestEntryCompare compare_fn; + ZRBTestEntryCompare compare_fn; const ZIntrusiveRBTreeNode* forward_node = forward_tree.first(); const ZIntrusiveRBTreeNode* reverse_node = reverse_tree.first(); const ZIntrusiveRBTreeNode* shuffle_node = shuffle_tree.first(); @@ -240,13 +240,13 @@ TEST_F(ZTreeTest, test_insert) { TEST_F(ZTreeTest, test_replace) { Arena arena(MemTag::mtTest); constexpr size_t num_entries = 1024; - ZTestEntry::ZTree tree; + ZRBTestEntry::ZTree tree; auto cursor = tree.root_cursor(); for (size_t i = 0; i < num_entries; i++) { ASSERT_TRUE(cursor.is_valid()); ASSERT_FALSE(cursor.found()); const int id = static_cast(i) * 2 + 1; - ZIntrusiveRBTreeNode* const new_node = ZTestEntry::cast_to_inner(new (&arena) ZTestEntry(id)); + ZIntrusiveRBTreeNode* const new_node = ZRBTestEntry::cast_to_inner(new (&arena) ZRBTestEntry(id)); tree.insert(new_node, cursor); cursor = tree.next_cursor(new_node); } @@ -261,14 +261,14 @@ TEST_F(ZTreeTest, test_replace) { switch (i++ % 4) { case 0: { // Decrement - ZTestEntry* new_entry = new (&arena) ZTestEntry(ZTestEntry::cast_to_outer(&node)->id() - 1); - it.replace(ZTestEntry::cast_to_inner(new_entry)); + ZRBTestEntry* new_entry = new (&arena) ZRBTestEntry(ZRBTestEntry::cast_to_outer(&node)->id() - 1); + it.replace(ZRBTestEntry::cast_to_inner(new_entry)); } break; case 1: break; case 2: { // Increment - ZTestEntry* new_entry = new (&arena) ZTestEntry(ZTestEntry::cast_to_outer(&node)->id() + 1); - it.replace(ZTestEntry::cast_to_inner(new_entry)); + ZRBTestEntry* new_entry = new (&arena) ZRBTestEntry(ZRBTestEntry::cast_to_outer(&node)->id() + 1); + it.replace(ZRBTestEntry::cast_to_inner(new_entry)); } break; case 3: break; default: @@ -279,7 +279,7 @@ TEST_F(ZTreeTest, test_replace) { int last_id = std::numeric_limits::min(); for (auto& node : tree) { - int id = ZTestEntry::cast_to_outer(&node)->id(); + int id = ZRBTestEntry::cast_to_outer(&node)->id(); ASSERT_LT(last_id, id); last_id = id; } @@ -287,7 +287,7 @@ TEST_F(ZTreeTest, test_replace) { last_id = std::numeric_limits::min(); for (auto it = tree.begin(), end = tree.end(); it != end; ++it) { - int id = ZTestEntry::cast_to_outer(&*it)->id(); + int id = ZRBTestEntry::cast_to_outer(&*it)->id(); ASSERT_LT(last_id, id); last_id = id; } @@ -295,7 +295,7 @@ TEST_F(ZTreeTest, test_replace) { last_id = std::numeric_limits::min(); for (auto it = tree.cbegin(), end = tree.cend(); it != end; ++it) { - int id = ZTestEntry::cast_to_outer(&*it)->id(); + int id = ZRBTestEntry::cast_to_outer(&*it)->id(); ASSERT_LT(last_id, id); last_id = id; } @@ -303,7 +303,7 @@ TEST_F(ZTreeTest, test_replace) { last_id = std::numeric_limits::max(); for (auto it = tree.rbegin(), end = tree.rend(); it != end; ++it) { - int id = ZTestEntry::cast_to_outer(&*it)->id(); + int id = ZRBTestEntry::cast_to_outer(&*it)->id(); ASSERT_GT(last_id, id); last_id = id; } @@ -311,7 +311,7 @@ TEST_F(ZTreeTest, test_replace) { last_id = std::numeric_limits::max(); for (auto it = tree.crbegin(), end = tree.crend(); it != end; ++it) { - int id = ZTestEntry::cast_to_outer(&*it)->id(); + int id = ZRBTestEntry::cast_to_outer(&*it)->id(); ASSERT_GT(last_id, id); last_id = id; } @@ -321,19 +321,19 @@ TEST_F(ZTreeTest, test_replace) { TEST_F(ZTreeTest, test_remove) { Arena arena(MemTag::mtTest); constexpr int num_entries = 1024; - ZTestEntry::ZTree tree; + ZRBTestEntry::ZTree tree; int id = 0; - tree.insert(ZTestEntry::cast_to_inner(new (&arena) ZTestEntry(++id)), tree.root_cursor()); + tree.insert(ZRBTestEntry::cast_to_inner(new (&arena) ZRBTestEntry(++id)), tree.root_cursor()); for (auto& node : tree) { - if (ZTestEntry::cast_to_outer(&node)->id() == num_entries) { + if (ZRBTestEntry::cast_to_outer(&node)->id() == num_entries) { break; } auto cursor = tree.next_cursor(&node); - ZIntrusiveRBTreeNode* const new_node = ZTestEntry::cast_to_inner(new (&arena) ZTestEntry(++id)); + ZIntrusiveRBTreeNode* const new_node = ZRBTestEntry::cast_to_inner(new (&arena) ZRBTestEntry(++id)); tree.insert(new_node, cursor); } tree.verify_tree(); - ASSERT_EQ(ZTestEntry::cast_to_outer(tree.last())->id(), num_entries); + ASSERT_EQ(ZRBTestEntry::cast_to_outer(tree.last())->id(), num_entries); int i = 0; int removed = 0; diff --git a/test/hotspot/jtreg/ProblemList-AotJdk.txt b/test/hotspot/jtreg/ProblemList-AotJdk.txt index 2528f8d377e..047fc6d33f8 100644 --- a/test/hotspot/jtreg/ProblemList-AotJdk.txt +++ b/test/hotspot/jtreg/ProblemList-AotJdk.txt @@ -3,6 +3,7 @@ runtime/NMT/NMTWithCDS.java 0000000 generic-all runtime/symbols/TestSharedArchiveConfigFile.java 0000000 generic-all gc/arguments/TestSerialHeapSizeFlags.java 0000000 generic-all +gc/arguments/TestCompressedClassFlags.java 0000000 generic-all gc/TestAllocateHeapAtMultiple.java 0000000 generic-all gc/TestAllocateHeapAt.java 0000000 generic-all diff --git a/test/hotspot/jtreg/ProblemList-StaticJdk.txt b/test/hotspot/jtreg/ProblemList-StaticJdk.txt index 2ca0961bae0..e5bdfaf8337 100644 --- a/test/hotspot/jtreg/ProblemList-StaticJdk.txt +++ b/test/hotspot/jtreg/ProblemList-StaticJdk.txt @@ -32,3 +32,14 @@ serviceability/sa/ClhsdbFindPC.java#no-xcomp-core 8346719 generic-all serviceability/sa/ClhsdbFindPC.java#xcomp-core 8346719 generic-all serviceability/sa/ClhsdbPmap.java#core 8346719 generic-all serviceability/sa/ClhsdbPstack.java#core 8346719 generic-all + +# Dynamically link with JDK/VM native libraries +gtest/GTestWrapper.java 8356201 generic-all +gtest/LargePageGtests.java#use-large-pages 8356201 generic-all +gtest/LargePageGtests.java#use-large-pages-1G 8356201 generic-all +gtest/LockStackGtests.java 8356201 generic-all +gtest/MetaspaceGtests.java#no-ccs 8356201 generic-all +gtest/NMTGtests.java#nmt-detail 8356201 generic-all +gtest/NMTGtests.java#nmt-off 8356201 generic-all +gtest/NMTGtests.java#nmt-summary 8356201 generic-all + diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index c5b24475107..94c957f77b9 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -108,7 +108,6 @@ gc/shenandoah/TestEvilSyncBug.java#generational 8345501 generic-all # :hotspot_runtime runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64 -runtime/handshake/HandshakeSuspendExitTest.java 8294313 generic-all runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#explicit-large-page-size 8267460 linux-aarch64 diff --git a/test/hotspot/jtreg/applications/jcstress/README b/test/hotspot/jtreg/applications/jcstress/README index 842b047a776..2aaf1210f9b 100644 --- a/test/hotspot/jtreg/applications/jcstress/README +++ b/test/hotspot/jtreg/applications/jcstress/README @@ -37,5 +37,5 @@ The */Test.java files should never be modified directly, because they are generated by TestGenerator and therefore all required changes must be made in that class. -[1] https://wiki.openjdk.org/display/CodeTools/jcstress +[1] https://github.com/openjdk/jcstress diff --git a/test/hotspot/jtreg/applications/jcstress/TestGenerator.java b/test/hotspot/jtreg/applications/jcstress/TestGenerator.java index cdb2093975b..b6b22e2ebdb 100644 --- a/test/hotspot/jtreg/applications/jcstress/TestGenerator.java +++ b/test/hotspot/jtreg/applications/jcstress/TestGenerator.java @@ -56,7 +56,7 @@ import java.util.function.Predicate; * added to classpath. One can replace @notest by @test in jtreg test * description above to run this class with jtreg. * - * @see jcstress + * @see jcstress */ public class TestGenerator { private static final String COPYRIGHT; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestIRFma.java b/test/hotspot/jtreg/compiler/c2/irTests/TestIRFma.java index 0e2cd067a13..da38a76f947 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestIRFma.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestIRFma.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Arm Limited. All rights reserved. + * Copyright (c) 2025, Rivos 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 @@ -87,6 +88,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_F, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static float test1(float a, float b, float c) { return Math.fma(-a, -b, c); } @@ -94,6 +97,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_D, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static double test2(double a, double b, double c) { return Math.fma(-a, -b, c); } @@ -101,6 +106,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_F, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static float test3(float a, float b, float c) { return Math.fma(-a, b, c); } @@ -108,6 +115,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_D, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static double test4(double a, double b, double c) { return Math.fma(-a, b, c); } @@ -115,6 +124,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_F, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static float test5(float a, float b, float c) { return Math.fma(a, -b, c); } @@ -122,6 +133,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_D, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static double test6(double a, double b, double c) { return Math.fma(a, -b, c); } @@ -129,6 +142,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMADD, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_F, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static float test7(float a, float b, float c) { return Math.fma(-a, b, -c); } @@ -136,6 +151,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMADD, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_D, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static double test8(double a, double b, double c) { return Math.fma(-a, b, -c); } @@ -143,6 +160,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMADD, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_F, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static float test9(float a, float b, float c) { return Math.fma(a, -b, -c); } @@ -150,6 +169,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMADD, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_D, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static double test10(double a, double b, double c) { return Math.fma(a, -b, -c); } @@ -157,6 +178,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_F, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static float test11(float a, float b, float c) { return Math.fma(a, b, -c); } @@ -164,6 +187,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMSUB, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_D, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static double test12(double a, double b, double c) { return Math.fma(a, b, -c); } @@ -171,6 +196,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMADD, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_F, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static float test13(float a, float b, float c) { return Math.fma(-a, -b, -c); } @@ -178,6 +205,8 @@ public class TestIRFma { @Test @IR(counts = {IRNode.FNMADD, "> 0"}, applyIfCPUFeature = {"asimd", "true"}) + @IR(counts = {IRNode.FMA_D, "= 1"}, + applyIfPlatform = {"riscv64", "true"}, applyIf = {"UseFMA", "true"}) static double test14(double a, double b, double c) { return Math.fma(-a, -b, -c); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index ebcbd9fbbc7..965b0083c49 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -2511,6 +2511,16 @@ public class IRNode { machOnlyNameRegex(FNMSUB, "mnsub(F|D)_reg_reg"); } + public static final String FMA_F = PREFIX + "FMA_F" + POSTFIX; + static { + beforeMatchingNameRegex(FMA_F, "FmaF"); + } + + public static final String FMA_D = PREFIX + "FMA_D" + POSTFIX; + static { + beforeMatchingNameRegex(FMA_D, "FmaD"); + } + public static final String VFMLA = PREFIX + "VFMLA" + POSTFIX; static { machOnlyNameRegex(VFMLA, "vfmla"); diff --git a/test/hotspot/jtreg/compiler/lib/verify/Verify.java b/test/hotspot/jtreg/compiler/lib/verify/Verify.java index 085ec591b0c..7b5e554b0e3 100644 --- a/test/hotspot/jtreg/compiler/lib/verify/Verify.java +++ b/test/hotspot/jtreg/compiler/lib/verify/Verify.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,33 +25,80 @@ package compiler.lib.verify; import java.util.Optional; import java.lang.foreign.*; +import java.lang.reflect.Method; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; + /** - * The {@link Verify} class provides a single {@link Verify#checkEQ} static method, which recursively - * compares the two {@link Object}s by value. It deconstructs {@link Object[]}, compares boxed primitive - * types, and compares the content of arrays and {@link MemorySegment}s. + * The {@link Verify} class provides {@link Verify#checkEQ} and {@link Verify#checkEQWithRawBits}, + * which recursively compare the two {@link Object}s by value. They deconstruct an array of objects, + * compare boxed primitive types, compare the content of arrays and {@link MemorySegment}s, and check + * that the messages of two {@link Exception}s are equal. They also check for the equivalent content + * in {@code Vector}s from the Vector API. * - * When a comparison fail, then methods print helpful messages, before throwing a {@link VerifyException}. + *

+ * Further, they compare Objects from arbitrary classes, using reflection. We check the fields of the + * Objects, and compare their recursive structure. Since we use reflection, this can be slow. + * + *

+ * When a comparison fails, then methods print helpful messages, before throwing a {@link VerifyException}. + * + *

+ * We have to take special care of {@link Float}s and {@link Double}s, since they both have various + * encodings for NaN values while the Java specification regards them as equal. Hence, we + * have two modes of comparison. With {@link Verify#checkEQ} different NaN values are regarded as equal. + * This applies to the boxed floating types, as well as arrays of floating arrays. With + * {@link Verify#checkEQWithRawBits} we compare the raw bits, and so different NaN encodings are not equal. + * Note: {@link MemorySegment} data is always compared with raw bits. */ public final class Verify { - - private Verify() {} + private final boolean isFloatCheckWithRawBits; /** - * Verify the content of two Objects, possibly recursively. Only limited types are implemented. + * When comparing arbitrary classes recursively, we need to remember which + * pairs of objects {@code (a, b)} we have already visited. The maps + * {@code a2b} and {@code b2a} track these edges. Caching which pairs + * we have already visited means the traversal only needs to visit every + * pair once. And should we ever find a pair {@code (a, b')} or {@code (a', b)}, + * then we know that the two structures are not structurally equivalent. + */ + private final HashMap a2b = new HashMap<>(); + private final HashMap b2a = new HashMap<>(); + + private Verify(boolean isFloatCheckWithRawBits) { + this.isFloatCheckWithRawBits = isFloatCheckWithRawBits; + } + + /** + * Verify the contents of two Objects on a raw bit level, possibly recursively. + * Different NaN encodings are considered non-equal, since we compare + * floating numbers by their raw bits. + * + * @param a First object to be recursively compared with the second. + * @param b Second object to be recursively compared with the first. + * @throws VerifyException If the comparison fails. + */ + public static void checkEQWithRawBits(Object a, Object b) { + Verify v = new Verify(true); + v.checkEQdispatch(a, b, "", null, null); + } + + /** + * Verify the contents of two Objects, possibly recursively. + * Different NaN encodings are considered equal. * * @param a First object to be recursively compared with the second. * @param b Second object to be recursively compared with the first. * @throws VerifyException If the comparison fails. */ public static void checkEQ(Object a, Object b) { - checkEQ(a, b, ""); + Verify v = new Verify(false); + v.checkEQdispatch(a, b, "", null, null); } - /** - * Verify the content of two Objects, possibly recursively. Only limited types are implemented. - */ - private static void checkEQ(Object a, Object b, String context) { + private void checkEQdispatch(Object a, Object b, String field, Object aParent, Object bParent) { // Both null if (a == null && b == null) { return; @@ -59,45 +106,53 @@ public final class Verify { // Null mismatch if (a == null || b == null) { - System.err.println("ERROR: Verify.checkEQ failed: null mismatch"); - print(a, "a " + context); - print(b, "b " + context); + System.err.println("ERROR: Equality matching failed: null mismatch"); + print(a, b, field, aParent, bParent); throw new VerifyException("Object array null mismatch."); } // Class mismatch - Class ca = a.getClass(); - Class cb = b.getClass(); + Class ca = a.getClass(); + Class cb = b.getClass(); if (ca != cb) { - System.err.println("ERROR: Verify.checkEQ failed: class mismatch."); + System.err.println("ERROR: Equality matching failed: class mismatch."); System.err.println(" " + ca.getName() + " vs " + cb.getName()); - print(a, "a " + context); - print(b, "b " + context); + print(a, b, field, aParent, bParent); throw new VerifyException("Object class mismatch."); } + // Already visited? This makes sure that we are not stuck in cycles, and that we have + // a mapping of pairs (a, b) for structurally equivalent Objects. + if (checkAlreadyVisited(a, b, field, aParent, bParent)) { + return; + } + switch (a) { - case Object[] x -> checkEQimpl(x, (Object[])b, context); - case Byte x -> checkEQimpl(x, ((Byte)b).byteValue(), context); - case Character x -> checkEQimpl(x, ((Character)b).charValue(), context); - case Short x -> checkEQimpl(x, ((Short)b).shortValue(), context); - case Integer x -> checkEQimpl(x, ((Integer)b).intValue(), context); - case Long x -> checkEQimpl(x, ((Long)b).longValue(), context); - case Float x -> checkEQimpl(x, ((Float)b).floatValue(), context); - case Double x -> checkEQimpl(x, ((Double)b).doubleValue(), context); - case byte[] x -> checkEQimpl(x, (byte[])b, context); - case char[] x -> checkEQimpl(x, (char[])b, context); - case short[] x -> checkEQimpl(x, (short[])b, context); - case int[] x -> checkEQimpl(x, (int[])b, context); - case long[] x -> checkEQimpl(x, (long[])b, context); - case float[] x -> checkEQimpl(x, (float[])b, context); - case double[] x -> checkEQimpl(x, (double[])b, context); - case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, context); + case Object[] x -> checkEQimpl(x, (Object[])b, field, aParent, bParent); + case Byte x -> checkEQimpl(x, (Byte)b, field, aParent, bParent); + case Character x -> checkEQimpl(x, (Character)b, field, aParent, bParent); + case Short x -> checkEQimpl(x, (Short)b, field, aParent, bParent); + case Integer x -> checkEQimpl(x, (Integer)b, field, aParent, bParent); + case Long x -> checkEQimpl(x, (Long)b, field, aParent, bParent); + case Float x -> checkEQimpl(x, (Float)b, field, aParent, bParent); + case Double x -> checkEQimpl(x, (Double)b, field, aParent, bParent); + case Boolean x -> checkEQimpl(x, (Boolean)b, field, aParent, bParent); + case byte[] x -> checkEQimpl(x, (byte[])b, field, aParent, bParent); + case char[] x -> checkEQimpl(x, (char[])b, field, aParent, bParent); + case short[] x -> checkEQimpl(x, (short[])b, field, aParent, bParent); + case int[] x -> checkEQimpl(x, (int[])b, field, aParent, bParent); + case long[] x -> checkEQimpl(x, (long[])b, field, aParent, bParent); + case float[] x -> checkEQimpl(x, (float[])b, field, aParent, bParent); + case double[] x -> checkEQimpl(x, (double[])b, field, aParent, bParent); + case boolean[] x -> checkEQimpl(x, (boolean[])b, field, aParent, bParent); + case MemorySegment x -> checkEQimpl(x, (MemorySegment) b, field, aParent, bParent); + case Exception x -> checkEQimpl(x, (Exception) b, field, aParent, bParent); default -> { - System.err.println("ERROR: Verify.checkEQ failed: type not supported: " + ca.getName()); - print(a, "a " + context); - print(b, "b " + context); - throw new VerifyException("Object array type not supported: " + ca.getName()); + if (isVectorAPIClass(ca)) { + checkEQForVectorAPIClass(a, b, field, aParent, bParent); + } else { + checkEQArbitraryClasses(a, b); + } } } } @@ -105,9 +160,10 @@ public final class Verify { /** * Verify that two bytes are identical. */ - private static void checkEQimpl(byte a, byte b, String context) { + private void checkEQimpl(byte a, byte b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + System.err.println("ERROR: Equality matching failed: value mismatch: " + a + " vs " + b); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -115,9 +171,12 @@ public final class Verify { /** * Verify that two chars are identical. */ - private static void checkEQimpl(char a, char b, String context) { + private void checkEQimpl(char a, char b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b + " for " + context); + // Note: we need to cast "(int)a", otherwise a char of numerical value "66" is + // formatted as character "B". + System.err.println("ERROR: Equality matching failed: value mismatch: " + (int)a + " vs " + (int)b); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + (int)a + " vs " + (int)b); } } @@ -125,19 +184,21 @@ public final class Verify { /** * Verify that two shorts are identical. */ - private static void checkEQimpl(short a, short b, String context) { + private void checkEQimpl(short a, short b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + (int)a + " vs " + (int)b + " for " + context); - throw new VerifyException("Value mismatch: " + (int)a + " vs " + (int)b); + System.err.println("ERROR: Equality matching failed: value mismatch: " + a + " vs " + b); + print(a, b, field, aParent, bParent); + throw new VerifyException("Value mismatch: " + a + " vs " + b); } } /** * Verify that two ints are identical. */ - private static void checkEQimpl(int a, int b, String context) { + private void checkEQimpl(int a, int b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + System.err.println("ERROR: Equality matching failed: value mismatch: " + a + " vs " + b); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } @@ -145,50 +206,88 @@ public final class Verify { /** * Verify that two longs are identical. */ - private static void checkEQimpl(long a, long b, String context) { + private void checkEQimpl(long a, long b, String field, Object aParent, Object bParent) { if (a != b) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch: " + a + " vs " + b + " for " + context); + System.err.println("ERROR: Equality matching failed: value mismatch: " + a + " vs " + b); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } /** - * Verify that two floats have identical bits. + * There are two comparison modes: one where we compare the raw bits, which sees different NaN + * encodings as different values, and one where we see all NaN encodings as identical. + * Ideally, we would want to assert that the Float.floatToRawIntBits are identical. + * But the Java spec allows us to return different bits for a NaN, which allows swapping the inputs + * of an add or mul (NaN1 * NaN2 does not have same bits as NaN2 * NaN1, because the multiplication + * of two NaN values should always return the first of the two). + * Hence, by default, we pick the non-raw comparison: we verify that we have the same bit + * pattern in all cases, except for NaN we project to the canonical NaN, using Float.floatToIntBits. */ - private static void checkEQimpl(float a, float b, String context) { - if (Float.floatToRawIntBits(a) != Float.floatToRawIntBits(b)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch for " + context); - System.err.println(" Values: " + a + " vs " + b); - System.err.println(" Values: " + Float.floatToRawIntBits(a) + " vs " + Float.floatToRawIntBits(b)); - throw new VerifyException("Value mismatch: " + a + " vs " + b); - } + private boolean isFloatEQ(float a, float b) { + return isFloatCheckWithRawBits ? Float.floatToRawIntBits(a) == Float.floatToRawIntBits(b) + : Float.floatToIntBits(a) == Float.floatToIntBits(b); } /** - * Verify that two doubles have identical bits. + * See comments for {@link #isFloatEQ}. */ - private static void checkEQimpl(double a, double b, String context) { - if (Double.doubleToRawLongBits(a) != Double.doubleToRawLongBits(b)) { - System.err.println("ERROR: Verify.checkEQ failed: value mismatch for " + context); - System.err.println(" Values: " + a + " vs " + b); - System.err.println(" Values: " + Double.doubleToRawLongBits(a) + " vs " + Double.doubleToRawLongBits(b)); + private boolean isDoubleEQ(double a, double b) { + return isFloatCheckWithRawBits ? Double.doubleToRawLongBits(a) == Double.doubleToRawLongBits(b) + : Double.doubleToLongBits(a) == Double.doubleToLongBits(b); + } + + /** + * Check that two floats are equal according to {@link #isFloatEQ}. + */ + private void checkEQimpl(float a, float b, String field, Object aParent, Object bParent) { + if (!isFloatEQ(a, b)) { + System.err.println("ERROR: Equality matching failed: value mismatch. check raw: " + isFloatCheckWithRawBits); + System.err.println(" Values: " + a + " vs " + b); + System.err.println(" Raw: " + Float.floatToRawIntBits(a) + " vs " + Float.floatToRawIntBits(b)); + print(a, b, field, aParent, bParent); throw new VerifyException("Value mismatch: " + a + " vs " + b); } } /** - * Verify that the content of two MemorySegments is identical. Note: we do not check the + * Check that two doubles are equal according to {@link #isDoubleEQ}. + */ + private void checkEQimpl(double a, double b, String field, Object aParent, Object bParent) { + if (!isDoubleEQ(a, b)) { + System.err.println("ERROR: Equality matching failed: value mismatch. check raw: " + isFloatCheckWithRawBits); + System.err.println(" Values: " + a + " vs " + b); + System.err.println(" Raw: " + Double.doubleToRawLongBits(a) + " vs " + Double.doubleToRawLongBits(b)); + print(a, b, field, aParent, bParent); + throw new VerifyException("Value mismatch: " + a + " vs " + b); + } + } + + /** + * Verify that two booleans are identical. + */ + private void checkEQimpl(boolean a, boolean b, String field, Object aParent, Object bParent) { + if (a != b) { + System.err.println("ERROR: Equality matching failed: value mismatch: " + a + " vs " + b); + print(a, b, field, aParent, bParent); + throw new VerifyException("Value mismatch: " + a + " vs " + b); + } + } + + /** + * Verify that the contents of two MemorySegments are identical. Note: we do not check the * backing type, only the size and content. */ - private static void checkEQimpl(MemorySegment a, MemorySegment b, String context) { + private void checkEQimpl(MemorySegment a, MemorySegment b, String field, Object aParent, Object bParent) { long offset = a.mismatch(b); if (offset == -1) { return; } // Print some general info - System.err.println("ERROR: Verify.checkEQ failed for: " + context); + System.err.println("ERROR: Equality matching failed"); - printMemorySegment(a, "a " + context); - printMemorySegment(b, "b " + context); + print(a, b, field, aParent, bParent); + printMemorySegment(a, "a"); + printMemorySegment(b, "b"); // (1) Mismatch on size if (a.byteSize() != b.byteSize()) { @@ -203,82 +302,232 @@ public final class Verify { } /** - * Verify that the content of two byte arrays is identical. + * Verify that two Exceptions have the same message. Messages are not always carried, + * they are often dropped for performance reasons, and that is okay. But if both Exceptions + * have the message, we should compare them. */ - private static void checkEQimpl(byte[] a, byte[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(Exception a, Exception b, String field, Object aParent, Object bParent) { + String am = a.getMessage(); + String bm = b.getMessage(); + + // Missing messages is expected, but if they both have one, they must agree. + if (am == null || bm == null) { return; } + if (am.equals(bm)) { return; } + + System.err.println("ERROR: Equality matching failed:"); + System.err.println("a: " + a.getMessage()); + System.err.println("b: " + b.getMessage()); + print(a, b, field, aParent, bParent); + throw new VerifyException("Exception message mismatch: " + a + " vs " + b); } /** - * Verify that the content of two char arrays is identical. + * Verify that the contents of two byte arrays are identical. */ - private static void checkEQimpl(char[] a, char[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(byte[] a, byte[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** - * Verify that the content of two short arrays is identical. + * Verify that the contents of two char arrays are identical. */ - private static void checkEQimpl(short[] a, short[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(char[] a, char[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** - * Verify that the content of two int arrays is identical. + * Verify that the contents of two short arrays are identical. */ - private static void checkEQimpl(int[] a, int[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(short[] a, short[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** - * Verify that the content of two long arrays is identical. + * Verify that the contents of two int arrays are identical. */ - private static void checkEQimpl(long[] a, long[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(int[] a, int[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** - * Verify that the content of two float arrays is identical. + * Verify that the contents of two long arrays are identical. */ - private static void checkEQimpl(float[] a, float[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(long[] a, long[] b, String field, Object aParent, Object bParent) { + checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), field + " -> to MemorySegment", aParent, bParent); } /** - * Verify that the content of two double arrays is identical. + * Check that two float arrays are equal according to {@link #isFloatEQ}. */ - private static void checkEQimpl(double[] a, double[] b, String context) { - checkEQimpl(MemorySegment.ofArray(a), MemorySegment.ofArray(b), context); + private void checkEQimpl(float[] a, float[] b, String field, Object aParent, Object bParent) { + if (a.length != b.length) { + System.err.println("ERROR: Equality matching failed: length mismatch: " + a.length + " vs " + b.length); + print(a, b, field, aParent, bParent); + throw new VerifyException("Float array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (!isFloatEQ(a[i], b[i])) { + System.err.println("ERROR: Equality matching failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); + print(a, b, field, aParent, bParent); + throw new VerifyException("Float array value mismatch " + a[i] + " vs " + b[i]); + } + } } /** - * Verify that the content of two Object arrays is identical, recursively: + * Check that two double arrays are equal according to {@link #isDoubleEQ}. + */ + private void checkEQimpl(double[] a, double[] b, String field, Object aParent, Object bParent) { + if (a.length != b.length) { + System.err.println("ERROR: Equality matching failed: length mismatch: " + a.length + " vs " + b.length); + print(a, b, field, aParent, bParent); + throw new VerifyException("Double array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (!isDoubleEQ(a[i], b[i])) { + System.err.println("ERROR: Equality matching failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i] + ". check raw: " + isFloatCheckWithRawBits); + print(a, b, field, aParent, bParent); + throw new VerifyException("Double array value mismatch " + a[i] + " vs " + b[i]); + } + } + } + + /** + * Verify that the contents of two boolean arrays are identical. + */ + private void checkEQimpl(boolean[] a, boolean[] b, String field, Object aParent, Object bParent) { + if (a.length != b.length) { + System.err.println("ERROR: Equality matching failed: length mismatch: " + a.length + " vs " + b.length); + print(a, b, field, aParent, bParent); + throw new VerifyException("Boolean array length mismatch."); + } + + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + System.err.println("ERROR: Equality matching failed: value mismatch at " + i + ": " + a[i] + " vs " + b[i]); + print(a, b, field, aParent, bParent); + throw new VerifyException("Boolean array value mismatch."); + } + } + } + + /** + * Verify that the contents of two Object arrays are identical, recursively: * every element is compared with checkEQimpl for the corresponding type. */ - private static void checkEQimpl(Object[] a, Object[] b, String context) { + private void checkEQimpl(Object[] a, Object[] b, String field, Object aParent, Object bParent) { // (1) Length mismatch if (a.length != b.length) { - System.err.println("ERROR: Verify.checkEQ failed: length mismatch: " + a.length + " vs " + b.length); + System.err.println("ERROR: Equality matching failed: length mismatch: " + a.length + " vs " + b.length); + print(a, b, field, aParent, bParent); throw new VerifyException("Object array length mismatch."); } for (int i = 0; i < a.length; i++) { // Recursive checkEQ call. - checkEQ(a[i], b[i], "[" + i + "]" + context); + checkEQdispatch(a[i], b[i], "[" + i + "]", a, b); } } - private static void print(Object a, String context) { - if (a == null) { - System.err.println(" " + context + ": null"); + private static boolean isVectorAPIClass(Class c) { + return c.getName().startsWith("jdk.incubator.vector") && + c.getName().contains("Vector"); + } + + /** + * We do not want to import jdk.incubator.vector explicitly, because it would mean we would also have + * to add "--add-modules=jdk.incubator.vector" to the command-line of every test that uses the Verify + * class. So we hack this via reflection. + */ + private void checkEQForVectorAPIClass(Object a, Object b, String field, Object aParent, Object bParent) { + Class ca = a.getClass(); + Object va; + Object vb; + try { + Method m = ca.getMethod("toArray"); + m.setAccessible(true); + va = m.invoke(a); + vb = m.invoke(b); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Could not invoke toArray on " + ca.getName(), e); + } + checkEQdispatch(va, vb, field + ".toArray", aParent, bParent); + } + + private void checkEQArbitraryClasses(Object a, Object b) { + Class c = a.getClass(); + while (c != Object.class) { + for (Field field : c.getDeclaredFields()) { + Object va; + Object vb; + try { + field.setAccessible(true); + va = field.get(a); + vb = field.get(b); + } catch (IllegalAccessException e) { + throw new VerifyException("Failure to access field: " + field + " of " + a); + } + checkEQdispatch(va, vb, field.getName(), a, b); + } + c = c.getSuperclass(); + } + } + + private void print(Object a, Object b, String field, Object aParent, Object bParent) { + System.err.println(" aParent: " + (aParent != null ? aParent : "")); + System.err.println(" bParent: " + (bParent != null ? bParent : "")); + System.err.println(" field: " + field); + System.err.println(" a: " + a); + System.err.println(" b: " + b); + } + /** + * When comparing arbitrary classes recursively, we need to remember which + * pairs of objects {@code (a, b)} we have already visited. The maps + * {@link #a2b} and {@link #b2a} track these edges. Caching which pairs + * we have already visited means the traversal only needs to visit every + * pair once. And should we ever find a pair {@code (a, b')} or {@code (a', b)}, + * then we know that the two structures are not structurally equivalent. + */ + private boolean checkAlreadyVisited(Object a, Object b, String field, Object aParent, Object bParent) { + // Boxed primitives are not guaranteed to be the same Object for the same primitive value. + // Hence, we cannot use the mapping below. We test these boxed primitive types by value anyway, + // and they are no recursive structures, so there is no point in optimizing here anyway. + switch (a) { + case Boolean _, + Byte _, + Short _, + Character _, + Integer _, + Long _, + Float _, + Double _ -> { return false; } + default -> {} + } + + Object bPrevious = a2b.get(a); + Object aPrevious = b2a.get(b); + if (aPrevious == null && bPrevious == null) { + // Record for next time. + a2b.put(a, b); + b2a.put(b, a); + return false; } else { - System.err.println(" " + context + ": " + a); + if (a != aPrevious || b != bPrevious) { + System.err.println("ERROR: Equality matching failed:"); + print(a, b, field, aParent, bParent); + System.err.println(" aPrevious: " + aPrevious); + System.err.println(" bPrevious: " + bPrevious); + throw new VerifyException("Mismatch with previous pair."); + } + return true; } } - private static void printMemorySegment(MemorySegment a, String context) { + private void printMemorySegment(MemorySegment a, String name) { Optional maybeBase = a.heapBase(); - System.err.println(" " + context + " via MemorySegment:"); + System.err.println(" MemorySegment " + name + ":"); if (maybeBase.isEmpty()) { System.err.println(" no heap base (native)."); } else { @@ -289,14 +538,14 @@ public final class Verify { System.err.println(" byteSize: " + a.byteSize()); } - private static void printMemorySegmentValue(MemorySegment a, long offset, int range) { + private void printMemorySegmentValue(MemorySegment a, long offset, int range) { long start = Long.max(offset - range, 0); long end = Long.min(offset + range, a.byteSize()); for (long i = start; i < end; i++) { byte b = a.get(ValueLayout.JAVA_BYTE, i); System.err.print(String.format("%02x ", b)); } - System.err.println(""); + System.err.println(); for (long i = start; i < end; i++) { if (i == offset) { System.err.print("^^ "); @@ -304,6 +553,6 @@ public final class Verify { System.err.print(" "); } } - System.err.println(""); + System.err.println(); } } diff --git a/test/hotspot/jtreg/compiler/membars/DekkerTest.java b/test/hotspot/jtreg/compiler/membars/DekkerTest.java index 2f329588abe..9e5f135a2b2 100644 --- a/test/hotspot/jtreg/compiler/membars/DekkerTest.java +++ b/test/hotspot/jtreg/compiler/membars/DekkerTest.java @@ -47,7 +47,7 @@ public class DekkerTest { /* Read After Write Test (basically a simple Dekker test with volatile variables) Derived from the original jcstress test, available at: - hhttps://git.openjdk.org/jcstress/commit/15acd86 + https://git.openjdk.org/jcstress/commit/15acd86 tests-custom/src/main/java/org/openjdk/jcstress/tests/volatiles/DekkerTest.java */ diff --git a/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java b/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java new file mode 100644 index 00000000000..22ce12f9641 --- /dev/null +++ b/test/hotspot/jtreg/compiler/print/TestPrintAssemblyDeoptRace.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8258229 + * @summary If a method is made not entrant while printing the assembly, hotspot crashes due to mismatched relocation information. + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:+DeoptimizeALot + * -XX:CompileCommand=print,java/math/BitSieve.bit compiler.print.TestPrintAssemblyDeoptRace + */ + +package compiler.print; + +import java.util.Arrays; + +import java.security.Security; +import java.security.Provider; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.MGF1ParameterSpec; + +import javax.crypto.Cipher; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; + +public class TestPrintAssemblyDeoptRace { + private static RSAPrivateKey privateKey; + private static RSAPublicKey publicKey; + static Provider cp; + + public static void main(String args[]) throws Exception { + cp = Security.getProvider("SunJCE"); + System.out.println("Testing provider " + cp.getName() + "..."); + Provider kfp = Security.getProvider("SunRsaSign"); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", kfp); + kpg.initialize(2048); + KeyPair kp = kpg.generateKeyPair(); + privateKey = (RSAPrivateKey) kp.getPrivate(); + publicKey = (RSAPublicKey) kp.getPublic(); + testEncryptDecrypt(new OAEPParameterSpec("SHA-512/256", "MGF1", + MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT), 190); + + } + + private static void testEncryptDecrypt(OAEPParameterSpec spec, + int dataLength) throws Exception { + + Cipher c = Cipher.getInstance("RSA/ECB/OAEPPadding", cp); + c.init(Cipher.ENCRYPT_MODE, publicKey, spec); + + byte[] data = new byte[dataLength]; + byte[] enc = c.doFinal(data); + c.init(Cipher.DECRYPT_MODE, privateKey, spec); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorCommutativeOperSharingTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorCommutativeOperSharingTest.java index c3e0cbc3a9a..a51946dd698 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorCommutativeOperSharingTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorCommutativeOperSharingTest.java @@ -147,7 +147,8 @@ public class VectorCommutativeOperSharingTest { } @Test - @IR(counts = {IRNode.SATURATING_ADD_VI, IRNode.VECTOR_SIZE_ANY, " 2 "}, applyIfCPUFeature = {"avx2", "true"}) + @IR(counts = {IRNode.SATURATING_ADD_VI, IRNode.VECTOR_SIZE_ANY, " 2 "}, + applyIfCPUFeatureOr = {"avx2", "true", "rvv", "true"}) public void testVectorIRSharing3(int index) { IntVector vec1 = IntVector.fromArray(I_SPECIES, ia, index); IntVector vec2 = IntVector.fromArray(I_SPECIES, ib, index); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java index 84aaacb3a01..887c41efd48 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java @@ -166,7 +166,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_ADD_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_ADD_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void sadd_byte() { for (int i = 0; i < COUNT; i += bspec.length()) { @@ -189,7 +189,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_ADD_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_ADD_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void sadd_short() { for (int i = 0; i < COUNT; i += sspec.length()) { @@ -212,7 +212,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_ADD_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_ADD_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void sadd_int() { for (int i = 0; i < COUNT; i += ispec.length()) { @@ -235,7 +235,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_ADD_VL, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_ADD_VL, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void sadd_long() { for (int i = 0; i < COUNT; i += lspec.length()) { @@ -260,7 +260,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_ADD_VB, " >0 " , "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void suadd_byte() { for (int i = 0; i < COUNT; i += bspec.length()) { @@ -285,7 +285,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_ADD_VS, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void suadd_short() { for (int i = 0; i < COUNT; i += sspec.length()) { @@ -310,7 +310,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_ADD_VI, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void suadd_int() { for (int i = 0; i < COUNT; i += ispec.length()) { @@ -335,7 +335,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_ADD_VL, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void suadd_long() { for (int i = 0; i < COUNT; i += lspec.length()) { @@ -358,7 +358,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_SUB_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_SUB_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void ssub_byte() { for (int i = 0; i < COUNT; i += bspec.length()) { @@ -381,7 +381,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_SUB_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_SUB_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void ssub_short() { for (int i = 0; i < COUNT; i += sspec.length()) { @@ -404,7 +404,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_SUB_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_SUB_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void ssub_int() { for (int i = 0; i < COUNT; i += ispec.length()) { @@ -427,7 +427,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_SUB_VL, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_SUB_VL, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void ssub_long() { for (int i = 0; i < COUNT; i += lspec.length()) { @@ -452,7 +452,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_SUB_VB, " >0 " , "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void susub_byte() { for (int i = 0; i < COUNT; i += bspec.length()) { @@ -477,7 +477,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_SUB_VS, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void susub_short() { for (int i = 0; i < COUNT; i += sspec.length()) { @@ -502,7 +502,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_SUB_VI, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void susub_int() { for (int i = 0; i < COUNT; i += ispec.length()) { @@ -527,7 +527,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_SUB_VL, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @Warmup(value = 10000) public void susub_long() { for (int i = 0; i < COUNT; i += lspec.length()) { @@ -550,7 +550,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_ADD_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_ADD_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.VECTOR_BLEND_B, " >0 "}, applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}) @IR(failOn = IRNode.VECTOR_BLEND_B, applyIfCPUFeature = {"sve2", "true"}) @Warmup(value = 10000) @@ -576,7 +576,7 @@ public class VectorSaturatedOperationsTest { } @Test - @IR(counts = {IRNode.SATURATING_SUB_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.SATURATING_SUB_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.VECTOR_BLEND_S, " >0 "}, applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}) @IR(failOn = IRNode.VECTOR_BLEND_S, applyIfCPUFeature = {"sve2", "true"}) @Warmup(value = 10000) @@ -604,7 +604,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_ADD_VI, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.VECTOR_BLEND_I, " >0 "}, applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}) @IR(failOn = IRNode.VECTOR_BLEND_I, applyIfCPUFeature = {"sve2", "true"}) @Warmup(value = 10000) @@ -632,7 +632,7 @@ public class VectorSaturatedOperationsTest { @Test @IR(counts = {IRNode.SATURATING_SUB_VL, " >0 ", "unsigned_vector_node", " >0 "}, phase = {CompilePhase.BEFORE_MATCHING}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.VECTOR_BLEND_L, " >0 "}, applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}) @IR(failOn = IRNode.VECTOR_BLEND_L, applyIfCPUFeature = {"sve2", "true"}) @Warmup(value = 10000) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java index c1bef52a8b1..9e5bf9c3753 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Rivos 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 @@ -36,7 +37,7 @@ * -XX:+WhiteBoxAPI * compiler.vectorization.runner.BasicDoubleOpTest * - * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") + * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled */ @@ -118,7 +119,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { // ---------------- Arithmetic ---------------- @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.NEG_VD, ">0"}) public double[] vectorNeg() { double[] res = new double[SIZE]; @@ -129,7 +130,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.ABS_VD, ">0"}) public double[] vectorAbs() { double[] res = new double[SIZE]; @@ -140,7 +141,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"}, counts = {IRNode.SQRT_VD, ">0"}) public double[] vectorSqrt() { double[] res = new double[SIZE]; @@ -184,7 +185,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.ADD_VD, ">0"}) public double[] vectorAdd() { double[] res = new double[SIZE]; @@ -195,7 +196,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.SUB_VD, ">0"}) public double[] vectorSub() { double[] res = new double[SIZE]; @@ -206,7 +207,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.MUL_VD, ">0"}) public double[] vectorMul() { double[] res = new double[SIZE]; @@ -217,7 +218,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.DIV_VD, ">0"}) public double[] vectorDiv() { double[] res = new double[SIZE]; @@ -228,7 +229,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"}, counts = {IRNode.MAX_VD, ">0"}) public double[] vectorMax() { double[] res = new double[SIZE]; @@ -239,7 +240,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"}, counts = {IRNode.MAX_VD, "0"}) public double[] vectorMax_8322090() { double[] res = new double[SIZE]; @@ -250,7 +251,7 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { } @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"}, counts = {IRNode.MIN_VD, ">0"}) public double[] vectorMin() { double[] res = new double[SIZE]; @@ -265,6 +266,8 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { counts = {IRNode.FMA_VD, ">0", IRNode.VFMLA, ">0"}) @IR(applyIfCPUFeatureAnd = {"fma", "true", "avx", "true"}, counts = {IRNode.FMA_VD, ">0"}) + @IR(applyIfCPUFeature = {"rvv", "true"}, applyIf = {"UseFMA", "true"}, + counts = {IRNode.FMA_VD, ">0"}) public double[] vectorMulAdd() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { @@ -278,6 +281,8 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { counts = {IRNode.FMA_VD, ">0", IRNode.VFMLS, ">0"}) @IR(applyIfCPUFeatureAnd = {"fma", "true", "avx", "true"}, counts = {IRNode.FMA_VD, ">0"}) + @IR(applyIfCPUFeature = {"rvv", "true"}, applyIf = {"UseFMA", "true"}, + counts = {IRNode.FMA_VD, ">0"}) public double[] vectorMulSub1() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { @@ -291,6 +296,8 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { counts = {IRNode.FMA_VD, ">0", IRNode.VFMLS, ">0"}) @IR(applyIfCPUFeatureAnd = {"fma", "true", "avx", "true"}, counts = {IRNode.FMA_VD, ">0"}) + @IR(applyIfCPUFeature = {"rvv", "true"}, applyIf = {"UseFMA", "true"}, + counts = {IRNode.FMA_VD, ">0"}) public double[] vectorMulSub2() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { @@ -306,6 +313,8 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { counts = {IRNode.VFNMLA, ">0"}) @IR(applyIfCPUFeatureAnd = {"fma", "true", "avx", "true"}, counts = {IRNode.FMA_VD, ">0"}) + @IR(applyIfCPUFeature = {"rvv", "true"}, applyIf = {"UseFMA", "true"}, + counts = {IRNode.FMA_VD, ">0"}) public double[] vectorNegateMulAdd1() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { @@ -321,6 +330,8 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { counts = {IRNode.VFNMLA, ">0"}) @IR(applyIfCPUFeatureAnd = {"fma", "true", "avx", "true"}, counts = {IRNode.FMA_VD, ">0"}) + @IR(applyIfCPUFeature = {"rvv", "true"}, applyIf = {"UseFMA", "true"}, + counts = {IRNode.FMA_VD, ">0"}) public double[] vectorNegateMulAdd2() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { @@ -334,6 +345,8 @@ public class BasicDoubleOpTest extends VectorizationTestRunner { counts = {IRNode.FMA_VD, ">0"}) @IR(applyIfCPUFeatureAnd = {"fma", "true", "avx", "true"}, counts = {IRNode.FMA_VD, ">0"}) + @IR(applyIfCPUFeature = {"rvv", "true"}, applyIf = {"UseFMA", "true"}, + counts = {IRNode.FMA_VD, ">0"}) public double[] vectorNegateMulSub() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { diff --git a/test/hotspot/jtreg/containers/docker/TestJcmd.java b/test/hotspot/jtreg/containers/docker/TestJcmd.java index ca8f1659fe9..4b604096b00 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmd.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmd.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,8 @@ */ import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import java.util.regex.Matcher; import java.util.stream.Collectors; import jdk.test.lib.Container; import jdk.test.lib.JDKToolFinder; @@ -264,14 +266,16 @@ public class TestJcmd { } private static PodmanVersion fromVersionString(String version) { - try { - // Example 'podman version 3.2.1' - String versNums = version.split("\\s+", 3)[2]; - String[] numbers = versNums.split("\\.", 3); - return new PodmanVersion(Integer.parseInt(numbers[0]), - Integer.parseInt(numbers[1]), - Integer.parseInt(numbers[2])); - } catch (Exception e) { + // Examples: 'podman version 3.2.1', 'podman version 4.9.4-rhel' + final Pattern pattern = Pattern.compile("podman version (\\d+)\\.(\\d+)\\.(\\d+).*"); + final Matcher matcher = pattern.matcher(version); + + if (matcher.matches()) { + return new PodmanVersion( + Integer.parseInt(matcher.group(1)), + Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3))); + } else { System.out.println("Failed to parse podman version: " + version); return DEFAULT; } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java b/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java index 090df514966..2ed29454df7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/SharedArchiveConsistency.java @@ -54,6 +54,7 @@ public class SharedArchiveConsistency { }; public static final String HELLO_WORLD = "Hello World"; + public static final String errMsg = "An error has occurred while processing the shared archive file."; public static int num_regions = shared_region_name.length; public static String[] matchMessages = { @@ -170,6 +171,7 @@ public class SharedArchiveConsistency { output = shareAuto ? TestCommon.execAuto(execArgs) : TestCommon.execCommon(execArgs); output.shouldContain("The shared archive file was created by a different version or build of HotSpot"); output.shouldNotContain("Checksum verification failed"); + output.shouldContain(errMsg); if (shareAuto) { output.shouldContain(HELLO_WORLD); } @@ -194,6 +196,7 @@ public class SharedArchiveConsistency { CDSArchiveUtils.modifyHeaderIntField(copiedJsa, CDSArchiveUtils.offsetVersion(), version); output = shareAuto ? TestCommon.execAuto(execArgs) : TestCommon.execCommon(execArgs); output.shouldContain("The shared archive file version " + hex(version) + " does not match the required version " + hex(currentCDSArchiveVersion)); + output.shouldContain(errMsg); if (shareAuto) { output.shouldContain(HELLO_WORLD); } @@ -237,26 +240,26 @@ public class SharedArchiveConsistency { System.out.println("\n5. Insert bytes at beginning of data section, should fail\n"); String insertBytes = startNewArchive("insert-bytes"); CDSArchiveUtils.insertBytesRandomlyAfterHeader(orgJsaFile, insertBytes); - testAndCheck(verifyExecArgs); + testAndCheck(verifyExecArgs, errMsg); // delete bytes in data section forward System.out.println("\n6a. Delete bytes at beginning of data section, should fail\n"); String deleteBytes = startNewArchive("delete-bytes"); CDSArchiveUtils.deleteBytesAtRandomPositionAfterHeader(orgJsaFile, deleteBytes, 4096 /*bytes*/); - testAndCheck(verifyExecArgs); + testAndCheck(verifyExecArgs, errMsg); // delete bytes at the end System.out.println("\n6b. Delete bytes at the end, should fail\n"); deleteBytes = startNewArchive("delete-bytes-end"); CDSArchiveUtils.deleteBytesAtTheEnd(orgJsaFile, deleteBytes); - testAndCheck(verifyExecArgs, "The shared archive file has been truncated."); + testAndCheck(verifyExecArgs, "The shared archive file has been truncated.", errMsg); // modify contents in random area System.out.println("\n7. modify Content in random areas, should fail\n"); String randomAreas = startNewArchive("random-areas"); copiedJsa = CDSArchiveUtils.copyArchiveFile(orgJsaFile, randomAreas); CDSArchiveUtils.modifyRegionContentRandomly(copiedJsa); - testAndCheck(verifyExecArgs); + testAndCheck(verifyExecArgs, errMsg); // modify _base_archive_name_offet to non-zero System.out.println("\n8. modify _base_archive_name_offset to non-zero\n"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java index 0fb72774ff4..7aef13abac7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java @@ -61,24 +61,24 @@ public class AOTClassLinkingVMOptions { testCase("Archived full module graph must be enabled at runtime"); TestCommon.run("-cp", appJar, "-Djdk.module.validation=1", "Hello") - .assertAbnormalExit("CDS archive has aot-linked classes." + + .assertAbnormalExit("shared archive file has aot-linked classes." + " It cannot be used when archived full module graph is not used"); testCase("Cannot use -Djava.system.class.loader"); TestCommon.run("-cp", appJar, "-Djava.system.class.loader=dummy", "Hello") - .assertAbnormalExit("CDS archive has aot-linked classes." + + .assertAbnormalExit("shared archive file has aot-linked classes." + " It cannot be used when the java.system.class.loader property is specified."); testCase("Cannot use a different main module"); TestCommon.run("-cp", appJar, "-Xlog:cds", "-m", "jdk.compiler/com.sun.tools.javac.Main") - .assertAbnormalExit("CDS archive has aot-linked classes." + + .assertAbnormalExit("shared archive file has aot-linked classes." + " It cannot be used when archived full module graph is not used."); testCase("Cannot use security manager"); TestCommon.run("-cp", appJar, "-Xlog:cds", "-Djava.security.manager=allow") - .assertAbnormalExit("CDS archive has aot-linked classes." + + .assertAbnormalExit("shared archive file has aot-linked classes." + " It cannot be used with -Djava.security.manager=allow."); TestCommon.run("-cp", appJar, "-Xlog:cds", "-Djava.security.manager=default") - .assertAbnormalExit("CDS archive has aot-linked classes." + + .assertAbnormalExit("shared archive file has aot-linked classes." + " It cannot be used with -Djava.security.manager=default."); // Dumping with AOTInvokeDynamicLinking disabled @@ -91,7 +91,7 @@ public class AOTClassLinkingVMOptions { testCase("Archived full module graph must be enabled at runtime (with -XX:-AOTInvokeDynamicLinking)"); TestCommon.run("-cp", appJar, "-Djdk.module.validation=1", "Hello") - .assertAbnormalExit("CDS archive has aot-linked classes." + + .assertAbnormalExit("shared archive file has aot-linked classes." + " It cannot be used when archived full module graph is not used"); // NOTE: tests for ClassFileLoadHook + AOTClassLinking is in @@ -132,7 +132,7 @@ public class AOTClassLinkingVMOptions { TestCommon.run("-Xlog:cds", "--module-path", modulePath + "/bad", "-m", MAIN_MODULE) - .assertAbnormalExit("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used."); + .assertAbnormalExit("shared archive file has aot-linked classes. It cannot be used when archived full module graph is not used."); testCase("Cannot use mis-matched --add-modules"); TestCommon.testDump(null, appClasses, @@ -150,6 +150,6 @@ public class AOTClassLinkingVMOptions { "--add-modules", "java.base", MAIN_CLASS) .assertAbnormalExit("Mismatched values for property jdk.module.addmods", - "CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used."); + "shared archive file has aot-linked classes. It cannot be used when archived full module graph is not used."); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java index cbc4644d5ca..aa34f479936 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java @@ -110,6 +110,7 @@ public class BulkLoaderTest { // Run without archived FMG -- fail to load { + final String archiveType = (args[0].equals("AOT")) ? "AOT cache" : "shared archive file"; String extraVmArgs[] = { "-Xlog:cds", "-Djdk.module.showModuleResolution=true" @@ -117,7 +118,7 @@ public class BulkLoaderTest { t.setCheckExitValue(false); OutputAnalyzer out = t.productionRun(extraVmArgs); out.shouldHaveExitValue(1); - out.shouldContain("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used."); + out.shouldContain(archiveType + " has aot-linked classes. It cannot be used when archived full module graph is not used."); t.setCheckExitValue(true); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java index af8f3acb5e9..1b20a371806 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/WrongTopClasspath.java @@ -63,6 +63,7 @@ public class WrongTopClasspath extends DynamicArchiveTestBase { String topArchiveMsg = "The top archive failed to load"; String mismatchMsg = "shared class paths mismatch"; String hintMsg = "(hint: enable -Xlog:class+path=info to diagnose the failure)"; + String errMsg = "An error has occurred while processing the shared archive file."; // ... but try to load the top archive using "-cp WrongJar.jar". // Use -Xshare:auto so top archive can fail after base archive has succeeded, @@ -75,7 +76,7 @@ public class WrongTopClasspath extends DynamicArchiveTestBase { "-cp", wrongJar, mainClass, "assertShared:java.lang.Object", // base archive still useable "assertNotShared:GenericTestApp") // but top archive is not useable - .assertNormalExit(topArchiveMsg); + .assertNormalExit(topArchiveMsg, errMsg); // Turn off all CDS logging, the "shared class paths mismatch" warning // message should still be there. @@ -84,7 +85,7 @@ public class WrongTopClasspath extends DynamicArchiveTestBase { "-cp", wrongJar, mainClass, "assertShared:java.lang.Object", // base archive still useable "assertNotShared:GenericTestApp") // but top archive is not useable - .assertNormalExit(topArchiveMsg, mismatchMsg, hintMsg); + .assertNormalExit(topArchiveMsg, mismatchMsg, hintMsg, errMsg); // Enable class+path logging and run with -Xshare:on, the mismatchMsg // should be there, the hintMsg should NOT be there. @@ -96,6 +97,7 @@ public class WrongTopClasspath extends DynamicArchiveTestBase { "assertNotShared:GenericTestApp") // but top archive is not useable .assertAbnormalExit( output -> { output.shouldContain(mismatchMsg) + .shouldContain(errMsg) .shouldNotContain(hintMsg); }); @@ -111,6 +113,7 @@ public class WrongTopClasspath extends DynamicArchiveTestBase { "assertNotShared:GenericTestApp") // but top archive is not useable .assertNormalExit(output -> { output.shouldContain(topArchiveMsg); + output.shouldContain(errMsg); output.shouldMatch("This file is not the one used while building the shared archive file:.*GenericTestApp.jar"); output.shouldMatch(".warning..cds.*GenericTestApp.jar.*timestamp has changed");}); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java index 7ccd4e5d312..a49054d10a1 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java @@ -111,7 +111,7 @@ public class ClassFileLoadHookTest { // the static archive was not created with aot-linked classes. out.shouldHaveExitValue(0); } else { - out.shouldContain("CDS archive has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use."); + out.shouldContain("shared archive file has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use."); out.shouldNotHaveExitValue(0); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/examples/TestWithVectorAPI.java b/test/hotspot/jtreg/testlibrary_tests/verify/examples/TestWithVectorAPI.java new file mode 100644 index 00000000000..83d16fb3a6d --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/verify/examples/TestWithVectorAPI.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Example test to show Verify.checkEQ with the VectorAPI. + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main verify.examples.TestWithVectorAPI + */ + +package verify.examples; + +import jdk.incubator.vector.*; + +import compiler.lib.verify.*; + +/** + * Example to show the use of Verify.checkEQ with the VectorAPI. + */ +public class TestWithVectorAPI { + public static void main(String[] args) { + IntVector iv1 = IntVector.broadcast(IntVector.SPECIES_64, 7); + IntVector iv2 = IntVector.broadcast(IntVector.SPECIES_64, 35); + IntVector iv3 = IntVector.broadcast(IntVector.SPECIES_64, 42); + IntVector iv4 = iv1.add(iv2); + Verify.checkEQ(iv3, iv4); + + FloatVector fv1 = FloatVector.broadcast(FloatVector.SPECIES_64, 7); + FloatVector fv2 = FloatVector.broadcast(FloatVector.SPECIES_64, 35); + FloatVector fv3 = FloatVector.broadcast(FloatVector.SPECIES_64, 42); + FloatVector fv4 = fv1.add(fv2); + Verify.checkEQ(fv3, fv4); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java index bdfeeb149eb..34cff79dd6e 100644 --- a/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java +++ b/test/hotspot/jtreg/testlibrary_tests/verify/tests/TestVerify.java @@ -50,9 +50,14 @@ public class TestVerify { testArrayFloat(); testArrayDouble(); testNativeMemorySegment(); + testException(); + + testRawFloat(); // Test recursive data: Object array of values, etc. testRecursive(); + + testArbitraryClasses(); } public static void testArrayByte() { @@ -290,6 +295,63 @@ public class TestVerify { checkNE(a, c); } + public static void testException() { + Exception e1 = new ArithmeticException("abc"); + Exception e2 = new ArithmeticException("abc"); + Exception e3 = new ArithmeticException(); + Exception e4 = new ArithmeticException("xyz"); + Exception e5 = new RuntimeException("abc"); + + Verify.checkEQ(e1, e1); + Verify.checkEQ(e1, e2); + Verify.checkEQ(e3, e3); + Verify.checkEQ(e1, e3); // one has no message + + checkNE(e1, e4); + checkNE(e2, e4); + Verify.checkEQ(e3, e4); + + Verify.checkEQ(e5, e5); + checkNE(e1, e5); + checkNE(e2, e5); + checkNE(e3, e5); + checkNE(e4, e5); + } + + public static void testRawFloat() { + float nanF1 = Float.intBitsToFloat(0x7f800001); + float nanF2 = Float.intBitsToFloat(0x7fffffff); + double nanD1 = Double.longBitsToDouble(0x7ff0000000000001L); + double nanD2 = Double.longBitsToDouble(0x7fffffffffffffffL); + + float[] arrF1 = new float[]{nanF1}; + float[] arrF2 = new float[]{nanF2}; + double[] arrD1 = new double[]{nanD1}; + double[] arrD2 = new double[]{nanD2}; + + Verify.checkEQ(nanF1, Float.NaN); + Verify.checkEQ(nanF1, nanF1); + Verify.checkEQWithRawBits(nanF1, nanF1); + Verify.checkEQ(nanF1, nanF2); + Verify.checkEQ(nanD1, Double.NaN); + Verify.checkEQ(nanD1, nanD1); + Verify.checkEQWithRawBits(nanD1, nanD1); + Verify.checkEQ(nanD1, nanD2); + + Verify.checkEQ(arrF1, arrF1); + Verify.checkEQWithRawBits(arrF1, arrF1); + Verify.checkEQ(arrF1, arrF2); + Verify.checkEQ(arrD1, arrD1); + Verify.checkEQWithRawBits(arrD1, arrD1); + Verify.checkEQ(arrD1, arrD2); + + checkNEWithRawBits(nanF1, nanF2); + checkNEWithRawBits(nanD1, nanD2); + + checkNEWithRawBits(arrF1, arrF2); + checkNEWithRawBits(arrD1, arrD2); + } + public static void testRecursive() { Verify.checkEQ(null, null); @@ -400,20 +462,233 @@ public class TestVerify { int v1 = (int)RANDOM.nextInt(); int v2 = (int)(v1 ^ (1 << RANDOM.nextInt(32))); checkNE(v1, v2); - checkNE(Float.intBitsToFloat(v1), Float.intBitsToFloat(v2)); + checkNEWithRawBits(Float.intBitsToFloat(v1), Float.intBitsToFloat(v2)); } for (int i = 0; i < 10; i++) { long v1 = (long)RANDOM.nextLong(); long v2 = (long)(v1 ^ (1L << RANDOM.nextInt(64))); checkNE(v1, v2); - checkNE(Double.longBitsToDouble(v1), Double.longBitsToDouble(v2)); + checkNEWithRawBits(Double.longBitsToDouble(v1), Double.longBitsToDouble(v2)); } } + static class A {} + + static class B {} + + static class C extends B {} + + static class D { + D(int x) { + this.x = x; + } + + private int x; + } + + static class E { + E(D d, E e1, E e2) { + this.d = d; + this.e1 = e1; + this.e2 = e2; + } + + private D d; + public E e1; + public E e2; + } + + static class F { + private int x; + + public F(int x) { + this.x = x; + } + } + + static class F2 extends F { + private int y; + + F2(int x, int y) { + super(x); + this.y = y; + } + } + + static class G { + private float x; + private float y; + + public G(float x, float y) { + this.x = x; + this.y = y; + } + } + + public static class H1 { + public boolean bool = true; + public byte b = (byte)242; + public short s = (short)24242; + public char c = (char)24242; + public int i = 1335836768; + public long l = 4242424242L; + public float f = 42.0f; + public double d = 42.0; + public H1() {} + } + + public static class H2 extends H1 { + public H1 h1 = new H1(); + public H2() {} + } + + static record R1() {} + static record R2() {} + static record R3(int x, int y) {} + static record R4(R4 x, R4 y) {} + + public static void testArbitraryClasses() { + A a1 = new A(); + A a2 = new A(); + B b1 = new B(); + B b2 = new B(); + C c1 = new C(); + C c2 = new C(); + + // Structurally equivalent. + Verify.checkEQ(a1, a1); + Verify.checkEQ(a1, a2); + Verify.checkEQ(b1, b1); + Verify.checkEQ(b1, b2); + Verify.checkEQ(c1, c1); + Verify.checkEQ(c1, c2); + + // Must fail because of different classes. + checkNE(a1, b1); + checkNE(b1, a1); + checkNE(a1, c1); + checkNE(c1, a1); + checkNE(b1, c1); + checkNE(c1, b1); + + // Objects with primitive values. + D d1 = new D(1); + D d2 = new D(1); + D d3 = new D(2); + Verify.checkEQ(d1, d1); + Verify.checkEQ(d1, d2); + Verify.checkEQ(d2, d1); + checkNE(d1, d3); + checkNE(d3, d1); + + // Object fields, including cycles. + E e1 = new E(d1, null, null); + E e2 = new E(d1, null, null); + E e3 = new E(d3, null, null); + E e4 = new E(d1, e1, null); + E e5 = new E(d1, e2, null); + E e6 = new E(d1, null, null); + e6.e1 = e6; + E e7 = new E(d1, null, null); + e7.e1 = e7; + E e8 = new E(d1, e1, e1); + E e9 = new E(d1, e1, e2); + + Verify.checkEQ(e1, e1); + Verify.checkEQ(e1, e2); + Verify.checkEQ(e2, e1); + checkNE(e1, e3); + checkNE(e3, e1); + Verify.checkEQ(e6, e6); + Verify.checkEQ(e6, e7); + Verify.checkEQ(e7, e6); + Verify.checkEQ(e8, e8); + checkNE(e8, e9); + checkNE(e9, e8); + + // Fields from superclass. + F2 f1 = new F2(1, 1); + F2 f2 = new F2(1, 1); + F2 f3 = new F2(2, 1); + F2 f4 = new F2(1, 2); + + Verify.checkEQ(f1, f1); + Verify.checkEQ(f1, f2); + Verify.checkEQ(f2, f1); + checkNE(f1, f3); + checkNE(f1, f4); + checkNE(f3, f1); + checkNE(f4, f1); + checkNE(f3, f4); + checkNE(f4, f3); + + G g1 = new G(1.0f, 1.0f); + G g2 = new G(1.0f, 1.0f); + G g3 = new G(Float.NaN, Float.NaN); + G g4 = new G(Float.NaN, Float.NaN); + + Verify.checkEQ(g1, g1); + Verify.checkEQ(g2, g1); + Verify.checkEQ(g1, g2); + Verify.checkEQ(g3, g3); + Verify.checkEQ(g3, g4); + Verify.checkEQ(g4, g3); + checkNE(g1, g3); + checkNE(g3, g1); + + // Nested class with primitive types, where the boxed types may not be cached, + // and so they would create different boxed objects. + Verify.checkEQ(new H2(), new H2()); + + // Records. + R1 r11 = new R1(); + R1 r12 = new R1(); + R2 r21 = new R2(); + R3 r31 = new R3(1, 1); + R3 r32 = new R3(1, 1); + R3 r33 = new R3(1, 2); + R3 r34 = new R3(2, 1); + + Verify.checkEQ(r11, r11); + Verify.checkEQ(r11, r12); + Verify.checkEQ(r12, r11); + checkNE(r11, r21); + Verify.checkEQ(r31, r31); + Verify.checkEQ(r31, r32); + Verify.checkEQ(r32, r31); + checkNE(r31, r33); + checkNE(r33, r31); + checkNE(r31, r34); + checkNE(r34, r31); + checkNE(r33, r34); + + R4 r41 = new R4(null, null); + R4 r42 = new R4(null, null); + R4 r43 = new R4(r41, null); + R4 r44 = new R4(r42, null); + R4 r45 = new R4(r43, r41); + R4 r46 = new R4(r44, r42); + R4 r47 = new R4(r44, r41); + + Verify.checkEQ(r45, r46); + Verify.checkEQ(r46, r45); + checkNE(r45, r47); + checkNE(r47, r45); + checkNE(r46, r47); + checkNE(r47, r46); + } + public static void checkNE(Object a, Object b) { try { Verify.checkEQ(a, b); - throw new RuntimeException("Should have thrown"); + throw new RuntimeException("Should have thrown: " + a + " vs " + b); + } catch (VerifyException e) {} + } + + public static void checkNEWithRawBits(Object a, Object b) { + try { + Verify.checkEQWithRawBits(a, b); + throw new RuntimeException("Should have thrown: " + a + " vs " + b); } catch (VerifyException e) {} } } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java index ee82d88f6fa..1c7a8c6172c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -245,6 +245,12 @@ public final class GarbageUtils { } } + private static Throwable ultimateCause(Throwable t) { + while (t.getCause() != null) { + t = t.getCause(); + } + return t; + } public static int eatMemory(ExecutionController stresser, GarbageProducer gp, long initialFactor, long minMemoryChunk, long factor, OOM_TYPE type) { try { @@ -253,6 +259,9 @@ public final class GarbageUtils { } catch (OutOfMemoryError e) { return numberOfOOMEs++; } catch (Throwable t) { + if (ultimateCause(t) instanceof OutOfMemoryError) { + return numberOfOOMEs++; + } throw new RuntimeException(t); } } diff --git a/test/jdk/ProblemList-AotJdk.txt b/test/jdk/ProblemList-AotJdk.txt index 9030ba60c78..b0737126534 100644 --- a/test/jdk/ProblemList-AotJdk.txt +++ b/test/jdk/ProblemList-AotJdk.txt @@ -1,3 +1,32 @@ +# +# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +############################################################################# +# +# List of quarantined tests for testing in AOT_JDK mode. +# +############################################################################# + java/math/BigInteger/largeMemory/DivisionOverflow.java 0000000 generic-all java/math/BigInteger/largeMemory/StringConstructorOverflow.java 0000000 generic-all @@ -9,3 +38,7 @@ java/lang/module/ModuleDescriptorHashCodeTest.java 0000000 generic- # The test case is incorrect. There's no guarantee that running a JVM with the following # parameters will always cause the class java/lang/invoke/MethodHandleStatics to be initialized java/lang/invoke/DumpMethodHandleInternals.java 0000000 generic-all + +# The test uses "--add-modules jdk.internal.le" during production. +# So the test is incompatible with AOT_JDK testing because because all runs must have consistent module options on the command line. +java/lang/IO/IO.java 0000000 generic-all diff --git a/test/jdk/ProblemList-StaticJdk.txt b/test/jdk/ProblemList-StaticJdk.txt new file mode 100644 index 00000000000..12ff29dbae9 --- /dev/null +++ b/test/jdk/ProblemList-StaticJdk.txt @@ -0,0 +1,14 @@ +# Require jarsigner +java/lang/System/LoggerFinder/SignedLoggerFinderTest/SignedLoggerFinderTest.java 8346719 generic-all +java/util/jar/JarFile/jarVerification/MultiProviderTest.java 8346719 generic-all + +# Require jar +java/lang/System/MacEncoding/TestFileEncoding.java 8346719 generic-all +java/util/ResourceBundle/modules/basic/BasicTest.java 8346719 generic-all + +# Require javac +java/util/ResourceBundle/modules/layer/LayerTest.java 8346719 generic-all +java/util/ResourceBundle/modules/unnamed/UnNamedTest.java 8346719 generic-all + +# Require jps +java/util/concurrent/locks/Lock/TimedAcquireLeak.java 8346719 generic-all diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 3edf4a9d18e..606ac735088 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -148,6 +148,8 @@ java/awt/EventQueue/6980209/bug6980209.java 8198615 macosx-all java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java 8024034 generic-all java/awt/grab/EmbeddedFrameTest1/EmbeddedFrameTest1.java 7080150 macosx-all java/awt/event/InputEvent/EventWhenTest/EventWhenTest.java 8168646 generic-all +java/awt/List/KeyEventsTest/KeyEventsTest.java 8201307 linux-all +java/awt/Paint/ListRepaint.java 8201307 linux-all java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java 8049405 macosx-all java/awt/Mixing/AWT_Mixing/OpaqueOverlapping.java 8294264 windows-x64 java/awt/Mixing/AWT_Mixing/OpaqueOverlappingChoice.java 8048171 generic-all diff --git a/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java b/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java index 7aa5b8c5132..c7ec6c6d969 100644 --- a/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java +++ b/test/jdk/java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,24 @@ * @test * @key headful * @summary To check proper WINDOW_EVENTS are triggered when Frame gains or losses the focus - * @author Jitender(jitender.singh@eng.sun.com) area=AWT - * @author yan * @library /lib/client * @build ExtendedRobot * @run main ActiveAWTWindowTest */ -import java.awt.*; -import java.awt.event.*; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; public class ActiveAWTWindowTest { @@ -47,12 +56,12 @@ public class ActiveAWTWindowTest { private boolean passed = true; private final int delay = 150; - public static void main(String[] args) { + public static void main(String[] args) throws Exception { ActiveAWTWindowTest test = new ActiveAWTWindowTest(); try { test.doTest(); } finally { - EventQueue.invokeLater(() -> { + EventQueue.invokeAndWait(() -> { if (test.frame != null) { test.frame.dispose(); } diff --git a/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java b/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java index 919ea72a494..b4c3e5e4f05 100644 --- a/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java +++ b/test/jdk/java/awt/List/KeyEventsTest/KeyEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,8 @@ */ import java.awt.BorderLayout; -import java.awt.EventQueue; -import java.awt.KeyboardFocusManager; import java.awt.Frame; +import java.awt.KeyboardFocusManager; import java.awt.List; import java.awt.Panel; import java.awt.Point; @@ -51,7 +50,7 @@ import jdk.test.lib.Platform; * @run main KeyEventsTest */ public class KeyEventsTest { - TestState currentState; + private volatile TestState currentState; final Object LOCK = new Object(); final int ACTION_TIMEOUT = 500; @@ -66,16 +65,14 @@ public class KeyEventsTest { r = new Robot(); KeyEventsTest app = new KeyEventsTest(); try { - EventQueue.invokeAndWait(app::initAndShowGui); + app.initAndShowGui(); r.waitForIdle(); r.delay(500); app.doTest(); } finally { - EventQueue.invokeAndWait(() -> { - if (app.keyFrame != null) { - app.keyFrame.dispose(); - } - }); + if (app.keyFrame != null) { + app.keyFrame.dispose(); + } } } @@ -184,52 +181,51 @@ public class KeyEventsTest { throw new RuntimeException("Test failed - list isn't focus owner."); } - EventQueue.invokeAndWait(() -> { - list.deselect(0); - list.deselect(1); - list.deselect(2); - list.deselect(3); - list.deselect(4); - list.deselect(5); - list.deselect(6); - list.deselect(7); - list.deselect(8); + list.deselect(0); + list.deselect(1); + list.deselect(2); + list.deselect(3); + list.deselect(4); + list.deselect(5); + list.deselect(6); + list.deselect(7); + list.deselect(8); - int selectIndex = 0; - int visibleIndex = 0; + int selectIndex = 0; + int visibleIndex = 0; - if (currentState.getScrollMoved()) { - if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || - currentState.getKeyID() == KeyEvent.VK_HOME) { - selectIndex = 8; - visibleIndex = 8; - } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || - currentState.getKeyID() == KeyEvent.VK_END) { - selectIndex = 0; - visibleIndex = 0; - } - } else { - if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || - currentState.getKeyID() == KeyEvent.VK_HOME) { - if (currentState.getSelectedMoved()) { - selectIndex = 1; - } else { - selectIndex = 0; - } - visibleIndex = 0; - } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || - currentState.getKeyID() == KeyEvent.VK_END) { - if (currentState.getSelectedMoved()) { - selectIndex = 7; - } else { - selectIndex = 8; - } - visibleIndex = 8; - } + if (currentState.getScrollMoved()) { + if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || + currentState.getKeyID() == KeyEvent.VK_HOME) { + selectIndex = 8; + visibleIndex = 8; + } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || + currentState.getKeyID() == KeyEvent.VK_END) { + selectIndex = 0; + visibleIndex = 0; } - list.select(selectIndex); - list.makeVisible(visibleIndex); - }); + } else { + if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || + currentState.getKeyID() == KeyEvent.VK_HOME) { + if (currentState.getSelectedMoved()) { + selectIndex = 1; + } else { + selectIndex = 0; + } + visibleIndex = 0; + } else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || + currentState.getKeyID() == KeyEvent.VK_END) { + if (currentState.getSelectedMoved()) { + selectIndex = 7; + } else { + selectIndex = 8; + } + visibleIndex = 8; + } + } + list.select(selectIndex); + list.makeVisible(visibleIndex); + r.delay(10); r.waitForIdle(); @@ -309,7 +305,7 @@ class TestState { private final boolean scrollMoved; private final int keyID; private final boolean template; - private boolean action; + private volatile boolean action; public TestState(boolean multiple, boolean selectedMoved, boolean scrollMoved, int keyID, boolean template){ this.multiple = multiple; diff --git a/test/jdk/java/awt/Paint/ButtonRepaint.java b/test/jdk/java/awt/Paint/ButtonRepaint.java index 77cd1a4649d..260e6c1097f 100644 --- a/test/jdk/java/awt/Paint/ButtonRepaint.java +++ b/test/jdk/java/awt/Paint/ButtonRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,28 +21,32 @@ * questions. */ - -import java.awt.*; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; /** * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class ButtonRepaint extends Button { public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - ButtonRepaint button = new ButtonRepaint(); - frame.add(button); - frame.setVisible(true); - sleep(); - button.test(); - frame.dispose(); + Frame frame = new Frame(); + try { + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + ButtonRepaint button = new ButtonRepaint(); + frame.add(button); + frame.setVisible(true); + sleep(); + button.test(); + } finally { + frame.dispose(); + } } } diff --git a/test/jdk/java/awt/Paint/CheckboxRepaint.java b/test/jdk/java/awt/Paint/CheckboxRepaint.java index 409c11f6f4d..5522497dc2a 100644 --- a/test/jdk/java/awt/Paint/CheckboxRepaint.java +++ b/test/jdk/java/awt/Paint/CheckboxRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,27 +21,32 @@ * questions. */ -import java.awt.*; +import java.awt.Checkbox; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; /** * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class CheckboxRepaint extends Checkbox { public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - CheckboxRepaint checkbox = new CheckboxRepaint(); - frame.add(checkbox); - frame.setVisible(true); - sleep(); - checkbox.test(); - frame.dispose(); + Frame frame = new Frame(); + try { + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + CheckboxRepaint checkbox = new CheckboxRepaint(); + frame.add(checkbox); + frame.setVisible(true); + sleep(); + checkbox.test(); + } finally { + frame.dispose(); + } } } diff --git a/test/jdk/java/awt/Paint/LabelRepaint.java b/test/jdk/java/awt/Paint/LabelRepaint.java index 7131f6d4f0f..56e096a2457 100644 --- a/test/jdk/java/awt/Paint/LabelRepaint.java +++ b/test/jdk/java/awt/Paint/LabelRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,21 +30,23 @@ import java.awt.Label; * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class LabelRepaint extends Label { public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { - final Frame frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - LabelRepaint label = new LabelRepaint(); - frame.add(label); - frame.setVisible(true); - sleep(); - label.test(); - frame.dispose(); + Frame frame = new Frame(); + try { + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + LabelRepaint label = new LabelRepaint(); + frame.add(label); + frame.setVisible(true); + sleep(); + label.test(); + } finally { + frame.dispose(); + } } } diff --git a/test/jdk/java/awt/Paint/ListRepaint.java b/test/jdk/java/awt/Paint/ListRepaint.java index 08cf06d63f6..a2d9c77a95b 100644 --- a/test/jdk/java/awt/Paint/ListRepaint.java +++ b/test/jdk/java/awt/Paint/ListRepaint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,26 +30,27 @@ import java.awt.List; * @test * @key headful * @bug 7090424 - * @author Sergey Bylokhov */ public final class ListRepaint extends List { - static ListRepaint listRepaint; - static Frame frame; - - public static void main(final String[] args) throws Exception { + public static void main(final String[] args) { for (int i = 0; i < 10; ++i) { + Frame frame = new Frame(); try { - EventQueue.invokeLater(ListRepaint::createAndShowGUI); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + ListRepaint list = new ListRepaint(); + list.add("1"); + list.add("2"); + list.add("3"); + list.add("4"); + list.select(0); + frame.add(list); + frame.setVisible(true); sleep(); - EventQueue.invokeAndWait(listRepaint::test); + list.test(); } finally { - EventQueue.invokeAndWait(() -> { - if (frame != null) { - frame.dispose(); - frame = null; - } - }); + frame.dispose(); } } } @@ -61,22 +62,6 @@ public final class ListRepaint extends List { } } - static void createAndShowGUI() { - frame = new Frame(); - frame.setSize(300, 300); - frame.setLocationRelativeTo(null); - - listRepaint = new ListRepaint(); - listRepaint.add("1"); - listRepaint.add("2"); - listRepaint.add("3"); - listRepaint.add("4"); - listRepaint.select(0); - - frame.add(listRepaint); - frame.setVisible(true); - } - @Override public void paint(final Graphics g) { super.paint(g); diff --git a/test/jdk/java/awt/ScrollPane/bug8077409Test.java b/test/jdk/java/awt/ScrollPane/bug8077409Test.java index 65d8da66ac9..c4a55307c9c 100644 --- a/test/jdk/java/awt/ScrollPane/bug8077409Test.java +++ b/test/jdk/java/awt/ScrollPane/bug8077409Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,92 +26,75 @@ * @key headful * @bug 8077409 * @summary Drawing deviates when validate() is invoked on java.awt.ScrollPane - * @author mikhail.cherkasov@oracle.com * @run main bug8077409Test */ - -import java.awt.*; -import java.awt.event.*; +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.ScrollPane; +import java.awt.event.KeyEvent; public class bug8077409Test extends Frame { - ScrollPane pane; - MyCanvas myCanvas; + ScrollPane pane; + MyCanvas myCanvas; - class MyCanvas extends Canvas { - public Dimension getPreferredSize() { - return new Dimension(400, 800); + public bug8077409Test() { + super(); + setLayout(new BorderLayout()); + pane = new ScrollPane(); + myCanvas = new MyCanvas(); + pane.add(myCanvas); + add(pane, BorderLayout.CENTER); + setSize(320, 480); } - public void paint(Graphics g) { - g.setColor(Color.BLACK); - g.drawLine(0, 0, 399, 0); - g.setColor(Color.RED); - g.drawLine(0, 1, 399, 1); - g.setColor(Color.BLUE); - g.drawLine(0, 2, 399, 2); - g.setColor(Color.GREEN); - g.drawLine(0, 3, 399, 3); - } - - } - - public bug8077409Test() { - super(); - setLayout(new BorderLayout()); - pane = new ScrollPane(); - - myCanvas = new MyCanvas(); - pane.add(myCanvas); - - add(pane, BorderLayout.CENTER); - setSize(320, 480); - - } - - @Override - protected void processKeyEvent(KeyEvent e) { - super.processKeyEvent(e); - - } - - public static void main(String[] args) throws AWTException, InterruptedException { - final bug8077409Test obj = new bug8077409Test(); - obj.setVisible(true); - Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { - @Override - public void eventDispatched(AWTEvent e) { - KeyEvent keyEvent = (KeyEvent) e; - if(keyEvent.getID() == KeyEvent.KEY_RELEASED) { - if (keyEvent.getKeyCode() == KeyEvent.VK_1) { - System.out.println(obj.pane.toString()); - System.out.println("obj.myCanvas.pos: " + obj.myCanvas.getBounds()); - System.out.println(obj.myCanvas.toString()); - } else if (keyEvent.getKeyCode() == KeyEvent.VK_2) { - obj.repaint(); - } else if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN) { - Point scrollPosition = obj.pane.getScrollPosition(); - scrollPosition.translate(0, 1); - obj.pane.setScrollPosition(scrollPosition); - } else if (keyEvent.getKeyCode() == KeyEvent.VK_UP) { - Point scrollPosition = obj.pane.getScrollPosition(); - scrollPosition.translate(0, -1); - obj.pane.setScrollPosition(scrollPosition); - } else if (keyEvent.getKeyCode() == KeyEvent.VK_SPACE) { - obj.pane.validate(); + public static void main(String[] args) throws AWTException, InterruptedException { + final bug8077409Test obj = new bug8077409Test(); + try { + obj.setLocationRelativeTo(null); + obj.setVisible(true); + Point scrollPosition = obj.pane.getScrollPosition(); + scrollPosition.translate(0, 1); + obj.pane.setScrollPosition(scrollPosition); + int y = obj.pane.getComponent(0).getLocation().y; + obj.pane.validate(); + if (y != obj.pane.getComponent(0).getLocation().y) { + throw new RuntimeException("Wrong position of component in ScrollPane"); } - } + } finally { + obj.dispose(); } - }, AWTEvent.KEY_EVENT_MASK); - Point scrollPosition = obj.pane.getScrollPosition(); - scrollPosition.translate(0, 1); - obj.pane.setScrollPosition(scrollPosition); + } - int y = obj.pane.getComponent(0).getLocation().y; - obj.pane.validate(); - if(y != obj.pane.getComponent(0).getLocation().y){ - throw new RuntimeException("Wrong position of component in ScrollPane"); - } - } + @Override + protected void processKeyEvent(KeyEvent e) { + super.processKeyEvent(e); + } + + class MyCanvas extends Canvas { + @Override + public Dimension getPreferredSize() { + return new Dimension(400, 800); + } + + @Override + public void paint(Graphics g) { + g.setColor(Color.BLACK); + g.drawLine(0, 0, 399, 0); + g.setColor(Color.RED); + g.drawLine(0, 1, 399, 1); + g.setColor(Color.BLUE); + g.drawLine(0, 2, 399, 2); + g.setColor(Color.GREEN); + g.drawLine(0, 3, 399, 3); + } + + } } diff --git a/test/jdk/java/awt/geom/Path2D/GetBounds2DPrecisionTest.java b/test/jdk/java/awt/geom/Path2D/GetBounds2DPrecisionTest.java index 71b22399cb1..f97c98a6b25 100644 --- a/test/jdk/java/awt/geom/Path2D/GetBounds2DPrecisionTest.java +++ b/test/jdk/java/awt/geom/Path2D/GetBounds2DPrecisionTest.java @@ -189,7 +189,7 @@ public class GetBounds2DPrecisionTest { int DIGIT_COUNT = 40; String str = decimal.toPlainString(); if (str.length() >= DIGIT_COUNT) { - str = str.substring(0,DIGIT_COUNT-1)+"…"; + str = str.substring(0,DIGIT_COUNT-1)+"..."; } while(str.length() < DIGIT_COUNT) { str = str + " "; @@ -260,4 +260,4 @@ public class GetBounds2DPrecisionTest { } return roots; } -} \ No newline at end of file +} diff --git a/test/jdk/java/awt/im/PinyinIMCapsTest.java b/test/jdk/java/awt/im/PinyinIMCapsTest.java index 4dca27d9922..707f56c6651 100644 --- a/test/jdk/java/awt/im/PinyinIMCapsTest.java +++ b/test/jdk/java/awt/im/PinyinIMCapsTest.java @@ -47,7 +47,7 @@ public class PinyinIMCapsTest { Test settings: Go to "System Preferences -> Keyboard -> Input Sources" and - add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. + add "Pinyin - Traditional" or "Pinyin - Simplified" IM from Chinese language group. Set current IM to "Pinyin". 1. Set focus to the text field shown below and press Caps Lock key on the keyboard. @@ -79,4 +79,3 @@ public class PinyinIMCapsTest { return panel; } } - diff --git a/test/jdk/java/awt/im/PinyinIMCommaTest.java b/test/jdk/java/awt/im/PinyinIMCommaTest.java index 273403e7d68..4fccd47ea7c 100644 --- a/test/jdk/java/awt/im/PinyinIMCommaTest.java +++ b/test/jdk/java/awt/im/PinyinIMCommaTest.java @@ -44,7 +44,7 @@ public class PinyinIMCommaTest { Test settings: Go to "System Preferences -> Keyboard -> Input Sources" and - add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. + add "Pinyin - Traditional" or "Pinyin - Simplified" IM from Chinese language group. Set current IM to "Pinyin". 1. Set focus to the text field below and press "comma" character diff --git a/test/jdk/java/awt/im/PinyinIMFullstopTest.java b/test/jdk/java/awt/im/PinyinIMFullstopTest.java index 82d66f950f9..2c8da202544 100644 --- a/test/jdk/java/awt/im/PinyinIMFullstopTest.java +++ b/test/jdk/java/awt/im/PinyinIMFullstopTest.java @@ -43,7 +43,7 @@ public class PinyinIMFullstopTest { Test settings: Go to "System Preferences -> Keyboard -> Input Sources" and - add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. + add "Pinyin - Traditional" or "Pinyin - Simplified" IM from Chinese language group. Set current IM to "Pinyin". 1. Set focus to the text area below and press "dot" character diff --git a/test/jdk/java/foreign/TestBufferStackStress2.java b/test/jdk/java/foreign/TestBufferStackStress2.java index 4b02f4691fb..8ec5d512ff6 100644 --- a/test/jdk/java/foreign/TestBufferStackStress2.java +++ b/test/jdk/java/foreign/TestBufferStackStress2.java @@ -29,6 +29,7 @@ */ import jdk.internal.foreign.BufferStack; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import java.io.FileDescriptor; @@ -62,6 +63,14 @@ final class TestBufferStackStress2 { */ @Test void movingVirtualThreadWithGc() throws InterruptedException { + + // If the VM is configured such that the main thread is virtual, + // this stress test will not work as the main thread is always alive causing + // us to wait forever for contraction. + // Hence, we will skip this test if the main thread is virtual. + Assumptions.assumeFalse(Thread.currentThread().isVirtual(), + "Skipped because the main thread is a virtual thread"); + final long begin = System.nanoTime(); var pool = BufferStack.of(POOL_SIZE, 1); diff --git a/test/jdk/java/lang/ProcessBuilder/Basic.java b/test/jdk/java/lang/ProcessBuilder/Basic.java index 68b3ab56b6d..b77c292766d 100644 --- a/test/jdk/java/lang/ProcessBuilder/Basic.java +++ b/test/jdk/java/lang/ProcessBuilder/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,8 +35,8 @@ * @requires !vm.musl * @requires vm.flagless * @library /test/lib - * @run main/othervm/native/timeout=300 Basic - * @run main/othervm/native/timeout=300 -Djdk.lang.Process.launchMechanism=fork Basic + * @run main/othervm/native/timeout=360 Basic + * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=fork Basic * @author Martin Buchholz */ @@ -55,7 +55,6 @@ import java.lang.ProcessHandle; import static java.lang.ProcessBuilder.Redirect.*; import java.io.*; -import java.lang.reflect.Field; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; @@ -207,7 +206,7 @@ public class Basic { private static final Runtime runtime = Runtime.getRuntime(); - private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"}; + private static final String[] winEnvCommand = {"cmd.exe", "/d", "/c", "set"}; private static String winEnvFilter(String env) { return env.replaceAll("\r", "") @@ -1841,7 +1840,9 @@ public class Basic { // Test Runtime.exec(...envp...) with envstrings without any `=' //---------------------------------------------------------------- try { - String[] cmdp = {"echo"}; + // In Windows CMD (`cmd.exe`), `echo/` outputs a newline (i.e., an empty line). + // Wrapping it with `cmd.exe /c` ensures compatibility in both native Windows and Cygwin environments. + String[] cmdp = Windows.is() ? new String[]{"cmd.exe", "/c", "echo/"} : new String[]{"echo"}; String[] envp = {"Hello", "World"}; // Yuck! Process p = Runtime.getRuntime().exec(cmdp, envp); equal(commandOutput(p), "\n"); diff --git a/test/jdk/java/lang/String/concat/HugeConcatTest.java b/test/jdk/java/lang/String/concat/HugeConcatTest.java new file mode 100644 index 00000000000..e0465be9178 --- /dev/null +++ b/test/jdk/java/lang/String/concat/HugeConcatTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8356152 + * @summary Check that huge concatenations throw OutOfMemoryError + * @requires os.maxMemory > 8G + * @run junit/othervm -Xmx8G -XX:+CompactStrings -Dcompact=true HugeConcatTest + * @run junit/othervm -Xmx8G -XX:-CompactStrings -Dcompact=false HugeConcatTest + */ + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertThrows; + +public class HugeConcatTest { + + private static final int HUGE_LENGTH_UTF16 = Integer.MAX_VALUE / 2 - 2; + + private static String hugeLatin1; + private static final String hugeUTF16 = "\u20AC".repeat(HUGE_LENGTH_UTF16); + + @BeforeAll + public static void initHugeLatin1() { + String compact = System.getProperty("compact", "true"); + int hugeLatin1Length = Boolean.parseBoolean(compact) + ? Integer.MAX_VALUE - 2 + : HUGE_LENGTH_UTF16; + hugeLatin1 = "a".repeat(hugeLatin1Length); + } + + @Test + public void testConcat_Latin1_Latin1() { + assertThrows(OutOfMemoryError.class, () -> { var s = hugeLatin1 + hugeLatin1; }); + assertThrows(OutOfMemoryError.class, () -> hugeLatin1.concat(hugeLatin1)); + } + + @Test + public void testConcat_Latin1_UTF16() { + assertThrows(OutOfMemoryError.class, () -> { var s = hugeLatin1 + hugeUTF16; }); + assertThrows(OutOfMemoryError.class, () -> hugeLatin1.concat(hugeUTF16)); + } + + @Test + public void testConcat_UTF16_Latin1() { + assertThrows(OutOfMemoryError.class, () -> { var s = hugeUTF16 + hugeLatin1; }); + assertThrows(OutOfMemoryError.class, () -> hugeUTF16.concat(hugeLatin1)); + } + + @Test + public void testConcat_UTF16_UTF16() { + assertThrows(OutOfMemoryError.class, () -> { var s = hugeUTF16 + hugeUTF16; }); + assertThrows(OutOfMemoryError.class, () -> hugeUTF16.concat(hugeUTF16)); + } + +} diff --git a/test/jdk/java/lang/ref/FinalizerHistogramTest.java b/test/jdk/java/lang/ref/FinalizerHistogramTest.java index 66cf76ea7f2..192c229c7b5 100644 --- a/test/jdk/java/lang/ref/FinalizerHistogramTest.java +++ b/test/jdk/java/lang/ref/FinalizerHistogramTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,9 @@ * questions. */ -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; +import jdk.test.whitebox.WhiteBox; + +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.lang.reflect.Method; @@ -32,37 +33,49 @@ import java.lang.reflect.Field; * @test * @summary Unit test for FinalizerHistogram * @modules java.base/java.lang.ref:open - * @run main FinalizerHistogramTest + * @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 + * FinalizerHistogramTest */ public class FinalizerHistogramTest { static ReentrantLock lock = new ReentrantLock(); - static volatile int wasInitialized = 0; - static volatile int wasTrapped = 0; - static final int objectsCount = 1000; + static final AtomicInteger initializedCount = new AtomicInteger(0); + static final int OBJECTS_COUNT = 1000; + + static WhiteBox wb; static class MyObject { public MyObject() { // Make sure object allocation/deallocation is not optimized out - wasInitialized += 1; + initializedCount.incrementAndGet(); } protected void finalize() { // Trap the object in a finalization queue - wasTrapped += 1; lock.lock(); } } - public static void main(String[] argvs) { + public static void main(String[] argvs) throws InterruptedException { try { lock.lock(); - for(int i = 0; i < objectsCount; ++i) { + for(int i = 0; i < OBJECTS_COUNT; ++i) { new MyObject(); } - System.out.println("Objects intialized: " + objectsCount); - System.gc(); - while(wasTrapped < 1); + System.out.println("Objects intialized: " + initializedCount.get()); + wb = WhiteBox.getWhiteBox(); + wb.fullGC(); + boolean refProResult; + do { + refProResult = wb.waitForReferenceProcessing(); + System.out.println("waitForReferenceProcessing returned: " + refProResult); + } while (refProResult); Class klass = Class.forName("java.lang.ref.FinalizerHistogram"); diff --git a/test/jdk/java/net/Inet4Address/PingThis.java b/test/jdk/java/net/Inet4Address/PingThis.java index 885b60341f3..476ff0ad8a6 100644 --- a/test/jdk/java/net/Inet4Address/PingThis.java +++ b/test/jdk/java/net/Inet4Address/PingThis.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,27 +27,20 @@ /* @test * @bug 7163874 8133015 + * @summary InetAddress.isReachable is returning false for InetAdress 0.0.0.0 and ::0 + * @requires os.family != "windows" * @library /test/lib - * @summary InetAddress.isReachable is returning false - * for InetAdress 0.0.0.0 and ::0 * @run main PingThis * @run main/othervm -Djava.net.preferIPv4Stack=true PingThis */ -import java.net.Inet6Address; import java.net.InetAddress; -import java.net.NetworkInterface; import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; import java.util.List; import jdk.test.lib.net.IPSupport; public class PingThis { public static void main(String args[]) throws Exception { - if (System.getProperty("os.name").startsWith("Windows")) { - return; - } IPSupport.throwSkippedExceptionIfNonOperational(); List addrs = new ArrayList(); diff --git a/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java b/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java index 3e1bc8b55ea..36b6f12413d 100644 --- a/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java +++ b/test/jdk/java/net/MulticastSocket/NoLoopbackPackets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,9 @@ /* * @test * @bug 4742177 - * @library /test/lib * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code + * @requires os.family != "windows" + * @library /test/lib */ import java.util.*; import java.net.*; @@ -33,20 +34,10 @@ import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.net.IPSupport; public class NoLoopbackPackets { - private static String osname; - - static boolean isWindows() { - if (osname == null) - osname = System.getProperty("os.name"); - return osname.contains("Windows"); - } private static final String MESSAGE = "hello world (" + System.nanoTime() + ")"; + public static void main(String[] args) throws Exception { - if (isWindows()) { - System.out.println("The test only run on non-Windows OS. Bye."); - return; - } MulticastSocket msock = null; List failedGroups = new ArrayList(); diff --git a/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java b/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java index 4ac2dd8f16f..8fc23ea96aa 100644 --- a/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java +++ b/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,9 @@ /* * @test * @bug 8215294 - * @requires os.family == "linux" + * @requires os.family == "linux" & !(os.version ~= "3\\.10\\.0.*") + * @comment This test should only be run on Linux. + * The behavior under test is known NOT to work on Linux 3.10.0* kernels. * @library /test/lib * @build jdk.test.lib.NetworkConfiguration * PromiscuousIPv6 @@ -148,18 +150,6 @@ public class PromiscuousIPv6 { } public static void main(String args[]) throws IOException { - String os = System.getProperty("os.name"); - - if (!os.equals("Linux")) { - throw new SkippedException("This test should be run only on Linux"); - } else { - String osVersion = System.getProperty("os.version"); - String prefix = "3.10.0"; - if (osVersion.startsWith(prefix)) { - throw new SkippedException( - String.format("The behavior under test is known NOT to work on '%s' kernels", prefix)); - } - } NetworkConfiguration.printSystemConfiguration(System.out); List nifs = NetworkConfiguration.probe() diff --git a/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java b/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java index 8a752ae2eee..e56a4598da7 100644 --- a/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java +++ b/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,9 +24,10 @@ /* * @test * @bug 4742177 8241786 + * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code + * @requires os.family != "windows" * @library /test/lib * @run main/othervm SetOutgoingIf - * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code */ import java.io.IOException; import java.net.*; @@ -36,7 +37,7 @@ import jdk.test.lib.NetworkConfiguration; public class SetOutgoingIf implements AutoCloseable { - private static String osname; + private final MulticastSocket SOCKET; private final int PORT; private final Map sendersMap = new ConcurrentHashMap<>(); @@ -49,12 +50,6 @@ public class SetOutgoingIf implements AutoCloseable { } } - static boolean isWindows() { - if (osname == null) - osname = System.getProperty("os.name"); - return osname.contains("Windows"); - } - static boolean isMacOS() { return System.getProperty("os.name").contains("OS X"); } @@ -82,10 +77,6 @@ public class SetOutgoingIf implements AutoCloseable { } public void run() throws Exception { - if (isWindows()) { - System.out.println("The test only run on non-Windows OS. Bye."); - return; - } if (!hasIPv6()) { System.out.println("No IPv6 available. Bye."); diff --git a/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java b/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java index 391c5fb0094..f50a886140a 100644 --- a/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java +++ b/test/jdk/java/net/ServerSocket/AnotherSelectFdsLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ * @test * @bug 8035897 * @summary FD_SETSIZE should be set on macosx + * @requires os.family == "mac" * @run main/othervm AnotherSelectFdsLimit 1023 * @run main/othervm AnotherSelectFdsLimit 1024 * @run main/othervm AnotherSelectFdsLimit 1025 @@ -41,10 +42,6 @@ public class AnotherSelectFdsLimit { static final int DEFAULT_FDS_TO_USE = 1600; public static void main(String [] args) throws Exception { - if (!System.getProperty("os.name").contains("OS X")) { - System.out.println("Test only run on MAC. Exiting."); - return; - } int fdsToUse = DEFAULT_FDS_TO_USE; if (args.length == 1) diff --git a/test/jdk/java/net/ServerSocket/SelectFdsLimit.java b/test/jdk/java/net/ServerSocket/SelectFdsLimit.java index 2ecb1a28e1e..5ee45bc2299 100644 --- a/test/jdk/java/net/ServerSocket/SelectFdsLimit.java +++ b/test/jdk/java/net/ServerSocket/SelectFdsLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @summary The total number of file descriptors is limited to * 1024(FDSET_SIZE) on MacOSX (the size of fd array passed to select() * call in java.net classes is limited to this value). + * @requires os.family == "mac" * @run main/othervm SelectFdsLimit * @author aleksej.efimov@oracle.com */ @@ -35,7 +36,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.net.ServerSocket; import java.net.SocketTimeoutException; @@ -72,12 +72,6 @@ public class SelectFdsLimit { public static void main(String [] args) throws IOException, FileNotFoundException { - //The bug 8021820 is a Mac specific and because of that test will pass on all - //other platforms - if (!System.getProperty("os.name").contains("OS X")) { - return; - } - //Create test directory with test files prepareTestEnv(); diff --git a/test/jdk/java/util/Currency/ISO4217-list-one.txt b/test/jdk/java/util/Currency/ISO4217-list-one.txt index 1912b5cc7db..aa8c2725899 100644 --- a/test/jdk/java/util/Currency/ISO4217-list-one.txt +++ b/test/jdk/java/util/Currency/ISO4217-list-one.txt @@ -1,12 +1,12 @@ # # -# Amendments up until ISO 4217 AMENDMENT NUMBER 177 -# (As of 20 June 2024) +# Amendments up until ISO 4217 AMENDMENT NUMBER 179 +# (As of 02 May 2025) # # Version FILEVERSION=3 -DATAVERSION=177 +DATAVERSION=179 # ISO 4217 currency data AF AFN 971 2 diff --git a/test/jdk/java/util/Currency/ValidateISO4217.java b/test/jdk/java/util/Currency/ValidateISO4217.java index 34635f5c53a..6996045fc3a 100644 --- a/test/jdk/java/util/Currency/ValidateISO4217.java +++ b/test/jdk/java/util/Currency/ValidateISO4217.java @@ -26,7 +26,7 @@ * @bug 4691089 4819436 4942982 5104960 6544471 6627549 7066203 7195759 * 8039317 8074350 8074351 8145952 8187946 8193552 8202026 8204269 * 8208746 8209775 8264792 8274658 8283277 8296239 8321480 8334653 - * 8354343 8354344 + * 8354343 8354344 8356096 * @summary Validate ISO 4217 data for Currency class. * @modules java.base/java.util:open * jdk.localedata @@ -89,7 +89,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** * This class tests the latest ISO 4217 data and Java's currency data which is * based on ISO 4217. The golden-data file, 'ISO4217-list-one.txt', based on the - * “List one: Currency, fund and precious metal codes” has the following + * "List one: Currency, fund and precious metal codes" has the following * format: \t\t\t[\t\t\t\t] * The Cutover Date is given in SimpleDateFormat's 'yyyy-MM-dd-HH-mm-ss' format in the GMT time zone. */ @@ -119,7 +119,7 @@ public class ValidateISO4217 { "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" + "DEM-EEK-ESP-FIM-FRF-GHC-GRD-GWP-HRK-IEP-ITL-LTL-LUF-LVL-MGF-MRO-MTL-MXV-MZM-NLG-" + "PTE-ROL-RUR-SDD-SIT-SLL-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-VED-" - + "XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" + + "XAD-XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" + "YUM-ZMK-ZWD-ZWL-ZWN-ZWR"; private static final String[][] extraCodes = { /* Defined in ISO 4217 list, but don't have code and minor unit info. */ diff --git a/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java b/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java index 5d5355a9755..9d44dd79c02 100644 --- a/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java +++ b/test/jdk/java/util/PluggableLocale/LocaleNameProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4052440 8000273 8062588 8210406 8174269 + * @bug 4052440 8000273 8062588 8210406 8174269 8356040 * @summary LocaleNameProvider tests * @library providersrc/foobarutils * providersrc/barprovider @@ -31,97 +31,113 @@ * java.base/sun.util.resources * @build com.foobar.Utils * com.bar.* - * @run main/othervm -Djava.locale.providers=CLDR,SPI LocaleNameProviderTest + * @run junit/othervm -Djava.locale.providers=CLDR,SPI LocaleNameProviderTest */ +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; +import java.util.ResourceBundle; import com.bar.LocaleNameProviderImpl; +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 sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.resources.OpenListResourceBundle; public class LocaleNameProviderTest extends ProviderTest { - public static void main(String[] s) { - new LocaleNameProviderTest(); + private static final LocaleNameProviderImpl LNP = new LocaleNameProviderImpl(); + + /* + * This is not an exhaustive test. Such a test would require iterating (1000x1000)+ + * inputs. Instead, we check against Japanese lang locales which guarantees + * we will run into cases where the CLDR is not the preferred provider as the + * SPI has defined variants of the Japanese locale (E.g. osaka). + * See LocaleNameProviderImpl and LocaleNames ResourceBundle. + */ + @ParameterizedTest + @MethodSource + void checkAvailLocValidityTest(Locale target, Locale test, ResourceBundle rb, + boolean jreSupports, boolean spiSupports) { + // codes + String lang = test.getLanguage(); + String ctry = test.getCountry(); + String vrnt = test.getVariant(); + + // the localized name + String langresult = test.getDisplayLanguage(target); + String ctryresult = test.getDisplayCountry(target); + String vrntresult = test.getDisplayVariant(target); + + // provider's name (if any) + String providerslang = null; + String providersctry = null; + String providersvrnt = null; + if (spiSupports) { + providerslang = LNP.getDisplayLanguage(lang, target); + providersctry = LNP.getDisplayCountry(ctry, target); + providersvrnt = LNP.getDisplayVariant(vrnt, target); + } + + // JRE's name + String jreslang = null; + String jresctry = null; + String jresvrnt = null; + if (!lang.isEmpty()) { + try { + jreslang = rb.getString(lang); + } catch (MissingResourceException mre) {} + } + if (!ctry.isEmpty()) { + try { + jresctry = rb.getString(ctry); + } catch (MissingResourceException mre) {} + } + if (!vrnt.isEmpty()) { + try { + jresvrnt = rb.getString("%%"+vrnt); + } catch (MissingResourceException mre) {} + } + + checkValidity(target, jreslang, providerslang, langresult, + jreSupports && jreslang != null); + checkValidity(target, jresctry, providersctry, ctryresult, + jreSupports && jresctry != null); + checkValidity(target, jresvrnt, providersvrnt, vrntresult, + jreSupports && jresvrnt != null); } - LocaleNameProviderTest() { - checkAvailLocValidityTest(); - variantFallbackTest(); - } + public static List checkAvailLocValidityTest() { + var args = new ArrayList(); + Locale[] availloc = Locale.availableLocales() + .filter(l -> l.getLanguage().equals("ja")) + .toArray(Locale[]::new); + List jreimplloc = Arrays.stream(LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.CLDR) + .getLocaleNameProvider().getAvailableLocales()) + .filter(l -> l.getLanguage().equals("ja")) + .toList(); + List providerloc = Arrays.asList(LNP.getAvailableLocales()); - void checkAvailLocValidityTest() { - LocaleNameProviderImpl lnp = new LocaleNameProviderImpl(); - Locale[] availloc = Locale.getAvailableLocales(); - Locale[] testloc = availloc.clone(); - List jreimplloc = Arrays.asList(LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.CLDR).getLocaleNameProvider().getAvailableLocales()); - List providerloc = Arrays.asList(lnp.getAvailableLocales()); - - for (Locale target: availloc) { + for (Locale target : availloc) { // pure JRE implementation - OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.CLDR)).getLocaleData().getLocaleNames(target); + OpenListResourceBundle rb = ((ResourceBundleBasedAdapter) LocaleProviderAdapter.forType(LocaleProviderAdapter.Type.CLDR)).getLocaleData().getLocaleNames(target); boolean jreSupportsTarget = jreimplloc.contains(target); - - for (Locale test: testloc) { - // codes - String lang = test.getLanguage(); - String ctry = test.getCountry(); - String vrnt = test.getVariant(); - - // the localized name - String langresult = test.getDisplayLanguage(target); - String ctryresult = test.getDisplayCountry(target); - String vrntresult = test.getDisplayVariant(target); - - // provider's name (if any) - String providerslang = null; - String providersctry = null; - String providersvrnt = null; - if (providerloc.contains(target)) { - providerslang = lnp.getDisplayLanguage(lang, target); - providersctry = lnp.getDisplayCountry(ctry, target); - providersvrnt = lnp.getDisplayVariant(vrnt, target); - } - - // JRE's name - String jreslang = null; - String jresctry = null; - String jresvrnt = null; - if (!lang.equals("")) { - try { - jreslang = rb.getString(lang); - } catch (MissingResourceException mre) {} - } - if (!ctry.equals("")) { - try { - jresctry = rb.getString(ctry); - } catch (MissingResourceException mre) {} - } - if (!vrnt.equals("")) { - try { - jresvrnt = rb.getString("%%"+vrnt); - } catch (MissingResourceException mre) {} - } - - System.out.print("For key: "+lang+" "); - checkValidity(target, jreslang, providerslang, langresult, - jreSupportsTarget && jreslang != null); - System.out.print("For key: "+ctry+" "); - checkValidity(target, jresctry, providersctry, ctryresult, - jreSupportsTarget && jresctry != null); - System.out.print("For key: "+vrnt+" "); - checkValidity(target, jresvrnt, providersvrnt, vrntresult, - jreSupportsTarget && jresvrnt != null); + boolean providerSupportsTarget = providerloc.contains(target); + for (Locale test : availloc) { + args.add(Arguments.of(target, test, rb, jreSupportsTarget, providerSupportsTarget)); } } + return args; } + @Test void variantFallbackTest() { Locale YY = Locale.of("yy", "YY", "YYYY"); Locale YY_suffix = Locale.of("yy", "YY", "YYYY_suffix"); diff --git a/test/jdk/java/util/stream/GathererTest.java b/test/jdk/java/util/stream/GathererTest.java index ca22fc21236..d85f466496f 100644 --- a/test/jdk/java/util/stream/GathererTest.java +++ b/test/jdk/java/util/stream/GathererTest.java @@ -106,11 +106,11 @@ public class GathererTest { } final Gatherer addOne = Gatherer.of( - Gatherer.Integrator.ofGreedy((vöid, element, downstream) -> downstream.push(element + 1)) + Gatherer.Integrator.ofGreedy((void_state, element, downstream) -> downstream.push(element + 1)) ); final Gatherer timesTwo = Gatherer.of( - Gatherer.Integrator.ofGreedy((vöid, element, downstream) -> downstream.push(element * 2)) + Gatherer.Integrator.ofGreedy((void_state, element, downstream) -> downstream.push(element * 2)) ); @ParameterizedTest diff --git a/test/jdk/sun/security/util/Debug/MultiOptions.java b/test/jdk/sun/security/util/Debug/MultiOptions.java deleted file mode 100644 index 82987f4348a..00000000000 --- a/test/jdk/sun/security/util/Debug/MultiOptions.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved. - * 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 1.1, 06/11/07 - * @author Xuelei Fan - * @bug 6466247 - * @summary java.security.debug permission= and codebase= - * options do not work - * @modules java.base/sun.security.util - * @run main/othervm -Djava.security.debug="stacknothing--=-30logincontextacCess:stack-domain,combiner;access:fAilure-jarpermission=sun.dummy.DummyPermission;peRmiSsion=sun.Dummy.DummyPermission2=permission=sun.dummy.DummyPermission3:codEbAse=/dir1/DIR2/Dir3/File.java,codebase=http://www.sun.com/search?q=SunMicro,codEbAse=/dir1/DIR2/Dir3/File.java;coDebase=www.sun.com;codebase=file:///C:/temp/foo%20more/a.txt" MultiOptions - */ -import sun.security.util.Debug; - -public class MultiOptions -{ - public static void main(String args[]) throws Exception { - - if (!Debug.isOn("access") || - !Debug.isOn("stack") || - !Debug.isOn("logincontext") || - !Debug.isOn("domain") || - !Debug.isOn("combiner") || - !Debug.isOn("failure") || - !Debug.isOn("jar") || - !Debug.isOn("permission=sun.dummy.DummyPermission") || - Debug.isOn("permission=sun.dummy.dummypermission") || - !Debug.isOn("permission=sun.Dummy.DummyPermission2") || - !Debug.isOn("permission=sun.dummy.DummyPermission3") || - !Debug.isOn("codebase=/dir1/DIR2/Dir3/File.java") || - Debug.isOn("codebase=/dir1/dir2/dir3/file.java") || - !Debug.isOn("codebase=www.sun.com") || - !Debug.isOn("codebase=file:///C:/temp/foo%20more/a.txt") || - !Debug.isOn("codebase=http://www.sun.com/search?q=SunMicro") ) { - throw new Exception("sun.security.Debug failed to parse options"); - } - } - -} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java index 88ce7169da7..7ab3b824aa4 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ApplicationLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ import java.util.Optional; public record ApplicationLayout(Path launchersDirectory, Path appDirectory, Path runtimeDirectory, Path runtimeHomeDirectory, Path appModsDirectory, - Path destktopIntegrationDirectory, Path contentDirectory) { + Path desktopIntegrationDirectory, Path contentDirectory, Path libapplauncher) { public ApplicationLayout resolveAt(Path root) { return new ApplicationLayout( @@ -38,8 +38,9 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory, resolve(root, runtimeDirectory), resolve(root, runtimeHomeDirectory), resolve(root, appModsDirectory), - resolve(root, destktopIntegrationDirectory), - resolve(root, contentDirectory)); + resolve(root, desktopIntegrationDirectory), + resolve(root, contentDirectory), + resolve(root, libapplauncher)); } public static ApplicationLayout linuxAppImage() { @@ -50,7 +51,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory, Path.of("lib/runtime"), Path.of("lib/app/mods"), Path.of("lib"), - Path.of("lib") + Path.of("lib"), + Path.of("lib/libapplauncher.so") ); } @@ -62,7 +64,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory, Path.of("runtime"), Path.of("app/mods"), Path.of(""), - Path.of("") + Path.of(""), + null ); } @@ -74,7 +77,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory, Path.of("Contents/runtime/Contents/Home"), Path.of("Contents/app/mods"), Path.of("Contents/Resources"), - Path.of("Contents") + Path.of("Contents"), + null ); } @@ -102,6 +106,7 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory, null, null, null, + null, null ); } @@ -116,7 +121,8 @@ public record ApplicationLayout(Path launchersDirectory, Path appDirectory, lib.resolve("runtime"), lib.resolve("app/mods"), lib, - lib + lib, + lib.resolve("lib/libapplauncher.so") ); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 722515887b8..8c5a653f5a0 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1008,9 +1008,9 @@ public class JPackageCommand extends CommandArguments { final Path lookupPath = AppImageFile.getPathInAppImage(appImageDir); if (!expectAppImageFile()) { - assertFileInAppImage(lookupPath, null); + assertFileNotInAppImage(lookupPath); } else { - assertFileInAppImage(lookupPath, lookupPath); + assertFileInAppImage(lookupPath); if (TKit.isOSX()) { final Path rootDir = isImagePackageType() ? outputBundle() : @@ -1035,22 +1035,39 @@ public class JPackageCommand extends CommandArguments { final Path lookupPath = PackageFile.getPathInAppImage(Path.of("")); if (isRuntime() || isImagePackageType() || TKit.isLinux()) { - assertFileInAppImage(lookupPath, null); + assertFileNotInAppImage(lookupPath); } else { if (TKit.isOSX() && hasArgument("--app-image")) { String appImage = getArgumentValue("--app-image"); if (AppImageFile.load(Path.of(appImage)).macSigned()) { - assertFileInAppImage(lookupPath, null); + assertFileNotInAppImage(lookupPath); } else { - assertFileInAppImage(lookupPath, lookupPath); + assertFileInAppImage(lookupPath); } } else { - assertFileInAppImage(lookupPath, lookupPath); + assertFileInAppImage(lookupPath); } } } + public void assertFileInAppImage(Path expectedPath) { + assertFileInAppImage(expectedPath.getFileName(), expectedPath); + } + + public void assertFileNotInAppImage(Path filename) { + assertFileInAppImage(filename, null); + } + private void assertFileInAppImage(Path filename, Path expectedPath) { + if (expectedPath != null) { + if (expectedPath.isAbsolute()) { + throw new IllegalArgumentException(); + } + if (!expectedPath.getFileName().equals(filename.getFileName())) { + throw new IllegalArgumentException(); + } + } + if (filename.getNameCount() > 1) { assertFileInAppImage(filename.getFileName(), expectedPath); return; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java index a5eb24980ae..a1971ee0835 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java @@ -57,7 +57,7 @@ public final class LauncherIconVerifier { label = String.format("[%s]", launcherName); } - Path iconPath = cmd.appLayout().destktopIntegrationDirectory().resolve( + Path iconPath = cmd.appLayout().desktopIntegrationDirectory().resolve( curLauncherName + TKit.ICON_SUFFIX); if (TKit.isWindows()) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index b0291fc52eb..1669d1f8233 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -82,7 +82,7 @@ public final class LinuxHelper { String desktopFileName = String.format("%s-%s.desktop", getPackageName( cmd), Optional.ofNullable(launcherName).orElseGet( () -> cmd.name()).replaceAll("\\s+", "_")); - return cmd.appLayout().destktopIntegrationDirectory().resolve( + return cmd.appLayout().desktopIntegrationDirectory().resolve( desktopFileName); } @@ -393,7 +393,7 @@ public final class LinuxHelper { test.addInstallVerifier(cmd -> { // Verify .desktop files. - try (var files = Files.list(cmd.appLayout().destktopIntegrationDirectory())) { + try (var files = Files.list(cmd.appLayout().desktopIntegrationDirectory())) { List desktopFiles = files .filter(path -> path.getFileName().toString().endsWith(".desktop")) .toList(); @@ -470,7 +470,7 @@ public final class LinuxHelper { for (var e : List.>, Function>>of( Map.entry(Map.entry("Exec", Optional.of(launcherPath)), ApplicationLayout::launchersDirectory), - Map.entry(Map.entry("Icon", Optional.empty()), ApplicationLayout::destktopIntegrationDirectory))) { + Map.entry(Map.entry("Icon", Optional.empty()), ApplicationLayout::desktopIntegrationDirectory))) { var path = e.getKey().getValue().or(() -> Optional.of(data.get( e.getKey().getKey()))).map(Path::of).get(); TKit.assertFileExists(cmd.pathToUnpackedPackageFile(path)); @@ -533,11 +533,11 @@ public final class LinuxHelper { TKit.assertEquals(fa.getMime(), mimeType, String.format( "Check mime type of [%s] file", testFile)); - String desktopFileName = queryMimeTypeDefaultHandler(mimeType); + String desktopFileName = queryMimeTypeDefaultHandler(mimeType).orElse(null); Path systemDesktopFile = getSystemDesktopFilesFolder().resolve( desktopFileName); - Path appDesktopFile = cmd.appLayout().destktopIntegrationDirectory().resolve( + Path appDesktopFile = cmd.appLayout().desktopIntegrationDirectory().resolve( desktopFileName); TKit.assertFileExists(systemDesktopFile); @@ -557,7 +557,7 @@ public final class LinuxHelper { TKit.assertNotEquals(fa.getMime(), mimeType, String.format( "Check mime type of [%s] file", testFile)); - String desktopFileName = queryMimeTypeDefaultHandler(fa.getMime()); + String desktopFileName = queryMimeTypeDefaultHandler(fa.getMime()).orElse(null); TKit.assertNull(desktopFileName, String.format( "Check there is no default handler for [%s] mime type", @@ -569,7 +569,7 @@ public final class LinuxHelper { final Path mimeTypeIconFileName = fa.getLinuxIconFileName(); if (mimeTypeIconFileName != null) { // Verify there are xdg registration commands for mime icon file. - Path mimeTypeIcon = cmd.appLayout().destktopIntegrationDirectory().resolve( + Path mimeTypeIcon = cmd.appLayout().desktopIntegrationDirectory().resolve( mimeTypeIconFileName); Map> scriptlets = getScriptlets(cmd); @@ -584,9 +584,9 @@ public final class LinuxHelper { .executeAndGetFirstLineOfOutput(); } - private static String queryMimeTypeDefaultHandler(String mimeType) { + private static Optional queryMimeTypeDefaultHandler(String mimeType) { return Executor.of("xdg-mime", "query", "default", mimeType) - .executeAndGetFirstLineOfOutput(); + .discardStderr().saveFirstLineOfOutput().execute().findFirstLineOfOutput(); } private static void verifyIconInScriptlet(Scriptlet scriptletType, @@ -708,7 +708,7 @@ public final class LinuxHelper { static final Map RPM_MAP = Stream.of(values()).collect( Collectors.toMap(v -> v.rpm, v -> v)); - }; + } public static String getDefaultPackageArch(PackageType type) { if (archs == null) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index fd26b4a5368..c1739b9095a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java @@ -42,7 +42,6 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -365,27 +364,37 @@ public final class PackageTest extends RunnablePackageTest { public static class Group extends RunnablePackageTest { public Group(PackageTest... tests) { - handlers = Stream.of(tests) - .map(PackageTest::createPackageTypeHandlers) - .flatMap(List>::stream) - .collect(Collectors.toUnmodifiableList()); + typeHandlers = Stream.of(PackageType.values()).map(type -> { + return Map.entry(type, Stream.of(tests).map(test -> { + return test.createPackageTypeHandler(type); + }).filter(Optional::isPresent).map(Optional::orElseThrow).toList()); + }).filter(e -> { + return !e.getValue().isEmpty(); + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } @Override protected void runAction(Action... action) { - if (Set.of(action).contains(Action.UNINSTALL)) { - ListIterator> listIterator = handlers.listIterator( - handlers.size()); + typeHandlers.entrySet().stream() + .sorted(Comparator.comparing(Map.Entry::getKey)) + .map(Map.Entry::getValue).forEachOrdered(handlers -> { + runAction(handlers, List.of(action)); + }); + } + + private static void runAction(List> handlers, List actions) { + if (actions.contains(Action.UNINSTALL)) { + final var listIterator = handlers.listIterator(handlers.size()); while (listIterator.hasPrevious()) { - var handler = listIterator.previous(); - List.of(action).forEach(handler::accept); + final var handler = listIterator.previous(); + actions.forEach(handler::accept); } } else { - handlers.forEach(handler -> List.of(action).forEach(handler::accept)); + handlers.forEach(handler -> actions.forEach(handler::accept)); } } - private final List> handlers; + private final Map>> typeHandlers; } PackageTest packageHandlers(PackageHandlers v) { @@ -455,16 +464,22 @@ public final class PackageTest extends RunnablePackageTest { throw new UnsupportedOperationException(); } + private Optional> createPackageTypeHandler(PackageType type) { + Objects.requireNonNull(type); + return Optional.ofNullable(handlers.get(type)).filter(Predicate.not(Handler::isVoid)).map(h -> { + return createPackageTypeHandler(type, h); + }); + } + private List> createPackageTypeHandlers() { if (handlers.keySet().stream().noneMatch(isPackageTypeEnabled)) { PackageType.throwSkippedExceptionIfNativePackagingUnavailable(); } - return handlers.entrySet().stream() - .filter(entry -> !entry.getValue().isVoid()) - .sorted(Comparator.comparing(Map.Entry::getKey)) - .map(entry -> { - return createPackageTypeHandler(entry.getKey(), entry.getValue()); - }).toList(); + return Stream.of(PackageType.values()).sorted() + .map(this::createPackageTypeHandler) + .filter(Optional::isPresent) + .map(Optional::orElseThrow) + .toList(); } private record PackageTypePipeline(PackageType type, int expectedJPackageExitCode, diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index 1c6ac9a7669..3ac302ce0fe 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -322,32 +322,33 @@ public class WindowsHelper { private static long[] findAppLauncherPIDs(JPackageCommand cmd, String launcherName) { // Get the list of PIDs and PPIDs of app launcher processes. Run setWinRunWithEnglishOutput(true) for JDK-8344275. - // wmic process where (name = "foo.exe") get ProcessID,ParentProcessID - final var result = Executor.of("wmic", "process", "where", "(name", - "=", - "\"" + cmd.appLauncherPath(launcherName).getFileName().toString() + "\"", - ")", "get", "ProcessID,ParentProcessID").dumpOutput(true).saveOutput(). - setWinRunWithEnglishOutput(true).execute(); - if ("No Instance(s) Available.".equals(result.stderr().findFirstLineOfOutput().map(String::trim).orElse(""))) { + // powershell -NoLogo -NoProfile -NonInteractive -Command + // "Get-CimInstance Win32_Process -Filter \"Name = 'foo.exe'\" | select ProcessID,ParentProcessID" + String command = "Get-CimInstance Win32_Process -Filter \\\"Name = '" + + cmd.appLauncherPath(launcherName).getFileName().toString() + + "'\\\" | select ProcessID,ParentProcessID"; + List output = Executor.of("powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command) + .dumpOutput(true).saveOutput().setWinRunWithEnglishOutput(true).executeAndGetOutput(); + + if (output.size() < 1) { return new long[0]; } - final var stdout = result.stdout(); - String[] headers = Stream.of(stdout.getFirstLineOfOutput().split("\\s+", 2)).map( + String[] headers = Stream.of(output.get(1).split("\\s+", 2)).map( String::trim).map(String::toLowerCase).toArray(String[]::new); - final Pattern pattern; + Pattern pattern; if (headers[0].equals("parentprocessid") && headers[1].equals( "processid")) { - pattern = Pattern.compile("^(?\\d+)\\s+(?\\d+)\\s+$"); + pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); } else if (headers[1].equals("parentprocessid") && headers[0].equals( "processid")) { - pattern = Pattern.compile("^(?\\d+)\\s+(?\\d+)\\s+$"); + pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); } else { throw new RuntimeException( - "Unrecognizable output of \'wmic process\' command"); + "Unrecognizable output of \'Get-CimInstance Win32_Process\' command"); } - List processes = stdout.getOutput().stream().skip(1).map(line -> { + List processes = output.stream().skip(3).map(line -> { Matcher m = pattern.matcher(line); long[] pids = null; if (m.matches()) { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PListReaderTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PListReaderTest.java index bae4921fda3..d766505cf14 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PListReaderTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PListReaderTest.java @@ -51,7 +51,7 @@ public class PListReaderTest { enum QueryType { STRING(PListReader::queryValue), BOOLEAN(PListReader::queryBoolValue), - STRING_ARRY(PListReader::queryArrayValue); + STRING_ARRAY(PListReader::queryArrayValue); QueryType(BiFunction queryMethod) { this.queryMethod = Objects.requireNonNull(queryMethod); @@ -98,7 +98,7 @@ public class PListReaderTest { } else if (v instanceof Boolean) { queryType(QueryType.BOOLEAN); } else if (v instanceof List) { - queryType(QueryType.STRING_ARRY); + queryType(QueryType.STRING_ARRAY); } return this; } @@ -196,7 +196,7 @@ public class PListReaderTest { testSpecs.add(builder.keyName("string-key").create()); testSpecs.add(builder.keyName("array-key").create()); } - case STRING_ARRY -> { + case STRING_ARRAY -> { testSpecs.add(builder.keyName("string-key").create()); testSpecs.add(builder.keyName("boolean-true-key").create()); testSpecs.add(builder.keyName("boolean-false-key").create()); @@ -230,7 +230,7 @@ public class PListReaderTest { testSpec(QueryType.BOOLEAN).xml("foo").create(), testSpec().expectedValue(List.of("foo", "bar")).xml("foofoobar").create(), testSpec().expectedValue(List.of()).xml("foo").create(), - testSpec(QueryType.STRING_ARRY).xml("foo").create(), + testSpec(QueryType.STRING_ARRAY).xml("foo").create(), testSpec().expectedValue("A").xml("fooAB").create(), testSpec().expectedValue("A").xml("fooAfooB").create() ); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java index 42578227cf5..579cf5f083c 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java @@ -23,16 +23,9 @@ package jdk.jpackage.internal.util; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.UnaryOperator; -import java.util.stream.Stream; +import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -40,6 +33,25 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import java.util.stream.IntStream; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; @@ -48,6 +60,7 @@ import org.junit.jupiter.params.provider.MethodSource; public class PathGroupTest { + @Test public void testNullId() { assertThrowsExactly(NullPointerException.class, () -> new PathGroup(Map.of()).getPath(null)); } @@ -113,6 +126,69 @@ public class PathGroupTest { assertEquals(Path.of("foo"), roots.get(0)); } + public record TestRootsSpec(Collection paths, Set expectedRoots) { + public TestRootsSpec { + paths.forEach(Objects::requireNonNull); + expectedRoots.forEach(Objects::requireNonNull); + } + + void test() { + final var pg = new PathGroup(paths.stream().collect(toMap(x -> new Object(), x -> x))); + final var actualRoots = pg.roots().stream().collect(toSet()); + assertEquals(expectedRoots, actualRoots); + } + + static TestRootsSpec create(Collection paths, Set expectedRoots) { + return new TestRootsSpec(paths.stream().map(Path::of).toList(), expectedRoots.stream().map(Path::of).collect(toSet())); + } + } + + @ParameterizedTest + @MethodSource("testRootsValues") + public void testRoots(TestRootsSpec testSpec) { + testSpec.test(); + } + + private static Collection testRootsValues() { + return List.of( + TestRootsSpec.create(List.of(""), Set.of("")), + TestRootsSpec.create(List.of("/", "a/b/c", "a/b", "a/b", "a/b/"), Set.of("a/b", "/")) + ); + } + + @ParameterizedTest + @MethodSource + public void testSizeInBytes(List paths, @TempDir Path tempDir) throws IOException { + final var files = Set.of("AA.txt", "a/b/c/BB.txt", "a/b/c/DD.txt", "d/foo.txt").stream().map(Path::of).toList(); + + int counter = 0; + for (var file : files) { + file = tempDir.resolve(file); + Files.createDirectories(file.getParent()); + Files.writeString(file, "x".repeat(++counter * 100)); + } + + final var expectedSize = Stream.of(walkFiles(tempDir)) + .map(tempDir::resolve) + .filter(Files::isRegularFile) + .map(toFunction(Files::size)) + .mapToLong(Long::longValue).sum(); + + final var pg = new PathGroup(paths.stream().collect(toMap(x -> new Object(), x -> x))).resolveAt(tempDir); + + assertEquals(expectedSize, pg.sizeInBytes()); + } + + private static Collection> testSizeInBytes() { + return Stream.of( + List.of(""), + List.of("AA.txt", "a/b", "d", "non-existant", "non-existant/foo/bar"), + List.of("AA.txt", "a/b", "d", "a/b/c/BB.txt", "a/b/c/BB.txt") + ).map(v -> { + return v.stream().map(Path::of).toList(); + }).toList(); + } + @Test public void testResolveAt() { final PathGroup pg = new PathGroup(Map.of( @@ -140,7 +216,7 @@ public class PathGroupTest { assertEquals(aPath, pg2.roots().get(0)); } - enum TransformType { COPY, MOVE, HANDLER }; + enum TransformType { COPY, MOVE, HANDLER } private static Stream testTransform() { return Stream.of(TransformType.values()).flatMap(transform -> { @@ -265,6 +341,253 @@ public class PathGroupTest { } } + private enum PathRole { + DESKTOP, + LINUX_APPLAUNCHER_LIB, + } + + private static PathGroup linuxAppImage() { + return new PathGroup(Map.of( + PathRole.DESKTOP, Path.of("lib"), + PathRole.LINUX_APPLAUNCHER_LIB, Path.of("lib/libapplauncher.so") + )); + } + + private static PathGroup linuxUsrTreePackageImage(Path prefix, String packageName) { + final Path lib = prefix.resolve(Path.of("lib", packageName)); + return new PathGroup(Map.of( + PathRole.DESKTOP, lib, + PathRole.LINUX_APPLAUNCHER_LIB, lib.resolve("lib/libapplauncher.so") + )); + } + + @Test + public void testLinuxLibapplauncher(@TempDir Path tempDir) throws IOException { + final Path srcDir = tempDir.resolve("src"); + final Path dstDir = tempDir.resolve("dst"); + + final Map> files = Map.of( + PathRole.LINUX_APPLAUNCHER_LIB, List.of(Path.of("")), + PathRole.DESKTOP, List.of(Path.of("UsrUsrTreeTest.png")) + ); + + final var srcAppLayout = linuxAppImage().resolveAt(srcDir); + final var dstAppLayout = linuxUsrTreePackageImage(dstDir.resolve("usr"), "foo"); + + for (final var e : files.entrySet()) { + final var pathRole = e.getKey(); + final var basedir = srcAppLayout.getPath(pathRole); + for (var file : e.getValue().stream().map(basedir::resolve).toList()) { + Files.createDirectories(file.getParent()); + Files.writeString(file, "foo"); + } + } + + srcAppLayout.copy(dstAppLayout); + + Stream.of(walkFiles(dstDir)).filter(p -> { + return p.getFileName().equals(dstAppLayout.getPath(PathRole.LINUX_APPLAUNCHER_LIB).getFileName()); + }).reduce((x, y) -> { + throw new AssertionError(String.format("Multiple libapplauncher: [%s], [%s]", x, y)); + }); + } + + + public enum TestFileContent { + A, + B, + C; + + void assertFileContent(Path path) { + try { + final var expected = name(); + final var actual = Files.readString(path); + assertEquals(expected, actual); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + void createFile(Path path) { + try { + Files.createDirectories(path.getParent()); + Files.writeString(path, name()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + public record PathGroupCopy(Path from, Path to) { + public PathGroupCopy { + Objects.requireNonNull(from); + } + + public PathGroupCopy(Path from) { + this(from, null); + } + } + + public record TestFile(Path path, TestFileContent content) { + public TestFile { + Objects.requireNonNull(content); + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + + void assertFileContent(Path basedir) { + content.assertFileContent(basedir.resolve(path)); + } + + void create(Path basedir) { + content.createFile(basedir.resolve(path)); + } + } + + public record TestCopySpec(List pathGroupSpecs, Collection src, Collection dst) { + public TestCopySpec { + pathGroupSpecs.forEach(Objects::requireNonNull); + src.forEach(Objects::requireNonNull); + dst.forEach(Objects::requireNonNull); + } + + PathGroup from() { + return createPathGroup(PathGroupCopy::from); + } + + PathGroup to() { + return createPathGroup(PathGroupCopy::to); + } + + void test(Path tempDir) throws IOException { + final Path srcDir = tempDir.resolve("src"); + final Path dstDir = tempDir.resolve("dst"); + + src.stream().forEach(testFile -> { + testFile.create(srcDir); + }); + + from().resolveAt(srcDir).copy(to().resolveAt(dstDir)); + + dst.stream().forEach(testFile -> { + testFile.assertFileContent(dstDir); + }); + + Files.createDirectories(dstDir); + final var actualFiles = Stream.of(walkFiles(dstDir)).filter(path -> { + return Files.isRegularFile(dstDir.resolve(path)); + }).collect(toSet()); + final var expectedFiles = dst.stream().map(TestFile::path).collect(toSet()); + + assertEquals(expectedFiles, actualFiles); + } + + static Builder build() { + return new Builder(); + } + + static class Builder { + Builder addPath(String from, String to) { + pathGroupSpecs.add(new PathGroupCopy(Path.of(from), Optional.ofNullable(to).map(Path::of).orElse(null))); + return this; + } + + Builder file(TestFileContent content, String srcPath, String ...dstPaths) { + srcFiles.putAll(Map.of(Path.of(srcPath), content)); + dstFiles.putAll(Stream.of(dstPaths).collect(toMap(Path::of, x -> content))); + return this; + } + + TestCopySpec create() { + return new TestCopySpec(pathGroupSpecs, convert(srcFiles), convert(dstFiles)); + } + + private static Collection convert(Map map) { + return map.entrySet().stream().map(e -> { + return new TestFile(e.getKey(), e.getValue()); + }).toList(); + } + + private final List pathGroupSpecs = new ArrayList<>(); + private final Map srcFiles = new HashMap<>(); + private final Map dstFiles = new HashMap<>(); + } + + private PathGroup createPathGroup(Function keyFunc) { + return new PathGroup(IntStream.range(0, pathGroupSpecs.size()).mapToObj(Integer::valueOf).map(i -> { + return Optional.ofNullable(keyFunc.apply(pathGroupSpecs.get(i))).map(path -> { + return Map.entry(i, path); + }); + }).filter(Optional::isPresent).map(Optional::orElseThrow).collect(toMap(Map.Entry::getKey, Map.Entry::getValue))); + } + } + + @ParameterizedTest + @MethodSource + public void testCopy(TestCopySpec testSpec, @TempDir Path tempDir) throws IOException { + testSpec.test(tempDir); + } + + private static Collection testCopy() { + return List.of( + TestCopySpec.build().create(), + TestCopySpec.build() + .addPath("a/b/c", "a") + .file(TestFileContent.A, "a/b/c/j/k/foo", "a/j/k/foo") + .create(), + TestCopySpec.build() + .addPath("a/b/c", "a") + .addPath("a/b/c", "d") + .file(TestFileContent.A, "a/b/c/j/k/foo", "a/j/k/foo", "d/j/k/foo") + .create(), + TestCopySpec.build() + .addPath("a/b/c", "") + .addPath("a/b/c", "d") + .file(TestFileContent.A, "a/b/c/j/k/foo", "j/k/foo", "d/j/k/foo") + .create(), + TestCopySpec.build() + .addPath("a/b/c", "cc") + .addPath("a/b/c", "dd") + .addPath("a/b/c/foo", null) + .file(TestFileContent.A, "a/b/c/foo") + .create(), + TestCopySpec.build() + .addPath("a/b/c", "cc") + .addPath("a/b/c", "dd") + .addPath("a/b/c/foo", null) + .file(TestFileContent.A, "a/b/c/foo") + .file(TestFileContent.B, "a/b/c/bar", "cc/bar", "dd/bar") + .create(), + TestCopySpec.build() + .addPath("a/b/c", "cc") + .addPath("a/b/c", "dd") + .addPath("a/b/c/bar", "dd/buz") + .addPath("a/b/c/foo", null) + .file(TestFileContent.A, "a/b/c/foo") + .file(TestFileContent.B, "a/b/c/bar", "dd/buz") + .create(), + TestCopySpec.build() + .addPath("a/b/c", "cc") + .addPath("a/b/c", "dd") + .addPath("a/b/c/bar", "dd/buz") + .addPath("a/b/c/bar", "cc/rab") + .addPath("a/b/c/foo", null) + .file(TestFileContent.A, "a/b/c/foo") + .file(TestFileContent.B, "a/b/c/bar", "cc/rab", "dd/buz") + .create(), + TestCopySpec.build() + .addPath("a/b", null) + .addPath("a/b/c", "cc") + .addPath("a/b/c", "dd") + .addPath("a/b/c/bar", "dd/buz") + .addPath("a/b/c/bar", "cc/rab") + .file(TestFileContent.A, "a/b/c/foo") + .file(TestFileContent.B, "a/b/c/bar") + .create() + ); + } + private static Path[] walkFiles(Path root) throws IOException { try (var files = Files.walk(root)) { return files.map(root::relativize).sorted().toArray(Path[]::new); diff --git a/test/jdk/tools/jpackage/linux/UsrTreeTest.java b/test/jdk/tools/jpackage/linux/UsrTreeTest.java index e33364c605d..1950aab39a3 100644 --- a/test/jdk/tools/jpackage/linux/UsrTreeTest.java +++ b/test/jdk/tools/jpackage/linux/UsrTreeTest.java @@ -21,15 +21,18 @@ * questions. */ +import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.test.ApplicationLayout.linuxAppImage; + import java.nio.file.Path; import java.util.List; import java.util.function.Consumer; -import java.util.stream.Collectors; -import jdk.jpackage.test.TKit; +import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.LinuxHelper; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.LinuxHelper; -import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.TKit; /** @@ -90,20 +93,13 @@ public class UsrTreeTest { "Check there is%spackage name [%s] in common path [%s] between [%s] and [%s]", expectedImageSplit ? " no " : " ", packageName, commonPath, launcherPath, launcherCfgPath)); - - List packageFiles = LinuxHelper.getPackageFiles(cmd).collect( - Collectors.toList()); - - Consumer packageFileVerifier = file -> { - TKit.assertTrue(packageFiles.stream().filter( - path -> path.equals(file)).findFirst().orElse( - null) != null, String.format( - "Check file [%s] is in [%s] package", file, - packageName)); - }; - - packageFileVerifier.accept(launcherPath); - packageFileVerifier.accept(launcherCfgPath); + }) + .addInstallVerifier(cmd -> { + Stream.of( + cmd.appLauncherPath(), + cmd.appLauncherCfgPath(null), + cmd.appLayout().libapplauncher() + ).map(cmd::pathToPackageFile).map(cmd.appInstallationDirectory()::relativize).forEachOrdered(cmd::assertFileInAppImage); }) .run(); } diff --git a/test/jdk/tools/jpackage/share/EmptyFolderTest.java b/test/jdk/tools/jpackage/share/EmptyFolderTest.java index 40a4db03d6f..f2cb72e097d 100644 --- a/test/jdk/tools/jpackage/share/EmptyFolderTest.java +++ b/test/jdk/tools/jpackage/share/EmptyFolderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,16 +97,21 @@ public class EmptyFolderTest { } private static void validateDirTree(JPackageCommand cmd) { + // When MSI package is unpacked and not installed, empty directories are not created. + final boolean emptyDirSupported = !(PackageType.WINDOWS.contains(cmd.packageType()) && cmd.isPackageUnpacked()); + validateDirTree(cmd, emptyDirSupported); + } + + private static void validateDirTree(JPackageCommand cmd, boolean emptyDirSupported) { var outputBaseDir = cmd.appLayout().appDirectory(); var inputBaseDir = cmd.inputDir(); for (var path : DIR_STRUCT) { Path outputPath = outputBaseDir.resolve(path); if (isFile(outputPath)) { TKit.assertFileExists(outputPath); - } else if (!PackageType.WINDOWS.contains(cmd.packageType())) { + } else if (emptyDirSupported) { TKit.assertDirectoryExists(outputPath); } else if (inputBaseDir.resolve(path).toFile().list().length == 0) { - // MSI packages don't support empty folders TKit.assertPathExists(outputPath, false); } else { TKit.assertDirectoryNotEmpty(outputPath); diff --git a/test/jdk/tools/jpackage/share/RuntimeImageTest.java b/test/jdk/tools/jpackage/share/RuntimeImageTest.java index 0e30d7ee179..6ed38f2c5c1 100644 --- a/test/jdk/tools/jpackage/share/RuntimeImageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimeImageTest.java @@ -21,13 +21,14 @@ * questions. */ +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import jdk.jpackage.test.TKit; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Executor; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JavaTool; -import jdk.jpackage.test.Executor; +import jdk.jpackage.test.TKit; /* * @test @@ -43,27 +44,49 @@ import jdk.jpackage.test.Executor; public class RuntimeImageTest { @Test - public static void test() throws Exception { - final Path workDir = TKit.createTempDirectory("runtime").resolve("data"); - final Path jlinkOutputDir = workDir.resolve("temp.runtime"); - Files.createDirectories(jlinkOutputDir.getParent()); + public static void test() throws IOException { - new Executor() - .setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments( - "--output", jlinkOutputDir.toString(), - "--add-modules", "java.desktop", - "--strip-debug", - "--no-header-files", - "--no-man-pages", - "--strip-native-commands") - .execute(); + JPackageCommand cmd = JPackageCommand.helloAppImage(); - JPackageCommand cmd = JPackageCommand.helloAppImage() - .setArgumentValue("--runtime-image", jlinkOutputDir.toString()); + if (JPackageCommand.DEFAULT_RUNTIME_IMAGE == null) { + final Path workDir = TKit.createTempDirectory("runtime").resolve("data"); + final Path jlinkOutputDir = workDir.resolve("temp.runtime"); + Files.createDirectories(jlinkOutputDir.getParent()); + + new Executor() + .setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", jlinkOutputDir.toString(), + "--add-modules", "java.desktop", + "--strip-debug", + "--no-header-files", + "--no-man-pages", + "--strip-native-commands") + .execute(); + + cmd.setArgumentValue("--runtime-image", jlinkOutputDir.toString()); + } cmd.executeAndAssertHelloAppImageCreated(); } + @Test + public static void testStrippedFiles() throws IOException { + final var cmd = JPackageCommand.helloAppImage().setFakeRuntime(); + + final var runtimePath = Path.of(cmd.executePrerequisiteActions().getArgumentValue("--runtime-image")); + + Files.createDirectories(runtimePath.resolve("jmods")); + Files.createDirectories(runtimePath.resolve("lib")); + Files.createFile(runtimePath.resolve("lib/src.zip")); + Files.createFile(runtimePath.resolve("src.zip")); + + (new JPackageCommand()).addArguments(cmd.getAllArguments()).executeAndAssertHelloAppImageCreated(); + + final var appRuntimeDir = cmd.appLayout().runtimeHomeDirectory(); + TKit.assertPathExists(appRuntimeDir.resolve("jmods"), false); + TKit.assertPathExists(appRuntimeDir.resolve("lib/src.zip"), false); + TKit.assertPathExists(appRuntimeDir.resolve("src.zip"), false); + } } diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index 9eb4f8d231e..2af6543c2db 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -40,6 +40,7 @@ import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.LinuxHelper; import static jdk.jpackage.test.TKit.assertTrue; import static jdk.jpackage.test.TKit.assertFalse; +import static jdk.internal.util.OperatingSystem.LINUX; /** * Test --runtime-image parameter. @@ -81,22 +82,22 @@ public class RuntimePackageTest { @Test public static void test() { - init(PackageType.NATIVE).run(); + init().run(); } - @Test + @Test(ifOS = LINUX) @Parameter("/usr") @Parameter("/usr/lib/Java") public static void testUsrInstallDir(String installDir) { - init(PackageType.LINUX) - .addInitializer(cmd -> cmd.addArguments("--install-dir", "/usr")) + init() + .addInitializer(cmd -> cmd.addArguments("--install-dir", installDir)) .run(); } @Test public static void testName() { // Test that jpackage can derive package name from the path to runtime image. - init(PackageType.NATIVE) + init() .addInitializer(cmd -> cmd.removeArgumentWithValue("--name")) // Don't attempt to install this package as it may have an odd name derived from // the runtime image path. Say, on Linux for `--runtime-image foo/bar/sed` @@ -105,9 +106,8 @@ public class RuntimePackageTest { .run(Action.CREATE_AND_UNPACK); } - private static PackageTest init(Set types) { + private static PackageTest init() { return new PackageTest() - .forTypes(types) .addInitializer(cmd -> { final Path runtimeImageDir; @@ -166,8 +166,7 @@ public class RuntimePackageTest { "Check the package doesn't deliver [%s] copyright file", copyright)); } - }) - .forTypes(types); + }); } private static Set listFiles(Path root) throws IOException { diff --git a/test/langtools/ProblemList-StaticJdk.txt b/test/langtools/ProblemList-StaticJdk.txt new file mode 100644 index 00000000000..85fa3a6e14d --- /dev/null +++ b/test/langtools/ProblemList-StaticJdk.txt @@ -0,0 +1,29 @@ +# Requires javadoc +jdk/javadoc/tool/6964914/TestStdDoclet.java 8346719 generic-all +jdk/javadoc/tool/6964914/TestUserDoclet.java 8346719 generic-all +jdk/javadoc/tool/AddOpensTest.java 8346719 generic-all +jdk/javadoc/tool/EncodingTest.java 8346719 generic-all +jdk/javadoc/tool/EnsureNewOldDoclet.java 8346719 generic-all +jdk/javadoc/tool/QuietOption.java 8346719 generic-all +jdk/javadoc/tool/testLocaleOption/TestLocaleOption.java 8346719 generic-all + +# Requires javac +tools/javac/ClassPathTest/ClassPathTest.java 8346719 generic-all +tools/javac/Paths/ClassPath.java 8346719 generic-all +tools/javac/Paths/WildcardMineField.java 8346719 generic-all +tools/javac/T8132562/ClassPathWithDoubleQuotesTest.java 8346719 generic-all +tools/javac/file/MultiReleaseJar/MultiReleaseJarTest.java 8346719 generic-all +tools/javac/modules/AllDefaultTest.java 8346719 generic-all +tools/javac/modules/EnvVarTest.java 8346719 generic-all +tools/javac/modules/InheritRuntimeEnvironmentTest.java 8346719 generic-all +tools/javac/modules/NPEEmptyFileTest.java 8346719 generic-all +tools/javac/newlines/NewLineTest.java 8346719 generic-all +tools/javac/options/smokeTests/OptionSmokeTest.java 8346719 generic-all +tools/javac/platform/PlatformProviderTest.java 8346719 generic-all +tools/javac/processing/options/testPrintProcessorInfo/TestWithXstdout.java 8346719 generic-all + +# Requires jar +tools/jdeps/MultiReleaseJar.java 8346719 generic-all + +# Requires jimage +tools/javac/Paths/MineField.java 8346719 generic-all diff --git a/test/langtools/tools/javac/lint/NoWarn.java b/test/langtools/tools/javac/lint/NoWarn.java index 7d6153807f2..4a1b99a7bed 100644 --- a/test/langtools/tools/javac/lint/NoWarn.java +++ b/test/langtools/tools/javac/lint/NoWarn.java @@ -1,15 +1,20 @@ /** - * @test /nodynamiccopyright/ - * @bug 6183484 - * @summary verify -nowarn is the same as -Xlint:none - * @compile/ref=NoWarn1.out -XDrawDiagnostics NoWarn.java - * @compile/ref=NoWarn2.out -XDrawDiagnostics -nowarn NoWarn.java - * @compile/ref=NoWarn2.out -XDrawDiagnostics -Xlint:none NoWarn.java + * @test /nodynamiccopyright/ + * @bug 6183484 8352612 + * @summary Restrict -Xlint:none to affect only lint categories, while -nowarn disables all warnings + * @compile/ref=NoWarn1.out -XDfind=diamond -XDrawDiagnostics -Xlint:none NoWarn.java + * @compile/ref=NoWarn2.out -XDfind=diamond -XDrawDiagnostics -Xlint:divzero,unchecked NoWarn.java + * @compile/ref=NoWarn2.out -XDfind=diamond -XDrawDiagnostics -Xlint:none,divzero,unchecked NoWarn.java + * @compile/ref=NoWarn3.out -XDfind=diamond -XDrawDiagnostics -Xlint:none -nowarn NoWarn.java + * @compile/ref=NoWarn4.out -XDfind=diamond -XDrawDiagnostics -Xlint:divzero,unchecked -nowarn NoWarn.java + * @compile/ref=NoWarn4.out -XDfind=diamond -XDrawDiagnostics -Xlint:none,divzero,unchecked -nowarn NoWarn.java */ - +import java.util.*; class NoWarn { - void m(Object... args) { } - void foo() { - m(null); - } + Set z = null; // Mandatory Lint Lint Category How can it be suppressed? + // --------- ---- ------------- ------------------------- + sun.misc.Unsafe b; // Yes No N/A Not possible + Set a = new HashSet(); // No No N/A "-nowarn" only (requires -XDfind=diamond) + Set d = (Set)z; // Yes Yes "unchecked" "-Xlint:-unchecked" only + int c = 1/0; // No Yes "divzero" "-Xlint:-divzero" or "-nowarn" } diff --git a/test/langtools/tools/javac/lint/NoWarn1.out b/test/langtools/tools/javac/lint/NoWarn1.out index c4a8456889f..5fea0f7b51a 100644 --- a/test/langtools/tools/javac/lint/NoWarn1.out +++ b/test/langtools/tools/javac/lint/NoWarn1.out @@ -1,2 +1,5 @@ -NoWarn.java:13:11: compiler.warn.inexact.non-varargs.call: java.lang.Object, java.lang.Object[] -1 warning +NoWarn.java:16:13: compiler.warn.sun.proprietary: sun.misc.Unsafe +NoWarn.java:17:32: compiler.warn.diamond.redundant.args +- compiler.note.unchecked.filename: NoWarn.java +- compiler.note.unchecked.recompile +2 warnings diff --git a/test/langtools/tools/javac/lint/NoWarn2.out b/test/langtools/tools/javac/lint/NoWarn2.out index e69de29bb2d..d33c2404e1e 100644 --- a/test/langtools/tools/javac/lint/NoWarn2.out +++ b/test/langtools/tools/javac/lint/NoWarn2.out @@ -0,0 +1,5 @@ +NoWarn.java:16:13: compiler.warn.sun.proprietary: sun.misc.Unsafe +NoWarn.java:18:34: compiler.warn.prob.found.req: (compiler.misc.unchecked.cast.to.type), java.util.Set, java.util.Set +NoWarn.java:19:15: compiler.warn.div.zero +NoWarn.java:17:32: compiler.warn.diamond.redundant.args +4 warnings diff --git a/test/langtools/tools/javac/lint/NoWarn3.out b/test/langtools/tools/javac/lint/NoWarn3.out new file mode 100644 index 00000000000..9491fe8b9ee --- /dev/null +++ b/test/langtools/tools/javac/lint/NoWarn3.out @@ -0,0 +1,4 @@ +NoWarn.java:16:13: compiler.warn.sun.proprietary: sun.misc.Unsafe +- compiler.note.unchecked.filename: NoWarn.java +- compiler.note.unchecked.recompile +1 warning diff --git a/test/langtools/tools/javac/lint/NoWarn4.out b/test/langtools/tools/javac/lint/NoWarn4.out new file mode 100644 index 00000000000..6586ff995bf --- /dev/null +++ b/test/langtools/tools/javac/lint/NoWarn4.out @@ -0,0 +1,3 @@ +NoWarn.java:16:13: compiler.warn.sun.proprietary: sun.misc.Unsafe +NoWarn.java:18:34: compiler.warn.prob.found.req: (compiler.misc.unchecked.cast.to.type), java.util.Set, java.util.Set +2 warnings diff --git a/test/langtools/tools/javac/processing/model/util/elements/overrides/S.java b/test/langtools/tools/javac/processing/model/util/elements/overrides/S.java new file mode 100644 index 00000000000..f6da3324acb --- /dev/null +++ b/test/langtools/tools/javac/processing/model/util/elements/overrides/S.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + */ + +/* + * This file models a few cases where Elements.overrides produces a false + * positive which warrants @apiNote. + */ + +// S.java does not compile because it violates the JLS rules for overrides +class S { + + public void m() { } +} + +// `protected` is a weaker modifier than `public` +class T1 extends S { + + protected void m() { } +} + +// `package-private` is a weaker modifier than `public` +class T2 extends S { + + void m() { } +} + +// `private` methods cannot override public method +class T3 extends S { + + private void m() { } +} + +// return type int is not compatible with void +class T4 extends S { + + public int m() { return 0; } +} + +// adding a checked exception violates the override rule +class T5 extends S { + + public void m() throws Exception { } +} diff --git a/test/langtools/tools/javac/processing/model/util/elements/overrides/TestOverrides.java b/test/langtools/tools/javac/processing/model/util/elements/overrides/TestOverrides.java new file mode 100644 index 00000000000..724dda1d482 --- /dev/null +++ b/test/langtools/tools/javac/processing/model/util/elements/overrides/TestOverrides.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 8174840 + * @library /tools/javac/lib + * @build JavacTestingAbstractProcessor TestOverrides + * @compile -processor TestOverrides -proc:only S.java + */ + +import java.util.Set; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; + +import static javax.lang.model.element.ElementKind.METHOD; + +public class TestOverrides extends JavacTestingAbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment round) { + if (!round.processingOver()) { + var sm = mIn(elements.getTypeElement("S")); + for (var subtypeName : new String[]{"T1", "T2", "T3", "T4", "T5"}) { + var t = elements.getTypeElement(subtypeName); + var tm = mIn(t); + if (!elements.overrides(tm, sm, t)) + messager.printError(String.format( + "%s does not override from %s method %s", tm, t.getQualifiedName(), sm)); + } + } + return true; + } + + private ExecutableElement mIn(TypeElement t) { + return t.getEnclosedElements().stream() + .filter(e -> e.getKind() == METHOD) + .filter(e -> e.getSimpleName().toString().equals("m")) + .map(e -> (ExecutableElement) e) + .findAny() + .get(); + } +} diff --git a/test/langtools/tools/javac/varargs/Warn1.java b/test/langtools/tools/javac/varargs/Warn1.java index eb5758d9a23..f65c2f9bc73 100644 --- a/test/langtools/tools/javac/varargs/Warn1.java +++ b/test/langtools/tools/javac/varargs/Warn1.java @@ -6,7 +6,7 @@ * * @compile Warn1.java * @compile/ref=Warn1.out -XDrawDiagnostics Warn1.java - * @compile -Werror -Xlint:none Warn1.java + * @compile -Werror -nowarn Warn1.java */ package varargs.warn1; diff --git a/test/langtools/tools/javac/varargs/Warn2.java b/test/langtools/tools/javac/varargs/Warn2.java index 9ba8b8f8832..6d411f31f57 100644 --- a/test/langtools/tools/javac/varargs/Warn2.java +++ b/test/langtools/tools/javac/varargs/Warn2.java @@ -6,7 +6,7 @@ * * @compile Warn2.java * @compile/fail/ref=Warn2.out -XDrawDiagnostics -Werror Warn2.java - * @compile -Werror -Xlint:none Warn2.java + * @compile -Werror -nowarn Warn2.java */ package varargs.warn2; diff --git a/test/langtools/tools/javac/versions/Versions.java b/test/langtools/tools/javac/versions/Versions.java index 6d885a55972..52b0f0d366d 100644 --- a/test/langtools/tools/javac/versions/Versions.java +++ b/test/langtools/tools/javac/versions/Versions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @bug 4981566 5028634 5094412 6304984 7025786 7025789 8001112 8028545 * 8000961 8030610 8028546 8188870 8173382 8173382 8193290 8205619 8028563 * 8245147 8245586 8257453 8286035 8306586 8320806 8306586 8319414 8330183 - * 8342982 + * 8342982 8356108 * @summary Check interpretation of -target and -source options * @modules java.compiler * jdk.compiler @@ -403,6 +403,14 @@ public class Versions { } } """), + + SOURCE_25(25, "New25.java", + // New feature in 25: module import declarations + """ + import module java.base; + public class New25 { + } + """), ; // Reduce code churn when appending new constants private int sourceLevel; diff --git a/test/lib-test/ProblemList-StaticJdk.txt b/test/lib-test/ProblemList-StaticJdk.txt new file mode 100644 index 00000000000..3d151e07d41 --- /dev/null +++ b/test/lib-test/ProblemList-StaticJdk.txt @@ -0,0 +1,2 @@ +# Requires jcmd +jdk/test/lib/hprof/HprofTest.java 8346719 generic-all