diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 4983ffd7042..090c1451a8e 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -88,12 +88,26 @@ jobs: id: jtreg uses: ./.github/actions/get-jtreg + - name: 'Check toolchain installed' + id: toolchain-check + run: | + set +e + '/c/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/vc/auxiliary/build/vcvars64.bat' -vcvars_ver=${{ inputs.msvc-toolset-version }} + if [ $? -eq 0 ]; then + echo "Toolchain is already installed" + echo "toolchain-installed=true" >> $GITHUB_OUTPUT + else + echo "Toolchain is not yet installed" + echo "toolchain-installed=false" >> $GITHUB_OUTPUT + fi + - name: 'Install toolchain and dependencies' run: | # Run Visual Studio Installer '/c/Program Files (x86)/Microsoft Visual Studio/Installer/vs_installer.exe' \ - modify --quiet --installPath 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise' \ + modify --quiet --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise' \ --add Microsoft.VisualStudio.Component.VC.${{ inputs.msvc-toolset-version }}.${{ inputs.msvc-toolset-architecture }} + if: steps.toolchain-check.outputs.toolchain-installed != 'true' - name: 'Configure' run: > diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4f1bedeeefd..77c3310c909 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -71,19 +71,17 @@ jobs: # 'false' otherwise. # arg $1: platform name or names to look for function check_platform() { - if [[ '${{ !secrets.JDK_SUBMIT_FILTER || startsWith(github.ref, 'refs/heads/submit/') }}' == 'false' ]]; then - # If JDK_SUBMIT_FILTER is set, and this is not a "submit/" branch, don't run anything - echo 'false' - return - fi - if [[ $GITHUB_EVENT_NAME == workflow_dispatch ]]; then input='${{ github.event.inputs.platforms }}' elif [[ $GITHUB_EVENT_NAME == push ]]; then - input='${{ secrets.JDK_SUBMIT_PLATFORMS }}' - else - echo 'Internal error in GHA' - exit 1 + if [[ '${{ !secrets.JDK_SUBMIT_FILTER || startsWith(github.ref, 'refs/heads/submit/') }}' == 'false' ]]; then + # If JDK_SUBMIT_FILTER is set, and this is not a "submit/" branch, don't run anything + >&2 echo 'JDK_SUBMIT_FILTER is set and not a "submit/" branch' + echo 'false' + return + else + input='${{ secrets.JDK_SUBMIT_PLATFORMS }}' + fi fi normalized_input="$(echo ,$input, | tr -d ' ')" @@ -223,7 +221,7 @@ jobs: uses: ./.github/workflows/build-windows.yml with: platform: windows-x64 - msvc-toolset-version: '14.28' + msvc-toolset-version: '14.29' msvc-toolset-architecture: 'x86.x64' if: needs.select.outputs.windows-x64 == 'true' diff --git a/.gitignore b/.gitignore index 608a4f606a9..2c9d161c742 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ test/nashorn/lib NashornProfile.txt **/JTreport/** **/JTwork/** +/compile_commands.json +/.cache diff --git a/.jcheck/conf b/.jcheck/conf index 40d493a9dc8..5b8b093c86f 100644 --- a/.jcheck/conf +++ b/.jcheck/conf @@ -1,7 +1,7 @@ [general] project=jdk-updates jbs=JDK -version=11.0.19 +version=11.0.21 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace diff --git a/bin/jib.sh b/bin/jib.sh index aab198990cc..9f34cf9ab7b 100644 --- a/bin/jib.sh +++ b/bin/jib.sh @@ -128,6 +128,15 @@ install_jib() { exit 1 fi fi + # Want to check the filetype using file, to see if we got served a HTML error page. + # This is sensitive to the filename containing a specific string, but good enough. + file "${installed_jib_script}.gz" | grep "gzip compressed data" > /dev/null + if [ $? -ne 0 ]; then + echo "Warning: ${installed_jib_script}.gz is not a gzip file." + echo "If you are behind a proxy you may need to configure exceptions using no_proxy." + echo "The download URL was: ${jib_url}" + exit 1 + fi echo "Extracting JIB bootstrap script" rm -f "${installed_jib_script}" gunzip "${installed_jib_script}.gz" diff --git a/make/ReleaseFile.gmk b/make/ReleaseFile.gmk index 0424e2fb623..5e8d123fac9 100644 --- a/make/ReleaseFile.gmk +++ b/make/ReleaseFile.gmk @@ -51,6 +51,7 @@ define create-info-file $(if $(VENDOR_VERSION_STRING), \ $(call info-file-item, "IMPLEMENTOR_VERSION", "$(VENDOR_VERSION_STRING)")) $(call info-file-item, "JAVA_VERSION_DATE", "$(VERSION_DATE)") + $(call info-file-item, "JAVA_RUNTIME_VERSION", "$(VERSION_STRING)") $(call info-file-item, "OS_NAME", "$(RELEASE_FILE_OS_NAME)") $(call info-file-item, "OS_ARCH", "$(RELEASE_FILE_OS_ARCH)") $(call info-file-item, "LIBC", "$(RELEASE_FILE_LIBC)") diff --git a/make/autoconf/basic.m4 b/make/autoconf/basic.m4 index dc02d44c630..9e8a22fd9ba 100644 --- a/make/autoconf/basic.m4 +++ b/make/autoconf/basic.m4 @@ -55,6 +55,7 @@ AC_DEFUN([BASIC_CHECK_LEFTOVER_OVERRIDDEN], ############################################################################### # Setup basic configuration paths, and platform-specific stuff related to PATHs. +# Make sure to only use tools set up in BASIC_SETUP_FUNDAMENTAL_TOOLS. AC_DEFUN_ONCE([BASIC_SETUP_PATHS], [ # Save the current directory this script was started from diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4 index c3c48f8c21b..e0ea90320f8 100644 --- a/make/autoconf/basic_tools.m4 +++ b/make/autoconf/basic_tools.m4 @@ -24,8 +24,8 @@ # ############################################################################### -# Setup the most fundamental tools that relies on not much else to set up, -# but is used by much of the early bootstrap code. +# Setup the most fundamental tools, used for setting up build platform and +# path handling. AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], [ # Bootstrapping: These tools are needed by UTIL_LOOKUP_PROGS @@ -37,7 +37,28 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], UTIL_CHECK_NONEMPTY(FILE) AC_PATH_PROGS(LDD, ldd) - # First are all the fundamental required tools. + # Required tools + UTIL_REQUIRE_PROGS(ECHO, echo) + UTIL_REQUIRE_PROGS(TR, tr) + UTIL_REQUIRE_PROGS(UNAME, uname) + UTIL_REQUIRE_PROGS(WC, wc) + + # Required tools with some special treatment + UTIL_REQUIRE_SPECIAL(GREP, [AC_PROG_GREP]) + UTIL_REQUIRE_SPECIAL(EGREP, [AC_PROG_EGREP]) + UTIL_REQUIRE_SPECIAL(SED, [AC_PROG_SED]) + + # Tools only needed on some platforms + UTIL_LOOKUP_PROGS(PATHTOOL, cygpath wslpath) + UTIL_LOOKUP_PROGS(CMD, cmd.exe, $PATH:/cygdrive/c/windows/system32:/mnt/c/windows/system32:/c/windows/system32) +]) + +############################################################################### +# Setup further tools that should be resolved early but after setting up +# build platform and path handling. +AC_DEFUN_ONCE([BASIC_SETUP_TOOLS], +[ + # Required tools UTIL_REQUIRE_PROGS(BASH, bash) UTIL_REQUIRE_PROGS(CAT, cat) UTIL_REQUIRE_PROGS(CHMOD, chmod) @@ -45,7 +66,6 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], UTIL_REQUIRE_PROGS(CUT, cut) UTIL_REQUIRE_PROGS(DATE, date) UTIL_REQUIRE_PROGS(DIFF, gdiff diff) - UTIL_REQUIRE_PROGS(ECHO, echo) UTIL_REQUIRE_PROGS(EXPR, expr) UTIL_REQUIRE_PROGS(FIND, find) UTIL_REQUIRE_PROGS(GUNZIP, gunzip) @@ -67,28 +87,20 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], UTIL_REQUIRE_PROGS(TAR, gtar tar) UTIL_REQUIRE_PROGS(TEE, tee) UTIL_REQUIRE_PROGS(TOUCH, touch) - UTIL_REQUIRE_PROGS(TR, tr) - UTIL_REQUIRE_PROGS(UNAME, uname) - UTIL_REQUIRE_PROGS(WC, wc) UTIL_REQUIRE_PROGS(WHICH, which) UTIL_REQUIRE_PROGS(XARGS, xargs) - # Then required tools that require some special treatment. + # Required tools with some special treatment UTIL_REQUIRE_SPECIAL(AWK, [AC_PROG_AWK]) - UTIL_REQUIRE_SPECIAL(GREP, [AC_PROG_GREP]) - UTIL_REQUIRE_SPECIAL(EGREP, [AC_PROG_EGREP]) UTIL_REQUIRE_SPECIAL(FGREP, [AC_PROG_FGREP]) - UTIL_REQUIRE_SPECIAL(SED, [AC_PROG_SED]) # Optional tools, we can do without them UTIL_LOOKUP_PROGS(DF, df) UTIL_LOOKUP_PROGS(NICE, nice) UTIL_LOOKUP_PROGS(READLINK, greadlink readlink) - # These are only needed on some platforms - UTIL_LOOKUP_PROGS(PATHTOOL, cygpath wslpath) + # Tools only needed on some platforms UTIL_LOOKUP_PROGS(LSB_RELEASE, lsb_release) - UTIL_LOOKUP_PROGS(CMD, cmd.exe, $PATH:/cygdrive/c/windows/system32:/mnt/c/windows/system32:/c/windows/system32) # For compare.sh only UTIL_LOOKUP_PROGS(CMP, cmp) diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index a2c0c1a8f5c..7d8fa96c2c8 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -85,6 +85,7 @@ PLATFORM_SETUP_OPENJDK_BUILD_AND_TARGET # Continue setting up basic stuff. Most remaining code require fundamental tools. BASIC_SETUP_PATHS +BASIC_SETUP_TOOLS # Check if it's a pure open build or if custom sources are to be used. JDKOPT_SETUP_OPEN_OR_CUSTOM diff --git a/make/autoconf/hotspot.m4 b/make/autoconf/hotspot.m4 index a3e1e00b2c9..9bb34363e5c 100644 --- a/make/autoconf/hotspot.m4 +++ b/make/autoconf/hotspot.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -165,8 +165,11 @@ AC_DEFUN_ONCE([HOTSPOT_SETUP_DTRACE], DTRACE_DEP_MISSING=false - AC_MSG_CHECKING([for dtrace tool]) - if test "x$DTRACE" != "x" && test -x "$DTRACE"; then + AC_MSG_CHECKING([for dtrace tool and platform support]) + if test "x$OPENJDK_TARGET_CPU_ARCH" = "xppc"; then + AC_MSG_RESULT([no, $OPENJDK_TARGET_CPU_ARCH]) + DTRACE_DEP_MISSING=true + elif test "x$DTRACE" != "x" && test -x "$DTRACE"; then AC_MSG_RESULT([$DTRACE]) else AC_MSG_RESULT([not found, cannot build dtrace]) diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index e67ffd47e5b..963b896b8d7 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -777,7 +777,7 @@ AC_DEFUN([JDKOPT_CHECK_CODESIGN_PARAMS], $RM "$CODESIGN_TESTFILE" $TOUCH "$CODESIGN_TESTFILE" CODESIGN_SUCCESS=false - $CODESIGN $PARAMS "$CODESIGN_TESTFILE" 2>&AS_MESSAGE_LOG_FD \ + eval \"$CODESIGN\" $PARAMS \"$CODESIGN_TESTFILE\" 2>&AS_MESSAGE_LOG_FD \ >&AS_MESSAGE_LOG_FD && CODESIGN_SUCCESS=true $RM "$CODESIGN_TESTFILE" AC_MSG_CHECKING([$MESSAGE]) @@ -790,7 +790,7 @@ AC_DEFUN([JDKOPT_CHECK_CODESIGN_PARAMS], AC_DEFUN([JDKOPT_CHECK_CODESIGN_HARDENED], [ - JDKOPT_CHECK_CODESIGN_PARAMS([-s "$MACOSX_CODESIGN_IDENTITY" --option runtime], + JDKOPT_CHECK_CODESIGN_PARAMS([-s \"$MACOSX_CODESIGN_IDENTITY\" --option runtime], [if codesign with hardened runtime is possible]) ]) diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index f89b22f5fcb..26a58eb2ee8 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -628,6 +628,7 @@ AC_DEFUN([PLATFORM_SET_MODULE_TARGET_OS_VALUES], ]) #%%% Build and target systems %%% +# Make sure to only use tools set up in BASIC_SETUP_FUNDAMENTAL_TOOLS. AC_DEFUN_ONCE([PLATFORM_SETUP_OPENJDK_BUILD_AND_TARGET], [ # Figure out the build and target systems. # Note that in autoconf terminology, "build" is obvious, but "target" @@ -711,7 +712,7 @@ AC_DEFUN_ONCE([PLATFORM_SETUP_OPENJDK_TARGET_ENDIANNESS], [ ############################################################################### # - # Is the target little of big endian? + # Is the target little or big endian? # AC_C_BIGENDIAN([ENDIAN="big"],[ENDIAN="little"],[ENDIAN="unknown"],[ENDIAN="universal_endianness"]) diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 index 06fba41d1a0..6c6c926edc1 100644 --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 @@ -600,14 +600,21 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_LD_VERSION], [ LINKER_VERSION_NUMBER=`$ECHO $LINKER_VERSION_STRING | \ $SED -e 's/.* \([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*/\1/'` ] elif test "x$TOOLCHAIN_TYPE" = xgcc; then - # gcc -Wl,-version output typically looks like + # gcc -Wl,-version output typically looks like: # GNU ld (GNU Binutils for Ubuntu) 2.26.1 # Copyright (C) 2015 Free Software Foundation, Inc. # This program is free software; [...] - LINKER_VERSION_STRING=`$LINKER -Wl,--version 2>&1 | $HEAD -n 1` + # If using gold it will look like: + # GNU gold (GNU Binutils 2.30) 1.15 + LINKER_VERSION_STRING=`$LINKER -Wl,--version 2> /dev/null | $HEAD -n 1` # Extract version number - [ LINKER_VERSION_NUMBER=`$ECHO $LINKER_VERSION_STRING | \ - $SED -e 's/.* \([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*/\1/'` ] + if [ [[ "$LINKER_VERSION_STRING" == *gold* ]] ]; then + [ LINKER_VERSION_NUMBER=`$ECHO $LINKER_VERSION_STRING | \ + $SED -e 's/.* \([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*) .*/\1/'` ] + else + [ LINKER_VERSION_NUMBER=`$ECHO $LINKER_VERSION_STRING | \ + $SED -e 's/.* \([0-9][0-9]*\(\.[0-9][0-9]*\)*\).*/\1/'` ] + fi elif test "x$TOOLCHAIN_TYPE" = xclang; then # clang -Wl,-v output typically looks like # @(#)PROGRAM:ld PROJECT:ld64-305 diff --git a/make/autoconf/version-numbers b/make/autoconf/version-numbers index 6ca0433951f..a9b6c2ea496 100644 --- a/make/autoconf/version-numbers +++ b/make/autoconf/version-numbers @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=11 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=19 +DEFAULT_VERSION_UPDATE=21 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2023-04-18 +DEFAULT_VERSION_DATE=2023-10-17 DEFAULT_VERSION_CLASSFILE_MAJOR=55 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_ACCEPTABLE_BOOT_VERSIONS="10 11" diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk index 4f846c78eba..95dc2d55b6f 100644 --- a/make/common/Modules.gmk +++ b/make/common/Modules.gmk @@ -337,6 +337,7 @@ $(MODULE_DEPS_MAKEFILE): $(MODULE_INFOS) \ sub(/\/\*.*\*\//, ""); \ gsub(/^ +\*.*/, ""); \ gsub(/ /, ""); \ + gsub(/\r/, ""); \ printf(" %s", $$0) } \ END { printf("\n") }' $m && \ $(PRINTF) "TRANSITIVE_MODULES_$(call GetModuleNameFromModuleInfo, $m) :=" && \ @@ -350,6 +351,7 @@ $(MODULE_DEPS_MAKEFILE): $(MODULE_INFOS) \ sub(/\/\*.*\*\//, ""); \ gsub(/^ +\*.*/, ""); \ gsub(/ /, ""); \ + gsub(/\r/, ""); \ printf(" %s", $$0) } \ END { printf("\n") }' $m \ ) >> $@ $(NEWLINE)) diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index ff982b62304..356f47998b3 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -862,18 +862,18 @@ var getJibProfilesProfiles = function (input, common, data) { var getJibProfilesDependencies = function (input, common) { var devkit_platform_revisions = { - linux_x64: "gcc7.3.0-OEL6.4+1.1", + linux_x64: "gcc8.2.0-OL6.4+1.0", macosx_x64: "Xcode11.3.1-MacOSX10.15+1.0", solaris_x64: "SS12u4-Solaris11u1+1.0", solaris_sparcv9: "SS12u4-Solaris11u1+1.1", windows_x64: "VS2017-15.9.16+1.1", linux_aarch64: (input.profile != null && input.profile.indexOf("arm64") >= 0 ? "gcc-linaro-aarch64-linux-gnu-4.8-2013.11_linux+1.0" - : "gcc7.3.0-Fedora27+1.1"), + : "gcc8.2.0-Fedora27+1.0"), linux_arm: (input.profile != null && input.profile.indexOf("hflt") >= 0 ? "gcc-linaro-arm-linux-gnueabihf-raspbian-2012.09-20120921_linux+1.0" : (input.profile != null && input.profile.indexOf("arm32") >= 0 - ? "gcc7.3.0-Fedora27+1.1" + ? "gcc8.2.0-Fedora27+1.0" : "arm-linaro-4.7+1.0" ) ) @@ -893,6 +893,11 @@ var getJibProfilesDependencies = function (input, common) { } } + var devkit_cross_prefix = ""; + if (input.target_platform != input.build_platform) { + devkit_cross_prefix = input.build_platform + "-to-"; + } + var boot_jdk_platform = (input.build_os == "macosx" ? "osx" : input.build_os) + "-" + input.build_cpu; @@ -916,7 +921,7 @@ var getJibProfilesDependencies = function (input, common) { devkit: { organization: common.organization, ext: "tar.gz", - module: "devkit-" + devkit_platform, + module: "devkit-" + devkit_cross_prefix + devkit_platform, revision: devkit_platform_revisions[devkit_platform], environment: { "DEVKIT_HOME": input.get("devkit", "home_path"), diff --git a/make/copy/Copy-java.base.gmk b/make/copy/Copy-java.base.gmk index 8fb59d2e655..320b4908549 100644 --- a/make/copy/Copy-java.base.gmk +++ b/make/copy/Copy-java.base.gmk @@ -31,7 +31,7 @@ $(eval $(call IncludeCustomExtension, copy/Copy-java.base.gmk)) ################################################################################ -ifeq ($(call isTargetOs, windows aix), true) +ifeq ($(call isTargetOs, aix), true) TZMAPPINGS_SRC := $(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/conf diff --git a/make/data/cacerts/gtsrootcar1 b/make/data/cacerts/gtsrootcar1 new file mode 100644 index 00000000000..ac879b232b2 --- /dev/null +++ b/make/data/cacerts/gtsrootcar1 @@ -0,0 +1,38 @@ +Owner: CN=GTS Root R1, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R1, O=Google Trust Services LLC, C=US +Serial number: 203e5936f31b01349886ba217 +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/gtsrootcar2 b/make/data/cacerts/gtsrootcar2 new file mode 100644 index 00000000000..a71e7c5e951 --- /dev/null +++ b/make/data/cacerts/gtsrootcar2 @@ -0,0 +1,38 @@ +Owner: CN=GTS Root R2, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R2, O=Google Trust Services LLC, C=US +Serial number: 203e5aec58d04251aab1125aa +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/gtsrootecccar3 b/make/data/cacerts/gtsrootecccar3 new file mode 100644 index 00000000000..38f7533ca58 --- /dev/null +++ b/make/data/cacerts/gtsrootecccar3 @@ -0,0 +1,20 @@ +Owner: CN=GTS Root R3, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R3, O=Google Trust Services LLC, C=US +Serial number: 203e5b882eb20f825276d3d66 +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/gtsrootecccar4 b/make/data/cacerts/gtsrootecccar4 new file mode 100644 index 00000000000..e812a16c24b --- /dev/null +++ b/make/data/cacerts/gtsrootecccar4 @@ -0,0 +1,20 @@ +Owner: CN=GTS Root R4, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R4, O=Google Trust Services LLC, C=US +Serial number: 203e5c068ef631a9c72905052 +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/microsoftecc2017 b/make/data/cacerts/microsoftecc2017 new file mode 100644 index 00000000000..8fc67332045 --- /dev/null +++ b/make/data/cacerts/microsoftecc2017 @@ -0,0 +1,22 @@ +Owner: CN=Microsoft ECC Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Issuer: CN=Microsoft ECC Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Serial number: 66f23daf87de8bb14aea0c573101c2ec +Valid from: Wed Dec 18 23:06:45 GMT 2019 until: Fri Jul 18 23:16:04 GMT 2042 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- diff --git a/make/data/cacerts/microsoftrsa2017 b/make/data/cacerts/microsoftrsa2017 new file mode 100644 index 00000000000..2d30c9688b7 --- /dev/null +++ b/make/data/cacerts/microsoftrsa2017 @@ -0,0 +1,40 @@ +Owner: CN=Microsoft RSA Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Issuer: CN=Microsoft RSA Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Serial number: 1ed397095fd8b4b347701eaabe7f45b3 +Valid from: Wed Dec 18 22:51:22 GMT 2019 until: Fri Jul 18 23:00:23 GMT 2042 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- diff --git a/make/data/cacerts/secomscrootca1 b/make/data/cacerts/secomscrootca1 deleted file mode 100644 index d300e31195d..00000000000 --- a/make/data/cacerts/secomscrootca1 +++ /dev/null @@ -1,27 +0,0 @@ -Owner: OU=Security Communication RootCA1, O=SECOM Trust.net, C=JP -Issuer: OU=Security Communication RootCA1, O=SECOM Trust.net, C=JP -Serial number: 0 -Valid from: Tue Sep 30 04:20:49 GMT 2003 until: Sat Sep 30 04:20:49 GMT 2023 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY -MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t -dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 -WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD -VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 -9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ -DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 -Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N -QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ -xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G -A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG -kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr -Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 -Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU -JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot -RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== ------END CERTIFICATE----- diff --git a/make/data/cacerts/twcaglobalrootca b/make/data/cacerts/twcaglobalrootca new file mode 100644 index 00000000000..f17112ad9a5 --- /dev/null +++ b/make/data/cacerts/twcaglobalrootca @@ -0,0 +1,38 @@ +Owner: CN=TWCA Global Root CA, OU=Root CA, O=TAIWAN-CA, C=TW +Issuer: CN=TWCA Global Root CA, OU=Root CA, O=TAIWAN-CA, C=TW +Serial number: cbe +Valid from: Wed Jun 27 06:28:33 GMT 2012 until: Tue Dec 31 15:59:59 GMT 2030 +Signature algorithm name: SHA256withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- diff --git a/make/data/characterdata/CharacterData00.java.template b/make/data/characterdata/CharacterData00.java.template index 7f61f7d970d..45557de3ea7 100644 --- a/make/data/characterdata/CharacterData00.java.template +++ b/make/data/characterdata/CharacterData00.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,9 +106,9 @@ class CharacterData00 extends CharacterData { boolean isJavaIdentifierStart(int ch) { // isJavaIdentifierStart strictly conforms to code points assigned - // in Unicode 10.0. Since code point {32FF} is not from Unicode 10.0, - // return false. - if(ch == 0x32FF) + // in Unicode 10.0. Since code points {32FF} and {9FEB..9FEF} are + // not from Unicode 10.0, return false. + if(ch == 0x32FF || (ch >= 0x9FEB && ch <= 0x9FEF)) return false; int props = getProperties(ch); return ((props & $$maskIdentifierInfo) >= $$lowJavaStart); @@ -116,9 +116,9 @@ class CharacterData00 extends CharacterData { boolean isJavaIdentifierPart(int ch) { // isJavaIdentifierPart strictly conforms to code points assigned - // in Unicode 10.0. Since code point {32FF} is not from Unicode 10.0, - // return false. - if(ch == 0x32FF) + // in Unicode 10.0. Since code points {32FF} and {9FEB..9FEF} are + // not from Unicode 10.0, return false. + if(ch == 0x32FF || (ch >= 0x9FEB && ch <= 0x9FEF)) return false; int props = getProperties(ch); return ((props & $$nonzeroJavaPart) != 0); diff --git a/make/data/charsetmapping/charsets b/make/data/charsetmapping/charsets index 7c946298e47..2198656dfdb 100644 --- a/make/data/charsetmapping/charsets +++ b/make/data/charsetmapping/charsets @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -477,6 +477,11 @@ charset x-IBM874 IBM874 alias ibm-874 alias 874 +# alias for GB18030 is generated at runtime +charset GB18030 GB18030 + package sun.nio.cs + type source + ######################################################## # # charsets provided by ExtendedCharsets provider. @@ -564,11 +569,6 @@ charset GBK GBK # Simplified Chinese alias windows-936 alias CP936 -charset GB18030 GB18030 - package sun.nio.cs.ext - type template - alias gb18030-2000 - charset GB2312 EUC_CN package sun.nio.cs.ext type dbcs diff --git a/make/data/charsetmapping/stdcs-aix b/make/data/charsetmapping/stdcs-aix index f17468bbdbc..750308b3bfb 100644 --- a/make/data/charsetmapping/stdcs-aix +++ b/make/data/charsetmapping/stdcs-aix @@ -7,7 +7,6 @@ Big5_HKSCS EUC_CN EUC_KR GBK -GB18030 IBM856 IBM921 IBM922 diff --git a/make/data/charsetmapping/stdcs-linux b/make/data/charsetmapping/stdcs-linux index 0a870b754e1..f74a095f283 100644 --- a/make/data/charsetmapping/stdcs-linux +++ b/make/data/charsetmapping/stdcs-linux @@ -11,7 +11,6 @@ EUC_JP_LINUX EUC_JP_Open EUC_TW GBK -GB18030 ISO_8859_11 ISO_8859_3 ISO_8859_6 diff --git a/make/data/charsetmapping/stdcs-solaris b/make/data/charsetmapping/stdcs-solaris index cf3c01f12f8..5583b8f70e2 100644 --- a/make/data/charsetmapping/stdcs-solaris +++ b/make/data/charsetmapping/stdcs-solaris @@ -11,7 +11,6 @@ EUC_JP_LINUX EUC_JP_Open EUC_TW GBK -GB18030 ISO_8859_11 ISO_8859_3 ISO_8859_6 diff --git a/make/data/charsetmapping/stdcs-windows b/make/data/charsetmapping/stdcs-windows index 6542de25d97..42da33d12d2 100644 --- a/make/data/charsetmapping/stdcs-windows +++ b/make/data/charsetmapping/stdcs-windows @@ -2,7 +2,6 @@ # generate these charsets into sun.nio.cs # GBK -GB18030 Johab MS1255 MS1256 diff --git a/make/data/currency/CurrencyData.properties b/make/data/currency/CurrencyData.properties index d234c96c476..12c0c69801e 100644 --- a/make/data/currency/CurrencyData.properties +++ b/make/data/currency/CurrencyData.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -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=174 +dataVersion=175 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. diff --git a/make/data/tzdata/VERSION b/make/data/tzdata/VERSION index 0f328a4a7ff..66bd061e8bc 100644 --- a/make/data/tzdata/VERSION +++ b/make/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2022g +tzdata2023c diff --git a/make/data/tzdata/africa b/make/data/tzdata/africa index 830d7d10b7e..a73405fdb01 100644 --- a/make/data/tzdata/africa +++ b/make/data/tzdata/africa @@ -344,6 +344,14 @@ Rule Egypt 2007 only - Sep Thu>=1 24:00 0 - # From Mina Samuel (2016-07-04): # Egyptian government took the decision to cancel the DST, +# From Ahmad ElDardiry (2023-03-01): +# Egypt officially announced today that daylight savings will be +# applied from last Friday of April to last Thursday of October. +# From Paul Eggert (2023-03-01): +# Assume transitions are at 00:00 and 24:00 respectively. +# From Amir Adib (2023-03-07): +# https://www.facebook.com/EgyptianCabinet/posts/638829614954129/ + Rule Egypt 2008 only - Aug lastThu 24:00 0 - Rule Egypt 2009 only - Aug 20 24:00 0 - Rule Egypt 2010 only - Aug 10 24:00 0 - @@ -353,6 +361,8 @@ Rule Egypt 2014 only - May 15 24:00 1:00 S Rule Egypt 2014 only - Jun 26 24:00 0 - Rule Egypt 2014 only - Jul 31 24:00 1:00 S Rule Egypt 2014 only - Sep lastThu 24:00 0 - +Rule Egypt 2023 max - Apr lastFri 0:00 1:00 S +Rule Egypt 2023 max - Oct lastThu 24:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] #STDOFF 2:05:08.9 @@ -452,7 +462,7 @@ Zone Africa/Nairobi 2:27:16 - LMT 1908 May # President William R. Tolbert, Jr., July 23, 1971-July 31, 1972. # Monrovia: Executive Mansion. # -# Use the abbreviation "MMT" before 1972, as the more-accurate numeric +# Use the abbreviation "MMT" before 1972, as the more accurate numeric # abbreviation "-004430" would be one byte over the POSIX limit. # # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -589,8 +599,8 @@ Zone Africa/Tripoli 0:52:44 - LMT 1920 # DST the coming summer... # # Some sources, in French: -# http://www.defimedia.info/news/946/Rashid-Beebeejaun-:-%C2%AB-L%E2%80%99heure-d%E2%80%99%C3%A9t%C3%A9-ne-sera-pas-appliqu%C3%A9e-cette-ann%C3%A9e-%C2%BB -# http://lexpress.mu/Story/3398~Beebeejaun---Les-objectifs-d-%C3%A9conomie-d-%C3%A9nergie-de-l-heure-d-%C3%A9t%C3%A9-ont-%C3%A9t%C3%A9-atteints- +# http://www.defimedia.info/news/946/Rashid-Beebeejaun-:-«-L%E2%80%99heure-d%E2%80%99été-ne-sera-pas-appliquée-cette-année-» +# http://lexpress.mu/Story/3398~Beebeejaun---Les-objectifs-d-économie-d-énergie-de-l-heure-d-été-ont-été-atteints- # # Our wrap-up: # https://www.timeanddate.com/news/time/mauritius-dst-will-not-repeat.html @@ -721,7 +731,7 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # More articles in the press # https://www.yabiladi.com/articles/details/5058/secret-l-heure-d-ete-maroc-leve.html # http://www.lematin.ma/Actualite/Express/Article.asp?id=148923 -# http://www.lavieeco.com/actualite/Le-Maroc-passe-sur-GMT%2B1-a-partir-de-dim +# http://www.lavieeco.com/actualite/Le-Maroc-passe-sur-GMT+1-a-partir-de-dim # From Petr Machata (2011-03-30): # They have it written in English here: @@ -736,7 +746,7 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # According to Infomédiaire web site from Morocco (infomediaire.ma), # on March 9, 2012, (in French) Heure légale: # Le Maroc adopte officiellement l'heure d'été -# http://www.infomediaire.ma/news/maroc/heure-l%C3%A9gale-le-maroc-adopte-officiellement-lheure-d%C3%A9t%C3%A9 +# http://www.infomediaire.ma/news/maroc/heure-légale-le-maroc-adopte-officiellement-lheure-dété # Governing Council adopted draft decree, that Morocco DST starts on # the last Sunday of March (March 25, 2012) and ends on # last Sunday of September (September 30, 2012) @@ -860,19 +870,28 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # Friday or Saturday (and so the 2 days off are on a weekend), the next time # shift will be the next weekend. # -# From Paul Eggert (2020-05-31): +# From Milamber (2021-03-31, 2022-03-10): +# https://www.mmsp.gov.ma/fr/actualites.aspx?id=2076 +# https://www.ecoactu.ma/horaires-administration-ramadan-gmtheure-gmt-a-partir-de-dimanche-27-mars/ +# +# From Milamber (2023-03-14, 2023-03-15): +# The return to legal GMT time will take place this Sunday, March 19 at 3 a.m. +# ... the return to GMT+1 will be made on Sunday April 23, 2023 at 2 a.m. +# https://www.mmsp.gov.ma/fr/actualites/passage-à-l%E2%80%99heure-gmt-à-partir-du-dimanche-19-mars-2023 +# +# From Paul Eggert (2023-03-14): # For now, guess that in the future Morocco will fall back at 03:00 # the last Sunday before Ramadan, and spring forward at 02:00 the -# first Sunday after two days after Ramadan. To implement this, +# first Sunday after one day after Ramadan. To implement this, # transition dates and times for 2019 through 2087 were determined by -# running the following program under GNU Emacs 26.3. (This algorithm +# running the following program under GNU Emacs 28.2. (This algorithm # also produces the correct transition dates for 2016 through 2018, # though the times differ due to Morocco's time zone change in 2018.) # (let ((islamic-year 1440)) # (require 'cal-islam) # (while (< islamic-year 1511) # (let ((a (calendar-islamic-to-absolute (list 9 1 islamic-year))) -# (b (+ 2 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) +# (b (+ 1 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) # (sunday 0)) # (while (/= sunday (mod (setq a (1- a)) 7))) # (while (/= sunday (mod b 7)) @@ -886,10 +905,6 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) # (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) # (setq islamic-year (+ 1 islamic-year)))) -# -# From Milamber (2021-03-31, 2022-03-10), confirming these predictions: -# https://www.mmsp.gov.ma/fr/actualites.aspx?id=2076 -# https://www.ecoactu.ma/horaires-administration-ramadan-gmtheure-gmt-a-partir-de-dimanche-27-mars/ # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Morocco 1939 only - Sep 12 0:00 1:00 - @@ -942,7 +957,7 @@ Rule Morocco 2021 only - May 16 2:00 0 - Rule Morocco 2022 only - Mar 27 3:00 -1:00 - Rule Morocco 2022 only - May 8 2:00 0 - Rule Morocco 2023 only - Mar 19 3:00 -1:00 - -Rule Morocco 2023 only - Apr 30 2:00 0 - +Rule Morocco 2023 only - Apr 23 2:00 0 - Rule Morocco 2024 only - Mar 10 3:00 -1:00 - Rule Morocco 2024 only - Apr 14 2:00 0 - Rule Morocco 2025 only - Feb 23 3:00 -1:00 - @@ -958,7 +973,7 @@ Rule Morocco 2029 only - Feb 18 2:00 0 - Rule Morocco 2029 only - Dec 30 3:00 -1:00 - Rule Morocco 2030 only - Feb 10 2:00 0 - Rule Morocco 2030 only - Dec 22 3:00 -1:00 - -Rule Morocco 2031 only - Feb 2 2:00 0 - +Rule Morocco 2031 only - Jan 26 2:00 0 - Rule Morocco 2031 only - Dec 14 3:00 -1:00 - Rule Morocco 2032 only - Jan 18 2:00 0 - Rule Morocco 2032 only - Nov 28 3:00 -1:00 - @@ -974,7 +989,7 @@ Rule Morocco 2036 only - Nov 23 2:00 0 - Rule Morocco 2037 only - Oct 4 3:00 -1:00 - Rule Morocco 2037 only - Nov 15 2:00 0 - Rule Morocco 2038 only - Sep 26 3:00 -1:00 - -Rule Morocco 2038 only - Nov 7 2:00 0 - +Rule Morocco 2038 only - Oct 31 2:00 0 - Rule Morocco 2039 only - Sep 18 3:00 -1:00 - Rule Morocco 2039 only - Oct 23 2:00 0 - Rule Morocco 2040 only - Sep 2 3:00 -1:00 - @@ -990,7 +1005,7 @@ Rule Morocco 2044 only - Aug 28 2:00 0 - Rule Morocco 2045 only - Jul 9 3:00 -1:00 - Rule Morocco 2045 only - Aug 20 2:00 0 - Rule Morocco 2046 only - Jul 1 3:00 -1:00 - -Rule Morocco 2046 only - Aug 12 2:00 0 - +Rule Morocco 2046 only - Aug 5 2:00 0 - Rule Morocco 2047 only - Jun 23 3:00 -1:00 - Rule Morocco 2047 only - Jul 28 2:00 0 - Rule Morocco 2048 only - Jun 7 3:00 -1:00 - @@ -1006,7 +1021,7 @@ Rule Morocco 2052 only - Jun 2 2:00 0 - Rule Morocco 2053 only - Apr 13 3:00 -1:00 - Rule Morocco 2053 only - May 25 2:00 0 - Rule Morocco 2054 only - Apr 5 3:00 -1:00 - -Rule Morocco 2054 only - May 17 2:00 0 - +Rule Morocco 2054 only - May 10 2:00 0 - Rule Morocco 2055 only - Mar 28 3:00 -1:00 - Rule Morocco 2055 only - May 2 2:00 0 - Rule Morocco 2056 only - Mar 12 3:00 -1:00 - @@ -1022,7 +1037,7 @@ Rule Morocco 2060 only - Mar 7 2:00 0 - Rule Morocco 2061 only - Jan 16 3:00 -1:00 - Rule Morocco 2061 only - Feb 27 2:00 0 - Rule Morocco 2062 only - Jan 8 3:00 -1:00 - -Rule Morocco 2062 only - Feb 19 2:00 0 - +Rule Morocco 2062 only - Feb 12 2:00 0 - Rule Morocco 2062 only - Dec 31 3:00 -1:00 - Rule Morocco 2063 only - Feb 4 2:00 0 - Rule Morocco 2063 only - Dec 16 3:00 -1:00 - @@ -1038,7 +1053,7 @@ Rule Morocco 2067 only - Dec 11 2:00 0 - Rule Morocco 2068 only - Oct 21 3:00 -1:00 - Rule Morocco 2068 only - Dec 2 2:00 0 - Rule Morocco 2069 only - Oct 13 3:00 -1:00 - -Rule Morocco 2069 only - Nov 24 2:00 0 - +Rule Morocco 2069 only - Nov 17 2:00 0 - Rule Morocco 2070 only - Oct 5 3:00 -1:00 - Rule Morocco 2070 only - Nov 9 2:00 0 - Rule Morocco 2071 only - Sep 20 3:00 -1:00 - @@ -1054,7 +1069,7 @@ Rule Morocco 2075 only - Sep 15 2:00 0 - Rule Morocco 2076 only - Jul 26 3:00 -1:00 - Rule Morocco 2076 only - Sep 6 2:00 0 - Rule Morocco 2077 only - Jul 18 3:00 -1:00 - -Rule Morocco 2077 only - Aug 29 2:00 0 - +Rule Morocco 2077 only - Aug 22 2:00 0 - Rule Morocco 2078 only - Jul 10 3:00 -1:00 - Rule Morocco 2078 only - Aug 14 2:00 0 - Rule Morocco 2079 only - Jun 25 3:00 -1:00 - @@ -1064,13 +1079,13 @@ Rule Morocco 2080 only - Jul 21 2:00 0 - Rule Morocco 2081 only - Jun 1 3:00 -1:00 - Rule Morocco 2081 only - Jul 13 2:00 0 - Rule Morocco 2082 only - May 24 3:00 -1:00 - -Rule Morocco 2082 only - Jul 5 2:00 0 - +Rule Morocco 2082 only - Jun 28 2:00 0 - Rule Morocco 2083 only - May 16 3:00 -1:00 - Rule Morocco 2083 only - Jun 20 2:00 0 - Rule Morocco 2084 only - Apr 30 3:00 -1:00 - Rule Morocco 2084 only - Jun 11 2:00 0 - Rule Morocco 2085 only - Apr 22 3:00 -1:00 - -Rule Morocco 2085 only - Jun 3 2:00 0 - +Rule Morocco 2085 only - May 27 2:00 0 - Rule Morocco 2086 only - Apr 14 3:00 -1:00 - Rule Morocco 2086 only - May 19 2:00 0 - Rule Morocco 2087 only - Mar 30 3:00 -1:00 - @@ -1213,15 +1228,15 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 # From P Chan (2020-12-03): # GMT was adopted as the standard time of Lagos on 1905-07-01. # Lagos Weekly Record, 1905-06-24, p 3 -# http://ddsnext.crl.edu/titles/31558#?c=0&m=668&s=0&cv=2&r=0&xywh=1446%2C5221%2C1931%2C1235 +# http://ddsnext.crl.edu/titles/31558#?c=0&m=668&s=0&cv=2&r=0&xywh=1446,5221,1931,1235 # says "It is officially notified that on and after the 1st of July 1905 -# Greenwich Mean Solar Time will be adopted thought the Colony and +# Greenwich Mean Solar Time will be adopted throughout the Colony and # Protectorate, and that it will be necessary to put all clocks 13 minutes and # 35 seconds back, recording local mean time." # # It seemed that Lagos returned to LMT on 1908-07-01. # [The Lagos Standard], 1908-07-01, p 5 -# http://ddsnext.crl.edu/titles/31556#?c=0&m=78&s=0&cv=4&r=0&xywh=-92%2C3590%2C3944%2C2523 +# http://ddsnext.crl.edu/titles/31556#?c=0&m=78&s=0&cv=4&r=0&xywh=-92,3590,3944,2523 # says "Scarcely have the people become accustomed to this new time, when # another official notice has now appeared announcing that from and after the # 1st July next, return will be made to local mean time." @@ -1233,7 +1248,7 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 # https://libsysdigi.library.illinois.edu/ilharvest/Africana/Books2011-05/3064634/3064634_1914/3064634_1914_opt.pdf#page=27 # "On January 1st [1914], a universal standard time for Nigeria was adopted, # viz., half an hour fast on Greenwich mean time, corresponding to the meridian -# 7 [degrees] 30' E. long." +# 7° 30' E. long." # Lloyd's Register of Shipping (1915) says "Hitherto the time observed in Lagos # was the local mean time. On 1st January, 1914, standard time for the whole of # Nigeria was introduced ... Lagos time has been advanced about 16 minutes @@ -1251,7 +1266,7 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 # The Lagos Weekly Record, 1919-09-20, p 3 details discussion on the first # reading of this Bill by the Legislative Council of the Colony of Nigeria on # Thursday 1919-08-28: -# http://ddsnext.crl.edu/titles/31558?terms&item_id=303484#?m=1118&c=1&s=0&cv=2&r=0&xywh=1261%2C3408%2C2994%2C1915 +# http://ddsnext.crl.edu/titles/31558?terms&item_id=303484#?m=1118&c=1&s=0&cv=2&r=0&xywh=1261,3408,2994,1915 # "The proposal is that the Globe should be divided into twelve zones East and # West of Greenwich, of one hour each, Nigeria falling into the zone with a # standard of one hour fast on Greenwich Mean Time. Nigeria standard time is diff --git a/make/data/tzdata/antarctica b/make/data/tzdata/antarctica index 792542b9224..3de5e726eb4 100644 --- a/make/data/tzdata/antarctica +++ b/make/data/tzdata/antarctica @@ -315,7 +315,7 @@ Zone Antarctica/Rothera 0 - -00 1976 Dec 1 # but that he found it more convenient to keep GMT+12 # as supplies for the station were coming from McMurdo Sound, # which was on GMT+12 because New Zealand was on GMT+12 all year -# at that time (1957). (Source: Siple's book 90 Degrees South.) +# at that time (1957). (Source: Siple's book 90° South.) # # From Susan Smith # http://www.cybertours.com/whs/pole10.html diff --git a/make/data/tzdata/asia b/make/data/tzdata/asia index ff81978bc47..6a048c3ad28 100644 --- a/make/data/tzdata/asia +++ b/make/data/tzdata/asia @@ -2714,6 +2714,40 @@ Zone Asia/Pyongyang 8:23:00 - LMT 1908 Apr 1 # Lebanon +# +# From Saadallah Itani (2023-03-23): +# Lebanon ... announced today delay of Spring forward from March 25 to April 20. +# +# From Paul Eggert (2023-03-27): +# This announcement was by the Lebanese caretaker prime minister Najib Mikati. +# https://www.mtv.com.lb/en/News/Local/1352516/lebanon-postpones-daylight-saving-time-adoption +# A video was later leaked to the media of parliament speaker Nabih Berri +# asking Mikati to postpone DST to aid observance of Ramadan, Mikati objecting +# that this would cause problems such as scheduling airline flights, to which +# Berri interjected, "What flights?" +# +# The change was controversial and led to a partly-sectarian divide. +# Many Lebanese institutions, including the education ministry, the Maronite +# church, and two news channels LCBI and MTV, ignored the announcement and +# went ahead with the long-scheduled spring-forward on March 25/26, some +# arguing that the prime minister had not followed the law because the change +# had not been approved by the cabinet. Google went with the announcement; +# Apple ignored it. At least one bank followed the announcement for its doors, +# but ignored the announcement in internal computer systems. +# Beirut international airport listed two times for each departure. +# Dan Azzi wrote "My view is that this whole thing is a Dumb and Dumber movie." +# Eventually the prime minister backed down, said the cabinet had decided to +# stick with its 1998 decision, and that DST would begin midnight March 29/30. +# https://www.nna-leb.gov.lb/en/miscellaneous/604093/lebanon-has-two-times-of-day-amid-daylight-savings +# https://www.cnbc.com/2023/03/27/lebanon-in-two-different-time-zones-as-government-disagrees-on-daylight-savings.html +# +# Although we could model the chaos with two Zones, that would likely cause +# more trouble than it would cure. Since so many manual clocks and +# computer-based timestamps ignored the announcement, stick with official +# cabinet resolutions in the data while recording the prime minister's +# announcement as a comment. This is how we treated a similar situation in +# Rio de Janeiro in spring 1993. +# # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Lebanon 1920 only - Mar 28 0:00 1:00 S Rule Lebanon 1920 only - Oct 25 0:00 0 - @@ -2739,6 +2773,10 @@ Rule Lebanon 1992 only - Oct 4 0:00 0 - Rule Lebanon 1993 max - Mar lastSun 0:00 1:00 S Rule Lebanon 1993 1998 - Sep lastSun 0:00 0 - Rule Lebanon 1999 max - Oct lastSun 0:00 0 - +# This one-time rule, announced by the prime minister first for April 21 +# then for March 30, is commented out for reasons described above. +#Rule Lebanon 2023 only - Mar 30 0:00 1:00 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Beirut 2:22:00 - LMT 1880 2:00 Lebanon EE%sT @@ -2977,7 +3015,7 @@ Zone Asia/Kathmandu 5:41:16 - LMT 1920 # 9pm and moving clocks forward by one hour for the next three months. ...." # # http://www.worldtimezone.com/dst_news/dst_news_pakistan01.html -# http://www.dailytimes.com.pk/default.asp?page=2008%5C05%5C15%5Cstory_15-5-2008_pg1_4 +# http://www.dailytimes.com.pk/default.asp?page=2008\05\15\story_15-5-2008_pg1_4 # From Arthur David Olson (2008-05-19): # XXX--midnight transitions is a guess; 2008 only is a guess. @@ -3300,7 +3338,7 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # Some of many sources in Arabic: # http://www.samanews.com/index.php?act=Show&id=122638 # -# http://safa.ps/details/news/74352/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-%D8%A8%D8%A7%D9%84%D8%B6%D9%81%D8%A9-%D9%88%D8%BA%D8%B2%D8%A9-%D9%84%D9%8A%D9%84%D8%A9-%D8%A7%D9%84%D8%AC%D9%85%D8%B9%D8%A9.html +# http://safa.ps/details/news/74352/بدء-التوقيت-الصيفي-بالضفة-وغزة-ليلة-الجمعة.html # # Our brief summary: # https://www.timeanddate.com/news/time/gaza-west-bank-dst-2012.html @@ -3310,7 +3348,7 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # time from midnight on Friday, March 29, 2013" (translated). # [These are in Arabic and are for Gaza and for Ramallah, respectively.] # http://www.samanews.com/index.php?act=Show&id=154120 -# http://safa.ps/details/news/99844/%D8%B1%D8%A7%D9%85-%D8%A7%D9%84%D9%84%D9%87-%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-29-%D8%A7%D9%84%D8%AC%D8%A7%D8%B1%D9%8A.html +# http://safa.ps/details/news/99844/رام-الله-بدء-التوقيت-الصيفي-29-الجاري.html # From Steffen Thorsen (2013-09-24): # The Gaza and West Bank are ending DST Thursday at midnight @@ -3408,9 +3446,41 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # (2022-08-31): ... the Saturday before the last Sunday in March and October # at 2:00 AM ,for the years from 2023 to 2026. # (2022-09-05): https://mtit.pna.ps/Site/New/1453 -# -# From Paul Eggert (2022-08-31): -# For now, assume that this rule will also be used after 2026. + +# From Heba Hamad (2023-03-22): +# ... summer time will begin in Palestine from Saturday 04-29-2023, +# 02:00 AM by 60 minutes forward. +# +# From Paul Eggert (2023-03-22): +# For now, guess that spring and fall transitions will normally +# continue to use 2022's rules, that during DST Palestine will switch +# to standard time at 02:00 the last Saturday before Ramadan and back +# to DST at 02:00 the first Saturday after Ramadan, and that +# if the normal spring-forward or fall-back transition occurs during +# Ramadan the former is delayed and the latter advanced. +# To implement this, I predicted Ramadan-oriented transition dates for +# 2023 through 2086 by running the following program under GNU Emacs 28.2, +# with the results integrated by hand into the table below. +# Predictions after 2086 are approximated without Ramadan. +# +# (let ((islamic-year 1444)) +# (require 'cal-islam) +# (while (< islamic-year 1510) +# (let ((a (calendar-islamic-to-absolute (list 9 1 islamic-year))) +# (b (+ 1 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) +# (saturday 6)) +# (while (/= saturday (mod (setq a (1- a)) 7))) +# (while (/= saturday (mod b 7)) +# (setq b (1+ b))) +# (setq a (calendar-gregorian-from-absolute a)) +# (setq b (calendar-gregorian-from-absolute b)) +# (insert +# (format +# (concat "Rule Palestine\t%d\tonly\t-\t%s\t%2d\t2:00\t0\t-\n" +# "Rule Palestine\t%d\tonly\t-\t%s\t%2d\t2:00\t1:00\tS\n") +# (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) +# (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) +# (setq islamic-year (+ 1 islamic-year)))) # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule EgyptAsia 1957 only - May 10 0:00 1:00 S @@ -3450,8 +3520,86 @@ Rule Palestine 2020 2021 - Mar Sat<=30 0:00 1:00 S Rule Palestine 2020 only - Oct 24 1:00 0 - Rule Palestine 2021 only - Oct 29 1:00 0 - Rule Palestine 2022 only - Mar 27 0:00 1:00 S -Rule Palestine 2022 max - Oct Sat<=30 2:00 0 - -Rule Palestine 2023 max - Mar Sat<=30 2:00 1:00 S +Rule Palestine 2022 2035 - Oct Sat<=30 2:00 0 - +Rule Palestine 2023 only - Apr 29 2:00 1:00 S +Rule Palestine 2024 only - Apr 13 2:00 1:00 S +Rule Palestine 2025 only - Apr 5 2:00 1:00 S +Rule Palestine 2026 2054 - Mar Sat<=30 2:00 1:00 S +Rule Palestine 2036 only - Oct 18 2:00 0 - +Rule Palestine 2037 only - Oct 10 2:00 0 - +Rule Palestine 2038 only - Sep 25 2:00 0 - +Rule Palestine 2039 only - Sep 17 2:00 0 - +Rule Palestine 2039 only - Oct 22 2:00 1:00 S +Rule Palestine 2039 2067 - Oct Sat<=30 2:00 0 - +Rule Palestine 2040 only - Sep 1 2:00 0 - +Rule Palestine 2040 only - Oct 13 2:00 1:00 S +Rule Palestine 2041 only - Aug 24 2:00 0 - +Rule Palestine 2041 only - Sep 28 2:00 1:00 S +Rule Palestine 2042 only - Aug 16 2:00 0 - +Rule Palestine 2042 only - Sep 20 2:00 1:00 S +Rule Palestine 2043 only - Aug 1 2:00 0 - +Rule Palestine 2043 only - Sep 12 2:00 1:00 S +Rule Palestine 2044 only - Jul 23 2:00 0 - +Rule Palestine 2044 only - Aug 27 2:00 1:00 S +Rule Palestine 2045 only - Jul 15 2:00 0 - +Rule Palestine 2045 only - Aug 19 2:00 1:00 S +Rule Palestine 2046 only - Jun 30 2:00 0 - +Rule Palestine 2046 only - Aug 11 2:00 1:00 S +Rule Palestine 2047 only - Jun 22 2:00 0 - +Rule Palestine 2047 only - Jul 27 2:00 1:00 S +Rule Palestine 2048 only - Jun 6 2:00 0 - +Rule Palestine 2048 only - Jul 18 2:00 1:00 S +Rule Palestine 2049 only - May 29 2:00 0 - +Rule Palestine 2049 only - Jul 3 2:00 1:00 S +Rule Palestine 2050 only - May 21 2:00 0 - +Rule Palestine 2050 only - Jun 25 2:00 1:00 S +Rule Palestine 2051 only - May 6 2:00 0 - +Rule Palestine 2051 only - Jun 17 2:00 1:00 S +Rule Palestine 2052 only - Apr 27 2:00 0 - +Rule Palestine 2052 only - Jun 1 2:00 1:00 S +Rule Palestine 2053 only - Apr 12 2:00 0 - +Rule Palestine 2053 only - May 24 2:00 1:00 S +Rule Palestine 2054 only - Apr 4 2:00 0 - +Rule Palestine 2054 only - May 16 2:00 1:00 S +Rule Palestine 2055 only - May 1 2:00 1:00 S +Rule Palestine 2056 only - Apr 22 2:00 1:00 S +Rule Palestine 2057 only - Apr 7 2:00 1:00 S +Rule Palestine 2058 max - Mar Sat<=30 2:00 1:00 S +Rule Palestine 2068 only - Oct 20 2:00 0 - +Rule Palestine 2069 only - Oct 12 2:00 0 - +Rule Palestine 2070 only - Oct 4 2:00 0 - +Rule Palestine 2071 only - Sep 19 2:00 0 - +Rule Palestine 2072 only - Sep 10 2:00 0 - +Rule Palestine 2072 only - Oct 15 2:00 1:00 S +Rule Palestine 2073 only - Sep 2 2:00 0 - +Rule Palestine 2073 only - Oct 7 2:00 1:00 S +Rule Palestine 2074 only - Aug 18 2:00 0 - +Rule Palestine 2074 only - Sep 29 2:00 1:00 S +Rule Palestine 2075 only - Aug 10 2:00 0 - +Rule Palestine 2075 only - Sep 14 2:00 1:00 S +Rule Palestine 2075 max - Oct Sat<=30 2:00 0 - +Rule Palestine 2076 only - Jul 25 2:00 0 - +Rule Palestine 2076 only - Sep 5 2:00 1:00 S +Rule Palestine 2077 only - Jul 17 2:00 0 - +Rule Palestine 2077 only - Aug 28 2:00 1:00 S +Rule Palestine 2078 only - Jul 9 2:00 0 - +Rule Palestine 2078 only - Aug 13 2:00 1:00 S +Rule Palestine 2079 only - Jun 24 2:00 0 - +Rule Palestine 2079 only - Aug 5 2:00 1:00 S +Rule Palestine 2080 only - Jun 15 2:00 0 - +Rule Palestine 2080 only - Jul 20 2:00 1:00 S +Rule Palestine 2081 only - Jun 7 2:00 0 - +Rule Palestine 2081 only - Jul 12 2:00 1:00 S +Rule Palestine 2082 only - May 23 2:00 0 - +Rule Palestine 2082 only - Jul 4 2:00 1:00 S +Rule Palestine 2083 only - May 15 2:00 0 - +Rule Palestine 2083 only - Jun 19 2:00 1:00 S +Rule Palestine 2084 only - Apr 29 2:00 0 - +Rule Palestine 2084 only - Jun 10 2:00 1:00 S +Rule Palestine 2085 only - Apr 21 2:00 0 - +Rule Palestine 2085 only - Jun 2 2:00 1:00 S +Rule Palestine 2086 only - Apr 13 2:00 0 - +Rule Palestine 2086 only - May 18 2:00 1:00 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Gaza 2:17:52 - LMT 1900 Oct @@ -3655,7 +3803,7 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 # standard time is SLST. # # From Paul Eggert (2016-10-18): -# "SLST" seems to be reasonably recent and rarely-used outside time +# "SLST" seems to be reasonably recent and rarely used outside time # zone nerd sources. I searched Google News and found three uses of # it in the International Business Times of India in February and # March of this year when discussing cricket match times, but nothing diff --git a/make/data/tzdata/australasia b/make/data/tzdata/australasia index fbe3b8a6d72..893d7055eab 100644 --- a/make/data/tzdata/australasia +++ b/make/data/tzdata/australasia @@ -346,7 +346,7 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov # From Steffen Thorsen (2013-01-10): # Fiji will end DST on 2014-01-19 02:00: -# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVINGS-TO-END-THIS-MONTH-%281%29.aspx +# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVINGS-TO-END-THIS-MONTH-(1).aspx # From Ken Rylander (2014-10-20): # DST will start Nov. 2 this year. @@ -746,7 +746,7 @@ Zone Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 # # Samoa's Daylight Saving Time Act 2009 is available here, but does not # contain any dates: -# http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20%28English%29%20-%20Final%207-7-091.pdf +# http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20(English)%20-%20Final%207-7-091.pdf # From Laupue Raymond Hughes (2010-10-07): # Please see @@ -1831,7 +1831,7 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # period. It would probably be reasonable to assume Guam use GMT+9 during # that period of time like the surrounding area. -# From Paul Eggert (2018-11-18): +# From Paul Eggert (2023-01-23): # Howse writes (p 153) "The Spaniards, on the other hand, reached the # Philippines and the Ladrones from America," and implies that the Ladrones # (now called the Marianas) kept American date for quite some time. @@ -1844,7 +1844,7 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # they did as that avoids the need for a separate zone due to our 1970 cutoff. # # US Public Law 106-564 (2000-12-23) made UT +10 the official standard time, -# under the name "Chamorro Standard Time". There is no official abbreviation, +# under the name "Chamorro standard time". There is no official abbreviation, # but Congressman Robert A. Underwood, author of the bill that became law, # wrote in a press release (2000-12-27) that he will seek the use of "ChST". @@ -2222,24 +2222,18 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # an international standard, there are some places on the high seas where the # correct date is ambiguous. -# From Wikipedia (2005-08-31): -# Before 1920, all ships kept local apparent time on the high seas by setting -# their clocks at night or at the morning sight so that, given the ship's -# speed and direction, it would be 12 o'clock when the Sun crossed the ship's -# meridian (12 o'clock = local apparent noon). During 1917, at the -# Anglo-French Conference on Time-keeping at Sea, it was recommended that all -# ships, both military and civilian, should adopt hourly standard time zones -# on the high seas. Whenever a ship was within the territorial waters of any -# nation it would use that nation's standard time. The captain was permitted -# to change his ship's clocks at a time of his choice following his ship's -# entry into another zone time - he often chose midnight. These zones were -# adopted by all major fleets between 1920 and 1925 but not by many -# independent merchant ships until World War II. - -# From Paul Eggert, using references suggested by Oscar van Vlijmen -# (2005-03-20): -# -# The American Practical Navigator (2002) -# http://pollux.nss.nima.mil/pubs/pubs_j_apn_sections.html?rid=187 -# talks only about the 180-degree meridian with respect to ships in -# international waters; it ignores the international date line. +# From Wikipedia (2023-01-23): +# The nautical time zone system is analogous to the terrestrial time zone +# system for use on high seas. Under the system time changes are required for +# changes of longitude in one-hour steps. The one-hour step corresponds to a +# time zone width of 15° longitude. The 15° gore that is offset from GMT or +# UT1 (not UTC) by twelve hours is bisected by the nautical date line into two +# 7°30' gores that differ from GMT by ±12 hours. A nautical date line is +# implied but not explicitly drawn on time zone maps. It follows the 180th +# meridian except where it is interrupted by territorial waters adjacent to +# land, forming gaps: it is a pole-to-pole dashed line. + +# From Paul Eggert (2023-01-23): +# The American Practical Navigator , +# 2019 edition, merely says that the International Date Line +# "coincides with the 180th meridian over most of its length." diff --git a/make/data/tzdata/backward b/make/data/tzdata/backward index fa44f655009..c0746d6dd1b 100644 --- a/make/data/tzdata/backward +++ b/make/data/tzdata/backward @@ -297,6 +297,7 @@ Link America/Argentina/Cordoba America/Rosario Link America/Tijuana America/Santa_Isabel Link America/Denver America/Shiprock Link America/Toronto America/Thunder_Bay +Link America/Edmonton America/Yellowknife Link Pacific/Auckland Antarctica/South_Pole Link Asia/Shanghai Asia/Chongqing Link Asia/Shanghai Asia/Harbin diff --git a/make/data/tzdata/europe b/make/data/tzdata/europe index acc5da3ec79..446d2e1e658 100644 --- a/make/data/tzdata/europe +++ b/make/data/tzdata/europe @@ -540,9 +540,7 @@ Zone Europe/London -0:01:15 - LMT 1847 Dec 1 # other form with a traditional approximation for Irish timestamps # after 1971-10-31 02:00 UTC; although this approximation has tm_isdst # flags that are reversed, its UTC offsets are correct and this often -# suffices. This source file currently uses only nonnegative SAVE -# values, but this is intended to change and downstream code should -# not rely on it. +# suffices.... # # The following is like GB-Eire and EU, except with standard time in # summer and negative daylight saving time in winter. It is for when @@ -1136,19 +1134,18 @@ Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn # # From Jürgen Appel (2022-11-25): # https://ina.gl/samlinger/oversigt-over-samlinger/samling/dagsordener/dagsorden.aspx?lang=da&day=24-11-2022 -# If I understand this correctly, from the next planned switch to -# summer time, Greenland will permanently stay at that time, i.e. no -# switch back to winter time in 2023 will occur. -# -# From Paul Eggert (2022-11-28): -# The official document in Danish -# https://naalakkersuisut.gl/-/media/naalakkersuisut/filer/kundgoerelser/2022/11/2511/31_da_inatsisartutlov-om-tidens-bestemmelse.pdf?la=da&hash=A33597D8A38CC7038465241119EF34F3 -# says standard time for Greenland is -02, that Naalakkersuisut can lay down -# rules for DST and can require some areas to use a different time zone, -# and that this all takes effect 2023-03-25 22:00. The abovementioned -# "bekymringer" URL says the intent is no transition March 25, that -# Greenland will not go back to winter time in fall 2023, and that -# only America/Nuuk is affected (though further changes may occur). +# +# From Thomas M. Steenholdt (2022-12-02): +# - The bill to move America/Nuuk from UTC-03 to UTC-02 passed. +# - The bill to stop observing DST did not (Greenland will stop observing DST +# when EU does). +# Details on the implementation are here (section 6): +# https://ina.gl/dvd/EM%202022/pdf/media/2553529/pkt17_em2022_tidens_bestemmelse_bem_da.pdf +# This is how the change will be implemented: +# 1. The shift *to* DST in 2023 happens as normal. +# 2. The shift *from* DST in 2023 happens as normal, but coincides with the +# shift to UTC-02 normaltime (people will not change their clocks here). +# 3. After this, DST is still observed, but as -02/-01 instead of -03/-02. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Thule 1991 1992 - Mar lastSun 2:00 1:00 D @@ -1172,8 +1169,8 @@ Zone America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 # Ittoqqortoormiit -1:00 EU -01/+00 Zone America/Nuuk -3:26:56 - LMT 1916 Jul 28 # Godthåb -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 2023 Mar 25 22:00 - -2:00 - -02 + -3:00 EU -03/-02 2023 Oct 29 1:00u + -2:00 EU -02/-01 Zone America/Thule -4:35:08 - LMT 1916 Jul 28 # Pituffik -4:00 Thule A%sT @@ -1509,9 +1506,9 @@ Zone Europe/Paris 0:09:21 - LMT 1891 Mar 16 Rule Germany 1946 only - Apr 14 2:00s 1:00 S Rule Germany 1946 only - Oct 7 2:00s 0 - Rule Germany 1947 1949 - Oct Sun>=1 2:00s 0 - -# http://www.ptb.de/de/org/4/44/441/salt.htm says the following transition -# occurred at 3:00 MEZ, not the 2:00 MEZ given in Shanks & Pottenger. -# Go with the PTB. +# https://www.ptb.de/cms/en/ptb/fachabteilungen/abt4/fb-44/ag-441/realisation-of-legal-time-in-germany/dst-and-midsummer-dst-in-germany-until-1979.html +# says the following transition occurred at 3:00 MEZ, not the 2:00 MEZ +# given in Shanks & Pottenger. Go with the PTB. Rule Germany 1947 only - Apr 6 3:00s 1:00 S Rule Germany 1947 only - May 11 2:00s 2:00 M Rule Germany 1947 only - Jun 29 3:00 1:00 S @@ -2272,7 +2269,7 @@ Zone Europe/Bucharest 1:44:24 - LMT 1891 Oct # the State Duma has approved ... the draft bill on returning to # winter time standard and return Russia 11 time zones. The new # regulations will come into effect on October 26, 2014 at 02:00 ... -# http://asozd2.duma.gov.ru/main.nsf/%28Spravka%29?OpenAgent&RN=431985-6&02 +# http://asozd2.duma.gov.ru/main.nsf/(Spravka)?OpenAgent&RN=431985-6&02 # Here is a link where we put together table (based on approved Bill N # 431985-6) with proposed 11 Russian time zones and corresponding # areas/cities/administrative centers in the Russian Federation (in English): @@ -2682,13 +2679,13 @@ Zone Europe/Volgograd 2:57:40 - LMT 1920 Jan 3 3:00 - +03 1930 Jun 21 4:00 - +04 1961 Nov 11 4:00 Russia +04/+05 1988 Mar 27 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s + 3:00 Russia MSK/MSD 1991 Mar 31 2:00s 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2018 Oct 28 2:00s + 3:00 Russia MSK/MSD 2011 Mar 27 2:00s + 4:00 - MSK 2014 Oct 26 2:00s + 3:00 - MSK 2018 Oct 28 2:00s 4:00 - +04 2020 Dec 27 2:00s - 3:00 - +03 + 3:00 - MSK # From Paul Eggert (2016-11-11): # Europe/Saratov covers: @@ -2719,11 +2716,11 @@ Zone Europe/Saratov 3:04:18 - LMT 1919 Jul 1 0:00u Zone Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0:00u 3:00 - +03 1930 Jun 21 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s + 3:00 Russia MSK/MSD 1991 Mar 31 2:00s 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 + 3:00 Russia MSK/MSD 2011 Mar 27 2:00s + 4:00 - MSK 2014 Oct 26 2:00s + 3:00 - MSK # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): # Europe/Samara covers... diff --git a/make/data/tzdata/iso3166.tab b/make/data/tzdata/iso3166.tab index fbfb74bec45..cea17732dd1 100644 --- a/make/data/tzdata/iso3166.tab +++ b/make/data/tzdata/iso3166.tab @@ -261,7 +261,7 @@ SY Syria SZ Eswatini (Swaziland) TC Turks & Caicos Is TD Chad -TF French Southern Territories +TF French S. Terr. TG Togo TH Thailand TJ Tajikistan diff --git a/make/data/tzdata/leapseconds b/make/data/tzdata/leapseconds index d6fb840f512..89ce8b89cd2 100644 --- a/make/data/tzdata/leapseconds +++ b/make/data/tzdata/leapseconds @@ -95,11 +95,11 @@ Leap 2016 Dec 31 23:59:60 + S # Any additional leap seconds will come after this. # This Expires line is commented out for now, # so that pre-2020a zic implementations do not reject this file. -#Expires 2023 Jun 28 00:00:00 +#Expires 2023 Dec 28 00:00:00 # POSIX timestamps for the data in this file: #updated 1467936000 (2016-07-08 00:00:00 UTC) -#expires 1687910400 (2023-06-28 00:00:00 UTC) +#expires 1703721600 (2023-12-28 00:00:00 UTC) -# Updated through IERS Bulletin C64 -# File expires on: 28 June 2023 +# Updated through IERS Bulletin C65 +# File expires on: 28 December 2023 diff --git a/make/data/tzdata/northamerica b/make/data/tzdata/northamerica index a5fd701f88c..e240cf35103 100644 --- a/make/data/tzdata/northamerica +++ b/make/data/tzdata/northamerica @@ -1,4 +1,3 @@ -# # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -299,9 +298,10 @@ Zone PST8PDT -8:00 US P%sT # -10 Standard Alaska Time (AST) Alaska-Hawaii standard time (AHST) # -11 (unofficial) Nome (NST) Bering standard time (BST) # -# From Paul Eggert (2000-01-08), following a heads-up from Rives McDow: -# Public law 106-564 (2000-12-23) introduced ... "Chamorro Standard Time" +# From Paul Eggert (2023-01-23), from a 2001-01-08 heads-up from Rives McDow: +# Public law 106-564 (2000-12-23) introduced "Chamorro standard time" # for time in Guam and the Northern Marianas. See the file "australasia". +# Also see 15 U.S.C. §263 . # # From Paul Eggert (2015-04-17): # HST and HDT are standardized abbreviations for Hawaii-Aleutian @@ -618,7 +618,7 @@ Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 20:00u # local times of other Alaskan locations so that they change simultaneously. # From Paul Eggert (2014-07-18): -# One opinion of the early-1980s turmoil in Alaska over time zones and +# One opinion of the early 1980s turmoil in Alaska over time zones and # daylight saving time appeared as graffiti on a Juneau airport wall: # "Welcome to Juneau. Please turn your watch back to the 19th century." # See: Turner W. Alaska's four time zones now two. NY Times 1983-11-01. @@ -690,6 +690,10 @@ Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 20:00u # So they won't be waiting for Alaska to join them on 2019-03-10, but will # rather change their clocks twice in seven weeks. +# From Paul Eggert (2023-01-23): +# America/Adak is for the Aleutian Islands that are part of Alaska +# and are west of 169.5° W. + # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Juneau 15:02:19 - LMT 1867 Oct 19 15:33:32 -8:57:41 - LMT 1900 Aug 20 12:00 @@ -2148,10 +2152,6 @@ Zone America/Fort_Nelson -8:10:47 - LMT 1884 # Nunavut ... moved ... to incorporate the whole territory into one time zone. # Nunavut moves to single time zone Oct. 31 # http://www.nunatsiaq.com/nunavut/nvt90903_13.html -# -# From Antoine Leca (1999-09-06): -# We then need to create a new timezone for the Kitikmeot region of Nunavut -# to differentiate it from the Yellowknife region. # From Paul Eggert (1999-09-20): # Basic Facts: The New Territory @@ -2345,9 +2345,6 @@ Zone America/Cambridge_Bay 0 - -00 1920 # trading post est.? -5:00 - EST 2000 Nov 5 0:00 -6:00 - CST 2001 Apr 1 3:00 -7:00 Canada M%sT -Zone America/Yellowknife 0 - -00 1935 # Yellowknife founded? - -7:00 NT_YK M%sT 1980 - -7:00 Canada M%sT Zone America/Inuvik 0 - -00 1953 # Inuvik founded -8:00 NT_YK P%sT 1979 Apr lastSun 2:00 -7:00 NT_YK M%sT 1980 @@ -2584,7 +2581,7 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 # and in addition changes all of Chihuahua to -06 with no DST. # From Heitor David Pinto (2022-11-28): -# Now the northern municipalities want to have the same time zone as the +# Now the northern [municipios] want to have the same time zone as the # respective neighboring cities in the US, for example Juárez in UTC-7 with # DST, matching El Paso, and Ojinaga in UTC-6 with DST, matching Presidio.... # the president authorized the publication of the decree for November 29, @@ -2621,7 +2618,7 @@ Zone America/Merida -5:58:28 - LMT 1922 Jan 1 6:00u -5:00 - EST 1982 Dec 2 -6:00 Mexico C%sT # Coahuila, Nuevo León, Tamaulipas (near US border) -# This includes the following municipalities: +# This includes the following municipios: # in Coahuila: Acuña, Allende, Guerrero, Hidalgo, Jiménez, Morelos, Nava, # Ocampo, Piedras Negras, Villa Unión, Zaragoza # in Nuevo León: Anáhuac @@ -2647,8 +2644,8 @@ Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u -6:00 - CST 2002 Feb 20 -6:00 Mexico C%sT # Chihuahua (near US border - western side) -# This includes the municipalities of Janos, Ascensión, Juárez, Guadalupe, -# and Práxedis G Guerrero. +# This includes the municipios of Janos, Ascensión, Juárez, Guadalupe, and +# Práxedis G Guerrero. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u -7:00 - MST 1927 Jun 10 23:00 @@ -2662,7 +2659,8 @@ Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u -6:00 - CST 2022 Nov 30 0:00 -7:00 US M%sT # Chihuahua (near US border - eastern side) -# The municipalities of Coyame del Sotol, Ojinaga, and Manuel Benavides. +# This includes the municipios of Coyame del Sotol, Ojinaga, and Manuel +# Benavides. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u -7:00 - MST 1927 Jun 10 23:00 @@ -3083,7 +3081,7 @@ Zone America/Costa_Rica -5:36:13 - LMT 1890 # San José # # He supplied these references: # -# http://www.prensalatina.com.mx/article.asp?ID=%7B4CC32C1B-A9F7-42FB-8A07-8631AFC923AF%7D&language=ES +# http://www.prensalatina.com.mx/article.asp?ID={4CC32C1B-A9F7-42FB-8A07-8631AFC923AF}&language=ES # http://actualidad.terra.es/sociedad/articulo/cuba_llama_ahorrar_energia_cambio_1957044.htm # # From Alex Krivenyshev (2007-10-25): diff --git a/make/data/tzdata/southamerica b/make/data/tzdata/southamerica index 81fdd793df4..4024e7180cd 100644 --- a/make/data/tzdata/southamerica +++ b/make/data/tzdata/southamerica @@ -231,7 +231,7 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 - # Hora de verano para la República Argentina # http://buenasiembra.com.ar/esoterismo/astrologia/hora-de-verano-de-la-republica-argentina-27.html # says that standard time in Argentina from 1894-10-31 -# to 1920-05-01 was -4:16:48.25. Go with this more-precise value +# to 1920-05-01 was -4:16:48.25. Go with this more precise value # over Shanks & Pottenger. It is upward compatible with Milne, who # says Córdoba time was -4:16:48.2. diff --git a/make/data/tzdata/zone.tab b/make/data/tzdata/zone.tab index 939432d3456..3edb0d61c80 100644 --- a/make/data/tzdata/zone.tab +++ b/make/data/tzdata/zone.tab @@ -144,9 +144,8 @@ CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) +CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) -CA +6227-11421 America/Yellowknife Mountain - NT (central) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +4906-11631 America/Creston MST - BC (Creston) CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) @@ -162,7 +161,7 @@ CG -0416+01517 Africa/Brazzaville CH +4723+00832 Europe/Zurich CI +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga -CL -3327-07040 America/Santiago Chile (most areas) +CL -3327-07040 America/Santiago most of Chile CL -5309-07055 America/Punta_Arenas Region of Magallanes CL -2709-10926 Pacific/Easter Easter Island CM +0403+00942 Africa/Douala @@ -174,10 +173,10 @@ CU +2308-08222 America/Havana CV +1455-02331 Atlantic/Cape_Verde CW +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas -CY +3510+03322 Asia/Nicosia Cyprus (most areas) +CY +3510+03322 Asia/Nicosia most of Cyprus CY +3507+03357 Asia/Famagusta Northern Cyprus CZ +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin Germany (most areas) +DE +5230+01322 Europe/Berlin most of Germany DE +4742+00841 Europe/Busingen Busingen DJ +1136+04309 Africa/Djibouti DK +5540+01235 Europe/Copenhagen @@ -210,7 +209,7 @@ GF +0456-05220 America/Cayenne GG +492717-0023210 Europe/Guernsey GH +0533-00013 Africa/Accra GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Nuuk Greenland (most areas) +GL +6411-05144 America/Nuuk most of Greenland GL +7646-01840 America/Danmarkshavn National Park (east coast) GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit GL +7634-06847 America/Thule Thule/Pituffik @@ -258,7 +257,7 @@ KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul KW +2920+04759 Asia/Kuwait KY +1918-08123 America/Cayman -KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) +KZ +4315+07657 Asia/Almaty most of Kazakhstan KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay KZ +5017+05710 Asia/Aqtobe Aqtobe/Aktobe @@ -282,12 +281,12 @@ MD +4700+02850 Europe/Chisinau ME +4226+01916 Europe/Podgorica MF +1804-06305 America/Marigot MG -1855+04731 Indian/Antananarivo -MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) +MH +0709+17112 Pacific/Majuro most of Marshall Islands MH +0905+16720 Pacific/Kwajalein Kwajalein MK +4159+02126 Europe/Skopje ML +1239-00800 Africa/Bamako MM +1647+09610 Asia/Yangon -MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) +MN +4755+10653 Asia/Ulaanbaatar most of Mongolia MN +4801+09139 Asia/Hovd Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan MN +4804+11430 Asia/Choibalsan Dornod, Sukhbaatar MO +221150+1133230 Asia/Macau @@ -325,7 +324,7 @@ NO +5955+01045 Europe/Oslo NP +2743+08519 Asia/Kathmandu NR -0031+16655 Pacific/Nauru NU -1901-16955 Pacific/Niue -NZ -3652+17446 Pacific/Auckland New Zealand (most areas) +NZ -3652+17446 Pacific/Auckland most of New Zealand NZ -4357-17633 Pacific/Chatham Chatham Islands OM +2336+05835 Asia/Muscat PA +0858-07932 America/Panama @@ -333,7 +332,7 @@ PE -1203-07703 America/Lima PF -1732-14934 Pacific/Tahiti Society Islands PF -0900-13930 Pacific/Marquesas Marquesas Islands PF -2308-13457 Pacific/Gambier Gambier Islands -PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) +PG -0930+14710 Pacific/Port_Moresby most of Papua New Guinea PG -0613+15534 Pacific/Bougainville Bougainville PH +1435+12100 Asia/Manila PK +2452+06703 Asia/Karachi @@ -379,7 +378,7 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky RU +5934+15048 Asia/Magadan MSK+08 - Magadan RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea RW -0157+03004 Africa/Kigali @@ -420,7 +419,7 @@ TT +1039-06131 America/Port_of_Spain TV -0831+17913 Pacific/Funafuti TW +2503+12130 Asia/Taipei TZ -0648+03917 Africa/Dar_es_Salaam -UA +5026+03031 Europe/Kyiv Ukraine (most areas) +UA +5026+03031 Europe/Kyiv most of Ukraine UG +0019+03225 Africa/Kampala UM +2813-17722 Pacific/Midway Midway Islands UM +1917+16637 Pacific/Wake Wake Island @@ -443,7 +442,7 @@ US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) US +433649-1161209 America/Boise Mountain - ID (south); OR (east) -US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) +US +332654-1120424 America/Phoenix MST - AZ (except Navajo) US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) US +581807-1342511 America/Juneau Alaska - Juneau area @@ -451,7 +450,7 @@ US +571035-1351807 America/Sitka Alaska - Sitka area US +550737-1313435 America/Metlakatla Alaska - Annette Island US +593249-1394338 America/Yakutat Alaska - Yakutat US +643004-1652423 America/Nome Alaska (west) -US +515248-1763929 America/Adak Aleutian Islands +US +515248-1763929 America/Adak Alaska - western Aleutians US +211825-1575130 Pacific/Honolulu Hawaii UY -345433-0561245 America/Montevideo UZ +3940+06648 Asia/Samarkand Uzbekistan (west) diff --git a/make/data/unicodedata/UnicodeData.txt b/make/data/unicodedata/UnicodeData.txt index dd137e2546d..45c43a0c91c 100644 --- a/make/data/unicodedata/UnicodeData.txt +++ b/make/data/unicodedata/UnicodeData.txt @@ -12053,7 +12053,7 @@ 4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;; 4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;; 4E00;;Lo;0;L;;;;;N;;;;; -9FEA;;Lo;0;L;;;;;N;;;;; +9FEF;;Lo;0;L;;;;;N;;;;; A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;; A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;; A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;; diff --git a/make/devkit/Makefile b/make/devkit/Makefile index c1dc8c44b76..c85a7c21d29 100644 --- a/make/devkit/Makefile +++ b/make/devkit/Makefile @@ -29,14 +29,10 @@ # gcc based cross compilation, portable, self contained packages, capable # of building OpenJDK. # -# In addition to the makefiles, access to Oracle Linux installation -# media is required. This has been tested against Oracle Enterprise Linux -# 6.4. -# # By default this Makefile will build a native toolchain for the current # platform if called with something like this: # -# make tars BASE_OS=OEL6 +# make tars BASE_OS=OL # # To build the full set of crosstools for additional platforms, use a command # line looking like this: @@ -48,9 +44,19 @@ # to build several devkits for a specific OS version at once. # You can find the final results under ../../build/devkit/result/-to- # +# You may want the native toolchain to be used when compiling the cross +# compilation toolchains. To achieve this, first build the native toolchain, +# then add the bin directory from this build to the path when invoking this +# makefile again for cross compilation. Ex: +# +# PATH=$PWD/../../build/devkit/result/x86_64-linux-gnu-to-x86_64-linux-gnu/bin:$PATH \ +# make TARGETS="arm-linux-gnueabihf,ppc64-linux-gnu" BASE_OS=Fedora +# # This is the makefile which iterates over all host and target platforms. # +COMMA := , + os := $(shell uname -o) cpu := $(shell uname -p) @@ -66,7 +72,7 @@ ifeq ($(TARGETS), ) platforms := $(me) host_platforms := $(platforms) else - platforms := $(TARGETS) + platforms := $(subst $(COMMA), , $(TARGETS)) host_platforms := $(me) endif target_platforms := $(platforms) @@ -93,7 +99,7 @@ $(host_platforms) : $(MAKE) -f Tools.gmk all $(submakevars) \ TARGET=$$p PREFIX=$(RESULT)/$@-to-$$p && \ $(MAKE) -f Tools.gmk ccache $(submakevars) \ - TARGET=$@ PREFIX=$(RESULT)/$@-to-$$p BUILDDIR=$(OUTPUT_ROOT)/$@/$$p || exit 1 ; \ + TARGET=$@ PREFIX=$(RESULT)/$@-to-$$p || exit 1 ; \ done @echo 'All done"' @@ -103,7 +109,6 @@ define Mktar $(1)-to-$(2)_tar = $$(RESULT)/sdk-$(1)-to-$(2)-$$(today).tar.gz $$($(1)-to-$(2)_tar) : PLATFORM = $(1)-to-$(2) TARFILES += $$($(1)-to-$(2)_tar) - $$($(1)-to-$(2)_tar) : $$(shell find $$(RESULT)/$(1)-to-$(2) -type f) endef $(foreach p,$(host_platforms),$(foreach t,$(target_platforms),$(eval $(call Mktar,$(p),$(t))))) @@ -111,9 +116,7 @@ $(foreach p,$(host_platforms),$(foreach t,$(target_platforms),$(eval $(call Mkta tars : all $(TARFILES) onlytars : $(TARFILES) %.tar.gz : - @echo 'Creating compiler package $@' - cd $(RESULT) && tar -czf $@ $(PLATFORM)/* - touch $@ + $(MAKE) -r -f Tars.gmk SRC_DIR=$(RESULT)/$(PLATFORM) TAR_FILE=$@ clean : rm -rf $(addprefix ../../build/devkit/, result $(host_platforms)) diff --git a/make/devkit/Tars.gmk b/make/devkit/Tars.gmk new file mode 100644 index 00000000000..80bba3d0242 --- /dev/null +++ b/make/devkit/Tars.gmk @@ -0,0 +1,49 @@ +# +# Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# This makefile creates the tar files. It needs to be called with make -r +# which is why it's separated out into its own file. + +# Input parameters +# SRC_DIR +# TAR_FILE + +ifeq ($(SRC_DIR), ) + $(error SRC_DIR not set) +endif +ifeq ($(TAR_FILE), ) + $(error TAR_FILE not set) +endif + +default: tars + +tars : $(TAR_FILE) + +$(TAR_FILE): $(shell find $(SRC_DIR) -type f) + @echo 'Creating compiler package $@' + cd $(dir $(SRC_DIR)) && tar -czf $@ $(notdir $(SRC_DIR))/* + touch $@ + +.PHONY: default tars diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk index 21069910b55..6a716fe37ef 100644 --- a/make/devkit/Tools.gmk +++ b/make/devkit/Tools.gmk @@ -51,9 +51,9 @@ endif $(info ARCH=$(ARCH)) -ifeq ($(BASE_OS), OEL6) +ifeq ($(BASE_OS), OL) BASE_URL := http://yum.oracle.com/repo/OracleLinux/OL6/4/base/$(ARCH)/ - LINUX_VERSION := OEL6.4 + LINUX_VERSION := OL6.4 else ifeq ($(BASE_OS), Fedora) DEFAULT_OS_VERSION := 27 ifeq ($(BASE_OS_VERSION), ) @@ -79,8 +79,17 @@ endif # Define external dependencies # Latest that could be made to work. -GCC_VER := 7.3.0 -ifeq ($(GCC_VER), 7.3.0) +GCC_VER := 8.2.0 +ifeq ($(GCC_VER), 8.2.0) + gcc_ver := gcc-8.2.0 + binutils_ver := binutils-2.30 + ccache_ver := ccache-3.5.1a + CCACHE_DIRNAME := ccache-3.5.1 + mpfr_ver := mpfr-3.1.5 + gmp_ver := gmp-6.1.2 + mpc_ver := mpc-1.0.3 + gdb_ver := gdb-8.2.1 +else ifeq ($(GCC_VER), 7.3.0) gcc_ver := gcc-7.3.0 binutils_ver := binutils-2.30 ccache_ver := ccache-3.3.6 @@ -174,14 +183,16 @@ download-rpms: # Generate downloading + unpacking of sources. define Download - $(1)_DIR = $(abspath $(SRCDIR)/$(basename $(basename $(notdir $($(1)))))) + # Allow override + $(1)_DIRNAME ?= $(basename $(basename $(notdir $($(1))))) + $(1)_DIR = $(abspath $(SRCDIR)/$$($(1)_DIRNAME)) $(1)_CFG = $$($(1)_DIR)/configure $(1)_FILE = $(DOWNLOAD)/$(notdir $($(1))) $$($(1)_CFG) : $$($(1)_FILE) mkdir -p $$(SRCDIR) tar -C $$(SRCDIR) -xf $$< - $$(foreach p,$$(abspath $$(wildcard $$(notdir $$($(1)_DIR)).patch)), \ + $$(foreach p,$$(abspath $$(wildcard patches/$$(notdir $$($(1)_DIR)).patch)), \ echo PATCHING $$(p) ; \ patch -d $$($(1)_DIR) -p1 -i $$(p) ; \ ) diff --git a/make/gensrc/GensrcCLDR.gmk b/make/gensrc/GensrcCLDR.gmk index 870cabe5501..2f742782637 100644 --- a/make/gensrc/GensrcCLDR.gmk +++ b/make/gensrc/GensrcCLDR.gmk @@ -37,9 +37,17 @@ CLDR_BASE_LOCALES := "en-US" ZONENAME_TEMPLATE := $(TOPDIR)/src/java.base/share/classes/java/time/format/ZoneName.java.template TZDATA_DIR := $(TOPDIR)/make/data/tzdata -$(CLDR_BASEMETAINFO_FILE): $(wildcard $(CLDRSRCDIR)/common/dtd/*.dtd) \ - $(wildcard $(CLDRSRCDIR)/common/main/en*.xml) \ - $(wildcard $(CLDRSRCDIR)/common/supplemental/*.xml) \ +# tzmappings generation for Windows. +# Since the rule is shared with CLDR_BASEMETAINFO_FILE target, note that +# just removing the target tzmappings will not recreate the tzmappings file. +ifeq ($(OPENJDK_TARGET_OS), windows) + CLDR_WINTZMAPPINGS := $(GENSRC_BASEDIR)/windows/conf/tzmappings + $(CLDR_WINTZMAPPINGS): $(CLDR_BASEMETAINFO_FILE) +endif + +$(CLDR_BASEMETAINFO_FILE): $(wildcard $(CLDRSRCDIR)/dtd/*.dtd) \ + $(wildcard $(CLDRSRCDIR)/main/en*.xml) \ + $(wildcard $(CLDRSRCDIR)/supplemental/*.xml) \ $(ZONENAME_TEMPLATE) \ $(BUILD_TOOLS_JDK) $(MKDIR) -p $(GENSRC_BASEDIR) @@ -50,14 +58,14 @@ $(CLDR_BASEMETAINFO_FILE): $(wildcard $(CLDRSRCDIR)/common/dtd/*.dtd) \ -zntempfile $(ZONENAME_TEMPLATE) \ -tzdatadir $(TZDATA_DIR) -$(CLDR_METAINFO_FILE): $(wildcard $(CLDRSRCDIR)/common/dtd/*.dtd) \ - $(wildcard $(CLDRSRCDIR)/common/main/*.xml) \ - $(wildcard $(CLDRSRCDIR)/common/supplemental/*.xml) \ +$(CLDR_METAINFO_FILE): $(wildcard $(CLDRSRCDIR)/dtd/*.dtd) \ + $(wildcard $(CLDRSRCDIR)/main/*.xml) \ + $(wildcard $(CLDRSRCDIR)/supplemental/*.xml) \ $(BUILD_TOOLS_JDK) $(MKDIR) -p $(GENSRC_DIR) $(TOOL_CLDRCONVERTER) -base $(CLDRSRCDIR) \ -baselocales $(CLDR_BASE_LOCALES) \ -o $(GENSRC_DIR) -GENSRC_JAVA_BASE += $(CLDR_BASEMETAINFO_FILE) +GENSRC_JAVA_BASE += $(CLDR_BASEMETAINFO_FILE) $(CLDR_WINTZMAPPINGS) GENSRC_JDK_LOCALEDATA += $(CLDR_METAINFO_FILE) diff --git a/make/jdk/src/classes/build/tools/charsetmapping/SPI.java b/make/jdk/src/classes/build/tools/charsetmapping/SPI.java index 3ed09fd938b..15e0b9fcf68 100644 --- a/make/jdk/src/classes/build/tools/charsetmapping/SPI.java +++ b/make/jdk/src/classes/build/tools/charsetmapping/SPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,18 +50,19 @@ public static void genClass(String type, out.println(line); } else { charsets.values() - .stream() - .filter(cs -> cs.pkgName.equals("sun.nio.cs.ext") && - !cs.isInternal && - (cs.os == null || cs.os.equals(os))) - .forEach( cs -> { - out.printf(" charset(\"%s\", \"%s\",%n", cs.csName, cs.clzName); - out.printf(" new String[] {%n"); - for (String alias : cs.aliases) { - out.printf(" \"%s\",%n", alias); - } - out.printf(" });%n%n"); - }); + .stream() + .filter(cs -> cs.pkgName.equals("sun.nio.cs.ext") && + !cs.isInternal && + (cs.os == null || cs.os.equals(os))) + .forEach( cs -> { + out.printf(" charset(\"%s\", \"%s\",%n", cs.csName, cs.clzName); + out.printf(" new String[] {%n"); + for (String alias : cs.aliases) { + out.printf(" \"%s\",%n", + alias); + } + out.printf(" });%n%n"); + }); } } } else if (type.startsWith("stdcs")) { // StandardCharsets.java @@ -93,8 +94,15 @@ public static void genClass(String type, .filter(cs -> cs.pkgName.equals("sun.nio.cs")) .forEach( cs -> { if (cs.aliases == null || cs.aliases.length == 0) { - out.printf(" static String[] aliases_%s() { return null; }%n%n", - cs.clzName); + if (cs.csName.equals("GB18030")) { + out.printf(" static String[] aliases_GB18030() { return new String[] {%n"); + out.printf(" GB18030.IS_2000 ? \"gb18030-2000\" : \"gb18030-2022\"%n"); + out.printf(" };%n"); + out.printf(" }%n%n"); + } else { + out.printf(" static String[] aliases_%s() { return null; }%n%n", + cs.clzName); + } } else { boolean methodEnd = true; // non-final for SJIS and MS932 to support sun.nio.cs.map diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index 034b8ade437..068b8701d70 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -68,6 +68,7 @@ public class CLDRConverter { private static String METAZONES_SOURCE_FILE; private static String LIKELYSUBTAGS_SOURCE_FILE; private static String TIMEZONE_SOURCE_FILE; + private static String WINZONES_SOURCE_FILE; static String DESTINATION_DIR = "build/gensrc"; static final String LOCALE_NAME_PREFIX = "locale.displayname."; @@ -92,6 +93,7 @@ public class CLDRConverter { private static SupplementDataParseHandler handlerSuppl; private static LikelySubtagsParseHandler handlerLikelySubtags; + private static WinZonesParseHandler handlerWinZones; static SupplementalMetadataParseHandler handlerSupplMeta; static NumberingSystemsParseHandler handlerNumbering; static MetaZonesParseHandler handlerMetaZones; @@ -242,6 +244,7 @@ public static void main(String[] args) throws Exception { METAZONES_SOURCE_FILE = CLDR_BASE + "/supplemental/metaZones.xml"; TIMEZONE_SOURCE_FILE = CLDR_BASE + "/bcp47/timezone.xml"; SPPL_META_SOURCE_FILE = CLDR_BASE + "/supplemental/supplementalMetadata.xml"; + WINZONES_SOURCE_FILE = CLDR_BASE + "/supplemental/windowsZones.xml"; if (BASE_LOCALES.isEmpty()) { setupBaseLocales("en-US"); @@ -256,9 +259,12 @@ public static void main(String[] args) throws Exception { List bundles = readBundleList(); convertBundles(bundles); - // Generate java.time.format.ZoneName.java if (isBaseModule) { + // Generate java.time.format.ZoneName.java generateZoneName(); + + // Generate Windows tzmappings + generateWindowsTZMappings(); } } @@ -442,6 +448,10 @@ private static void parseSupplemental() throws Exception { // Currently interested in deprecated time zone ids and language aliases. handlerSupplMeta = new SupplementalMetadataParseHandler(); parseLDMLFile(new File(SPPL_META_SOURCE_FILE), handlerSupplMeta); + + // Parse windowsZones + handlerWinZones = new WinZonesParseHandler(); + parseLDMLFile(new File(WINZONES_SOURCE_FILE), handlerWinZones); } // Parsers for data in "bcp47" directory @@ -1062,4 +1072,42 @@ private static Stream extractLinks(Path tzFile) { throw new UncheckedIOException(e); } } + + // Generate tzmappings for Windows. The format is: + // + // (Windows Zone Name):(REGION):(Java TZID) + // + // where: + // Windows Zone Name: arbitrary time zone name string used in Windows + // REGION: ISO3166 or UN M.49 code + // Java TZID: Java's time zone ID + // + // Note: the entries are alphabetically sorted, *except* the "world" region + // code, i.e., "001". It should be the last entry for the same windows time + // zone name entries. (cf. TimeZone_md.c) + private static void generateWindowsTZMappings() throws Exception { + Files.createDirectories(Paths.get(DESTINATION_DIR, "windows", "conf")); + Files.write(Paths.get(DESTINATION_DIR, "windows", "conf", "tzmappings"), + handlerWinZones.keySet().stream() + .map(k -> k + ":" + handlerWinZones.get(k) + ":") + .sorted(new Comparator() { + public int compare(String t1, String t2) { + String[] s1 = t1.split(":"); + String[] s2 = t2.split(":"); + if (s1[0].equals(s2[0])) { + if (s1[1].equals("001")) { + return 1; + } else if (s2[1].equals("001")) { + return -1; + } else { + return s1[1].compareTo(s2[1]); + } + } else { + return s1[0].compareTo(s2[0]); + } + } + }) + .collect(Collectors.toList()), + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } } diff --git a/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java new file mode 100644 index 00000000000..a584358f0cb --- /dev/null +++ b/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package build.tools.cldrconverter; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Handles parsing of files in Locale Data Markup Language for + * windowsZones.xml + */ + +class WinZonesParseHandler extends AbstractLDMLHandler { + @Override + public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException { + // avoid HTTP traffic to unicode.org + if (systemID.startsWith(CLDRConverter.SPPL_LDML_DTD_SYSTEM_ID)) { + return new InputSource((new File(CLDRConverter.LOCAL_SPPL_LDML_DTD)).toURI().toString()); + } + return null; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + switch (qName) { + case "mapZone": + String zoneName = attributes.getValue("other"); + String territory = attributes.getValue("territory"); + String javatz = attributes.getValue("type").replaceFirst("\\s.*", ""); + put(zoneName + ":" + territory, javatz); + pushIgnoredContainer(qName); + break; + default: + // treat anything else as a container + pushContainer(qName, attributes); + break; + } + } +} diff --git a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java index 148cdbc3eca..39aa5e35d6e 100644 --- a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java +++ b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java @@ -65,8 +65,6 @@ import java.text.ParsePosition; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -76,6 +74,7 @@ import java.util.regex.Matcher; import java.util.regex.MatchResult; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * A compiler that reads a set of TZDB time-zone files and builds a single @@ -256,8 +255,10 @@ private void outputFile(Path dstFile, String version, for (String regionId : regionArray) { out.writeUTF(regionId); } - // rules -- hashset -> remove the dup - List rulesList = new ArrayList<>(new HashSet<>(builtZones.values())); + // rules -- remove the dup + List rulesList = builtZones.values().stream() + .distinct() + .collect(Collectors.toList()); out.writeShort(rulesList.size()); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); for (ZoneRules rules : rulesList) { diff --git a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java index 2622a8f9267..f02537c305d 100644 --- a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java +++ b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java @@ -33,7 +33,7 @@ import java.nio.file.Paths; import java.util.*; import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; import java.time.*; import java.time.Year; import java.time.chrono.IsoChronology; @@ -119,18 +119,18 @@ public ZoneRules getZoneRules(String zoneId) { /** * Zone region to rules mapping */ - private final Map zones = new ConcurrentHashMap<>(); + private final Map zones = new ConcurrentSkipListMap<>(); /** * compatibility list */ - private static HashSet excludedZones; + private static Set excludedZones; static { // (1) exclude EST, HST and MST. They are supported // via the short-id mapping // (2) remove UTC and GMT // (3) remove ROC, which is not supported in j.u.tz - excludedZones = new HashSet<>(10); + excludedZones = new TreeSet<>(); excludedZones.add("EST"); excludedZones.add("HST"); excludedZones.add("MST"); @@ -139,8 +139,8 @@ public ZoneRules getZoneRules(String zoneId) { excludedZones.add("ROC"); } - private Map links = new HashMap<>(150); - private Map> rules = new HashMap<>(500); + private Map links = new TreeMap<>(); + private Map> rules = new TreeMap<>(); private void load(List files) throws IOException { diff --git a/make/lib/Awt2dLibraries.gmk b/make/lib/Awt2dLibraries.gmk index 282b59a76ce..19ef7ba0410 100644 --- a/make/lib/Awt2dLibraries.gmk +++ b/make/lib/Awt2dLibraries.gmk @@ -551,7 +551,7 @@ else -DHB_NO_PRAGMA_GCC_DIAGNOSTIC endif ifeq ($(call isTargetOs, linux macosx), true) - HARFBUZZ_CFLAGS += -DHAVE_INTEL_ATOMIC_PRIMITIVES + HARFBUZZ_CFLAGS += -DHAVE_INTEL_ATOMIC_PRIMITIVES -DHB_NO_VISIBILITY endif ifeq ($(call isTargetOs, solaris), true) HARFBUZZ_CFLAGS += -DHAVE_SOLARIS_ATOMIC_OPS @@ -559,9 +559,12 @@ else # hb-ft.cc is not presently needed, and requires freetype 2.4.2 or later. LIBFONTMANAGER_EXCLUDE_FILES += libharfbuzz/hb-ft.cc - HARFBUZZ_DISABLED_WARNINGS_gcc := type-limits missing-field-initializers strict-aliasing + HARFBUZZ_DISABLED_WARNINGS_gcc := type-limits missing-field-initializers strict-aliasing \ + array-bounds + # noexcept-type required for GCC 7 builds. Not required for GCC 8+. + # expansion-to-defined required for GCC 9 builds. Not required for GCC 10+. HARFBUZZ_DISABLED_WARNINGS_CXX_gcc := reorder delete-non-virtual-dtor strict-overflow \ - maybe-uninitialized class-memaccess unused-result extra + maybe-uninitialized class-memaccess unused-result extra noexcept-type expansion-to-defined HARFBUZZ_DISABLED_WARNINGS_clang := unused-value incompatible-pointer-types \ tautological-constant-out-of-range-compare int-to-pointer-cast \ undef missing-field-initializers deprecated-declarations c++11-narrowing range-loop-analysis diff --git a/make/lib/Lib-java.base.gmk b/make/lib/Lib-java.base.gmk index e1fc94b5b41..0d1d83cf3e3 100644 --- a/make/lib/Lib-java.base.gmk +++ b/make/lib/Lib-java.base.gmk @@ -231,3 +231,14 @@ ifeq ($(STATIC_BUILD), true) TARGETS += $(JAVA_BASE_EXPORT_SYMBOL_FILE) endif + +################################################################################ +# Copy tzmappings file for Windows + +ifeq ($(OPENJDK_TARGET_OS), windows) + $(eval $(call SetupCopyFiles, COPY_TZMAPPINGS, \ + FILES := $(SUPPORT_OUTPUTDIR)/gensrc/java.base/windows/conf/tzmappings, \ + DEST := $(call FindLibDirForModule, $(MODULE)), \ + )) + TARGETS += $(COPY_TZMAPPINGS) +endif diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index faa09baca13..09885e688d5 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -876,7 +876,7 @@ endif ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT - BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c + BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libnativeStack.c BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exejvm-test-launcher := jvm.lib @@ -1515,6 +1515,7 @@ else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libgetphase002 += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libterminatedThread += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm + BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread endif $(eval $(call SetupTestFilesCompilation, BUILD_HOTSPOT_JTREG_LIBRARIES, \ diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 1e4ee33a9db..9d03d720480 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -971,6 +971,26 @@ reg_class v3_reg( V3, V3_H ); +// Class for 128 bit register v4 +reg_class v4_reg( + V4, V4_H +); + +// Class for 128 bit register v5 +reg_class v5_reg( + V5, V5_H +); + +// Class for 128 bit register v6 +reg_class v6_reg( + V6, V6_H +); + +// Class for 128 bit register v7 +reg_class v7_reg( + V7, V7_H +); + // Singleton class for condition codes reg_class int_flags(RFLAGS); @@ -4884,6 +4904,42 @@ operand vRegD_V3() interface(REG_INTER); %} +operand vRegD_V4() +%{ + constraint(ALLOC_IN_RC(v4_reg)); + match(RegD); + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vRegD_V5() +%{ + constraint(ALLOC_IN_RC(v5_reg)); + match(RegD); + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vRegD_V6() +%{ + constraint(ALLOC_IN_RC(v6_reg)); + match(RegD); + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vRegD_V7() +%{ + constraint(ALLOC_IN_RC(v7_reg)); + match(RegD); + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + // Flags register, used as output of signed compare instructions // note that on AArch64 we also use this register as the output for @@ -15390,14 +15446,17 @@ instruct string_compareLU(iRegP_R1 str1, iRegI_R2 cnt1, iRegP_R3 str2, iRegI_R4 %} instruct string_indexofUU(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, iRegI_R2 cnt2, - iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, - iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr) + iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, + iRegINoSp tmp3, iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, rFlagsReg cr) %{ predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, - TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UU)" %} + TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, + TEMP vtmp0, TEMP vtmp1, KILL cr); + format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UU) " + "# KILL $str1 $cnt1 $str2 $cnt2 $tmp1 $tmp2 $tmp3 $tmp4 $tmp5 $tmp6 V0-V1 cr" %} ins_encode %{ __ string_indexof($str1$$Register, $str2$$Register, @@ -15411,14 +15470,17 @@ instruct string_indexofUU(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, iRegI_R2 %} instruct string_indexofLL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, iRegI_R2 cnt2, - iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, - iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr) + iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, + iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, rFlagsReg cr) %{ predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, - TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (LL)" %} + TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, + TEMP vtmp0, TEMP vtmp1, KILL cr); + format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (LL) " + "# KILL $str1 $cnt1 $str2 $cnt2 $tmp1 $tmp2 $tmp3 $tmp4 $tmp5 $tmp6 V0-V1 cr" %} ins_encode %{ __ string_indexof($str1$$Register, $str2$$Register, @@ -15432,14 +15494,17 @@ instruct string_indexofLL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, iRegI_R2 %} instruct string_indexofUL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, iRegI_R2 cnt2, - iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, - iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr) + iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, + iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, rFlagsReg cr) %{ predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, - TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UL)" %} + TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, + TEMP tmp6, TEMP vtmp0, TEMP vtmp1, KILL cr); + format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UL) " + "# KILL $str1 cnt1 $str2 $cnt2 $tmp1 $tmp2 $tmp3 $tmp4 $tmp5 $tmp6 V0-V1 cr" %} ins_encode %{ __ string_indexof($str1$$Register, $str2$$Register, @@ -15453,14 +15518,15 @@ instruct string_indexofUL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, iRegI_R2 %} instruct string_indexof_conUU(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, - immI_le_4 int_cnt2, iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, - iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) + immI_le_4 int_cnt2, iRegI_R0 result, iRegINoSp tmp1, + iRegINoSp tmp2, iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) %{ predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UU)" %} + format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UU) " + "# KILL $str1 $cnt1 $str2 $tmp1 $tmp2 $tmp3 $tmp4 cr" %} ins_encode %{ int icnt2 = (int)$int_cnt2$$constant; @@ -15474,14 +15540,15 @@ instruct string_indexof_conUU(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, %} instruct string_indexof_conLL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, - immI_le_4 int_cnt2, iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, - iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) + immI_le_4 int_cnt2, iRegI_R0 result, iRegINoSp tmp1, + iRegINoSp tmp2, iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) %{ predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (LL)" %} + format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (LL) " + "# KILL $str1 $cnt1 $str2 $tmp1 $tmp2 $tmp3 $tmp4 cr" %} ins_encode %{ int icnt2 = (int)$int_cnt2$$constant; @@ -15495,14 +15562,15 @@ instruct string_indexof_conLL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, %} instruct string_indexof_conUL(iRegP_R1 str1, iRegI_R4 cnt1, iRegP_R3 str2, - immI_1 int_cnt2, iRegI_R0 result, iRegINoSp tmp1, iRegINoSp tmp2, - iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) + immI_1 int_cnt2, iRegI_R0 result, iRegINoSp tmp1, + iRegINoSp tmp2, iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) %{ predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL); match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); - format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UL)" %} + format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UL) " + "# KILL $str1 $cnt1 $str2 $tmp1 $tmp2 $tmp3 $tmp4 cr" %} ins_encode %{ int icnt2 = (int)$int_cnt2$$constant; @@ -15567,13 +15635,17 @@ instruct string_equalsU(iRegP_R1 str1, iRegP_R3 str2, iRegI_R4 cnt, instruct array_equalsB(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result, iRegP_R3 tmp1, iRegP_R4 tmp2, iRegP_R5 tmp3, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, vRegD_V2 vtmp2, vRegD_V3 vtmp3, + vRegD_V4 vtmp4, vRegD_V5 vtmp5, vRegD_V6 vtmp6, vRegD_V7 vtmp7, iRegP_R10 tmp, rFlagsReg cr) %{ predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL); match(Set result (AryEq ary1 ary2)); - effect(KILL tmp, USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + effect(KILL tmp, USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, + TEMP vtmp0, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, TEMP vtmp4, TEMP vtmp5, + TEMP vtmp6, TEMP vtmp7, KILL cr); - format %{ "Array Equals $ary1,ary2 -> $result // KILL $tmp" %} + format %{ "Array Equals $ary1,ary2 -> $result # KILL $ary1 $ary2 $tmp $tmp1 $tmp2 $tmp3 V0-V7 cr" %} ins_encode %{ address tpc = __ arrays_equals($ary1$$Register, $ary2$$Register, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, @@ -15588,13 +15660,17 @@ instruct array_equalsB(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result, instruct array_equalsC(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result, iRegP_R3 tmp1, iRegP_R4 tmp2, iRegP_R5 tmp3, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, vRegD_V2 vtmp2, vRegD_V3 vtmp3, + vRegD_V4 vtmp4, vRegD_V5 vtmp5, vRegD_V6 vtmp6, vRegD_V7 vtmp7, iRegP_R10 tmp, rFlagsReg cr) %{ predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU); match(Set result (AryEq ary1 ary2)); - effect(KILL tmp, USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + effect(KILL tmp, USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, + TEMP vtmp0, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, TEMP vtmp4, TEMP vtmp5, + TEMP vtmp6, TEMP vtmp7, KILL cr); - format %{ "Array Equals $ary1,ary2 -> $result // KILL $tmp" %} + format %{ "Array Equals $ary1,ary2 -> $result # KILL $ary1 $ary2 $tmp $tmp1 $tmp2 $tmp3 V0-V7 cr" %} ins_encode %{ address tpc = __ arrays_equals($ary1$$Register, $ary2$$Register, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, @@ -15624,35 +15700,40 @@ instruct has_negatives(iRegP_R1 ary1, iRegI_R2 len, iRegI_R0 result, rFlagsReg c // fast char[] to byte[] compression instruct string_compress(iRegP_R2 src, iRegP_R1 dst, iRegI_R3 len, - vRegD_V0 tmp1, vRegD_V1 tmp2, - vRegD_V2 tmp3, vRegD_V3 tmp4, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, vRegD_V2 vtmp2, + vRegD_V3 vtmp3, vRegD_V4 vtmp4, vRegD_V5 vtmp5, iRegI_R0 result, rFlagsReg cr) %{ match(Set result (StrCompressedCopy src (Binary dst len))); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr); + effect(TEMP vtmp0, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, TEMP vtmp4, TEMP vtmp5, + USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr); - format %{ "String Compress $src,$dst -> $result // KILL R1, R2, R3, R4" %} + format %{ "String Compress $src,$dst -> $result # KILL $src $dst $len V0-V5 cr" %} ins_encode %{ __ char_array_compress($src$$Register, $dst$$Register, $len$$Register, - $tmp1$$FloatRegister, $tmp2$$FloatRegister, - $tmp3$$FloatRegister, $tmp4$$FloatRegister, + $vtmp0$$FloatRegister, $vtmp1$$FloatRegister, + $vtmp2$$FloatRegister, $vtmp3$$FloatRegister, + $vtmp4$$FloatRegister, $vtmp5$$FloatRegister, $result$$Register); %} ins_pipe( pipe_slow ); %} // fast byte[] to char[] inflation -instruct string_inflate(Universe dummy, iRegP_R0 src, iRegP_R1 dst, iRegI_R2 len, - vRegD_V0 tmp1, vRegD_V1 tmp2, vRegD_V2 tmp3, iRegP_R3 tmp4, rFlagsReg cr) +instruct string_inflate(Universe dummy, iRegP_R0 src, iRegP_R1 dst, iRegI_R2 len, iRegP_R3 tmp, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, vRegD_V2 vtmp2, vRegD_V3 vtmp3, + vRegD_V4 vtmp4, vRegD_V5 vtmp5, vRegD_V6 vtmp6, rFlagsReg cr) %{ match(Set dummy (StrInflatedCopy src (Binary dst len))); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr); + effect(TEMP vtmp0, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, + TEMP vtmp4, TEMP vtmp5, TEMP vtmp6, TEMP tmp, + USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr); - format %{ "String Inflate $src,$dst // KILL $tmp1, $tmp2" %} + format %{ "String Inflate $src,$dst # KILL $tmp $src $dst $len V0-V6 cr" %} ins_encode %{ address tpc = __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register, - $tmp1$$FloatRegister, $tmp2$$FloatRegister, - $tmp3$$FloatRegister, $tmp4$$Register); + $vtmp0$$FloatRegister, $vtmp1$$FloatRegister, + $vtmp2$$FloatRegister, $tmp$$Register); if (tpc == NULL) { ciEnv::current()->record_failure("CodeCache is full"); return; @@ -15663,19 +15744,20 @@ instruct string_inflate(Universe dummy, iRegP_R0 src, iRegP_R1 dst, iRegI_R2 len // encode char[] to byte[] in ISO_8859_1 instruct encode_iso_array(iRegP_R2 src, iRegP_R1 dst, iRegI_R3 len, - vRegD_V0 Vtmp1, vRegD_V1 Vtmp2, - vRegD_V2 Vtmp3, vRegD_V3 Vtmp4, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, vRegD_V2 vtmp2, + vRegD_V3 vtmp3, vRegD_V4 vtmp4, vRegD_V5 vtmp5, iRegI_R0 result, rFlagsReg cr) %{ match(Set result (EncodeISOArray src (Binary dst len))); - effect(USE_KILL src, USE_KILL dst, USE_KILL len, - KILL Vtmp1, KILL Vtmp2, KILL Vtmp3, KILL Vtmp4, KILL cr); + effect(USE_KILL src, USE_KILL dst, USE_KILL len, KILL vtmp0, KILL vtmp1, + KILL vtmp2, KILL vtmp3, KILL vtmp4, KILL vtmp5, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result" %} + format %{ "Encode array $src,$dst,$len -> $result # KILL $src $dst $len V0-V5 cr" %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, - $result$$Register, $Vtmp1$$FloatRegister, $Vtmp2$$FloatRegister, - $Vtmp3$$FloatRegister, $Vtmp4$$FloatRegister); + $result$$Register, $vtmp0$$FloatRegister, $vtmp1$$FloatRegister, + $vtmp2$$FloatRegister, $vtmp3$$FloatRegister, + $vtmp4$$FloatRegister, $vtmp5$$FloatRegister); %} ins_pipe( pipe_class_memory ); %} diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index ccb8d75f3d8..e07bbba3f6e 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -2286,6 +2286,8 @@ void mvnw(Register Rd, Register Rm, INSN(umullv, 1, 0b110000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(umlalv, 1, 0b100000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S + INSN(cmhi, 1, 0b001101, true); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D + #undef INSN #define INSN(NAME, opc, opc2, accepted) \ diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 5753cc9a611..7f329a45d30 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -4332,6 +4332,7 @@ void MacroAssembler::remove_frame(int framesize) { typedef void (MacroAssembler::* chr_insn)(Register Rt, const Address &adr); // Search for str1 in str2 and return index or -1 +// Clobbers: rscratch1, rscratch2, rflags. May also clobber v0-v1, when icnt1==-1. void MacroAssembler::string_indexof(Register str2, Register str1, Register cnt2, Register cnt1, Register tmp1, Register tmp2, @@ -5123,6 +5124,8 @@ address MacroAssembler::has_negatives(Register ary1, Register len, Register resu return pc(); } +// Clobbers: rscratch1, rscratch2, rflags +// May also clobber v0-v7 when (!UseSimpleArrayEquals && UseSIMDForArrayEquals) address MacroAssembler::arrays_equals(Register a1, Register a2, Register tmp3, Register tmp4, Register tmp5, Register result, Register cnt1, int elem_size) { @@ -5615,10 +5618,13 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) // Intrinsic for sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray and // java/lang/StringUTF16.compress. +// +// Clobbers: src, dst, res, rscratch1, rscratch2, rflags void MacroAssembler::encode_iso_array(Register src, Register dst, - Register len, Register result, - FloatRegister Vtmp1, FloatRegister Vtmp2, - FloatRegister Vtmp3, FloatRegister Vtmp4) + Register len, Register result, + FloatRegister Vtmp1, FloatRegister Vtmp2, + FloatRegister Vtmp3, FloatRegister Vtmp4, + FloatRegister Vtmp5, FloatRegister Vtmp6) { Label DONE, SET_RESULT, NEXT_32, NEXT_32_PRFM, LOOP_8, NEXT_8, LOOP_1, NEXT_1, NEXT_32_START, NEXT_32_PRFM_START; @@ -5641,13 +5647,13 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, ld1(Vtmp1, Vtmp2, Vtmp3, Vtmp4, T8H, src); BIND(NEXT_32_PRFM_START); prfm(Address(src, SoftwarePrefetchHintDistance)); - orr(v4, T16B, Vtmp1, Vtmp2); - orr(v5, T16B, Vtmp3, Vtmp4); + orr(Vtmp5, T16B, Vtmp1, Vtmp2); + orr(Vtmp6, T16B, Vtmp3, Vtmp4); uzp1(Vtmp1, T16B, Vtmp1, Vtmp2); uzp1(Vtmp3, T16B, Vtmp3, Vtmp4); - uzp2(v5, T16B, v4, v5); // high bytes - umov(tmp2, v5, D, 1); - fmovd(tmp1, v5); + uzp2(Vtmp6, T16B, Vtmp5, Vtmp6); // high bytes + umov(tmp2, Vtmp6, D, 1); + fmovd(tmp1, Vtmp6); orr(tmp1, tmp1, tmp2); cbnz(tmp1, LOOP_8); stpq(Vtmp1, Vtmp3, dst); @@ -5666,8 +5672,8 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, ld1(Vtmp1, Vtmp2, Vtmp3, Vtmp4, T8H, src); } prfm(Address(src, SoftwarePrefetchHintDistance)); - uzp1(v4, T16B, Vtmp1, Vtmp2); - uzp1(v5, T16B, Vtmp3, Vtmp4); + uzp1(Vtmp5, T16B, Vtmp1, Vtmp2); + uzp1(Vtmp6, T16B, Vtmp3, Vtmp4); orr(Vtmp1, T16B, Vtmp1, Vtmp2); orr(Vtmp3, T16B, Vtmp3, Vtmp4); uzp2(Vtmp1, T16B, Vtmp1, Vtmp3); // high bytes @@ -5675,7 +5681,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, fmovd(tmp1, Vtmp1); orr(tmp1, tmp1, tmp2); cbnz(tmp1, LOOP_8); - stpq(v4, v5, dst); + stpq(Vtmp5, Vtmp6, dst); sub(len, len, 32); add(dst, dst, 32); add(src, src, 64); @@ -5720,6 +5726,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, // Inflate byte[] array to char[]. +// Clobbers: src, dst, len, rflags, rscratch1, v0-v6 address MacroAssembler::byte_array_inflate(Register src, Register dst, Register len, FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, Register tmp4) { @@ -5828,9 +5835,10 @@ address MacroAssembler::byte_array_inflate(Register src, Register dst, Register void MacroAssembler::char_array_compress(Register src, Register dst, Register len, FloatRegister tmp1Reg, FloatRegister tmp2Reg, FloatRegister tmp3Reg, FloatRegister tmp4Reg, + FloatRegister tmp5Reg, FloatRegister tmp6Reg, Register result) { encode_iso_array(src, dst, len, result, - tmp1Reg, tmp2Reg, tmp3Reg, tmp4Reg); + tmp1Reg, tmp2Reg, tmp3Reg, tmp4Reg, tmp5Reg, tmp6Reg); cmp(len, zr); csel(result, result, zr, EQ); } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 7e23c16a442..01fdf16a01c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1245,12 +1245,14 @@ class MacroAssembler: public Assembler { void char_array_compress(Register src, Register dst, Register len, FloatRegister tmp1Reg, FloatRegister tmp2Reg, FloatRegister tmp3Reg, FloatRegister tmp4Reg, + FloatRegister tmp5Reg, FloatRegister tmp6Reg, Register result); void encode_iso_array(Register src, Register dst, Register len, Register result, FloatRegister Vtmp1, FloatRegister Vtmp2, - FloatRegister Vtmp3, FloatRegister Vtmp4); + FloatRegister Vtmp3, FloatRegister Vtmp4, + FloatRegister Vtmp5, FloatRegister Vtmp6); void string_indexof(Register str1, Register str2, Register cnt1, Register cnt2, Register tmp1, Register tmp2, diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index bd4b5d7c13f..598a25969d8 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2882,6 +2882,22 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Big-endian 128-bit + 64-bit -> 128-bit addition. + // Inputs: 128-bits. in is preserved. + // The least-significant 64-bit word is in the upper dword of the vector + // inc (the 64-bit increment) is preserved. Its lower dword must be zero + // Output: result + void be_add_128_64(FloatRegister result, FloatRegister in, + FloatRegister inc, FloatRegister tmp) { + assert_different_registers(result, tmp, inc); + + __ addv(result, __ T2D, in, inc); // Add inc to the least-significant dword of input + __ cmhi(tmp, __ T2D, inc, result); // Check for result overflowing + __ ins(tmp, __ D, tmp, 0, 1); // Move LSD of comparison result to MSD + __ ins(tmp, __ D, inc, 1, 0); // Move 0 to LSD of comparison result + __ subv(result, __ T2D, result, tmp); // Subtract -1 from MSD if there was an overflow + } + // CTR AES crypt. // Arguments: // @@ -2991,13 +3007,16 @@ class StubGenerator: public StubCodeGenerator { // Setup the counter __ movi(v4, __ T4S, 0); __ movi(v5, __ T4S, 1); - __ ins(v4, __ S, v5, 3, 3); // v4 contains { 0, 0, 0, 1 } + __ ins(v4, __ S, v5, 2, 2); // v4 contains { 0, 1 } - __ ld1(v0, __ T16B, counter); // Load the counter into v0 - __ rev32(v16, __ T16B, v0); - __ addv(v16, __ T4S, v16, v4); - __ rev32(v16, __ T16B, v16); - __ st1(v16, __ T16B, counter); // Save the incremented counter back + // 128-bit big-endian increment + __ ld1(v0, __ T16B, counter); + __ rev64(v16, __ T16B, v0); + be_add_128_64(v16, v16, v4, /*tmp*/v5); + __ rev64(v16, __ T16B, v16); + __ st1(v16, __ T16B, counter); + // Previous counter value is in v0 + // v4 contains { 0, 1 } { // We have fewer than bulk_width blocks of data left. Encrypt @@ -3029,9 +3048,9 @@ class StubGenerator: public StubCodeGenerator { // Increment the counter, store it back __ orr(v0, __ T16B, v16, v16); - __ rev32(v16, __ T16B, v16); - __ addv(v16, __ T4S, v16, v4); - __ rev32(v16, __ T16B, v16); + __ rev64(v16, __ T16B, v16); + be_add_128_64(v16, v16, v4, /*tmp*/v5); + __ rev64(v16, __ T16B, v16); __ st1(v16, __ T16B, counter); // Save the incremented counter back __ b(inner_loop); @@ -3079,7 +3098,7 @@ class StubGenerator: public StubCodeGenerator { // Keys should already be loaded into the correct registers __ ld1(v0, __ T16B, counter); // v0 contains the first counter - __ rev32(v16, __ T16B, v0); // v16 contains byte-reversed counter + __ rev64(v16, __ T16B, v0); // v16 contains byte-reversed counter // AES/CTR loop { @@ -3089,11 +3108,11 @@ class StubGenerator: public StubCodeGenerator { // Setup the counters __ movi(v8, __ T4S, 0); __ movi(v9, __ T4S, 1); - __ ins(v8, __ S, v9, 3, 3); // v8 contains { 0, 0, 0, 1 } + __ ins(v8, __ S, v9, 2, 2); // v8 contains { 0, 1 } for (FloatRegister f = v0; f < v0 + bulk_width; f++) { - __ rev32(f, __ T16B, v16); - __ addv(v16, __ T4S, v16, v8); + __ rev64(f, __ T16B, v16); + be_add_128_64(v16, v16, v8, /*tmp*/v9); } __ ld1(v8, v9, v10, v11, __ T16B, __ post(in, 4 * 16)); @@ -3121,7 +3140,7 @@ class StubGenerator: public StubCodeGenerator { } // Save the counter back where it goes - __ rev32(v16, __ T16B, v16); + __ rev64(v16, __ T16B, v16); __ st1(v16, __ T16B, counter); __ pop(saved_regs, sp); @@ -4099,6 +4118,7 @@ class StubGenerator: public StubCodeGenerator { // result = r0 - return value. Already contains "false" // cnt1 = r10 - amount of elements left to check, reduced by wordSize // r3-r5 are reserved temporary registers + // Clobbers: v0-v7 when UseSIMDForArrayEquals, rscratch1, rscratch2 address generate_large_array_equals() { Register a1 = r1, a2 = r2, result = r0, cnt1 = r10, tmp1 = rscratch1, tmp2 = rscratch2, tmp3 = r3, tmp4 = r4, tmp5 = r5, tmp6 = r11, @@ -4503,6 +4523,8 @@ class StubGenerator: public StubCodeGenerator { // R2 = cnt1 // R3 = str1 // R4 = cnt2 + // Clobbers: rscratch1, rscratch2, v0, v1, rflags + // // This generic linear code use few additional ideas, which makes it faster: // 1) we can safely keep at least 1st register of pattern(since length >= 8) // in order to skip initial loading(help in systems with 1 ld pipeline) @@ -4817,6 +4839,7 @@ class StubGenerator: public StubCodeGenerator { // R3 = len >> 3 // V0 = 0 // v1 = loaded 8 bytes + // Clobbers: r0, r1, r3, rscratch1, rflags, v0-v6 address generate_large_byte_array_inflate() { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", "large_byte_array_inflate"); diff --git a/src/hotspot/cpu/arm/macroAssembler_arm.cpp b/src/hotspot/cpu/arm/macroAssembler_arm.cpp index 9e22fd152fc..a3f75fa0e2b 100644 --- a/src/hotspot/cpu/arm/macroAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/macroAssembler_arm.cpp @@ -3085,7 +3085,7 @@ void MacroAssembler::fast_unlock(Register Roop, Register Rbox, Register Rscratch // Restore the object header bool allow_fallthrough_on_failure = true; bool one_shot = true; - cas_for_lock_release(Rmark, Rbox, Roop, Rscratch, done, allow_fallthrough_on_failure, one_shot); + cas_for_lock_release(Rbox, Rmark, Roop, Rscratch, done, allow_fallthrough_on_failure, one_shot); bind(done); diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index e816cf067a0..a863665fed8 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -1976,6 +1976,19 @@ void Assembler::evpabsq(XMMRegister dst, XMMRegister src, int vector_len) { emit_int8((unsigned char)(0xC0 | encode)); } +void Assembler::evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false,/* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD4); emit_int8(0xC0 | encode); +} + void Assembler::decl(Address dst) { // Don't use it directly. Use MacroAssembler::decrement() instead. InstructionMark im(this); @@ -2322,6 +2335,15 @@ void Assembler::movddup(XMMRegister dst, XMMRegister src) { emit_int8(0xC0 | encode); } +void Assembler::kshiftlbl(KRegister dst, KRegister src, int imm8) { + assert(VM_Version::supports_avx512dq(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0 , src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8(0x32); emit_int8(0xC0 | encode); + emit_int8(imm8); +} + + void Assembler::kmovbl(KRegister dst, Register src) { assert(VM_Version::supports_avx512dq(), ""); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); @@ -3657,6 +3679,14 @@ void Assembler::evpcmpuw(KRegister kdst, XMMRegister nds, Address src, Compariso emit_int8(vcc); } +void Assembler::evpcmpuq(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len) { + assert(VM_Version::supports_avx512vlbw(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8(0x1E); emit_int8(0xC0 | encode); emit_int8(vcc); +} + void Assembler::evpcmpeqb(KRegister kdst, XMMRegister nds, Address src, int vector_len) { assert(VM_Version::supports_avx512bw(), ""); InstructionMark im(this); diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 704cf22e563..621aaacadce 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1423,6 +1423,8 @@ class Assembler : public AbstractAssembler { void ktestql(KRegister dst, KRegister src); + void kshiftlbl(KRegister dst, KRegister src, int imm8); + void movdl(XMMRegister dst, Register src); void movdl(Register dst, XMMRegister src); void movdl(XMMRegister dst, Address src); @@ -1621,6 +1623,7 @@ class Assembler : public AbstractAssembler { void evpcmpuw(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len); void evpcmpuw(KRegister kdst, KRegister mask, XMMRegister nds, XMMRegister src, ComparisonPredicate of, int vector_len); void evpcmpuw(KRegister kdst, XMMRegister nds, Address src, ComparisonPredicate vcc, int vector_len); + void evpcmpuq(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len); void pcmpeqw(XMMRegister dst, XMMRegister src); void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); @@ -2043,6 +2046,9 @@ class Assembler : public AbstractAssembler { void vpaddd(XMMRegister dst, XMMRegister nds, Address src, int vector_len); void vpaddq(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + // Leaf level assembler routines for masked operations. + void evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); + // Sub packed integers void psubb(XMMRegister dst, XMMRegister src); void psubw(XMMRegister dst, XMMRegister src); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 1bed0cce92f..cbb4b60d5f1 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -977,6 +977,8 @@ class MacroAssembler: public Assembler { void roundDec(XMMRegister key, int rnum); void lastroundDec(XMMRegister key, int rnum); void ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask); + void ev_add128(XMMRegister xmmdst, XMMRegister xmmsrc1, XMMRegister xmmsrc2, + int vector_len, KRegister ktmp, Register rscratch = noreg); public: void aesecb_encrypt(Register source_addr, Register dest_addr, Register key, Register len); @@ -1315,6 +1317,9 @@ class MacroAssembler: public Assembler { void vnegatess(XMMRegister dst, XMMRegister nds, AddressLiteral src); void vnegatesd(XMMRegister dst, XMMRegister nds, AddressLiteral src); + using Assembler::evpaddq; + void evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, AddressLiteral src, bool merge, int vector_len, Register rscratch = noreg); + // AVX Vector instructions void vxorpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { Assembler::vxorpd(dst, nds, src, vector_len); } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp index 4f86bc5b695..dcc48a6c1f0 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp @@ -779,6 +779,19 @@ void MacroAssembler::avx_ghash(Register input_state, Register htbl, vpxor(xmm15, xmm15, xmm15, Assembler::AVX_128bit); } +// Add 128-bit integers in xmmsrc1 to xmmsrc2, then place the result in xmmdst. +// Clobber ktmp and rscratch. +// Used by aesctr_encrypt. +void MacroAssembler::ev_add128(XMMRegister xmmdst, XMMRegister xmmsrc1, XMMRegister xmmsrc2, + int vector_len, KRegister ktmp, Register rscratch) { + vpaddq(xmmdst, xmmsrc1, xmmsrc2, vector_len); + evpcmpuq(ktmp, xmmdst, xmmsrc2, lt, vector_len); // set mask[0/1] bit if addq to dst[0/1] wraps + kshiftlbl(ktmp, ktmp, 1); // mask[1] <- mask[0], mask[0] <- 0, etc + + evpaddq(xmmdst, ktmp, xmmdst, xmm17, /*merge*/true, + vector_len); // dst[1]++ if mask[1] set +} + // AES Counter Mode using VAES instructions void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Register key, Register counter, Register len_reg, Register used, Register used_addr, Register saved_encCounter_start) { @@ -831,19 +844,23 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis //shuffle counter using lbswap_mask vpshufb(xmm8, xmm8, xmm16, Assembler::AVX_512bit); + // Vector value to propagate carries + evmovdquq(xmm17, ExternalAddress(StubRoutines::x86::counter_mask_ones_addr()), Assembler::AVX_512bit, r15); // pre-increment and propagate counter values to zmm9-zmm15 registers. // Linc0 increments the zmm8 by 1 (initial value being 0), Linc4 increments the counters zmm9-zmm15 by 4 // The counter is incremented after each block i.e. 16 bytes is processed; // each zmm register has 4 counter values as its MSB // the counters are incremented in parallel - vpaddd(xmm8, xmm8, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 64), Assembler::AVX_512bit, r15);//linc0 - vpaddd(xmm9, xmm8, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//linc4(rip) - vpaddd(xmm10, xmm9, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm11, xmm10, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm12, xmm11, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm13, xmm12, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm14, xmm13, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm15, xmm14, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) + evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 64), Assembler::AVX_512bit, r15 /*rscratch*/);//linc0 + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15 /*rscratch*/);//linc4 + ev_add128(xmm9, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm10, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm11, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm12, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm13, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm14, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm15, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); // load linc32 mask in zmm register.linc32 increments counter by 32 evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 256), Assembler::AVX_512bit, r15);//Linc32 @@ -891,21 +908,21 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis // This is followed by incrementing counter values in zmm8-zmm15. // Since we will be processing 32 blocks at a time, the counter is incremented by 32. roundEnc(xmm21, 7); - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm22, 7); - vpaddq(xmm9, xmm9, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm23, 7); - vpaddq(xmm10, xmm10, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm10, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm24, 7); - vpaddq(xmm11, xmm11, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm11, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm25, 7); - vpaddq(xmm12, xmm12, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm12, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm26, 7); - vpaddq(xmm13, xmm13, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm13, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm27, 7); - vpaddq(xmm14, xmm14, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm14, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm28, 7); - vpaddq(xmm15, xmm15, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm15, xmm15, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm29, 7); cmpl(rounds, 52); @@ -983,8 +1000,8 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis vpshufb(xmm3, xmm11, xmm16, Assembler::AVX_512bit); evpxorq(xmm3, xmm3, xmm20, Assembler::AVX_512bit); // Increment counter values by 16 - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); - vpaddq(xmm9, xmm9, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128/*!!!*/(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); // AES encode rounds roundEnc(xmm21, 3); roundEnc(xmm22, 3); @@ -1051,7 +1068,7 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis vpshufb(xmm1, xmm9, xmm16, Assembler::AVX_512bit); evpxorq(xmm1, xmm1, xmm20, Assembler::AVX_512bit); // increment counter by 8 - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); // AES encode roundEnc(xmm21, 1); roundEnc(xmm22, 1); @@ -1109,7 +1126,7 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_512bit); evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_512bit); // Increment counter - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_512bit); vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_512bit); vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_512bit); @@ -1159,7 +1176,7 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_128bit); vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_128bit); // Increment counter by 1 - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_128bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_128bit, /*ktmp*/k1, r15 /*rscratch*/); vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_128bit); vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_128bit); vaesenc(xmm0, xmm0, xmm24, Assembler::AVX_128bit); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 87cd4994517..d6a58a7b3b0 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -3901,7 +3901,19 @@ class StubGenerator: public StubCodeGenerator { return start; } - // Vector AES Counter implementation + // Vector AES Counter implementation + + address counter_mask_ones_addr() { + __ align(64); + StubCodeMark mark(this, "StubRoutines", "counter_mask_addr"); + address start = __ pc(); + for (int i = 0; i < 4; i ++) { + __ emit_data64(0x0000000000000000, relocInfo::none); + __ emit_data64(0x0000000000000001, relocInfo::none); + } + return start; + } + address generate_counterMode_VectorAESCrypt() { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); @@ -6022,6 +6034,7 @@ address generate_avx_ghash_processBlocks() { if (UseAESCTRIntrinsics) { if (VM_Version::supports_vaes() && VM_Version::supports_avx512bw() && VM_Version::supports_avx512vl()) { StubRoutines::x86::_counter_mask_addr = counter_mask_addr(); + StubRoutines::x86::_counter_mask_ones_addr = counter_mask_ones_addr(); StubRoutines::_counterMode_AESCrypt = generate_counterMode_VectorAESCrypt(); } else { StubRoutines::x86::_counter_shuffle_mask_addr = generate_counter_shuffle_mask(); diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index 5d93d118e7b..048e3d439e7 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -62,6 +62,7 @@ address StubRoutines::x86::_right_shift_mask = NULL; address StubRoutines::x86::_left_shift_mask = NULL; address StubRoutines::x86::_and_mask = NULL; address StubRoutines::x86::_url_charset = NULL; +address StubRoutines::x86::_counter_mask_ones_addr = NULL; address StubRoutines::x86::_counter_mask_addr = NULL; #endif address StubRoutines::x86::_pshuffle_byte_flip_mask_addr = NULL; diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index e54fe25251d..669d45e61ae 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -154,6 +154,7 @@ class x86 { // byte flip mask for sha512 static address _pshuffle_byte_flip_mask_addr_sha512; static address _counter_mask_addr; + static address _counter_mask_ones_addr; // Masks for base64 static address _base64_charset; static address _bswap_mask; @@ -264,6 +265,7 @@ class x86 { static address base64_left_shift_mask_addr() { return _left_shift_mask; } static address base64_and_mask_addr() { return _and_mask; } static address counter_mask_addr() { return _counter_mask_addr; } + static address counter_mask_ones_addr() { return _counter_mask_ones_addr; } #endif static address pshuffle_byte_flip_mask_addr() { return _pshuffle_byte_flip_mask_addr; } static void generate_CRC32C_table(bool is_pclmulqdq_supported); diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 79dd256ac61..e62dcf4f759 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -38,40 +38,26 @@ * on the contents of the mountinfo and cgroup files. */ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { - char buf[MAXPATHLEN+1]; + stringStream ss; if (_root != NULL && cgroup_path != NULL) { if (strcmp(_root, "/") == 0) { - int buflen; - strncpy(buf, _mount_point, MAXPATHLEN); - buf[MAXPATHLEN-1] = '\0'; + ss.print_raw(_mount_point); if (strcmp(cgroup_path,"/") != 0) { - buflen = strlen(buf); - if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { - return; - } - strncat(buf, cgroup_path, MAXPATHLEN-buflen); - buf[MAXPATHLEN-1] = '\0'; + ss.print_raw(cgroup_path); } - _path = os::strdup(buf); + _path = os::strdup(ss.base()); } else { if (strcmp(_root, cgroup_path) == 0) { - strncpy(buf, _mount_point, MAXPATHLEN); - buf[MAXPATHLEN-1] = '\0'; - _path = os::strdup(buf); + ss.print_raw(_mount_point); + _path = os::strdup(ss.base()); } else { char *p = strstr(cgroup_path, _root); if (p != NULL && p == _root) { if (strlen(cgroup_path) > strlen(_root)) { - int buflen; - strncpy(buf, _mount_point, MAXPATHLEN); - buf[MAXPATHLEN-1] = '\0'; - buflen = strlen(buf); - if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) { - return; - } - strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen); - buf[MAXPATHLEN-1] = '\0'; - _path = os::strdup(buf); + ss.print_raw(_mount_point); + const char* cg_path_sub = cgroup_path + strlen(_root); + ss.print_raw(cg_path_sub); + _path = os::strdup(ss.base()); } } } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index e6c7632032d..2fee098aa37 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -234,17 +234,12 @@ void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { } char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { - char buf[MAXPATHLEN+1]; - int buflen; - strncpy(buf, mount_path, MAXPATHLEN); - buf[MAXPATHLEN] = '\0'; - buflen = strlen(buf); - if ((buflen + strlen(cgroup_path)) > MAXPATHLEN) { - return NULL; + stringStream ss; + ss.print_raw(mount_path); + if (strcmp(cgroup_path, "/") != 0) { + ss.print_raw(cgroup_path); } - strncat(buf, cgroup_path, MAXPATHLEN-buflen); - buf[MAXPATHLEN] = '\0'; - return os::strdup(buf); + return os::strdup(ss.base()); } char* CgroupV2Subsystem::pids_max_val() { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 74945999e75..3799adf5dd9 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -835,6 +835,106 @@ static void *thread_native_entry(Thread *thread) { return 0; } +// On Linux, glibc places static TLS blocks (for __thread variables) on +// the thread stack. This decreases the stack size actually available +// to threads. +// +// For large static TLS sizes, this may cause threads to malfunction due +// to insufficient stack space. This is a well-known issue in glibc: +// http://sourceware.org/bugzilla/show_bug.cgi?id=11787. +// +// As a workaround, we call a private but assumed-stable glibc function, +// __pthread_get_minstack() to obtain the minstack size and derive the +// static TLS size from it. We then increase the user requested stack +// size by this TLS size. The same function is used to determine whether +// adjustStackSizeForGuardPages() needs to be true. +// +// Due to compatibility concerns, this size adjustment is opt-in and +// controlled via AdjustStackSizeForTLS. +typedef size_t (*GetMinStack)(const pthread_attr_t *attr); + +GetMinStack _get_minstack_func = NULL; // Initialized via os::init_2() + +// Returns the size of the static TLS area glibc puts on thread stacks. +// The value is cached on first use, which occurs when the first thread +// is created during VM initialization. +static size_t get_static_tls_area_size(const pthread_attr_t *attr) { + size_t tls_size = 0; + if (_get_minstack_func != NULL) { + // Obtain the pthread minstack size by calling __pthread_get_minstack. + size_t minstack_size = _get_minstack_func(attr); + + // Remove non-TLS area size included in minstack size returned + // by __pthread_get_minstack() to get the static TLS size. + // If adjustStackSizeForGuardPages() is true, minstack size includes + // guard_size. Otherwise guard_size is automatically added + // to the stack size by pthread_create and is no longer included + // in minstack size. In both cases, the guard_size is taken into + // account, so there is no need to adjust the result for that. + // + // Although __pthread_get_minstack() is a private glibc function, + // it is expected to have a stable behavior across future glibc + // versions while glibc still allocates the static TLS blocks off + // the stack. Following is glibc 2.28 __pthread_get_minstack(): + // + // size_t + // __pthread_get_minstack (const pthread_attr_t *attr) + // { + // return GLRO(dl_pagesize) + __static_tls_size + PTHREAD_STACK_MIN; + // } + // + // + // The following 'minstack_size > os::vm_page_size() + PTHREAD_STACK_MIN' + // if check is done for precaution. + if (minstack_size > (size_t)os::vm_page_size() + PTHREAD_STACK_MIN) { + tls_size = minstack_size - (size_t)os::vm_page_size() - PTHREAD_STACK_MIN; + } + } + + log_info(os, thread)("Stack size adjustment for TLS is " SIZE_FORMAT, + tls_size); + return tls_size; +} + +// In glibc versions prior to 2.27 the guard size mechanism +// was not implemented properly. The POSIX standard requires adding +// the size of the guard pages to the stack size, instead glibc +// took the space out of 'stacksize'. Thus we need to adapt the requested +// stack_size by the size of the guard pages to mimic proper behaviour. +// The fix in glibc 2.27 has now been backported to numerous earlier +// glibc versions so we need to do a dynamic runtime check. +static bool _adjustStackSizeForGuardPages = true; +bool os::Linux::adjustStackSizeForGuardPages() { + return _adjustStackSizeForGuardPages; +} + +// Dummy decl as substitute for cmdline parameter. TLS not in jdk11. +static const bool AdjustStackSizeForTLS = false; + +#if defined(__GLIBC__) +static void init_adjust_stacksize_for_guard_pages() { + assert(_get_minstack_func == NULL, "initialization error"); + _get_minstack_func =(GetMinStack)dlsym(RTLD_DEFAULT, "__pthread_get_minstack"); + log_info(os, thread)("Lookup of __pthread_get_minstack %s", + _get_minstack_func == NULL ? "failed" : "succeeded"); + + if (_get_minstack_func != NULL) { + pthread_attr_t attr; + pthread_attr_init(&attr); + size_t min_stack = _get_minstack_func(&attr); + size_t guard = 16 * K; // Actual value doesn't matter as it is not examined + pthread_attr_setguardsize(&attr, guard); + size_t min_stack2 = _get_minstack_func(&attr); + pthread_attr_destroy(&attr); + // If the minimum stack size changed when we added the guard page space + // then we need to perform the adjustment. + _adjustStackSizeForGuardPages = (min_stack2 != min_stack); + log_info(os)("Glibc stack size guard page adjustment is %sneeded", + _adjustStackSizeForGuardPages ? "" : "not "); + } +} +#endif // GLIBC + bool os::create_thread(Thread* thread, ThreadType thr_type, size_t req_stack_size) { assert(thread->osthread() == NULL, "caller responsible"); @@ -860,16 +960,24 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, // Calculate stack size if it's not specified by caller. size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size); - // In the Linux NPTL pthread implementation the guard size mechanism - // is not implemented properly. The posix standard requires adding - // the size of the guard pages to the stack size, instead Linux - // takes the space out of 'stacksize'. Thus we adapt the requested - // stack_size by the size of the guard pages to mimick proper - // behaviour. However, be careful not to end up with a size - // of zero due to overflow. Don't add the guard page in that case. size_t guard_size = os::Linux::default_guard_size(thr_type); - if (stack_size <= SIZE_MAX - guard_size) { - stack_size += guard_size; + // Configure glibc guard page. Must happen before calling + // get_static_tls_area_size(), which uses the guard_size. + pthread_attr_setguardsize(&attr, guard_size); + + // Apply stack size adjustments if needed. However, be careful not to end up + // with a size of zero due to overflow. Don't add the adjustment in that case. + size_t stack_adjust_size = 0; + if (AdjustStackSizeForTLS) { + // Adjust the stack_size for on-stack TLS - see get_static_tls_area_size(). + stack_adjust_size += get_static_tls_area_size(&attr); + } else if (os::Linux::adjustStackSizeForGuardPages()) { + stack_adjust_size += guard_size; + } + + stack_adjust_size = align_up(stack_adjust_size, os::vm_page_size()); + if (stack_size <= SIZE_MAX - stack_adjust_size) { + stack_size += stack_adjust_size; } assert(is_aligned(stack_size, os::vm_page_size()), "stack_size not aligned"); @@ -1005,8 +1113,10 @@ bool os::create_attached_thread(JavaThread* thread) { // and save the caller's signal mask os::Linux::hotspot_sigmask(thread); - log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT ").", - os::current_thread_id(), (uintx) pthread_self()); + log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT + ", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "k) ).", + os::current_thread_id(), (uintx) pthread_self(), + p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size() / K); return true; } @@ -1421,7 +1531,7 @@ void os::Linux::fast_thread_clock_init() { // Note, that some kernels may support the current thread // clock (CLOCK_THREAD_CPUTIME_ID) but not the clocks // returned by the pthread_getcpuclockid(). - // If the fast Posix clocks are supported then the sys_clock_getres() + // If the fast POSIX clocks are supported then the clock_getres() // must return at least tp.tv_sec == 0 which means a resolution // better than 1 sec. This is extra check for reliability. @@ -5518,6 +5628,11 @@ jint os::init_2(void) { log_info(os)("HotSpot is running with %s, %s", Linux::libc_version(), Linux::libpthread_version()); +#ifdef __GLIBC__ + // Check if we need to adjust the stack size for glibc guard pages. + init_adjust_stacksize_for_guard_pages(); +#endif + if (UseNUMA) { if (!Linux::libnuma_init()) { UseNUMA = false; @@ -6410,9 +6525,9 @@ bool os::start_debugging(char *buf, int buflen) { // // ** P1 (aka bottom) and size (P2 = P1 - size) are the address and stack size // returned from pthread_attr_getstack(). -// ** Due to NPTL implementation error, linux takes the glibc guard page out -// of the stack size given in pthread_attr. We work around this for -// threads created by the VM. (We adapt bottom to be P1 and size accordingly.) +// ** If adjustStackSizeForGuardPages() is true the guard pages have been taken +// out of the stack size given in pthread_attr. We work around this for +// threads created by the VM. We adjust bottom to be P1 and size accordingly. // #ifndef ZERO static void current_stack_region(address * bottom, size_t * size) { @@ -6439,14 +6554,15 @@ static void current_stack_region(address * bottom, size_t * size) { fatal("Cannot locate current stack attributes!"); } - // Work around NPTL stack guard error. - size_t guard_size = 0; - rslt = pthread_attr_getguardsize(&attr, &guard_size); - if (rslt != 0) { - fatal("pthread_attr_getguardsize failed with error = %d", rslt); - } - *bottom += guard_size; - *size -= guard_size; + if (os::Linux::adjustStackSizeForGuardPages()) { + size_t guard_size = 0; + rslt = pthread_attr_getguardsize(&attr, &guard_size); + if (rslt != 0) { + fatal("pthread_attr_getguardsize failed with error = %d", rslt); + } + *bottom += guard_size; + *size -= guard_size; + } pthread_attr_destroy(&attr); diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp index 8ad992f57a9..3172de60522 100644 --- a/src/hotspot/os/linux/os_linux.hpp +++ b/src/hotspot/os/linux/os_linux.hpp @@ -198,6 +198,8 @@ class Linux { // Return default guard size for the specified thread type static size_t default_guard_size(os::ThreadType thr_type); + static bool adjustStackSizeForGuardPages(); // See comments in os_linux.cpp + static void capture_initial_stack(size_t max_size); // Stack overflow handling diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index c5bb956e169..94beefe0189 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -1429,8 +1429,8 @@ char* os::Posix::describe_pthread_attr(char* buf, size_t buflen, const pthread_a int detachstate = 0; pthread_attr_getstacksize(attr, &stack_size); pthread_attr_getguardsize(attr, &guard_size); - // Work around linux NPTL implementation error, see also os::create_thread() in os_linux.cpp. - LINUX_ONLY(stack_size -= guard_size); + // Work around glibc stack guard issue, see os::create_thread() in os_linux.cpp. + LINUX_ONLY(if (os::Linux::adjustStackSizeForGuardPages()) stack_size -= guard_size;) pthread_attr_getdetachstate(attr, &detachstate); jio_snprintf(buf, buflen, "stacksize: " SIZE_FORMAT "k, guardsize: " SIZE_FORMAT "k, %s", stack_size / 1024, guard_size / 1024, diff --git a/src/hotspot/os/windows/perfMemory_windows.cpp b/src/hotspot/os/windows/perfMemory_windows.cpp index 851b667ac6e..84e32f5777a 100644 --- a/src/hotspot/os/windows/perfMemory_windows.cpp +++ b/src/hotspot/os/windows/perfMemory_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1238,6 +1238,7 @@ static bool make_user_tmp_dir(const char* dirname) { if (PrintMiscellaneous && Verbose) { warning("%s directory is insecure\n", dirname); } + free_security_attr(pDirSA); return false; } // The administrator should be able to delete this directory. @@ -1253,18 +1254,15 @@ static bool make_user_tmp_dir(const char* dirname) { dirname, lasterror); } } - } - else { + } else { if (PrintMiscellaneous && Verbose) { warning("CreateDirectory failed: %d\n", GetLastError()); } + free_security_attr(pDirSA); return false; } } - - // free the security attributes structure free_security_attr(pDirSA); - return true; } @@ -1296,6 +1294,8 @@ static HANDLE create_sharedmem_resources(const char* dirname, const char* filena if (!make_user_tmp_dir(dirname)) { // could not make/find the directory or the found directory // was not secure + free_security_attr(lpFileSA); + free_security_attr(lpSmoSA); return NULL; } @@ -1327,6 +1327,7 @@ static HANDLE create_sharedmem_resources(const char* dirname, const char* filena if (PrintMiscellaneous && Verbose) { warning("could not create file %s: %d\n", filename, lasterror); } + free_security_attr(lpSmoSA); return NULL; } diff --git a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp index 02e8721cc95..4f2092774b3 100644 --- a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp +++ b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -249,17 +249,16 @@ void RangeCheckEliminator::Visitor::do_ArithmeticOp(ArithmeticOp *ao) { Bound * bound = _rce->get_bound(y); if (bound->has_upper() && bound->has_lower()) { - int new_lower = bound->lower() + const_value; - jlong new_lowerl = ((jlong)bound->lower()) + const_value; - int new_upper = bound->upper() + const_value; - jlong new_upperl = ((jlong)bound->upper()) + const_value; - - if (((jlong)new_lower) == new_lowerl && ((jlong)new_upper == new_upperl)) { - Bound *newBound = new Bound(new_lower, bound->lower_instr(), new_upper, bound->upper_instr()); - _bound = newBound; - } else { - // overflow + jint t_lo = bound->lower(); + jint t_hi = bound->upper(); + jint new_lower = java_add(t_lo, const_value); + jint new_upper = java_add(t_hi, const_value); + bool overflow = ((const_value < 0 && (new_lower > t_lo)) || + (const_value > 0 && (new_upper < t_hi))); + if (overflow) { _bound = new Bound(); + } else { + _bound = new Bound(new_lower, bound->lower_instr(), new_upper, bound->upper_instr()); } } else { _bound = new Bound(); @@ -1490,7 +1489,6 @@ void RangeCheckEliminator::Bound::add_assertion(Instruction *instruction, Instru NOT_PRODUCT(ao->set_printable_bci(position->printable_bci())); result = result->insert_after(ao); compare_with = ao; - // TODO: Check that add operation does not overflow! } } assert(compare_with != NULL, "You have to compare with something!"); diff --git a/src/hotspot/share/classfile/altHashing.cpp b/src/hotspot/share/classfile/altHashing.cpp index 25d2728b445..253266c16f1 100644 --- a/src/hotspot/share/classfile/altHashing.cpp +++ b/src/hotspot/share/classfile/altHashing.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,9 +31,7 @@ /* SipHash reference C implementation - Copyright (c) 2012-2021 Jean-Philippe Aumasson - - Copyright (c) 2012-2014 Daniel J. Bernstein + Copyright (c) 2016 Jean-Philippe Aumasson To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp index 9a0872c313b..9198cd938ff 100644 --- a/src/hotspot/share/classfile/dictionary.cpp +++ b/src/hotspot/share/classfile/dictionary.cpp @@ -208,6 +208,7 @@ void DictionaryEntry::add_protection_domain(Dictionary* dict, Handle protection_ if (lt.is_enabled()) { LogStream ls(lt); print_count(&ls); + ls.cr(); } } diff --git a/src/hotspot/share/classfile/dictionary.hpp b/src/hotspot/share/classfile/dictionary.hpp index d26f1f11fb4..fca5398c3a0 100644 --- a/src/hotspot/share/classfile/dictionary.hpp +++ b/src/hotspot/share/classfile/dictionary.hpp @@ -195,7 +195,7 @@ class DictionaryEntry : public HashtableEntry { current = current->_next) { count++; } - st->print_cr("pd set count = #%d", count); + st->print("pd set count = #%d", count); } void verify(); diff --git a/src/hotspot/share/classfile/loaderConstraints.cpp b/src/hotspot/share/classfile/loaderConstraints.cpp index 7cccf23efb7..b7d339dd222 100644 --- a/src/hotspot/share/classfile/loaderConstraints.cpp +++ b/src/hotspot/share/classfile/loaderConstraints.cpp @@ -189,6 +189,7 @@ void log_ldr_constraint_msg(Symbol* class_name, const char* reason, bool LoaderConstraintTable::add_entry(Symbol* class_name, InstanceKlass* klass1, Handle class_loader1, InstanceKlass* klass2, Handle class_loader2) { + LogTarget(Info, class, loader, constraints) lt; if (klass1 != NULL && klass2 != NULL) { if (klass1 == klass2) { @@ -242,9 +243,8 @@ bool LoaderConstraintTable::add_entry(Symbol* class_name, p->set_loaders(NEW_C_HEAP_ARRAY(ClassLoaderData*, 2, mtClass)); p->set_loader(0, class_loader1()); p->set_loader(1, class_loader2()); - p->set_klass(klass); - p->set_next(bucket(index)); - set_entry(index, p); + Hashtable::add_entry(index, p); + if (lt.is_enabled()) { ResourceMark rm; lt.print("adding new constraint for name: %s, loader[0]: %s," @@ -478,13 +478,15 @@ void LoaderConstraintTable::print_on(outputStream* st) const { probe != NULL; probe = probe->next()) { st->print("%4d: ", cindex); - probe->name()->print_on(st); - st->print(" , loaders:"); + st->print("Symbol: %s loaders:", probe->name()->as_C_string()); for (int n = 0; n < probe->num_loaders(); n++) { + st->cr(); + st->print(" "); probe->loader_data(n)->print_value_on(st); - st->print(", "); } st->cr(); } } } + +void LoaderConstraintTable::print() const { print_on(tty); } diff --git a/src/hotspot/share/classfile/loaderConstraints.hpp b/src/hotspot/share/classfile/loaderConstraints.hpp index 8a7a1248e62..8d706a8b0e9 100644 --- a/src/hotspot/share/classfile/loaderConstraints.hpp +++ b/src/hotspot/share/classfile/loaderConstraints.hpp @@ -79,6 +79,7 @@ class LoaderConstraintTable : public Hashtable { void purge_loader_constraints(); void verify(PlaceholderTable* placeholders); + void print() const; void print_on(outputStream* st) const; }; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index ed22bb21e84..0e4ef28858b 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1805,14 +1805,17 @@ void SystemDictionary::add_to_hierarchy(InstanceKlass* k, TRAPS) { assert(k != NULL, "just checking"); assert_locked_or_safepoint(Compile_lock); - // Link into hierachy. Make sure the vtables are initialized before linking into + k->set_init_state(InstanceKlass::loaded); + // make sure init_state store is already done. + // The compiler reads the hierarchy outside of the Compile_lock. + // Access ordering is used to add to hierarchy. + + // Link into hierachy. k->append_to_sibling_list(); // add to superklass/sibling list k->process_interfaces(THREAD); // handle all "implements" declarations - k->set_init_state(InstanceKlass::loaded); + // Now flush all code that depended on old class hierarchy. // Note: must be done *after* linking k into the hierarchy (was bug 12/9/97) - // Also, first reinitialize vtable because it may have gotten out of synch - // while the new class wasn't connected to the class hierarchy. CodeCache::flush_dependents_on(k); } diff --git a/src/hotspot/share/classfile/verifier.hpp b/src/hotspot/share/classfile/verifier.hpp index 05239c57866..c95c01f223d 100644 --- a/src/hotspot/share/classfile/verifier.hpp +++ b/src/hotspot/share/classfile/verifier.hpp @@ -36,7 +36,6 @@ class Verifier : AllStatic { public: enum { - STRICTER_ACCESS_CTRL_CHECK_VERSION = 49, STACKMAP_ATTRIBUTE_MAJOR_VERSION = 50, INVOKEDYNAMIC_MAJOR_VERSION = 51, NO_RELAX_ACCESS_CTRL_CHECK_VERSION = 52, diff --git a/src/hotspot/share/code/codeHeapState.cpp b/src/hotspot/share/code/codeHeapState.cpp index f27f8c14efd..2c25140edac 100644 --- a/src/hotspot/share/code/codeHeapState.cpp +++ b/src/hotspot/share/code/codeHeapState.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "code/codeHeapState.hpp" +#include "code/codeBlob.hpp" #include "compiler/compileBroker.hpp" #include "runtime/safepoint.hpp" #include "runtime/sweeper.hpp" @@ -1154,10 +1155,9 @@ void CodeHeapState::aggregate(outputStream* out, CodeHeap* heap, size_t granular ast->cr(); int reset_val = NMethodSweeper::hotness_counter_reset_val(); - double reverse_free_ratio = (res_size > size) ? (double)res_size/(double)(res_size-size) : (double)res_size; printBox(ast, '-', "Method hotness information at time of this analysis", NULL); ast->print_cr("Highest possible method temperature: %12d", reset_val); - ast->print_cr("Threshold for method to be considered 'cold': %12.3f", -reset_val + reverse_free_ratio * NmethodSweepActivity); + ast->print_cr("Threshold for method to be considered 'cold': %12.3f", -reset_val + (CodeCache::reverse_free_ratio(heap->code_blob_type()) * NmethodSweepActivity)); if (n_methods > 0) { avgTemp = hotnessAccumulator/n_methods; ast->print_cr("min. hotness = %6d", minTemp); diff --git a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp index 3dc29c7a57f..2d83b3fb40d 100644 --- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp +++ b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp @@ -80,6 +80,7 @@ #include "runtime/timer.hpp" #include "runtime/vmThread.hpp" #include "services/memoryService.hpp" +#include "services/memTracker.hpp" #include "services/runtimeService.hpp" #include "utilities/align.hpp" #include "utilities/stack.inline.hpp" @@ -5068,8 +5069,9 @@ void CMSRefProcTaskProxy::work(uint worker_id) { CMSParDrainMarkingStackClosure par_drain_stack(_collector, _span, _mark_bit_map, work_queue(worker_id)); + BarrierEnqueueDiscoveredFieldClosure enqueue; CMSIsAliveClosure is_alive_closure(_span, _mark_bit_map); - _task.work(worker_id, is_alive_closure, par_keep_alive, par_drain_stack); + _task.work(worker_id, is_alive_closure, par_keep_alive, enqueue, par_drain_stack); if (_task.marks_oops_alive()) { do_work_steal(worker_id, &par_drain_stack, &par_keep_alive, _collector->hash_seed(worker_id)); @@ -5167,6 +5169,7 @@ void CMSCollector::refProcessingWork() { // Setup keep_alive and complete closures. CMSKeepAliveClosure cmsKeepAliveClosure(this, _span, &_markBitMap, &_markStack, false /* !preclean */); + BarrierEnqueueDiscoveredFieldClosure cmsEnqueue; CMSDrainMarkingStackClosure cmsDrainMarkingStackClosure(this, _span, &_markBitMap, &_markStack, &cmsKeepAliveClosure, false /* !preclean */); @@ -5192,12 +5195,14 @@ void CMSCollector::refProcessingWork() { CMSRefProcTaskExecutor task_executor(*this); stats = rp->process_discovered_references(&_is_alive_closure, &cmsKeepAliveClosure, + &cmsEnqueue, &cmsDrainMarkingStackClosure, &task_executor, &pt); } else { stats = rp->process_discovered_references(&_is_alive_closure, &cmsKeepAliveClosure, + &cmsEnqueue, &cmsDrainMarkingStackClosure, NULL, &pt); @@ -5651,6 +5656,9 @@ bool CMSBitMap::allocate(MemRegion mr) { log_warning(gc)("CMS bit map backing store failure"); return false; } + + // Record NMT memory type + MemTracker::record_virtual_memory_type(brs.base(), mtGC); assert(_virtual_space.committed_size() == brs.size(), "didn't reserve backing store for all of CMS bit map?"); assert(_virtual_space.committed_size() << (_shifter + LogBitsPerByte) >= @@ -5739,6 +5747,9 @@ bool CMSMarkStack::allocate(size_t size) { log_warning(gc)("CMSMarkStack backing store failure"); return false; } + + // Record NMT memory type + MemTracker::record_virtual_memory_type(rs.base(), mtGC); assert(_virtual_space.committed_size() == rs.size(), "didn't reserve backing store for all of CMS stack?"); _base = (oop*)(_virtual_space.low()); diff --git a/src/hotspot/share/gc/cms/parNewGeneration.cpp b/src/hotspot/share/gc/cms/parNewGeneration.cpp index f6c55900f92..71116cc7e56 100644 --- a/src/hotspot/share/gc/cms/parNewGeneration.cpp +++ b/src/hotspot/share/gc/cms/parNewGeneration.cpp @@ -780,8 +780,10 @@ void ParNewRefProcTaskProxy::work(uint worker_id) { HandleMark hm; ParScanThreadState& par_scan_state = _state_set.thread_state(worker_id); par_scan_state.set_young_old_boundary(_young_old_boundary); + BarrierEnqueueDiscoveredFieldClosure enqueue; _task.work(worker_id, par_scan_state.is_alive_closure(), par_scan_state.keep_alive_closure(), + enqueue, par_scan_state.evacuate_followers_closure()); } @@ -948,6 +950,7 @@ void ParNewGeneration::collect(bool full, IsAliveClosure is_alive(this); ScanWeakRefClosure scan_weak_ref(this); KeepAliveClosure keep_alive(&scan_weak_ref); + BarrierEnqueueDiscoveredFieldClosure enqueue; ScanClosure scan_without_gc_barrier(this, false); ScanClosureWithParBarrier scan_with_gc_barrier(this, true); set_promo_failure_scan_stack_closure(&scan_without_gc_barrier); @@ -960,13 +963,13 @@ void ParNewGeneration::collect(bool full, ReferenceProcessorPhaseTimes pt(_gc_timer, rp->max_num_queues()); if (rp->processing_is_mt()) { ParNewRefProcTaskExecutor task_executor(*this, *_old_gen, thread_state_set); - stats = rp->process_discovered_references(&is_alive, &keep_alive, + stats = rp->process_discovered_references(&is_alive, &keep_alive, &enqueue, &evacuate_followers, &task_executor, &pt); } else { thread_state_set.flush(); gch->save_marks(); - stats = rp->process_discovered_references(&is_alive, &keep_alive, + stats = rp->process_discovered_references(&is_alive, &keep_alive, &enqueue, &evacuate_followers, NULL, &pt); } diff --git a/src/hotspot/share/gc/g1/dirtyCardQueue.hpp b/src/hotspot/share/gc/g1/dirtyCardQueue.hpp index 58a72aae375..72baed99e17 100644 --- a/src/hotspot/share/gc/g1/dirtyCardQueue.hpp +++ b/src/hotspot/share/gc/g1/dirtyCardQueue.hpp @@ -160,7 +160,6 @@ class DirtyCardQueueSet: public PtrQueueSet { // If any threads have partial logs, add them to the global list of logs. void concatenate_logs(); - void clear_n_completed_buffers() { _n_completed_buffers = 0;} jint processed_buffers_mut() { return _processed_buffers_mut; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 3b0e6eb6587..f10541717a6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1865,7 +1865,6 @@ void G1CollectedHeap::iterate_dirty_card_closure(CardTableEntryClosure* cl, uint n_completed_buffers++; } g1_policy()->phase_times()->record_thread_work_item(G1GCPhaseTimes::UpdateRS, worker_i, n_completed_buffers, G1GCPhaseTimes::UpdateRSProcessedBuffers); - dcqs.clear_n_completed_buffers(); assert(!dcqs.completed_buffers_exist_dirty(), "Completed buffers exist!"); } @@ -3806,6 +3805,31 @@ class G1CopyingKeepAliveClosure: public OopClosure { } }; +// Special closure for enqueuing discovered fields: during enqueue the card table +// may not be in shape to properly handle normal barrier calls (e.g. card marks +// in regions that failed evacuation, scribbling of various values by card table +// scan code). Additionally the regular barrier enqueues into the "global" +// DCQS, but during GC we need these to-be-refined entries in the GC local queue +// so that after clearing the card table, the redirty cards phase will properly +// mark all dirty cards to be picked up by refinement. +class G1EnqueueDiscoveredFieldClosure : public EnqueueDiscoveredFieldClosure { + G1CollectedHeap* _g1h; + G1ParScanThreadState* _pss; + +public: + G1EnqueueDiscoveredFieldClosure(G1CollectedHeap* g1h, G1ParScanThreadState* pss) : _g1h(g1h), _pss(pss) { } + + virtual void enqueue(HeapWord* discovered_field_addr, oop value) { + assert(_g1h->is_in(discovered_field_addr), PTR_FORMAT " is not in heap ", p2i(discovered_field_addr)); + // Store the value first, whatever it is. + RawAccess<>::oop_store(discovered_field_addr, value); + if (value == NULL) { + return; + } + _pss->write_ref_field_post(discovered_field_addr, value); + } +}; + // Serial drain queue closure. Called as the 'complete_gc' // closure for each discovered list in some of the // reference processing phases. @@ -3895,11 +3919,12 @@ class G1STWRefProcTaskProxy: public AbstractGangTask { // Keep alive closure. G1CopyingKeepAliveClosure keep_alive(_g1h, pss->closures()->raw_strong_oops(), pss); + G1EnqueueDiscoveredFieldClosure enqueue(_g1h, pss); // Complete GC closure G1ParEvacuateFollowersClosure drain_queue(_g1h, pss, _task_queues, _terminator); // Call the reference processing task's work routine. - _proc_task.work(worker_id, is_alive, keep_alive, drain_queue); + _proc_task.work(worker_id, is_alive, keep_alive, enqueue, drain_queue); // Note we cannot assert that the refs array is empty here as not all // of the processing tasks (specifically phase2 - pp2_work) execute @@ -3947,6 +3972,7 @@ void G1CollectedHeap::process_discovered_references(G1ParScanThreadStateSet* per // Keep alive closure. G1CopyingKeepAliveClosure keep_alive(this, pss->closures()->raw_strong_oops(), pss); + G1EnqueueDiscoveredFieldClosure enqueue(this, pss); // Serial Complete GC closure G1STWDrainQueueClosure drain_queue(this, pss); @@ -3960,6 +3986,7 @@ void G1CollectedHeap::process_discovered_references(G1ParScanThreadStateSet* per // Serial reference processing... stats = rp->process_discovered_references(&is_alive, &keep_alive, + &enqueue, &drain_queue, NULL, pt); @@ -3974,6 +4001,7 @@ void G1CollectedHeap::process_discovered_references(G1ParScanThreadStateSet* per G1STWRefProcTaskExecutor par_task_executor(this, per_thread_states, workers(), _task_queues); stats = rp->process_discovered_references(&is_alive, &keep_alive, + &enqueue, &drain_queue, &par_task_executor, pt); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 94194d1be6d..b9c0627a370 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1550,9 +1550,10 @@ class G1CMRefProcTaskProxy : public AbstractGangTask { G1CMTask* task = _cm->task(worker_id); G1CMIsAliveClosure g1_is_alive(_g1h); G1CMKeepAliveAndDrainClosure g1_par_keep_alive(_cm, task, false /* is_serial */); + BarrierEnqueueDiscoveredFieldClosure enqueue; G1CMDrainMarkingStackClosure g1_par_drain(_cm, task, false /* is_serial */); - _proc_task.work(worker_id, g1_is_alive, g1_par_keep_alive, g1_par_drain); + _proc_task.work(worker_id, g1_is_alive, g1_par_keep_alive, enqueue, g1_par_drain); } }; @@ -1609,6 +1610,7 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) { // their own instances of these closures, which do their own // synchronization among themselves. G1CMKeepAliveAndDrainClosure g1_keep_alive(this, task(0), true /* is_serial */); + BarrierEnqueueDiscoveredFieldClosure g1_enqueue; G1CMDrainMarkingStackClosure g1_drain_mark_stack(this, task(0), true /* is_serial */); // We need at least one active thread. If reference processing @@ -1640,6 +1642,7 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) { const ReferenceProcessorStats& stats = rp->process_discovered_references(&g1_is_alive, &g1_keep_alive, + &g1_enqueue, &g1_drain_mark_stack, executor, &pt); diff --git a/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp b/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp index 1e180192612..26eba134542 100644 --- a/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp @@ -58,10 +58,12 @@ G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::G1RefProcTaskProxy(Proc void G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::work(uint worker_id) { G1FullGCMarker* marker = _collector->marker(worker_id); G1IsAliveClosure is_alive(_collector->mark_bitmap()); + BarrierEnqueueDiscoveredFieldClosure enqueue; G1FullKeepAliveClosure keep_alive(marker); _proc_task.work(worker_id, is_alive, keep_alive, + enqueue, *marker->stack_closure()); } @@ -84,6 +86,7 @@ void G1FullGCReferenceProcessingExecutor::execute(STWGCTimer* timer, G1FullGCTra G1FullGCMarker* marker = _collector->marker(0); G1IsAliveClosure is_alive(_collector->mark_bitmap()); G1FullKeepAliveClosure keep_alive(marker); + BarrierEnqueueDiscoveredFieldClosure enqueue; ReferenceProcessorPhaseTimes pt(timer, _reference_processor->max_num_queues()); AbstractRefProcTaskExecutor* executor = _reference_processor->processing_is_mt() ? this : NULL; @@ -92,6 +95,7 @@ void G1FullGCReferenceProcessingExecutor::execute(STWGCTimer* timer, G1FullGCTra const ReferenceProcessorStats& stats = _reference_processor->process_discovered_references(&is_alive, &keep_alive, + &enqueue, marker->stack_closure(), executor, &pt); diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 7e6369269a0..5f6fc0778c5 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -105,6 +105,12 @@ class G1ParScanThreadState : public CHeapObj { template void do_oop_ext(T* ref); template void push_on_queue(T* ref); + // Apply the post barrier to the given reference field. Enqueues the card of p + // if the barrier does not filter out the reference for some reason (e.g. + // p and q are in the same region, p is in survivor, p is in collection set) + // To be called during GC if nothing particular about p and obj are known. + template void write_ref_field_post(T* p, oop obj); + template void update_rs(HeapRegion* from, T* p, oop o) { assert(!HeapRegion::is_in_same_region(p, o), "Caller should have filtered out cross-region references already."); // If the field originates from the to-space, we don't need to include it diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp index f1fba8e947a..10754bafac1 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp @@ -55,11 +55,16 @@ template void G1ParScanThreadState::do_oop_evac(T* p) { "In_cset_state must be NotInCSet here, but is " CSETSTATE_FORMAT, in_cset_state.value()); } + write_ref_field_post(p, obj); +} + +template void G1ParScanThreadState::write_ref_field_post(T* p, oop obj) { assert(obj != NULL, "Must be"); - if (!HeapRegion::is_in_same_region(p, obj)) { - HeapRegion* from = _g1h->heap_region_containing(p); - update_rs(from, p, obj); + if (HeapRegion::is_in_same_region(p, obj)) { + return; } + HeapRegion* from = _g1h->heap_region_containing(p); + update_rs(from, p, obj); } template inline void G1ParScanThreadState::push_on_queue(T* ref) { diff --git a/src/hotspot/share/gc/g1/ptrQueue.hpp b/src/hotspot/share/gc/g1/ptrQueue.hpp index ac8ce1d1670..d07575c6695 100644 --- a/src/hotspot/share/gc/g1/ptrQueue.hpp +++ b/src/hotspot/share/gc/g1/ptrQueue.hpp @@ -329,7 +329,7 @@ class PtrQueueSet { bool process_or_enqueue_complete_buffer(BufferNode* node); bool completed_buffers_exist_dirty() { - return _n_completed_buffers > 0; + return _n_completed_buffers > 0 || _completed_buffers_head != NULL; } bool process_completed_buffers() { return _process_completed; } diff --git a/src/hotspot/share/gc/parallel/pcTasks.cpp b/src/hotspot/share/gc/parallel/pcTasks.cpp index 57ff2355088..17caffae87a 100644 --- a/src/hotspot/share/gc/parallel/pcTasks.cpp +++ b/src/hotspot/share/gc/parallel/pcTasks.cpp @@ -137,9 +137,10 @@ void RefProcTaskProxy::do_it(GCTaskManager* manager, uint which) ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(which); ParCompactionManager::MarkAndPushClosure mark_and_push_closure(cm); + BarrierEnqueueDiscoveredFieldClosure enqueue; ParCompactionManager::FollowStackClosure follow_stack_closure(cm); _rp_task.work(_work_id, *PSParallelCompact::is_alive_closure(), - mark_and_push_closure, follow_stack_closure); + mark_and_push_closure, enqueue, follow_stack_closure); } // diff --git a/src/hotspot/share/gc/parallel/psMarkSweep.cpp b/src/hotspot/share/gc/parallel/psMarkSweep.cpp index b6f3490080c..e97f3b00bd4 100644 --- a/src/hotspot/share/gc/parallel/psMarkSweep.cpp +++ b/src/hotspot/share/gc/parallel/psMarkSweep.cpp @@ -534,9 +534,10 @@ void PSMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) { ref_processor()->setup_policy(clear_all_softrefs); ReferenceProcessorPhaseTimes pt(_gc_timer, ref_processor()->max_num_queues()); + BarrierEnqueueDiscoveredFieldClosure enqueue; const ReferenceProcessorStats& stats = ref_processor()->process_discovered_references( - is_alive_closure(), mark_and_push_closure(), follow_stack_closure(), NULL, &pt); + is_alive_closure(), mark_and_push_closure(), &enqueue, follow_stack_closure(), NULL, &pt); gc_tracer()->report_gc_reference_stats(stats); pt.print_all_references(); } diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index 2954863160c..6efab33434e 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -2118,16 +2118,18 @@ void PSParallelCompact::marking_phase(ParCompactionManager* cm, ReferenceProcessorStats stats; ReferenceProcessorPhaseTimes pt(&_gc_timer, ref_processor()->max_num_queues()); + BarrierEnqueueDiscoveredFieldClosure enqueue; + if (ref_processor()->processing_is_mt()) { ref_processor()->set_active_mt_degree(active_gc_threads); RefProcTaskExecutor task_executor; stats = ref_processor()->process_discovered_references( - is_alive_closure(), &mark_and_push_closure, &follow_stack_closure, + is_alive_closure(), &mark_and_push_closure, &enqueue, &follow_stack_closure, &task_executor, &pt); } else { stats = ref_processor()->process_discovered_references( - is_alive_closure(), &mark_and_push_closure, &follow_stack_closure, NULL, + is_alive_closure(), &mark_and_push_closure, &enqueue, &follow_stack_closure, NULL, &pt); } diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index e962d95b0a6..0c7b910daa7 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -145,9 +145,10 @@ void PSRefProcTaskProxy::do_it(GCTaskManager* manager, uint which) PSPromotionManager::gc_thread_promotion_manager(which); assert(promotion_manager != NULL, "sanity check"); PSKeepAliveClosure keep_alive(promotion_manager); + BarrierEnqueueDiscoveredFieldClosure enqueue; PSEvacuateFollowersClosure evac_followers(promotion_manager); PSIsAliveClosure is_alive; - _rp_task.work(_work_id, is_alive, keep_alive, evac_followers); + _rp_task.work(_work_id, is_alive, keep_alive, enqueue, evac_followers); } class PSRefProcTaskExecutor: public AbstractRefProcTaskExecutor { @@ -405,17 +406,18 @@ bool PSScavenge::invoke_no_policy() { reference_processor()->setup_policy(false); // not always_clear reference_processor()->set_active_mt_degree(active_workers); PSKeepAliveClosure keep_alive(promotion_manager); + BarrierEnqueueDiscoveredFieldClosure enqueue; PSEvacuateFollowersClosure evac_followers(promotion_manager); ReferenceProcessorStats stats; ReferenceProcessorPhaseTimes pt(&_gc_timer, reference_processor()->max_num_queues()); if (reference_processor()->processing_is_mt()) { PSRefProcTaskExecutor task_executor; stats = reference_processor()->process_discovered_references( - &_is_alive_closure, &keep_alive, &evac_followers, &task_executor, + &_is_alive_closure, &keep_alive, &enqueue, &evac_followers, &task_executor, &pt); } else { stats = reference_processor()->process_discovered_references( - &_is_alive_closure, &keep_alive, &evac_followers, NULL, &pt); + &_is_alive_closure, &keep_alive, &enqueue, &evac_followers, NULL, &pt); } _gc_tracer.report_gc_reference_stats(stats); diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index beb7f85248a..3ebd029bc0d 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -619,11 +619,12 @@ void DefNewGeneration::collect(bool full, evacuate_followers.do_void(); FastKeepAliveClosure keep_alive(this, &scan_weak_ref); + BarrierEnqueueDiscoveredFieldClosure enqueue; ReferenceProcessor* rp = ref_processor(); rp->setup_policy(clear_all_soft_refs); ReferenceProcessorPhaseTimes pt(_gc_timer, rp->max_num_queues()); const ReferenceProcessorStats& stats = - rp->process_discovered_references(&is_alive, &keep_alive, &evacuate_followers, + rp->process_discovered_references(&is_alive, &keep_alive, &enqueue, &evacuate_followers, NULL, &pt); gc_tracer.report_gc_reference_stats(stats); gc_tracer.report_tenuring_threshold(tenuring_threshold()); diff --git a/src/hotspot/share/gc/serial/genMarkSweep.cpp b/src/hotspot/share/gc/serial/genMarkSweep.cpp index b05c6602cba..a39ffb8aac5 100644 --- a/src/hotspot/share/gc/serial/genMarkSweep.cpp +++ b/src/hotspot/share/gc/serial/genMarkSweep.cpp @@ -210,9 +210,10 @@ void GenMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) { ref_processor()->setup_policy(clear_all_softrefs); ReferenceProcessorPhaseTimes pt(_gc_timer, ref_processor()->max_num_queues()); + BarrierEnqueueDiscoveredFieldClosure enqueue; const ReferenceProcessorStats& stats = ref_processor()->process_discovered_references( - &is_alive, &keep_alive, &follow_stack_closure, NULL, &pt); + &is_alive, &keep_alive, &enqueue, &follow_stack_closure, NULL, &pt); pt.print_all_references(); gc_tracer()->report_gc_reference_stats(stats); } diff --git a/src/hotspot/share/gc/shared/referenceProcessor.cpp b/src/hotspot/share/gc/shared/referenceProcessor.cpp index 252435f4a76..64ae3cddc94 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp @@ -199,11 +199,12 @@ void ReferenceProcessor::verify_total_count_zero(DiscoveredList lists[], const c #endif ReferenceProcessorStats ReferenceProcessor::process_discovered_references( - BoolObjectClosure* is_alive, - OopClosure* keep_alive, - VoidClosure* complete_gc, - AbstractRefProcTaskExecutor* task_executor, - ReferenceProcessorPhaseTimes* phase_times) { + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor, + ReferenceProcessorPhaseTimes* phase_times) { double start_time = os::elapsedTime(); @@ -236,17 +237,17 @@ ReferenceProcessorStats ReferenceProcessor::process_discovered_references( { RefProcTotalPhaseTimesTracker tt(RefPhase2, phase_times, this); - process_soft_weak_final_refs(is_alive, keep_alive, complete_gc, task_executor, phase_times); + process_soft_weak_final_refs(is_alive, keep_alive, enqueue, complete_gc, task_executor, phase_times); } { RefProcTotalPhaseTimesTracker tt(RefPhase3, phase_times, this); - process_final_keep_alive(keep_alive, complete_gc, task_executor, phase_times); + process_final_keep_alive(keep_alive, enqueue, complete_gc, task_executor, phase_times); } { RefProcTotalPhaseTimesTracker tt(RefPhase4, phase_times, this); - process_phantom_refs(is_alive, keep_alive, complete_gc, task_executor, phase_times); + process_phantom_refs(is_alive, keep_alive, enqueue, complete_gc, task_executor, phase_times); } if (task_executor != NULL) { @@ -259,6 +260,12 @@ ReferenceProcessorStats ReferenceProcessor::process_discovered_references( return stats; } +void BarrierEnqueueDiscoveredFieldClosure::enqueue(HeapWord* discovered_field_addr, oop value) { + assert(Universe::heap()->is_in(discovered_field_addr), PTR_FORMAT " not in heap", p2i(discovered_field_addr)); + HeapAccess::oop_store(discovered_field_addr, + value); +} + void DiscoveredListIterator::load_ptrs(DEBUG_ONLY(bool allow_null_referent)) { _current_discovered_addr = java_lang_ref_Reference::discovered_addr_raw(_current_discovered); oop discovered = java_lang_ref_Reference::discovered(_current_discovered); @@ -316,7 +323,7 @@ void DiscoveredListIterator::complete_enqueue() { // Swap refs_list into pending list and set obj's // discovered to what we read from the pending list. oop old = Universe::swap_reference_pending_list(_refs_list.head()); - HeapAccess::oop_store_at(_prev_discovered, java_lang_ref_Reference::discovered_offset, old); + _enqueue->enqueue(java_lang_ref_Reference::discovered_addr_raw(_prev_discovered), old); } } @@ -344,7 +351,7 @@ size_t ReferenceProcessor::process_soft_ref_reconsider_work(DiscoveredList& r OopClosure* keep_alive, VoidClosure* complete_gc) { assert(policy != NULL, "Must have a non-NULL policy"); - DiscoveredListIterator iter(refs_list, keep_alive, is_alive); + DiscoveredListIterator iter(refs_list, keep_alive, is_alive, NULL /* enqueue */); // Decide which softly reachable refs should be kept alive. while (iter.has_next()) { iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */)); @@ -372,8 +379,9 @@ size_t ReferenceProcessor::process_soft_ref_reconsider_work(DiscoveredList& r size_t ReferenceProcessor::process_soft_weak_final_refs_work(DiscoveredList& refs_list, BoolObjectClosure* is_alive, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, bool do_enqueue_and_clear) { - DiscoveredListIterator iter(refs_list, keep_alive, is_alive); + DiscoveredListIterator iter(refs_list, keep_alive, is_alive, enqueue); while (iter.has_next()) { iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */)); if (iter.referent() == NULL) { @@ -416,8 +424,9 @@ size_t ReferenceProcessor::process_soft_weak_final_refs_work(DiscoveredList& size_t ReferenceProcessor::process_final_keep_alive_work(DiscoveredList& refs_list, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc) { - DiscoveredListIterator iter(refs_list, keep_alive, NULL); + DiscoveredListIterator iter(refs_list, keep_alive, NULL /* is_alive */, enqueue); while (iter.has_next()) { iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */)); // keep the referent and followers around @@ -441,10 +450,11 @@ size_t ReferenceProcessor::process_final_keep_alive_work(DiscoveredList& refs_li } size_t ReferenceProcessor::process_phantom_refs_work(DiscoveredList& refs_list, - BoolObjectClosure* is_alive, - OopClosure* keep_alive, - VoidClosure* complete_gc) { - DiscoveredListIterator iter(refs_list, keep_alive, is_alive); + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, + VoidClosure* complete_gc) { + DiscoveredListIterator iter(refs_list, keep_alive, is_alive, enqueue); while (iter.has_next()) { iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */)); @@ -527,6 +537,7 @@ class RefProcPhase1Task : public AbstractRefProcTaskExecutor::ProcessTask { virtual void work(uint worker_id, BoolObjectClosure& is_alive, OopClosure& keep_alive, + EnqueueDiscoveredFieldClosure& enqueue, VoidClosure& complete_gc) { RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::SoftRefSubPhase1, _phase_times, worker_id); @@ -546,11 +557,13 @@ class RefProcPhase2Task: public AbstractRefProcTaskExecutor::ProcessTask { DiscoveredList list[], BoolObjectClosure& is_alive, OopClosure& keep_alive, + EnqueueDiscoveredFieldClosure& enqueue, bool do_enqueue_and_clear, ReferenceType ref_type) { size_t const removed = _ref_processor.process_soft_weak_final_refs_work(list[worker_id], &is_alive, &keep_alive, + &enqueue, do_enqueue_and_clear); _phase_times->add_ref_cleared(ref_type, removed); } @@ -563,19 +576,20 @@ class RefProcPhase2Task: public AbstractRefProcTaskExecutor::ProcessTask { virtual void work(uint worker_id, BoolObjectClosure& is_alive, OopClosure& keep_alive, + EnqueueDiscoveredFieldClosure& enqueue, VoidClosure& complete_gc) { RefProcWorkerTimeTracker t(_phase_times->phase2_worker_time_sec(), worker_id); { RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::SoftRefSubPhase2, _phase_times, worker_id); - run_phase2(worker_id, _ref_processor._discoveredSoftRefs, is_alive, keep_alive, true /* do_enqueue_and_clear */, REF_SOFT); + run_phase2(worker_id, _ref_processor._discoveredSoftRefs, is_alive, keep_alive, enqueue, true /* do_enqueue_and_clear */, REF_SOFT); } { RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::WeakRefSubPhase2, _phase_times, worker_id); - run_phase2(worker_id, _ref_processor._discoveredWeakRefs, is_alive, keep_alive, true /* do_enqueue_and_clear */, REF_WEAK); + run_phase2(worker_id, _ref_processor._discoveredWeakRefs, is_alive, keep_alive, enqueue, true /* do_enqueue_and_clear */, REF_WEAK); } { RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::FinalRefSubPhase2, _phase_times, worker_id); - run_phase2(worker_id, _ref_processor._discoveredFinalRefs, is_alive, keep_alive, false /* do_enqueue_and_clear */, REF_FINAL); + run_phase2(worker_id, _ref_processor._discoveredFinalRefs, is_alive, keep_alive, enqueue, false /* do_enqueue_and_clear */, REF_FINAL); } // Close the reachable set; needed for collectors which keep_alive_closure do // not immediately complete their work. @@ -592,10 +606,11 @@ class RefProcPhase3Task: public AbstractRefProcTaskExecutor::ProcessTask { virtual void work(uint worker_id, BoolObjectClosure& is_alive, OopClosure& keep_alive, + EnqueueDiscoveredFieldClosure& enqueue, VoidClosure& complete_gc) { RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::FinalRefSubPhase3, _phase_times, worker_id); - _ref_processor.process_final_keep_alive_work(_ref_processor._discoveredFinalRefs[worker_id], &keep_alive, &complete_gc); + _ref_processor.process_final_keep_alive_work(_ref_processor._discoveredFinalRefs[worker_id], &keep_alive, &enqueue, &complete_gc); } }; @@ -608,12 +623,14 @@ class RefProcPhase4Task: public AbstractRefProcTaskExecutor::ProcessTask { virtual void work(uint worker_id, BoolObjectClosure& is_alive, OopClosure& keep_alive, + EnqueueDiscoveredFieldClosure& enqueue, VoidClosure& complete_gc) { RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::PhantomRefSubPhase4, _phase_times, worker_id); size_t const removed = _ref_processor.process_phantom_refs_work(_ref_processor._discoveredPhantomRefs[worker_id], &is_alive, &keep_alive, + &enqueue, &complete_gc); _phase_times->add_ref_cleared(REF_PHANTOM, removed); } @@ -821,6 +838,7 @@ void ReferenceProcessor::process_soft_ref_reconsider(BoolObjectClosure* is_alive void ReferenceProcessor::process_soft_weak_final_refs(BoolObjectClosure* is_alive, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc, AbstractRefProcTaskExecutor* task_executor, ReferenceProcessorPhaseTimes* phase_times) { @@ -864,7 +882,7 @@ void ReferenceProcessor::process_soft_weak_final_refs(BoolObjectClosure* is_aliv RefProcSubPhasesWorkerTimeTracker tt2(SoftRefSubPhase2, phase_times, 0); for (uint i = 0; i < _max_num_queues; i++) { - removed += process_soft_weak_final_refs_work(_discoveredSoftRefs[i], is_alive, keep_alive, true /* do_enqueue */); + removed += process_soft_weak_final_refs_work(_discoveredSoftRefs[i], is_alive, keep_alive, enqueue, true /* do_enqueue */); } phase_times->add_ref_cleared(REF_SOFT, removed); @@ -874,7 +892,7 @@ void ReferenceProcessor::process_soft_weak_final_refs(BoolObjectClosure* is_aliv RefProcSubPhasesWorkerTimeTracker tt2(WeakRefSubPhase2, phase_times, 0); for (uint i = 0; i < _max_num_queues; i++) { - removed += process_soft_weak_final_refs_work(_discoveredWeakRefs[i], is_alive, keep_alive, true /* do_enqueue */); + removed += process_soft_weak_final_refs_work(_discoveredWeakRefs[i], is_alive, keep_alive, enqueue, true /* do_enqueue */); } phase_times->add_ref_cleared(REF_WEAK, removed); @@ -884,7 +902,7 @@ void ReferenceProcessor::process_soft_weak_final_refs(BoolObjectClosure* is_aliv RefProcSubPhasesWorkerTimeTracker tt2(FinalRefSubPhase2, phase_times, 0); for (uint i = 0; i < _max_num_queues; i++) { - removed += process_soft_weak_final_refs_work(_discoveredFinalRefs[i], is_alive, keep_alive, false /* do_enqueue */); + removed += process_soft_weak_final_refs_work(_discoveredFinalRefs[i], is_alive, keep_alive, enqueue, false /* do_enqueue */); } phase_times->add_ref_cleared(REF_FINAL, removed); @@ -897,6 +915,7 @@ void ReferenceProcessor::process_soft_weak_final_refs(BoolObjectClosure* is_aliv } void ReferenceProcessor::process_final_keep_alive(OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc, AbstractRefProcTaskExecutor* task_executor, ReferenceProcessorPhaseTimes* phase_times) { @@ -928,7 +947,7 @@ void ReferenceProcessor::process_final_keep_alive(OopClosure* keep_alive, } else { RefProcSubPhasesWorkerTimeTracker tt2(FinalRefSubPhase3, phase_times, 0); for (uint i = 0; i < _max_num_queues; i++) { - process_final_keep_alive_work(_discoveredFinalRefs[i], keep_alive, complete_gc); + process_final_keep_alive_work(_discoveredFinalRefs[i], keep_alive, enqueue, complete_gc); } } verify_total_count_zero(_discoveredFinalRefs, "FinalReference"); @@ -936,6 +955,7 @@ void ReferenceProcessor::process_final_keep_alive(OopClosure* keep_alive, void ReferenceProcessor::process_phantom_refs(BoolObjectClosure* is_alive, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc, AbstractRefProcTaskExecutor* task_executor, ReferenceProcessorPhaseTimes* phase_times) { @@ -970,7 +990,7 @@ void ReferenceProcessor::process_phantom_refs(BoolObjectClosure* is_alive, RefProcSubPhasesWorkerTimeTracker tt(PhantomRefSubPhase4, phase_times, 0); for (uint i = 0; i < _max_num_queues; i++) { - removed += process_phantom_refs_work(_discoveredPhantomRefs[i], is_alive, keep_alive, complete_gc); + removed += process_phantom_refs_work(_discoveredPhantomRefs[i], is_alive, keep_alive, enqueue, complete_gc); } phase_times->add_ref_cleared(REF_PHANTOM, removed); @@ -1316,7 +1336,7 @@ bool ReferenceProcessor::preclean_discovered_reflist(DiscoveredList& refs_lis OopClosure* keep_alive, VoidClosure* complete_gc, YieldClosure* yield) { - DiscoveredListIterator iter(refs_list, keep_alive, is_alive); + DiscoveredListIterator iter(refs_list, keep_alive, is_alive, NULL /* enqueue */); while (iter.has_next()) { if (yield->should_return_fine_grain()) { return true; diff --git a/src/hotspot/share/gc/shared/referenceProcessor.hpp b/src/hotspot/share/gc/shared/referenceProcessor.hpp index e4f1f627269..3feb8492ef7 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.hpp @@ -36,6 +36,27 @@ class GCTimer; class ReferencePolicy; class ReferenceProcessorPhaseTimes; +// Provides a callback to the garbage collector to set the given value to the +// discovered field of the j.l.ref.Reference instance. This is called during STW +// reference processing when iterating over the discovered lists for all +// discovered references. +// Typically garbage collectors may just call the barrier, but for some garbage +// collectors the barrier environment (e.g. card table) may not be set up correctly +// at the point of invocation. +class EnqueueDiscoveredFieldClosure { +public: + // For the given j.l.ref.Reference discovered field address, set the discovered + // field to value and apply any barriers to it. + virtual void enqueue(HeapWord* discovered_field_addr, oop value) = 0; +}; + +// EnqueueDiscoveredFieldClosure that executes the default barrier on the discovered +// field of the j.l.ref.Reference with the given value. +class BarrierEnqueueDiscoveredFieldClosure : public EnqueueDiscoveredFieldClosure { +public: + virtual void enqueue(HeapWord* discovered_field_addr, oop value); +}; + // List of discovered references. class DiscoveredList { public: @@ -77,6 +98,7 @@ class DiscoveredListIterator { OopClosure* _keep_alive; BoolObjectClosure* _is_alive; + EnqueueDiscoveredFieldClosure* _enqueue; DEBUG_ONLY( oop _first_seen; // cyclic linked list check @@ -88,7 +110,8 @@ class DiscoveredListIterator { public: inline DiscoveredListIterator(DiscoveredList& refs_list, OopClosure* keep_alive, - BoolObjectClosure* is_alive); + BoolObjectClosure* is_alive, + EnqueueDiscoveredFieldClosure* enqueue); // End Of List. inline bool has_next() const { return _current_discovered != NULL; } @@ -257,12 +280,14 @@ class ReferenceProcessor : public ReferenceDiscoverer { // and enqueue non-Final references. void process_soft_weak_final_refs(BoolObjectClosure* is_alive, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc, AbstractRefProcTaskExecutor* task_executor, ReferenceProcessorPhaseTimes* phase_times); // Phase 3: Keep alive followers of Final references, and enqueue. void process_final_keep_alive(OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc, AbstractRefProcTaskExecutor* task_executor, ReferenceProcessorPhaseTimes* phase_times); @@ -270,6 +295,7 @@ class ReferenceProcessor : public ReferenceDiscoverer { // Phase 4: Drop and keep alive live Phantom references, or clear and enqueue if dead. void process_phantom_refs(BoolObjectClosure* is_alive, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc, AbstractRefProcTaskExecutor* task_executor, ReferenceProcessorPhaseTimes* phase_times); @@ -292,17 +318,20 @@ class ReferenceProcessor : public ReferenceDiscoverer { size_t process_soft_weak_final_refs_work(DiscoveredList& refs_list, BoolObjectClosure* is_alive, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, bool do_enqueue_and_clear); // Keep alive followers of referents for FinalReferences. Must only be called for // those. size_t process_final_keep_alive_work(DiscoveredList& refs_list, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc); size_t process_phantom_refs_work(DiscoveredList& refs_list, BoolObjectClosure* is_alive, OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, VoidClosure* complete_gc); public: @@ -451,11 +480,12 @@ class ReferenceProcessor : public ReferenceDiscoverer { // Process references found during GC (called by the garbage collector) ReferenceProcessorStats - process_discovered_references(BoolObjectClosure* is_alive, - OopClosure* keep_alive, - VoidClosure* complete_gc, - AbstractRefProcTaskExecutor* task_executor, - ReferenceProcessorPhaseTimes* phase_times); + process_discovered_references(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + EnqueueDiscoveredFieldClosure* enqueue, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor, + ReferenceProcessorPhaseTimes* phase_times); // If a discovery is in process that is being superceded, abandon it: all // the discovered lists will be empty, and all the objects on them will @@ -673,6 +703,7 @@ class AbstractRefProcTaskExecutor::ProcessTask { virtual void work(uint worker_id, BoolObjectClosure& is_alive, OopClosure& keep_alive, + EnqueueDiscoveredFieldClosure& enqueue, VoidClosure& complete_gc) = 0; bool marks_oops_alive() const { return _marks_oops_alive; } diff --git a/src/hotspot/share/gc/shared/referenceProcessor.inline.hpp b/src/hotspot/share/gc/shared/referenceProcessor.inline.hpp index 8c1422398a6..795e917ffb6 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.inline.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.inline.hpp @@ -59,7 +59,8 @@ void DiscoveredList::clear() { DiscoveredListIterator::DiscoveredListIterator(DiscoveredList& refs_list, OopClosure* keep_alive, - BoolObjectClosure* is_alive): + BoolObjectClosure* is_alive, + EnqueueDiscoveredFieldClosure* enqueue): _refs_list(refs_list), _prev_discovered_addr(refs_list.adr_head()), _prev_discovered(NULL), @@ -71,7 +72,8 @@ DiscoveredListIterator::DiscoveredListIterator(DiscoveredList& refs_list, _removed(0), _next_discovered(NULL), _keep_alive(keep_alive), - _is_alive(is_alive) { + _is_alive(is_alive), + _enqueue(enqueue) { } #endif // SHARE_VM_GC_SHARED_REFERENCEPROCESSOR_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp index 7b94f6fb888..a987c1ad8d4 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp @@ -506,6 +506,7 @@ uintx StringDedupTable::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, while (*entry != NULL) { oop* p = (oop*)(*entry)->obj_addr(); if (cl->is_alive(*p)) { + typeArrayOop value = (typeArrayOop)*p; cl->keep_alive(p); if (is_resizing()) { // We are resizing the table, transfer entry to the new table @@ -517,7 +518,6 @@ uintx StringDedupTable::unlink_or_oops_do(StringDedupUnlinkOrOopsDoClosure* cl, // at this point since we don't have exclusive access to all // destination partitions. finish_rehash() will do a single // threaded transfer of all entries. - typeArrayOop value = (typeArrayOop)*p; bool latin1 = (*entry)->latin1(); unsigned int hash = hash_code(value, latin1); (*entry)->set_hash(hash); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 5efc0978d77..4a3c57265f6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -612,15 +612,16 @@ class ShenandoahRefProcTaskProxy : public AbstractGangTask { HandleMark hm; assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); ShenandoahHeap* heap = ShenandoahHeap::heap(); + BarrierEnqueueDiscoveredFieldClosure enqueue; ShenandoahCMDrainMarkingStackClosure complete_gc(worker_id, _terminator); if (heap->has_forwarded_objects()) { ShenandoahForwardedIsAliveClosure is_alive; ShenandoahCMKeepAliveUpdateClosure keep_alive(heap->concurrent_mark()->get_queue(worker_id)); - _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + _proc_task.work(worker_id, is_alive, keep_alive, enqueue, complete_gc); } else { ShenandoahIsAliveClosure is_alive; ShenandoahCMKeepAliveClosure keep_alive(heap->concurrent_mark()->get_queue(worker_id)); - _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + _proc_task.work(worker_id, is_alive, keep_alive, enqueue, complete_gc); } } }; @@ -699,6 +700,7 @@ void ShenandoahConcurrentMark::weak_refs_work_doit(bool full_gc) { // times, we need to be able to reuse the terminator. uint serial_worker_id = 0; ShenandoahTaskTerminator terminator(1, task_queues()); + BarrierEnqueueDiscoveredFieldClosure enqueue; ShenandoahCMDrainMarkingStackClosure complete_gc(serial_worker_id, &terminator, /* reset_terminator = */ true); ShenandoahRefProcTaskExecutor executor(workers); @@ -712,14 +714,14 @@ void ShenandoahConcurrentMark::weak_refs_work_doit(bool full_gc) { ShenandoahCMKeepAliveUpdateClosure keep_alive(get_queue(serial_worker_id)); const ReferenceProcessorStats& stats = rp->process_discovered_references(is_alive.is_alive_closure(), &keep_alive, - &complete_gc, &executor, + &enqueue, &complete_gc, &executor, &pt); _heap->tracer()->report_gc_reference_stats(stats); } else { ShenandoahCMKeepAliveClosure keep_alive(get_queue(serial_worker_id)); const ReferenceProcessorStats& stats = rp->process_discovered_references(is_alive.is_alive_closure(), &keep_alive, - &complete_gc, &executor, + &enqueue, &complete_gc, &executor, &pt); _heap->tracer()->report_gc_reference_stats(stats); } diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 24e4c98175b..6483159136a 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -854,24 +854,17 @@ void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code byte CallInfo info; constantPoolHandle pool(thread, last_frame.method()->constants()); + methodHandle resolved_method; + { JvmtiHideSingleStepping jhss(thread); LinkResolver::resolve_invoke(info, receiver, pool, last_frame.get_index_u2_cpcache(bytecode), bytecode, CHECK); - if (JvmtiExport::can_hotswap_or_post_breakpoint()) { - int retry_count = 0; - while (info.resolved_method()->is_old()) { - // It is very unlikely that method is redefined more than 100 times - // in the middle of resolve. If it is looping here more than 100 times - // means then there could be a bug here. - guarantee((retry_count++ < 100), - "Could not resolve to latest version of redefined method"); - // method is redefined in the middle of resolve so re-try. - LinkResolver::resolve_invoke(info, receiver, pool, - last_frame.get_index_u2_cpcache(bytecode), bytecode, - CHECK); - } + if (JvmtiExport::can_hotswap_or_post_breakpoint() && info.resolved_method()->is_old()) { + resolved_method = methodHandle(thread, info.resolved_method()->get_new_method()); + } else { + resolved_method = info.resolved_method(); } } // end JvmtiHideSingleStepping @@ -881,22 +874,20 @@ void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code byte #ifdef ASSERT if (bytecode == Bytecodes::_invokeinterface) { - if (info.resolved_method()->method_holder() == - SystemDictionary::Object_klass()) { + if (resolved_method->method_holder() == SystemDictionary::Object_klass()) { // NOTE: THIS IS A FIX FOR A CORNER CASE in the JVM spec // (see also CallInfo::set_interface for details) assert(info.call_kind() == CallInfo::vtable_call || info.call_kind() == CallInfo::direct_call, ""); - methodHandle rm = info.resolved_method(); - assert(rm->is_final() || info.has_vtable_index(), + assert(resolved_method->is_final() || info.has_vtable_index(), "should have been set already"); - } else if (!info.resolved_method()->has_itable_index()) { + } else if (!resolved_method->has_itable_index()) { // Resolved something like CharSequence.toString. Use vtable not itable. assert(info.call_kind() != CallInfo::itable_call, ""); } else { // Setup itable entry assert(info.call_kind() == CallInfo::itable_call, ""); - int index = info.resolved_method()->itable_index(); + int index = resolved_method->itable_index(); assert(info.itable_index() == index, ""); } } else if (bytecode == Bytecodes::_invokespecial) { @@ -916,20 +907,20 @@ void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code byte case CallInfo::direct_call: cp_cache_entry->set_direct_call( bytecode, - info.resolved_method(), + resolved_method, sender->is_interface()); break; case CallInfo::vtable_call: cp_cache_entry->set_vtable_call( bytecode, - info.resolved_method(), + resolved_method, info.vtable_index()); break; case CallInfo::itable_call: cp_cache_entry->set_itable_call( bytecode, info.resolved_klass(), - info.resolved_method(), + resolved_method, info.itable_index()); break; default: ShouldNotReachHere(); diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 80958b04694..c3a884fafe7 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -758,6 +758,7 @@ void MetaspaceUtils::print_report(outputStream* out, size_t scale, int flags) { out->print_cr(" Non-Class:"); } Metaspace::space_list()->print_on(out, scale); + out->cr(); if (Metaspace::using_class_space()) { out->print_cr(" Class:"); Metaspace::class_space_list()->print_on(out, scale); diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index 5199c86c378..c636ddbe978 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -1344,12 +1344,6 @@ void VM_PopulateDumpSharedSpace::doit() { FileMapInfo::check_nonempty_dir_in_shared_path_table(); NOT_PRODUCT(SystemDictionary::verify();) - // The following guarantee is meant to ensure that no loader constraints - // exist yet, since the constraints table is not shared. This becomes - // more important now that we don't re-initialize vtables/itables for - // shared classes at runtime, where constraints were previously created. - guarantee(SystemDictionary::constraints()->number_of_entries() == 0, - "loader constraints are not saved"); guarantee(SystemDictionary::placeholders()->number_of_entries() == 0, "placeholders are not saved"); // Revisit and implement this if we prelink method handle call sites: diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index ee2270d222f..6612aceb93f 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3696,15 +3696,15 @@ void JNIid::verify(Klass* holder) { } } -#ifdef ASSERT void InstanceKlass::set_init_state(ClassState state) { +#ifdef ASSERT bool good_state = is_shared() ? (_init_state <= state) : (_init_state < state); assert(good_state || state == allocated, "illegal state transition"); assert(_init_thread == NULL, "should be cleared before state change"); +#endif _init_state = (u1)state; } -#endif #if INCLUDE_JVMTI diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 5dcf297fb66..176bed5b5e0 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1259,11 +1259,7 @@ class InstanceKlass: public Klass { private: // initialization state -#ifdef ASSERT void set_init_state(ClassState state); -#else - void set_init_state(ClassState state) { _init_state = (u1)state; } -#endif void set_rewritten() { _misc_flags |= _misc_rewritten; } void set_init_thread(Thread *thread) { _init_thread = thread; } diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 01c5ed5bd46..4b97cf7eb2c 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/dictionary.hpp" -#include "classfile/javaClasses.hpp" +#include "classfile/javaClasses.inline.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "gc/shared/collectedHeap.inline.hpp" @@ -735,7 +735,7 @@ void Klass::verify_on(outputStream* st) { } if (java_mirror_no_keepalive() != NULL) { - guarantee(oopDesc::is_oop(java_mirror_no_keepalive()), "should be instance"); + guarantee(java_lang_Class::is_instance(java_mirror_no_keepalive()), "should be instance"); } } diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 1d1c524fe88..3ade5867a66 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -269,9 +269,8 @@ class Klass : public Metadata { // Both mirrors are on the ClassLoaderData::_handles list already so no // barriers are needed. void set_java_mirror_handle(OopHandle mirror) { _java_mirror = mirror; } - OopHandle java_mirror_handle() const { - return _java_mirror; - } + OopHandle java_mirror_handle() const { return _java_mirror; } + void swap_java_mirror_handle(OopHandle& mirror) { _java_mirror.swap(mirror); } // modifier flags jint modifier_flags() const { return _modifier_flags; } diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index da8468dead4..63c656f52f4 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -984,6 +984,15 @@ class Method : public Metadata { // Deallocation function for redefine classes or if an error occurs void deallocate_contents(ClassLoaderData* loader_data); + Method* get_new_method() const { + InstanceKlass* holder = method_holder(); + Method* new_method = holder->method_with_idnum(orig_method_idnum()); + + assert(new_method != NULL, "method_with_idnum() should not be null"); + assert(this != new_method, "sanity check"); + return new_method; + } + // Printing #ifndef PRODUCT void print_on(outputStream* st) const; diff --git a/src/hotspot/share/oops/oopHandle.hpp b/src/hotspot/share/oops/oopHandle.hpp index e822d3710c9..1b7dea9413c 100644 --- a/src/hotspot/share/oops/oopHandle.hpp +++ b/src/hotspot/share/oops/oopHandle.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,10 @@ class OopHandle { OopHandle() : _obj(NULL) {} OopHandle(oop* w) : _obj(w) {} + void swap(OopHandle& copy) { + ::swap(_obj, copy._obj); + } + inline oop resolve() const; inline oop peek() const; diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 6f4091eab0b..3efa4c7daa7 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1156,8 +1156,14 @@ Node *SafePointNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* SafePointNode::Identity(PhaseGVN* phase) { // If you have back to back safepoints, remove one - if( in(TypeFunc::Control)->is_SafePoint() ) - return in(TypeFunc::Control); + if (in(TypeFunc::Control)->is_SafePoint()) { + Node* out_c = unique_ctrl_out(); + // This can be the safepoint of an outer strip mined loop if the inner loop's backedge was removed. Replacing the + // outer loop's safepoint could confuse removal of the outer loop. + if (out_c != NULL && !out_c->is_OuterStripMinedLoopEnd()) { + return in(TypeFunc::Control); + } + } if( in(0)->is_Proj() ) { Node *n0 = in(0)->in(0); diff --git a/src/hotspot/share/opto/ifg.cpp b/src/hotspot/share/opto/ifg.cpp index 0e92b8e777f..5b66437f403 100644 --- a/src/hotspot/share/opto/ifg.cpp +++ b/src/hotspot/share/opto/ifg.cpp @@ -37,6 +37,8 @@ #include "opto/memnode.hpp" #include "opto/opcodes.hpp" +#include + PhaseIFG::PhaseIFG( Arena *arena ) : Phase(Interference_Graph), _arena(arena) { } @@ -760,7 +762,7 @@ void PhaseChaitin::add_input_to_liveout(Block* b, Node* n, IndexSet* liveout, do assert(int_pressure.current_pressure() == count_int_pressure(liveout), "the int pressure is incorrect"); assert(float_pressure.current_pressure() == count_float_pressure(liveout), "the float pressure is incorrect"); } - assert(lrg._area >= 0.0, "negative spill area" ); + assert(lrg._area >= 0.0, "unexpected spill area value %g (rounding mode %x)", lrg._area, (int)fegetround()); } } @@ -871,7 +873,7 @@ uint PhaseChaitin::build_ifg_physical( ResourceArea *a ) { if (g_isfinite(cost)) { lrg._area -= cost; } - assert(lrg._area >= 0.0, "negative spill area" ); + assert(lrg._area >= 0.0, "unexpected spill area value %g (rounding mode %x)", lrg._area, (int)fegetround()); assign_high_score_to_immediate_copies(block, n, lrg, location + 1, last_inst); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 6b6aa9e9bd6..515665ffa44 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -279,8 +279,13 @@ class LibraryCallKit : public GraphKit { bool inline_arraycopy(); AllocateArrayNode* tightly_coupled_allocation(Node* ptr, RegionNode* slow_region); + static CallStaticJavaNode* get_uncommon_trap_from_success_proj(Node* node); + SafePointNode* create_safepoint_with_state_before_array_allocation(const AllocateArrayNode* alloc) const; + void replace_unrelated_uncommon_traps_with_alloc_state(AllocateArrayNode* alloc, JVMState* saved_jvms_before_guards); + void replace_unrelated_uncommon_traps_with_alloc_state(JVMState* saved_jvms_before_guards); + void create_new_uncommon_trap(CallStaticJavaNode* uncommon_trap_call); JVMState* arraycopy_restore_alloc_state(AllocateArrayNode* alloc, int& saved_reexecute_sp); - void arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms, int saved_reexecute_sp, + void arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms_before_guards, int saved_reexecute_sp, uint new_idx); typedef enum { LS_get_add, LS_get_set, LS_cmp_swap, LS_cmp_swap_weak, LS_cmp_exchange } LoadStoreKind; @@ -1428,7 +1433,7 @@ bool LibraryCallKit::inline_string_indexOfChar() { } assert(callee()->signature()->size() == 4, "String.indexOfChar() has 4 arguments"); Node* src = argument(0); // byte[] - Node* tgt = argument(1); // tgt is int ch + Node* int_ch = argument(1); Node* from_index = argument(2); Node* max = argument(3); @@ -1440,6 +1445,15 @@ bool LibraryCallKit::inline_string_indexOfChar() { // Range checks generate_string_range_check(src, src_offset, src_count, true); + + // Check for int_ch >= 0 + Node* int_ch_cmp = _gvn.transform(new CmpINode(int_ch, intcon(0))); + Node* int_ch_bol = _gvn.transform(new BoolNode(int_ch_cmp, BoolTest::ge)); + { + BuildCutout unless(this, int_ch_bol, PROB_MAX); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } if (stopped()) { return true; } @@ -1447,7 +1461,7 @@ bool LibraryCallKit::inline_string_indexOfChar() { RegionNode* region = new RegionNode(3); Node* phi = new PhiNode(region, TypeInt::INT); - Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, tgt, StrIntrinsicNode::none); + Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, int_ch, StrIntrinsicNode::none); C->set_has_split_ifs(true); // Has chance for split-if optimization _gvn.transform(result); @@ -1496,10 +1510,13 @@ bool LibraryCallKit::inline_string_copy(bool compress) { AllocateArrayNode* alloc = tightly_coupled_allocation(dst, NULL); // Figure out the size and type of the elements we will be copying. - const Type* src_type = src->Value(&_gvn); - const Type* dst_type = dst->Value(&_gvn); - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + const TypeAryPtr* src_type = src->Value(&_gvn)->isa_aryptr(); + const TypeAryPtr* dst_type = dst->Value(&_gvn)->isa_aryptr(); + if (src_type == NULL || dst_type == NULL) { + return false; + } + BasicType src_elem = src_type->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dst_elem = dst_type->klass()->as_array_klass()->element_type()->basic_type(); assert((compress && dst_elem == T_BYTE && (src_elem == T_BYTE || src_elem == T_CHAR)) || (!compress && src_elem == T_BYTE && (dst_elem == T_BYTE || dst_elem == T_CHAR)), "Unsupported array types for inline_string_copy"); @@ -4531,24 +4548,7 @@ JVMState* LibraryCallKit::arraycopy_restore_alloc_state(AllocateArrayNode* alloc } if (no_interfering_store) { - JVMState* old_jvms = alloc->jvms()->clone_shallow(C); - uint size = alloc->req(); - SafePointNode* sfpt = new SafePointNode(size, old_jvms); - old_jvms->set_map(sfpt); - for (uint i = 0; i < size; i++) { - sfpt->init_req(i, alloc->in(i)); - } - // re-push array length for deoptimization - sfpt->ins_req(old_jvms->stkoff() + old_jvms->sp(), alloc->in(AllocateNode::ALength)); - old_jvms->set_sp(old_jvms->sp()+1); - old_jvms->set_monoff(old_jvms->monoff()+1); - old_jvms->set_scloff(old_jvms->scloff()+1); - old_jvms->set_endoff(old_jvms->endoff()+1); - old_jvms->set_should_reexecute(true); - - sfpt->set_i_o(map()->i_o()); - sfpt->set_memory(map()->memory()); - sfpt->set_control(map()->control()); + SafePointNode* sfpt = create_safepoint_with_state_before_array_allocation(alloc); JVMState* saved_jvms = jvms(); saved_reexecute_sp = _reexecute_sp; @@ -4563,6 +4563,30 @@ JVMState* LibraryCallKit::arraycopy_restore_alloc_state(AllocateArrayNode* alloc return NULL; } +// Clone the JVMState of the array allocation and create a new safepoint with it. Re-push the array length to the stack +// such that uncommon traps can be emitted to re-execute the array allocation in the interpreter. +SafePointNode* LibraryCallKit::create_safepoint_with_state_before_array_allocation(const AllocateArrayNode* alloc) const { + JVMState* old_jvms = alloc->jvms()->clone_shallow(C); + uint size = alloc->req(); + SafePointNode* sfpt = new SafePointNode(size, old_jvms); + old_jvms->set_map(sfpt); + for (uint i = 0; i < size; i++) { + sfpt->init_req(i, alloc->in(i)); + } + // re-push array length for deoptimization + sfpt->ins_req(old_jvms->stkoff() + old_jvms->sp(), alloc->in(AllocateNode::ALength)); + old_jvms->set_sp(old_jvms->sp()+1); + old_jvms->set_monoff(old_jvms->monoff()+1); + old_jvms->set_scloff(old_jvms->scloff()+1); + old_jvms->set_endoff(old_jvms->endoff()+1); + old_jvms->set_should_reexecute(true); + + sfpt->set_i_o(map()->i_o()); + sfpt->set_memory(map()->memory()); + sfpt->set_control(map()->control()); + return sfpt; +} + // In case of a deoptimization, we restart execution at the // allocation, allocating a new array. We would leave an uninitialized // array in the heap that GCs wouldn't expect. Move the allocation @@ -4570,18 +4594,20 @@ JVMState* LibraryCallKit::arraycopy_restore_alloc_state(AllocateArrayNode* alloc // deoptimize. This is possible because tightly_coupled_allocation() // guarantees there's no observer of the allocated array at this point // and the control flow is simple enough. -void LibraryCallKit::arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms, +void LibraryCallKit::arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms_before_guards, int saved_reexecute_sp, uint new_idx) { - if (saved_jvms != NULL && !stopped()) { + if (saved_jvms_before_guards != NULL && !stopped()) { + replace_unrelated_uncommon_traps_with_alloc_state(alloc, saved_jvms_before_guards); + assert(alloc != NULL, "only with a tightly coupled allocation"); // restore JVM state to the state at the arraycopy - saved_jvms->map()->set_control(map()->control()); - assert(saved_jvms->map()->memory() == map()->memory(), "memory state changed?"); - assert(saved_jvms->map()->i_o() == map()->i_o(), "IO state changed?"); + saved_jvms_before_guards->map()->set_control(map()->control()); + assert(saved_jvms_before_guards->map()->memory() == map()->memory(), "memory state changed?"); + assert(saved_jvms_before_guards->map()->i_o() == map()->i_o(), "IO state changed?"); // If we've improved the types of some nodes (null check) while // emitting the guards, propagate them to the current state - map()->replaced_nodes().apply(saved_jvms->map(), new_idx); - set_jvms(saved_jvms); + map()->replaced_nodes().apply(saved_jvms_before_guards->map(), new_idx); + set_jvms(saved_jvms_before_guards); _reexecute_sp = saved_reexecute_sp; // Remove the allocation from above the guards @@ -4657,6 +4683,58 @@ void LibraryCallKit::arraycopy_move_allocation_here(AllocateArrayNode* alloc, No } } +// Unrelated UCTs between the array allocation and the array copy, which are considered safe by tightly_coupled_allocation(), +// need to be replaced by an UCT with a state before the array allocation (including the array length). This is necessary +// because we could hit one of these UCTs (which are executed before the emitted array copy guards and the actual array +// allocation which is moved down in arraycopy_move_allocation_here()). When later resuming execution in the interpreter, +// we would have wrongly skipped the array allocation. To prevent this, we resume execution at the array allocation in +// the interpreter similar to what we are doing for the newly emitted guards for the array copy. +void LibraryCallKit::replace_unrelated_uncommon_traps_with_alloc_state(AllocateArrayNode* alloc, + JVMState* saved_jvms_before_guards) { + if (saved_jvms_before_guards->map()->control()->is_IfProj()) { + // There is at least one unrelated uncommon trap which needs to be replaced. + SafePointNode* sfpt = create_safepoint_with_state_before_array_allocation(alloc); + + JVMState* saved_jvms = jvms(); + const int saved_reexecute_sp = _reexecute_sp; + set_jvms(sfpt->jvms()); + _reexecute_sp = jvms()->sp(); + + replace_unrelated_uncommon_traps_with_alloc_state(saved_jvms_before_guards); + + // Restore state + set_jvms(saved_jvms); + _reexecute_sp = saved_reexecute_sp; + } +} + +// Replace the unrelated uncommon traps with new uncommon trap nodes by reusing the action and reason. The new uncommon +// traps will have the state of the array allocation. Let the old uncommon trap nodes die. +void LibraryCallKit::replace_unrelated_uncommon_traps_with_alloc_state(JVMState* saved_jvms_before_guards) { + Node* if_proj = saved_jvms_before_guards->map()->control(); // Start the search right before the newly emitted guards + while (if_proj->is_IfProj()) { + CallStaticJavaNode* uncommon_trap = get_uncommon_trap_from_success_proj(if_proj); + if (uncommon_trap != NULL) { + create_new_uncommon_trap(uncommon_trap); + } + assert(if_proj->in(0)->is_If(), "must be If"); + if_proj = if_proj->in(0)->in(0); + } + assert(if_proj->is_Proj() && if_proj->in(0)->is_Initialize(), + "must have reached control projection of init node"); +} + +void LibraryCallKit::create_new_uncommon_trap(CallStaticJavaNode* uncommon_trap_call) { + const int trap_request = uncommon_trap_call->uncommon_trap_request(); + assert(trap_request != 0, "no valid UCT trap request"); + PreserveJVMState pjvms(this); + set_control(uncommon_trap_call->in(0)); + uncommon_trap(Deoptimization::trap_request_reason(trap_request), + Deoptimization::trap_request_action(trap_request)); + assert(stopped(), "Should be stopped"); + _gvn.hash_delete(uncommon_trap_call); + uncommon_trap_call->set_req(0, top()); // not used anymore, kill it +} //------------------------------inline_arraycopy----------------------- // public static native void java.lang.System.arraycopy(Object src, int srcPos, @@ -4677,12 +4755,12 @@ bool LibraryCallKit::inline_arraycopy() { AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL); int saved_reexecute_sp = -1; - JVMState* saved_jvms = arraycopy_restore_alloc_state(alloc, saved_reexecute_sp); + JVMState* saved_jvms_before_guards = arraycopy_restore_alloc_state(alloc, saved_reexecute_sp); // See arraycopy_restore_alloc_state() comment // if alloc == NULL we don't have to worry about a tightly coupled allocation so we can emit all needed guards - // if saved_jvms != NULL (then alloc != NULL) then we can handle guards and a tightly coupled allocation - // if saved_jvms == NULL and alloc != NULL, we can't emit any guards - bool can_emit_guards = (alloc == NULL || saved_jvms != NULL); + // if saved_jvms_before_guards != NULL (then alloc != NULL) then we can handle guards and a tightly coupled allocation + // if saved_jvms_before_guards == NULL and alloc != NULL, we can't emit any guards + bool can_emit_guards = (alloc == NULL || saved_jvms_before_guards != NULL); // The following tests must be performed // (1) src and dest are arrays. @@ -4698,12 +4776,12 @@ bool LibraryCallKit::inline_arraycopy() { // (3) src and dest must not be null. // always do this here because we need the JVM state for uncommon traps Node* null_ctl = top(); - src = saved_jvms != NULL ? null_check_oop(src, &null_ctl, true, true) : null_check(src, T_ARRAY); + src = saved_jvms_before_guards != NULL ? null_check_oop(src, &null_ctl, true, true) : null_check(src, T_ARRAY); assert(null_ctl->is_top(), "no null control here"); dest = null_check(dest, T_ARRAY); if (!can_emit_guards) { - // if saved_jvms == NULL and alloc != NULL, we don't emit any + // if saved_jvms_before_guards == NULL and alloc != NULL, we don't emit any // guards but the arraycopy node could still take advantage of a // tightly allocated allocation. tightly_coupled_allocation() is // called again to make sure it takes the null check above into @@ -4817,7 +4895,7 @@ bool LibraryCallKit::inline_arraycopy() { ciMethod* trap_method = method(); int trap_bci = bci(); - if (saved_jvms != NULL) { + if (saved_jvms_before_guards != NULL) { trap_method = alloc->jvms()->method(); trap_bci = alloc->jvms()->bci(); } @@ -4888,10 +4966,9 @@ bool LibraryCallKit::inline_arraycopy() { const TypeKlassPtr* dest_klass_t = _gvn.type(dest_klass)->is_klassptr(); const Type *toop = TypeOopPtr::make_from_klass(dest_klass_t->klass()); src = _gvn.transform(new CheckCastPPNode(control(), src, toop)); + arraycopy_move_allocation_here(alloc, dest, saved_jvms_before_guards, saved_reexecute_sp, new_idx); } - arraycopy_move_allocation_here(alloc, dest, saved_jvms, saved_reexecute_sp, new_idx); - if (stopped()) { return true; } @@ -4957,32 +5034,27 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr, // There may be guards which feed into the slow_region. // Any other control flow means that we might not get a chance // to finish initializing the allocated object. - if ((ctl->is_IfFalse() || ctl->is_IfTrue()) && ctl->in(0)->is_If()) { - IfNode* iff = ctl->in(0)->as_If(); - Node* not_ctl = iff->proj_out_or_null(1 - ctl->as_Proj()->_con); - assert(not_ctl != NULL && not_ctl != ctl, "found alternate"); - if (slow_region != NULL && slow_region->find_edge(not_ctl) >= 1) { - ctl = iff->in(0); // This test feeds the known slow_region. - continue; - } - // One more try: Various low-level checks bottom out in - // uncommon traps. If the debug-info of the trap omits - // any reference to the allocation, as we've already - // observed, then there can be no objection to the trap. - bool found_trap = false; - for (DUIterator_Fast jmax, j = not_ctl->fast_outs(jmax); j < jmax; j++) { - Node* obs = not_ctl->fast_out(j); - if (obs->in(0) == not_ctl && obs->is_Call() && - (obs->as_Call()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point())) { - found_trap = true; break; + // This code is removed by "8263615: Cleanup tightly_coupled_allocation" + // I had to adapt it when backporting "8297730: C2: Arraycopy intrinsic throws incorrect exception" + if (slow_region != NULL) { + if ((ctl->is_IfFalse() || ctl->is_IfTrue()) && ctl->in(0)->is_If()) { + IfNode* iff = ctl->in(0)->as_If(); + Node* not_ctl = iff->proj_out_or_null(1 - ctl->as_Proj()->_con); + if (slow_region->find_edge(not_ctl) >= 1) { + ctl = iff->in(0); // This test feeds the known slow_region. + continue; } } - if (found_trap) { - ctl = iff->in(0); // This test feeds a harmless uncommon trap. - continue; - } } - return NULL; + // Various low-level checks bottom out in uncommon traps. These + // are considered safe since we've already checked above that + // there is no unexpected observer of this allocation. + if (get_uncommon_trap_from_success_proj(ctl) != NULL) { + assert(ctl->in(0)->is_If(), "must be If"); + ctl = ctl->in(0)->in(0); + } else { + return NULL; + } } // If we get this far, we have an allocation which immediately @@ -4993,6 +5065,20 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr, return alloc; } +CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* node) { + if (node->is_IfProj()) { + Node* other_proj = node->as_IfProj()->other_if_proj(); + for (DUIterator_Fast jmax, j = other_proj->fast_outs(jmax); j < jmax; j++) { + Node* obs = other_proj->fast_out(j); + if (obs->in(0) == other_proj && obs->is_CallStaticJava() && + (obs->as_CallStaticJava()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point())) { + return obs->as_CallStaticJava(); + } + } + } + return NULL; +} + //-------------inline_encodeISOArray----------------------------------- // encode char[] to byte[] in ISO_8859_1 bool LibraryCallKit::inline_encodeISOArray() { @@ -5018,8 +5104,8 @@ bool LibraryCallKit::inline_encodeISOArray() { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dst_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type(); if (!((src_elem == T_CHAR) || (src_elem== T_BYTE)) || dst_elem != T_BYTE) { return false; } @@ -5072,8 +5158,8 @@ bool LibraryCallKit::inline_multiplyToLen() { return false; } - BasicType x_elem = x_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType y_elem = y_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType x_elem = top_x->klass()->as_array_klass()->element_type()->basic_type(); + BasicType y_elem = top_y->klass()->as_array_klass()->element_type()->basic_type(); if (x_elem != T_INT || y_elem != T_INT) { return false; } @@ -5180,8 +5266,8 @@ bool LibraryCallKit::inline_squareToLen() { return false; } - BasicType x_elem = x_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType z_elem = z_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType x_elem = top_x->klass()->as_array_klass()->element_type()->basic_type(); + BasicType z_elem = top_z->klass()->as_array_klass()->element_type()->basic_type(); if (x_elem != T_INT || z_elem != T_INT) { return false; } @@ -5229,8 +5315,8 @@ bool LibraryCallKit::inline_mulAdd() { return false; } - BasicType out_elem = out_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType in_elem = in_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType out_elem = top_out->klass()->as_array_klass()->element_type()->basic_type(); + BasicType in_elem = top_in->klass()->as_array_klass()->element_type()->basic_type(); if (out_elem != T_INT || in_elem != T_INT) { return false; } @@ -5284,10 +5370,10 @@ bool LibraryCallKit::inline_montgomeryMultiply() { return false; } - BasicType a_elem = a_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType b_elem = b_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType n_elem = n_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType m_elem = m_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType a_elem = top_a->klass()->as_array_klass()->element_type()->basic_type(); + BasicType b_elem = top_b->klass()->as_array_klass()->element_type()->basic_type(); + BasicType n_elem = top_n->klass()->as_array_klass()->element_type()->basic_type(); + BasicType m_elem = top_m->klass()->as_array_klass()->element_type()->basic_type(); if (a_elem != T_INT || b_elem != T_INT || n_elem != T_INT || m_elem != T_INT) { return false; } @@ -5340,9 +5426,9 @@ bool LibraryCallKit::inline_montgomerySquare() { return false; } - BasicType a_elem = a_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType n_elem = n_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType m_elem = m_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType a_elem = top_a->klass()->as_array_klass()->element_type()->basic_type(); + BasicType n_elem = top_n->klass()->as_array_klass()->element_type()->basic_type(); + BasicType m_elem = top_m->klass()->as_array_klass()->element_type()->basic_type(); if (a_elem != T_INT || n_elem != T_INT || m_elem != T_INT) { return false; } @@ -5465,7 +5551,7 @@ bool LibraryCallKit::inline_updateBytesCRC32() { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -5554,7 +5640,7 @@ bool LibraryCallKit::inline_updateBytesCRC32C() { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -5647,7 +5733,7 @@ bool LibraryCallKit::inline_updateBytesAdler32() { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -6462,7 +6548,7 @@ bool LibraryCallKit::inline_sha_implCompress(vmIntrinsics::ID id) { return false; } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -6532,7 +6618,7 @@ bool LibraryCallKit::inline_digestBase_implCompressMB(int predicate) { return false; } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index f1f11901641..8d6cfb21d0c 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -1212,11 +1212,13 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree *loop, ProjNode* upper_bound_iff->set_req(1, upper_bound_bol); if (TraceLoopPredicate) tty->print_cr("upper bound check if: %s %d ", negate ? " negated" : "", lower_bound_iff->_idx); - // Fall through into rest of the cleanup code which will move any dependent nodes to the skeleton predicates of the - // upper bound test. We always need to create skeleton predicates in order to properly remove dead loops when later - // splitting the predicated loop into (unreachable) sub-loops (i.e. done by unrolling, peeling, pre/main/post etc.). - new_predicate_proj = insert_initial_skeleton_predicate(iff, loop, proj, predicate_proj, upper_bound_proj, scale, - offset, init, limit, stride, rng, overflow, reason); + // Fall through into rest of the clean up code which will move + // any dependent nodes onto the upper bound test. + new_predicate_proj = upper_bound_proj; + + if (iff->is_RangeCheck()) { + new_predicate_proj = insert_initial_skeleton_predicate(iff, loop, proj, predicate_proj, upper_bound_proj, scale, offset, init, limit, stride, rng, overflow, reason); + } #ifndef PRODUCT if (TraceLoopOpts && !TraceLoopPredicate) { diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 044fd16d1a4..e401eb4460f 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -3240,7 +3240,12 @@ bool IdealLoopTree::do_remove_empty_loop(PhaseIdealLoop *phase) { // counted loop has limit check predicate. Node* phi = cl->phi(); Node* exact_limit = phase->exact_limit(this); - Node* final_iv = new SubINode(exact_limit, cl->stride()); + + // We need to pin the exact limit to prevent it from floating above the zero trip guard. + Node * cast_ii = ConstraintCastNode::make_cast(Op_CastII, cl->in(LoopNode::EntryControl), exact_limit, phase->_igvn.type(exact_limit), true); + phase->register_new_node(cast_ii, cl->in(LoopNode::EntryControl)); + + Node* final_iv = new SubINode(cast_ii, cl->stride()); phase->register_new_node(final_iv, cl->in(LoopNode::EntryControl)); phase->_igvn.replace_node(phi, final_iv); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 2010adb9f24..707a97c069c 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1344,6 +1344,9 @@ SHENANDOAHGC_ONLY(public:) bool identical_backtoback_ifs(Node *n); bool can_split_if(Node *n_ctrl); SHENANDOAHGC_ONLY(private:) + bool cannot_split_division(const Node* n, const Node* region) const; + static bool is_divisor_counted_loop_phi(const Node* divisor, const Node* loop); + bool loop_phi_backedge_type_contains_zero(const Node* phi_divisor, const Type* zero) const; // Clone loop predicates to slow and fast loop when unswitching a loop void clone_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, ProjNode*& iffast_pred, ProjNode*& ifslow_pred); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index f0201d82614..b9db718b390 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,19 +63,8 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { return NULL; } - // Bail out if 'n' is a Div or Mod node whose zero check was removed earlier (i.e. control is NULL) and its divisor is an induction variable - // phi p of a trip-counted (integer) loop whose inputs could be zero (include zero in their type range). p could have a more precise type - // range that does not necessarily include all values of its inputs. Since each of these inputs will be a divisor of the newly cloned nodes - // of 'n', we need to bail out of one of these divisors could be zero (zero in its type range). - if ((n->Opcode() == Op_DivI || n->Opcode() == Op_ModI) && n->in(0) == NULL - && region->is_CountedLoop() && n->in(2) == region->as_CountedLoop()->phi()) { - Node* phi = region->as_CountedLoop()->phi(); - for (uint i = 1; i < phi->req(); i++) { - if (_igvn.type(phi->in(i))->filter_speculative(TypeInt::ZERO) != Type::TOP) { - // Zero could be a possible value but we already removed the zero check. Bail out to avoid a possible division by zero at a later point. - return NULL; - } - } + if (cannot_split_division(n, region)) { + return NULL; } int wins = 0; @@ -227,6 +216,42 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { return phi; } +// Return true if 'n' is a Div or Mod node (without zero check If node which was removed earlier) with a loop phi divisor +// of a trip-counted (integer or long) loop with a backedge input that could be zero (include zero in its type range). In +// this case, we cannot split the division to the backedge as it could freely float above the loop exit check resulting in +// a division by zero. This situation is possible because the type of an increment node of an iv phi (trip-counter) could +// include zero while the iv phi does not (see PhiNode::Value() for trip-counted loops where we improve types of iv phis). +// We also need to check other loop phis as they could have been created in the same split-if pass when applying +// PhaseIdealLoop::split_thru_phi() to split nodes through an iv phi. +bool PhaseIdealLoop::cannot_split_division(const Node* n, const Node* region) const { + const Type* zero; + switch (n->Opcode()) { + case Op_DivI: + case Op_ModI: + zero = TypeInt::ZERO; + break; + case Op_DivL: + case Op_ModL: + zero = TypeLong::ZERO; + break; + default: + return false; + } + + assert(n->in(0) == NULL, "divisions with zero check should already have bailed out earlier in split-if"); + Node* divisor = n->in(2); + return is_divisor_counted_loop_phi(divisor, region) && + loop_phi_backedge_type_contains_zero(divisor, zero); +} + +bool PhaseIdealLoop::is_divisor_counted_loop_phi(const Node* divisor, const Node* loop) { + return loop->is_CountedLoop() && divisor->is_Phi() && divisor->in(0) == loop; +} + +bool PhaseIdealLoop::loop_phi_backedge_type_contains_zero(const Node* phi_divisor, const Type* zero) const { + return _igvn.type(phi_divisor->in(LoopNode::LoopBackControl))->filter_speculative(zero) != Type::TOP; +} + //------------------------------dominated_by------------------------------------ // Replace the dominated test with an obvious true or false. Place it on the // IGVN worklist for later cleanup. Move control-dependent data Nodes on the @@ -479,7 +504,7 @@ Node *PhaseIdealLoop::remix_address_expressions( Node *n ) { n23_loop == n_loop ) { Node *add1 = new AddPNode( n->in(1), n->in(2)->in(2), n->in(3) ); // Stuff new AddP in the loop preheader - register_new_node( add1, n_loop->_head->in(LoopNode::EntryControl) ); + register_new_node( add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl) ); Node *add2 = new AddPNode( n->in(1), add1, n->in(2)->in(3) ); register_new_node( add2, n_ctrl ); _igvn.replace_node( n, add2 ); @@ -500,7 +525,7 @@ Node *PhaseIdealLoop::remix_address_expressions( Node *n ) { if (!is_member(n_loop,get_ctrl(I))) { Node *add1 = new AddPNode(n->in(1), n->in(2), I); // Stuff new AddP in the loop preheader - register_new_node(add1, n_loop->_head->in(LoopNode::EntryControl)); + register_new_node(add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl)); Node *add2 = new AddPNode(n->in(1), add1, V); register_new_node(add2, n_ctrl); _igvn.replace_node(n, add2); @@ -1228,8 +1253,8 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n, bool last_round) { return; // Compare must be in same blk as if } } else if (iff->is_CMove()) { // Trying to split-up a CMOVE - // Can't split CMove with different control edge. - if (iff->in(0) != NULL && iff->in(0) != n_ctrl ) { + // Can't split CMove with different control. + if (get_ctrl(iff) != n_ctrl) { return; } if (get_ctrl(iff->in(2)) == n_ctrl || diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 4e06d08ccca..8286f8c4d62 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -610,8 +610,6 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) // Parse all the basic blocks. do_all_blocks(); - C->set_default_node_notes(caller_nn); - // Check for bailouts during conversion to graph if (failing()) { if (log) log->done("parse"); @@ -622,6 +620,10 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) set_map(entry_map); do_exits(); + // Only reset this now, to make sure that debug information emitted + // for exiting control flow still refers to the inlined method. + C->set_default_node_notes(caller_nn); + if (log) log->done("parse nodes='%d' live='%d' memory='" SIZE_FORMAT "'", C->unique(), C->live_nodes(), C->node_arena()->used()); } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index d76a080af07..8c9e954ed7f 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -482,6 +482,14 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, uint worklist_size = worklist->size(); + GrowableArray* old_node_note_array = C->node_note_array(); + if (old_node_note_array != NULL) { + int new_size = (_useful.size() >> 8) + 1; // The node note array uses blocks, see C->_log2_node_notes_block_size + new_size = MAX2(8, new_size); + C->set_node_note_array(new (C->comp_arena()) GrowableArray (C->comp_arena(), new_size, 0, NULL)); + C->grow_node_notes(C->node_note_array(), new_size); + } + // Iterate over the set of live nodes. for (uint current_idx = 0; current_idx < _useful.size(); current_idx++) { Node* n = _useful.at(current_idx); @@ -497,6 +505,11 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, assert(_old2new_map.at(n->_idx) == -1, "already seen"); _old2new_map.at_put(n->_idx, current_idx); + if (old_node_note_array != NULL) { + Node_Notes* nn = C->locate_node_notes(old_node_note_array, n->_idx); + C->set_node_notes_at(current_idx, nn); + } + n->set_idx(current_idx); // Update node ID. if (in_worklist) { diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index 012eb93c332..e20969b8141 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -736,7 +736,7 @@ JNI_ENTRY(void, jni_FatalError(JNIEnv *env, const char *msg)) HOTSPOT_JNI_FATALERROR_ENTRY(env, (char *) msg); tty->print_cr("FATAL ERROR in native method: %s", msg); - thread->print_stack(); + thread->print_jni_stack(); os::abort(); // Dump core and abort JNI_END diff --git a/src/hotspot/share/prims/jniCheck.cpp b/src/hotspot/share/prims/jniCheck.cpp index 26db55ae100..96b41d33651 100644 --- a/src/hotspot/share/prims/jniCheck.cpp +++ b/src/hotspot/share/prims/jniCheck.cpp @@ -139,7 +139,7 @@ static const char * fatal_non_string = "JNI string operation received a non-stri // When in VM state: static void ReportJNIWarning(JavaThread* thr, const char *msg) { tty->print_cr("WARNING in native method: %s", msg); - thr->print_stack(); + thr->print_jni_stack(); } // When in NATIVE state: @@ -190,7 +190,7 @@ check_pending_exception(JavaThread* thr) { IN_VM( tty->print_cr("WARNING in native method: JNI call made without checking exceptions when required to from %s", thr->get_pending_jni_exception_check()); - thr->print_stack(); + thr->print_jni_stack(); ) thr->clear_pending_jni_exception_check(); // Just complain once } diff --git a/src/hotspot/share/prims/jniCheck.hpp b/src/hotspot/share/prims/jniCheck.hpp index cdf96d11a6c..b1c704e1817 100644 --- a/src/hotspot/share/prims/jniCheck.hpp +++ b/src/hotspot/share/prims/jniCheck.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ extern "C" { // When in VM state: static inline void ReportJNIFatalError(JavaThread* thr, const char *msg) { tty->print_cr("FATAL ERROR in native method: %s", msg); - thr->print_stack(); + thr->print_jni_stack(); os::abort(true); } } diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 5eeb88a8601..e6caf6a24eb 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -411,14 +411,14 @@ class RedefineVerifyMark : public StackObj { JvmtiThreadState *state) : _state(state), _scratch_class(scratch_class) { _state->set_class_versions_map(the_class, scratch_class); - _scratch_mirror = _scratch_class->java_mirror_handle(); - _scratch_class->set_java_mirror_handle(the_class->java_mirror_handle()); + _scratch_mirror = the_class->java_mirror_handle(); // this is a copy that is swapped + _scratch_class->swap_java_mirror_handle(_scratch_mirror); } ~RedefineVerifyMark() { // Restore the scratch class's mirror, so when scratch_class is removed // the correct mirror pointing to it can be cleared. - _scratch_class->set_java_mirror_handle(_scratch_mirror); + _scratch_class->swap_java_mirror_handle(_scratch_mirror); _state->clear_class_versions_map(); } }; diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 2dc69195748..a56758eb5c4 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,6 +94,7 @@ #endif // INCLUDE_AOT #ifdef LINUX +#include "os_linux.hpp" #include "osContainer_linux.hpp" #include "cgroupSubsystem_linux.hpp" #endif @@ -1832,7 +1833,7 @@ WB_ENTRY(jboolean, WB_AreOpenArchiveHeapObjectsMapped(JNIEnv* env)) return MetaspaceShared::open_archive_heap_region_mapped(); WB_END -WB_ENTRY(jboolean, WB_IsCDSIncludedInVmBuild(JNIEnv* env)) +WB_ENTRY(jboolean, WB_IsCDSIncluded(JNIEnv* env)) #if INCLUDE_CDS # ifdef _LP64 if (!UseCompressedOops || !UseCompressedClassPointers) { @@ -1851,7 +1852,7 @@ WB_ENTRY(jboolean, WB_IsJavaHeapArchiveSupported(JNIEnv* env)) WB_END -WB_ENTRY(jboolean, WB_IsJFRIncludedInVmBuild(JNIEnv* env)) +WB_ENTRY(jboolean, WB_IsJFRIncluded(JNIEnv* env)) #if INCLUDE_JFR return true; #else @@ -2026,6 +2027,18 @@ WB_ENTRY(jboolean, WB_IsContainerized(JNIEnv* env, jobject o)) return false; WB_END +// Physical memory of the host machine (including containers) +WB_ENTRY(jlong, WB_HostPhysicalMemory(JNIEnv* env, jobject o)) + LINUX_ONLY(return os::Linux::physical_memory();) + return os::physical_memory(); +WB_END + +// Physical swap of the host machine (including containers), Linux only. +WB_ENTRY(jlong, WB_HostPhysicalSwap(JNIEnv* env, jobject o)) + LINUX_ONLY(return (jlong)os::Linux::host_swap();) + return -1; // Not used/implemented on other platforms +WB_END + WB_ENTRY(jint, WB_ValidateCgroup(JNIEnv* env, jobject o, jstring proc_cgroups, @@ -2282,8 +2295,8 @@ static JNINativeMethod methods[] = { {CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored }, {CC"getResolvedReferences", CC"(Ljava/lang/Class;)Ljava/lang/Object;", (void*)&WB_GetResolvedReferences}, {CC"areOpenArchiveHeapObjectsMapped", CC"()Z", (void*)&WB_AreOpenArchiveHeapObjectsMapped}, - {CC"isCDSIncludedInVmBuild", CC"()Z", (void*)&WB_IsCDSIncludedInVmBuild }, - {CC"isJFRIncludedInVmBuild", CC"()Z", (void*)&WB_IsJFRIncludedInVmBuild }, + {CC"isCDSIncluded", CC"()Z", (void*)&WB_IsCDSIncluded }, + {CC"isJFRIncluded", CC"()Z", (void*)&WB_IsJFRIncluded }, {CC"isJavaHeapArchiveSupported", CC"()Z", (void*)&WB_IsJavaHeapArchiveSupported }, {CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches }, @@ -2305,6 +2318,8 @@ static JNINativeMethod methods[] = { {CC"validateCgroup", CC"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", (void*)&WB_ValidateCgroup }, + {CC"hostPhysicalMemory", CC"()J", (void*)&WB_HostPhysicalMemory }, + {CC"hostPhysicalSwap", CC"()J", (void*)&WB_HostPhysicalSwap }, {CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo }, {CC"disableElfSectionCache", CC"()V", (void*)&WB_DisableElfSectionCache }, {CC"aotLibrariesCount", CC"()I", (void*)&WB_AotLibrariesCount }, diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index fd54d45cc36..87a95e0bbd6 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2065,7 +2065,7 @@ bool Arguments::check_vm_args_consistency() { if (status && EnableJVMCI) { PropertyList_unique_add(&_system_properties, "jdk.internal.vm.ci.enabled", "true", AddProperty, UnwriteableProperty, InternalProperty); - if (!create_numbered_property("jdk.module.addmods", "jdk.internal.vm.ci", addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", "jdk.internal.vm.ci", addmods_count++)) { return false; } } @@ -2139,7 +2139,9 @@ bool Arguments::parse_uintx(const char* value, return false; } -bool Arguments::create_property(const char* prop_name, const char* prop_value, PropertyInternal internal) { +bool Arguments::create_module_property(const char* prop_name, const char* prop_value, PropertyInternal internal) { + assert(is_internal_module_property(prop_name) || + strcmp(prop_name, "jdk.module.illegalAccess") == 0, "unknown module property: '%s'", prop_name); size_t prop_len = strlen(prop_name) + strlen(prop_value) + 2; char* property = AllocateHeap(prop_len, mtArguments); int ret = jio_snprintf(property, prop_len, "%s=%s", prop_name, prop_value); @@ -2147,12 +2149,18 @@ bool Arguments::create_property(const char* prop_name, const char* prop_value, P FreeHeap(property); return false; } - bool added = add_property(property, UnwriteableProperty, internal); + // These are not strictly writeable properties as they cannot be set via -Dprop=val. But that + // is enforced by checking is_internal_module_property(). We need the property to be writeable so + // that multiple occurrences of the associated flag just causes the existing property value to be + // replaced ("last option wins"). Otherwise we would need to keep track of the flags and only convert + // to a property after we have finished flag processing. + bool added = add_property(property, WriteableProperty, internal); FreeHeap(property); return added; } -bool Arguments::create_numbered_property(const char* prop_base_name, const char* prop_value, unsigned int count) { +bool Arguments::create_numbered_module_property(const char* prop_base_name, const char* prop_value, unsigned int count) { + assert(is_internal_module_property(prop_base_name), "unknown module property: '%s'", prop_base_name); const unsigned int props_count_limit = 1000; const int max_digits = 3; const int extra_symbols_count = 3; // includes '.', '=', '\0' @@ -2311,7 +2319,7 @@ int Arguments::process_patch_mod_option(const char* patch_mod_tail, bool* patch_ // The path piece begins one past the module_equal sign add_patch_mod_prefix(module_name, module_equal + 1, patch_mod_javabase); FREE_C_HEAP_ARRAY(char, module_name); - if (!create_numbered_property("jdk.module.patch", patch_mod_tail, patch_mod_count++)) { + if (!create_numbered_module_property("jdk.module.patch", patch_mod_tail, patch_mod_count++)) { return JNI_ENOMEM; } } else { @@ -2458,31 +2466,31 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m add_init_library(name, options); } } else if (match_option(option, "--add-reads=", &tail)) { - if (!create_numbered_property("jdk.module.addreads", tail, addreads_count++)) { + if (!create_numbered_module_property("jdk.module.addreads", tail, addreads_count++)) { return JNI_ENOMEM; } } else if (match_option(option, "--add-exports=", &tail)) { - if (!create_numbered_property("jdk.module.addexports", tail, addexports_count++)) { + if (!create_numbered_module_property("jdk.module.addexports", tail, addexports_count++)) { return JNI_ENOMEM; } } else if (match_option(option, "--add-opens=", &tail)) { - if (!create_numbered_property("jdk.module.addopens", tail, addopens_count++)) { + if (!create_numbered_module_property("jdk.module.addopens", tail, addopens_count++)) { return JNI_ENOMEM; } } else if (match_option(option, "--add-modules=", &tail)) { - if (!create_numbered_property("jdk.module.addmods", tail, addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", tail, addmods_count++)) { return JNI_ENOMEM; } } else if (match_option(option, "--limit-modules=", &tail)) { - if (!create_property("jdk.module.limitmods", tail, InternalProperty)) { + if (!create_module_property("jdk.module.limitmods", tail, InternalProperty)) { return JNI_ENOMEM; } } else if (match_option(option, "--module-path=", &tail)) { - if (!create_property("jdk.module.path", tail, ExternalProperty)) { + if (!create_module_property("jdk.module.path", tail, ExternalProperty)) { return JNI_ENOMEM; } } else if (match_option(option, "--upgrade-module-path=", &tail)) { - if (!create_property("jdk.module.upgrade.path", tail, ExternalProperty)) { + if (!create_module_property("jdk.module.upgrade.path", tail, ExternalProperty)) { return JNI_ENOMEM; } } else if (match_option(option, "--patch-module=", &tail)) { @@ -2492,7 +2500,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m return res; } } else if (match_option(option, "--illegal-access=", &tail)) { - if (!create_property("jdk.module.illegalAccess", tail, ExternalProperty)) { + if (!create_module_property("jdk.module.illegalAccess", tail, ExternalProperty)) { return JNI_ENOMEM; } // -agentlib and -agentpath @@ -2536,7 +2544,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m jio_snprintf(options, length, "%s", tail); add_instrument_agent("instrument", options, false); // java agents need module java.instrument - if (!create_numbered_property("jdk.module.addmods", "java.instrument", addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", "java.instrument", addmods_count++)) { return JNI_ENOMEM; } } @@ -2745,7 +2753,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m return JNI_EINVAL; } // management agent in module jdk.management.agent - if (!create_numbered_property("jdk.module.addmods", "jdk.management.agent", addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", "jdk.management.agent", addmods_count++)) { return JNI_ENOMEM; } #else @@ -4199,14 +4207,15 @@ void Arguments::PropertyList_unique_add(SystemProperty** plist, const char* k, c if (plist == NULL) return; - // If property key exist then update with new value. + // If property key exists and is writeable, then update with new value. + // Trying to update a non-writeable property is silently ignored. SystemProperty* prop; for (prop = *plist; prop != NULL; prop = prop->next()) { if (strcmp(k, prop->key()) == 0) { if (append == AppendProperty) { - prop->append_value(v); + prop->append_writeable_value(v); } else { - prop->set_value(v); + prop->set_writeable_value(v); } return; } diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index f0a53bad20f..a0481108b52 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -120,6 +120,11 @@ class SystemProperty : public PathString { } return false; } + void append_writeable_value(const char *value) { + if (writeable()) { + append_value(value); + } + } // Constructor SystemProperty(const char* key, const char* value, bool writeable, bool internal = false); @@ -396,8 +401,11 @@ class Arguments : AllStatic { static bool add_property(const char* prop, PropertyWriteable writeable=WriteableProperty, PropertyInternal internal=ExternalProperty); - static bool create_property(const char* prop_name, const char* prop_value, PropertyInternal internal); - static bool create_numbered_property(const char* prop_base_name, const char* prop_value, unsigned int count); + // Used for module system related properties: converted from command-line flags. + // Basic properties are writeable as they operate as "last one wins" and will get overwritten. + // Numbered properties are never writeable, and always internal. + static bool create_module_property(const char* prop_name, const char* prop_value, PropertyInternal internal); + static bool create_numbered_module_property(const char* prop_base_name, const char* prop_value, unsigned int count); static int process_patch_mod_option(const char* patch_mod_tail, bool* patch_mod_javabase); diff --git a/src/hotspot/share/runtime/reflection.cpp b/src/hotspot/share/runtime/reflection.cpp index b8f736a7dbc..95e202374ff 100644 --- a/src/hotspot/share/runtime/reflection.cpp +++ b/src/hotspot/share/runtime/reflection.cpp @@ -453,11 +453,9 @@ static bool can_relax_access_check_for(const Klass* accessor, under_host_klass(accessee_ik, accessor_ik)) return true; - if ((RelaxAccessControlCheck && + if (RelaxAccessControlCheck && accessor_ik->major_version() < Verifier::NO_RELAX_ACCESS_CTRL_CHECK_VERSION && - accessee_ik->major_version() < Verifier::NO_RELAX_ACCESS_CTRL_CHECK_VERSION) || - (accessor_ik->major_version() < Verifier::STRICTER_ACCESS_CTRL_CHECK_VERSION && - accessee_ik->major_version() < Verifier::STRICTER_ACCESS_CTRL_CHECK_VERSION)) { + accessee_ik->major_version() < Verifier::NO_RELAX_ACCESS_CTRL_CHECK_VERSION) { return classloader_only && Verifier::relax_access_for(accessor_ik->class_loader()) && accessor_ik->protection_domain() == accessee_ik->protection_domain() && diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 7f9da69ab5e..40c56c3fcd0 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -1491,8 +1491,8 @@ void WatcherThread::run() { os::die(); } - // Wait a second, then recheck for timeout. - os::naked_short_sleep(999); + // Wait a bit, then recheck for timeout. + os::naked_short_sleep(250); } } @@ -3225,6 +3225,25 @@ oop JavaThread::current_park_blocker() { return NULL; } +// Print current stack trace for checked JNI warnings and JNI fatal errors. +// This is the external format, selecting the platform +// as applicable, and allowing for a native-only stack. +void JavaThread::print_jni_stack() { + assert(this == JavaThread::current(), "Can't print stack of other threads"); + if (!has_last_Java_frame()) { + ResourceMark rm(this); + char* buf = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN); + if (buf == NULL) { + tty->print_cr("Unable to print native stack - out of memory"); + return; + } + frame f = os::current_frame(); + VMError::print_native_stack(tty, f, this, + buf, O_BUFLEN); + } else { + print_stack_on(tty); + } +} void JavaThread::print_stack_on(outputStream* st) { if (!has_last_Java_frame()) return; diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 0a9c45f850f..aa914eccafc 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -1862,6 +1862,10 @@ class JavaThread: public Thread { // Print stack trace in external format void print_stack_on(outputStream* st); void print_stack() { print_stack_on(tty); } + // Print current stack trace for checked JNI warnings and JNI fatal errors. + // This is the external format from above, but selecting the platform + // as applicable. + void print_jni_stack(); // Print stack traces in various internal formats void trace_stack() PRODUCT_RETURN; diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index b24d7293a02..d0331c3afe3 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -923,7 +923,8 @@ void JMXStatusDCmd::execute(DCmdSource source, TRAPS) { if (str != NULL) { char* out = java_lang_String::as_utf8_string(str); if (out) { - output()->print_cr("%s", out); + // Avoid using print_cr() because length maybe longer than O_BUFLEN + output()->print_raw_cr(out); return; } } diff --git a/src/hotspot/share/services/heapDumperCompression.cpp b/src/hotspot/share/services/heapDumperCompression.cpp index 96cbf7e2762..be9a5dac31b 100644 --- a/src/hotspot/share/services/heapDumperCompression.cpp +++ b/src/hotspot/share/services/heapDumperCompression.cpp @@ -53,10 +53,14 @@ char const* FileWriter::write_buf(char* buf, ssize_t size) { assert(_fd >= 0, "Must be open"); assert(size > 0, "Must write at least one byte"); - ssize_t n = (ssize_t) os::write(_fd, buf, (uint) size); + while (size > 0) { + ssize_t n = os::write(_fd, buf, (uint) size); + if (n <= 0) { + return os::strerror(errno); + } - if (n <= 0) { - return os::strerror(errno); + buf += n; + size -= n; } return NULL; diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 4c0b285044f..350498181d2 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1689,42 +1690,71 @@ void VMError::show_message_box(char *buf, int buflen) { } while (yes); } -// Timeout handling: check if a timeout happened (either a single step did -// timeout or the whole of error reporting hit ErrorLogTimeout). Interrupt -// the reporting thread if that is the case. +// Fatal error handling is subject to several timeouts: +// - a global timeout (controlled via ErrorLogTimeout) +// - local error reporting step timeouts. +// +// The latter aims to "give the JVM a kick" if it gets stuck in one particular place during +// error reporting. This prevents one error reporting step from hogging all the time allotted +// to error reporting under ErrorLogTimeout. +// +// VMError::check_timeout() is called from the watcher thread and checks for either global +// or step timeout. If a timeout happened, we interrupt the reporting thread and set either +// _reporting_did_timeout or _step_did_timeout to signal which timeout fired. Function returns +// true if the *global* timeout fired, which will cause WatcherThread to shut down the JVM +// immediately. bool VMError::check_timeout() { + // This function is supposed to be called from watcher thread during fatal error handling only. + assert(VMError::is_error_reported(), "Only call during error handling"); + assert(Thread::current()->is_Watcher_thread(), "Only call from watcher thread"); + if (ErrorLogTimeout == 0) { return false; } - // Do not check for timeouts if we still have a message box to show to the - // user or if there are OnError handlers to be run. - if (ShowMessageBoxOnError - || (OnError != NULL && OnError[0] != '\0') - || Arguments::abort_hook() != NULL) { - return false; - } + // There are three situations where we suppress the *global* error timeout: + // - if the JVM is embedded and the launcher has its abort hook installed. + // That must be allowed to run. + // - if the user specified one or more OnError commands to run, and these + // did not yet run. These must have finished. + // - if the user (typically developer) specified ShowMessageBoxOnError, + // and the error box has not yet been shown + const bool ignore_global_timeout = + (ShowMessageBoxOnError + || (OnError != NULL && OnError[0] != '\0') + || Arguments::abort_hook() != NULL); - const jlong reporting_start_time_l = get_reporting_start_time(); const jlong now = get_current_timestamp(); - // Timestamp is stored in nanos. - if (reporting_start_time_l > 0) { - const jlong end = reporting_start_time_l + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR; - if (end <= now) { - _reporting_did_timeout = true; - interrupt_reporting_thread(); - return true; // global timeout + + // Global timeout hit? + if (!ignore_global_timeout) { + const jlong reporting_start_time = get_reporting_start_time(); + // Timestamp is stored in nanos. + if (reporting_start_time > 0) { + const jlong end = reporting_start_time + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR; + if (end <= now && !_reporting_did_timeout) { + // We hit ErrorLogTimeout and we haven't interrupted the reporting + // thread yet. + _reporting_did_timeout = true; + interrupt_reporting_thread(); + return true; // global timeout + } } } - const jlong step_start_time_l = get_step_start_time(); - if (step_start_time_l > 0) { + // Reporting step timeout? + const jlong step_start_time = get_step_start_time(); + if (step_start_time > 0) { // A step times out after a quarter of the total timeout. Steps are mostly fast unless they // hang for some reason, so this simple rule allows for three hanging step and still // hopefully leaves time enough for the rest of the steps to finish. - const jlong end = step_start_time_l + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR / 4; - if (end <= now) { + const int max_step_timeout_secs = 5; + const jlong timeout_duration = MAX2((jlong)max_step_timeout_secs, (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR / 4); + const jlong end = step_start_time + timeout_duration; + if (end <= now && !_step_did_timeout) { + // The step timed out and we haven't interrupted the reporting + // thread yet. _step_did_timeout = true; interrupt_reporting_thread(); return false; // (Not a global timeout) diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index e53e5760437..cddf3137b48 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -104,12 +104,6 @@ class VMError : public AllStatic { static void print_stack_trace(outputStream* st, JavaThread* jt, char* buf, int buflen, bool verbose = false); - // public for use by the internal non-product debugger. - NOT_PRODUCT(public:) - static void print_native_stack(outputStream* st, frame fr, Thread* t, - char* buf, int buf_size); - NOT_PRODUCT(private:) - static bool should_report_bug(unsigned int id) { return (id != OOM_MALLOC_ERROR) && (id != OOM_MMAP_ERROR); } @@ -142,6 +136,12 @@ class VMError : public AllStatic { public: + // print_source_info: if true, we try to resolve the source information on platforms that support it + // (useful but may slow down, timeout or misfunction in error situations) + // max_frames: if not -1, overrides StackPrintLimit + static void print_native_stack(outputStream* st, frame fr, Thread* t, + char* buf, int buf_size); + // return a string to describe the error static char* error_string(char* buf, int buflen); diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java index cf8230b0bfc..c7e83faa59d 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java +++ b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java @@ -53,16 +53,6 @@ public interface CgroupV1Metrics extends Metrics { */ public long getKernelMemoryFailCount(); - /** - * Returns the maximum amount of kernel physical memory, in bytes, that - * can be allocated in the Isolation Group. - * - * @return The maximum amount of memory in bytes or -1 if - * there is no limit set. - * - */ - public long getKernelMemoryLimit(); - /** * Returns the largest amount of kernel physical memory, in bytes, that * have been allocated in the Isolation Group. @@ -93,16 +83,6 @@ public interface CgroupV1Metrics extends Metrics { */ public long getTcpMemoryFailCount(); - /** - * Returns the maximum amount of networking physical memory, in bytes, - * that can be allocated in the Isolation Group. - * - * @return The maximum amount of memory in bytes or -1 if - * there is no limit. - * - */ - public long getTcpMemoryLimit(); - /** * Returns the largest amount of networking physical memory, in bytes, * that have been allocated in the Isolation Group. diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java index 443b58f1f9d..ce9b0c2cc20 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java +++ b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java @@ -48,11 +48,6 @@ public long getKernelMemoryFailCount() { return metrics.getKernelMemoryFailCount(); } - @Override - public long getKernelMemoryLimit() { - return metrics.getKernelMemoryLimit(); - } - @Override public long getKernelMemoryMaxUsage() { return metrics.getKernelMemoryMaxUsage(); @@ -68,11 +63,6 @@ public long getTcpMemoryFailCount() { return metrics.getTcpMemoryFailCount(); } - @Override - public long getTcpMemoryLimit() { - return metrics.getTcpMemoryLimit(); - } - @Override public long getTcpMemoryMaxUsage() { return metrics.getTcpMemoryMaxUsage(); diff --git a/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java index 07e9a168ef4..949a8622e56 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java +++ b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java @@ -333,10 +333,6 @@ public long getKernelMemoryFailCount() { return getLongValue(memory, "memory.kmem.failcnt"); } - public long getKernelMemoryLimit() { - return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.limit_in_bytes")); - } - public long getKernelMemoryMaxUsage() { return getLongValue(memory, "memory.kmem.max_usage_in_bytes"); } @@ -349,10 +345,6 @@ public long getTcpMemoryFailCount() { return getLongValue(memory, "memory.kmem.tcp.failcnt"); } - public long getTcpMemoryLimit() { - return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.tcp.limit_in_bytes")); - } - public long getTcpMemoryMaxUsage() { return getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); } diff --git a/src/java.base/macosx/classes/apple/security/KeychainStore.java b/src/java.base/macosx/classes/apple/security/KeychainStore.java index 74a531dae4d..9f00cf81a9c 100644 --- a/src/java.base/macosx/classes/apple/security/KeychainStore.java +++ b/src/java.base/macosx/classes/apple/security/KeychainStore.java @@ -69,7 +69,7 @@ class TrustedCertEntry { Certificate cert; long certRef; // SecCertificateRef for this key - // Each KeyStore.TrustedCertificateEntry have 2 attributes: + // Each KeyStore.TrustedCertificateEntry has 2 attributes: // 1. "trustSettings" -> trustSettings.toString() // 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue // The 1st one is mainly for debugging use. The 2nd one is similar @@ -108,12 +108,13 @@ class TrustedCertEntry { private Hashtable entries = new Hashtable<>(); /** - * Algorithm identifiers and corresponding OIDs for the contents of the PKCS12 bag we get from the Keychain. + * Algorithm identifiers and corresponding OIDs for the contents of the + * PKCS12 bag we get from the Keychain. */ - private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; - private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = {1, 2, 840, 113549, 1, 12, 1, 3}; - private static ObjectIdentifier PKCS8ShroudedKeyBag_OID; - private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID; + private static ObjectIdentifier PKCS8ShroudedKeyBag_OID = + ObjectIdentifier.of(KnownOIDs.PKCS8ShroudedKeyBag); + private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID = + ObjectIdentifier.of(KnownOIDs.PBEWithSHA1AndDESede); /** * Constnats used in PBE decryption. @@ -131,12 +132,6 @@ public Void run() { return null; } }); - try { - PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag); - pbeWithSHAAnd3KeyTripleDESCBC_OID = new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC); - } catch (IOException ioe) { - // should not happen - } } private static void permissionCheck() { @@ -701,7 +696,6 @@ public void engineStore(OutputStream stream, char[] password) _releaseKeychainItemRef(((TrustedCertEntry)entry).certRef); } } else { - Certificate certElem; KeyEntry keyEntry = (KeyEntry)entry; if (keyEntry.chain != null) { @@ -853,8 +847,27 @@ private void createTrustedCertEntry(String alias, List inputTrust, tce.cert = cert; tce.certRef = keychainItemRef; + // Check whether a certificate with same alias already exists and is the same + // If yes, we can return here - the existing entry must have the same + // properties and trust settings + if (entries.contains(alias.toLowerCase())) { + int uniqueVal = 1; + String originalAlias = alias; + var co = entries.get(alias.toLowerCase()); + while (co != null) { + if (co instanceof TrustedCertEntry) { + var tco = (TrustedCertEntry)co; + if (tco.cert.equals(tce.cert)) { + return; + } + } + alias = originalAlias + " " + uniqueVal++; + co = entries.get(alias.toLowerCase()); + } + } + tce.trustSettings = new ArrayList<>(); - Map tmpMap = new LinkedHashMap<>(); + Map tmpMap = new LinkedHashMap<>(); for (int i = 0; i < inputTrust.size(); i++) { if (inputTrust.get(i) == null) { tce.trustSettings.add(tmpMap); @@ -877,9 +890,10 @@ private void createTrustedCertEntry(String alias, List inputTrust, } catch (Exception e) { isSelfSigned = false; } + if (tce.trustSettings.isEmpty()) { if (isSelfSigned) { - // If a self-signed certificate has an empty trust settings, + // If a self-signed certificate has trust settings without specific entries, // trust it for all purposes tce.trustedKeyUsageValue = KnownOIDs.anyExtendedKeyUsage.value(); } else { @@ -892,11 +906,19 @@ private void createTrustedCertEntry(String alias, List inputTrust, for (var oneTrust : tce.trustSettings) { var result = oneTrust.get("kSecTrustSettingsResult"); // https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc - // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot + // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot, + // 3 = kSecTrustSettingsResultDeny // If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed - // for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings). + // (see doc for SecTrustSettingsCopyTrustSettings). // Note that the same SecPolicyOid can appear in multiple trust settings // for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString. + + // If we find explicit distrust in some record, we ignore the certificate + if ("3".equals(result)) { + return; + } + + // Trust, if explicitly trusted or result is null and certificate is self signed if ((result == null && isSelfSigned) || "1".equals(result) || "2".equals(result)) { // When no kSecTrustSettingsPolicy, it means everything @@ -916,20 +938,13 @@ private void createTrustedCertEntry(String alias, List inputTrust, tce.trustedKeyUsageValue = values.toString(); } } + // Make a creation date. if (creationDate != 0) tce.date = new Date(creationDate); else tce.date = new Date(); - int uniqueVal = 1; - String originalAlias = alias; - - while (entries.containsKey(alias.toLowerCase())) { - alias = originalAlias + " " + uniqueVal; - uniqueVal++; - } - entries.put(alias.toLowerCase(), tce); } catch (Exception e) { // The certificate will be skipped. diff --git a/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m b/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m index 3f0c1de962d..b4e19a27995 100644 --- a/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m +++ b/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -285,9 +285,14 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore) OSErr searchResult = noErr; jclass jc_KeychainStore = (*env)->FindClass(env, "apple/security/KeychainStore"); - CHECK_NULL(jc_KeychainStore); + if (jc_KeychainStore == NULL) { + goto errOut; + } jmethodID jm_createKeyEntry = (*env)->GetMethodID(env, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V"); - CHECK_NULL(jm_createKeyEntry); + if (jm_createKeyEntry == NULL) { + goto errOut; + } + do { searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity); @@ -376,6 +381,35 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore) #define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL) + +static void addTrustSettingsToInputTrust(JNIEnv *env, jmethodID jm_listAdd, CFArrayRef trustSettings, jobject inputTrust) +{ + CFIndex count = CFArrayGetCount(trustSettings); + for (int i = 0; i < count; i++) { + CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i); + CFIndex size = CFDictionaryGetCount(oneTrust); + const void * keys [size]; + const void * values [size]; + CFDictionaryGetKeysAndValues(oneTrust, keys, values); + for (int j = 0; j < size; j++) { + NSString* s = [NSString stringWithFormat:@"%@", keys[j]]; + ADD(inputTrust, s); + s = [NSString stringWithFormat:@"%@", values[j]]; + ADD(inputTrust, s); + } + SecPolicyRef certPolicy; + certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy); + if (certPolicy != NULL) { + CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy); + ADD(inputTrust, @"SecPolicyOid"); + NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")]; + ADD(inputTrust, s); + CFRelease(policyDict); + } + ADDNULL(inputTrust); + } +} + static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) { // Search the user keychain list for all X509 certificates. @@ -385,16 +419,30 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) OSErr searchResult = noErr; jclass jc_KeychainStore = (*env)->FindClass(env, "apple/security/KeychainStore"); - CHECK_NULL(jc_KeychainStore); + if (jc_KeychainStore == NULL) { + goto errOut; + } + jmethodID jm_createTrustedCertEntry = (*env)->GetMethodID( env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;Ljava/util/List;JJ[B)V"); - CHECK_NULL(jm_createTrustedCertEntry); + if (jm_createTrustedCertEntry == NULL) { + goto errOut; + } + jclass jc_arrayListClass = (*env)->FindClass(env, "java/util/ArrayList"); - CHECK_NULL(jc_arrayListClass); + if (jc_arrayListClass == NULL) { + goto errOut; + } + jmethodID jm_arrayListCons = (*env)->GetMethodID(env, jc_arrayListClass, "", "()V"); - CHECK_NULL(jm_arrayListCons); + if (jm_arrayListCons == NULL) { + goto errOut; + } + jmethodID jm_listAdd = (*env)->GetMethodID(env, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z"); - CHECK_NULL(jm_listAdd); + if (jm_listAdd == NULL) { + goto errOut; + } do { searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem); @@ -416,43 +464,40 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) goto errOut; } - // Only add certificates with trusted settings - CFArrayRef trustSettings; - if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) - == errSecItemNotFound) { - continue; - } - // See KeychainStore::createTrustedCertEntry for content of inputTrust - jobject inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); - CHECK_NULL(inputTrust); - - // Dump everything inside trustSettings into inputTrust - CFIndex count = CFArrayGetCount(trustSettings); - for (int i = 0; i < count; i++) { - CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i); - CFIndex size = CFDictionaryGetCount(oneTrust); - const void * keys [size]; - const void * values [size]; - CFDictionaryGetKeysAndValues(oneTrust, keys, values); - for (int j = 0; j < size; j++) { - NSString* s = [NSString stringWithFormat:@"%@", keys[j]]; - ADD(inputTrust, s); - s = [NSString stringWithFormat:@"%@", values[j]]; - ADD(inputTrust, s); + // We load trust settings from domains kSecTrustSettingsDomainUser and kSecTrustSettingsDomainAdmin + // kSecTrustSettingsDomainSystem is ignored because it seems to only contain data for root certificates + jobject inputTrust = NULL; + CFArrayRef trustSettings = NULL; + + // Load user trustSettings into inputTrust + if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) == errSecSuccess && trustSettings != NULL) { + inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); + if (inputTrust == NULL) { + CFRelease(trustSettings); + goto errOut; } - SecPolicyRef certPolicy; - certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy); - if (certPolicy != NULL) { - CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy); - ADD(inputTrust, @"SecPolicyOid"); - NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")]; - ADD(inputTrust, s); - CFRelease(policyDict); + addTrustSettingsToInputTrust(env, jm_listAdd, trustSettings, inputTrust); + CFRelease(trustSettings); + } + // Load admin trustSettings into inputTrust + trustSettings = NULL; + if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainAdmin, &trustSettings) == errSecSuccess && trustSettings != NULL) { + if (inputTrust == NULL) { + inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); } - ADDNULL(inputTrust); + if (inputTrust == NULL) { + CFRelease(trustSettings); + goto errOut; + } + addTrustSettingsToInputTrust(env, jm_listAdd, trustSettings, inputTrust); + CFRelease(trustSettings); + } + + // Only add certificates with trust settings + if (inputTrust == NULL) { + continue; } - CFRelease(trustSettings); // Find the creation date. jlong creationDate = getModDateFromItem(env, theItem); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java b/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java index f641eaa320f..b62cfa6c1c4 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -915,10 +915,10 @@ int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int estOutSize = getOutputSizeByOperation(inputLen, true); int outputCapacity = checkOutputCapacity(output, outputOffset, estOutSize); - int offset = decrypting ? 0 : outputOffset; // 0 for decrypting + int offset = outputOffset; // 0 for decrypting byte[] finalBuf = prepareInputBuffer(input, inputOffset, inputLen, output, outputOffset); - byte[] outWithPadding = null; // for decrypting only + byte[] internalOutput = null; // for decrypting only int finalOffset = (finalBuf == input) ? inputOffset : 0; int finalBufLen = (finalBuf == input) ? inputLen : finalBuf.length; @@ -932,11 +932,15 @@ int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, if (outputCapacity < estOutSize) { cipher.save(); } - // create temporary output buffer so that only "real" - // data bytes are passed to user's output buffer. - outWithPadding = new byte[estOutSize]; + if (outputCapacity < estOutSize || padding != null) { + // create temporary output buffer if the estimated size is larger + // than the user-provided buffer or a padding needs to be removed + // before copying the unpadded result to the output buffer + internalOutput = new byte[estOutSize]; + offset = 0; + } } - byte[] outBuffer = decrypting ? outWithPadding : output; + byte[] outBuffer = (internalOutput != null) ? internalOutput : output; int outLen = fillOutputBuffer(finalBuf, finalOffset, outBuffer, offset, finalBufLen, input); @@ -952,9 +956,11 @@ int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, + " bytes needed"); } // copy the result into user-supplied output buffer - System.arraycopy(outWithPadding, 0, output, outputOffset, outLen); - // decrypt mode. Zero out output data that's not required - Arrays.fill(outWithPadding, (byte) 0x00); + if (internalOutput != null) { + System.arraycopy(internalOutput, 0, output, outputOffset, outLen); + // decrypt mode. Zero out output data that's not required + Arrays.fill(internalOutput, (byte) 0x00); + } } endDoFinal(); return outLen; diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java index c87cc1c5d62..a886dc7c31a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,8 +71,6 @@ final class DHPrivateKey implements PrivateKey, // the private-value length (optional) private int l; - private int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 }; - /** * Make a DH private key out of a private value x, a prime * modulus p, and a base generator g. @@ -219,7 +217,7 @@ public synchronized byte[] getEncoded() { DerOutputStream algid = new DerOutputStream(); // store OID - algid.putOID(new ObjectIdentifier(DH_data)); + algid.putOID(DHPublicKey.DH_OID); // encode parameters DerOutputStream params = new DerOutputStream(); params.putInteger(this.p); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java index f2264ad43b0..0e8b396bff6 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,7 +68,9 @@ final class DHPublicKey implements PublicKey, // the private-value length (optional) private int l; - private int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 }; + // Note: this OID is used by DHPrivateKey as well. + static ObjectIdentifier DH_OID = + ObjectIdentifier.of(KnownOIDs.DiffieHellman); /** * Make a DH public key out of a public value y, a prime @@ -202,7 +204,7 @@ public synchronized byte[] getEncoded() { DerOutputStream algid = new DerOutputStream(); // store oid in algid - algid.putOID(new ObjectIdentifier(DH_data)); + algid.putOID(DH_OID); // encode parameters DerOutputStream params = new DerOutputStream(); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java index e8a3399d8f1..9eb6b5f28ca 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java @@ -48,6 +48,7 @@ import sun.security.x509.AlgorithmId; import sun.security.util.ObjectIdentifier; +import sun.security.util.KnownOIDs; import sun.security.util.SecurityProperties; /** @@ -67,14 +68,6 @@ final class KeyProtector { - // defined by SunSoft (SKI project) - private static final String PBE_WITH_MD5_AND_DES3_CBC_OID - = "1.3.6.1.4.1.42.2.19.1"; - - // JavaSoft proprietary key-protection algorithm (used to protect private - // keys in the keystore implementation that comes with JDK 1.2) - private static final String KEY_PROTECTOR_OID = "1.3.6.1.4.1.42.2.17.1.1"; - private static final int MAX_ITERATION_COUNT = 5000000; private static final int MIN_ITERATION_COUNT = 10000; private static final int DEFAULT_ITERATION_COUNT = 200000; @@ -154,7 +147,8 @@ byte[] protect(PrivateKey key) pbeParams.init(pbeSpec); AlgorithmId encrAlg = new AlgorithmId - (new ObjectIdentifier(PBE_WITH_MD5_AND_DES3_CBC_OID), pbeParams); + (ObjectIdentifier.of(KnownOIDs.JAVASOFT_JCEKeyProtector), + pbeParams); return new EncryptedPrivateKeyInfo(encrAlg,encrKey).getEncoded(); } @@ -169,13 +163,13 @@ Key recover(EncryptedPrivateKeyInfo encrInfo) SecretKey sKey = null; try { String encrAlg = encrInfo.getAlgorithm().getOID().toString(); - if (!encrAlg.equals(PBE_WITH_MD5_AND_DES3_CBC_OID) - && !encrAlg.equals(KEY_PROTECTOR_OID)) { + if (!encrAlg.equals(KnownOIDs.JAVASOFT_JCEKeyProtector.value()) + && !encrAlg.equals(KnownOIDs.JAVASOFT_JDKKeyProtector.value())) { throw new UnrecoverableKeyException("Unsupported encryption " + "algorithm"); } - if (encrAlg.equals(KEY_PROTECTOR_OID)) { + if (encrAlg.equals(KnownOIDs.JAVASOFT_JDKKeyProtector.value())) { // JDK 1.2 style recovery plain = recover(encrInfo.getEncryptedData()); } else { diff --git a/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java index 6e90f3fe15d..e4879b50dc4 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java @@ -55,24 +55,10 @@ public final class OAEPParameters extends AlgorithmParametersSpi { private String mdName; private MGF1ParameterSpec mgfSpec; private byte[] p; - private static ObjectIdentifier OID_MGF1; - private static ObjectIdentifier OID_PSpecified; - - static { - try { - OID_MGF1 = new ObjectIdentifier(new int[] {1,2,840,113549,1,1,8}); - } catch (IOException ioe) { - // should not happen - OID_MGF1 = null; - } - try { - OID_PSpecified = - new ObjectIdentifier(new int[] {1,2,840,113549,1,1,9}); - } catch (IOException ioe) { - // should not happen - OID_PSpecified = null; - } - } + private static ObjectIdentifier OID_MGF1 = + ObjectIdentifier.of(KnownOIDs.MGF1); + private static ObjectIdentifier OID_PSpecified = + ObjectIdentifier.of(KnownOIDs.PSpecified); public OAEPParameters() { } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java index 968d591ac6c..8b199f7abd2 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -90,57 +90,18 @@ * * */ - abstract class PBES2Parameters extends AlgorithmParametersSpi { - private static final int pkcs5PBKDF2[] = - {1, 2, 840, 113549, 1, 5, 12}; - private static final int pkcs5PBES2[] = - {1, 2, 840, 113549, 1, 5, 13}; - private static final int hmacWithSHA1[] = - {1, 2, 840, 113549, 2, 7}; - private static final int hmacWithSHA224[] = - {1, 2, 840, 113549, 2, 8}; - private static final int hmacWithSHA256[] = - {1, 2, 840, 113549, 2, 9}; - private static final int hmacWithSHA384[] = - {1, 2, 840, 113549, 2, 10}; - private static final int hmacWithSHA512[] = - {1, 2, 840, 113549, 2, 11}; - private static final int aes128CBC[] = - {2, 16, 840, 1, 101, 3, 4, 1, 2}; - private static final int aes192CBC[] = - {2, 16, 840, 1, 101, 3, 4, 1, 22}; - private static final int aes256CBC[] = - {2, 16, 840, 1, 101, 3, 4, 1, 42}; - - private static ObjectIdentifier pkcs5PBKDF2_OID; - private static ObjectIdentifier pkcs5PBES2_OID; - private static ObjectIdentifier hmacWithSHA1_OID; - private static ObjectIdentifier hmacWithSHA224_OID; - private static ObjectIdentifier hmacWithSHA256_OID; - private static ObjectIdentifier hmacWithSHA384_OID; - private static ObjectIdentifier hmacWithSHA512_OID; - private static ObjectIdentifier aes128CBC_OID; - private static ObjectIdentifier aes192CBC_OID; - private static ObjectIdentifier aes256CBC_OID; - - static { - try { - pkcs5PBKDF2_OID = new ObjectIdentifier(pkcs5PBKDF2); - pkcs5PBES2_OID = new ObjectIdentifier(pkcs5PBES2); - hmacWithSHA1_OID = new ObjectIdentifier(hmacWithSHA1); - hmacWithSHA224_OID = new ObjectIdentifier(hmacWithSHA224); - hmacWithSHA256_OID = new ObjectIdentifier(hmacWithSHA256); - hmacWithSHA384_OID = new ObjectIdentifier(hmacWithSHA384); - hmacWithSHA512_OID = new ObjectIdentifier(hmacWithSHA512); - aes128CBC_OID = new ObjectIdentifier(aes128CBC); - aes192CBC_OID = new ObjectIdentifier(aes192CBC); - aes256CBC_OID = new ObjectIdentifier(aes256CBC); - } catch (IOException ioe) { - // should not happen - } - } + private static ObjectIdentifier pkcs5PBKDF2_OID = + ObjectIdentifier.of(KnownOIDs.PBKDF2WithHmacSHA1); + private static ObjectIdentifier pkcs5PBES2_OID = + ObjectIdentifier.of(KnownOIDs.PBES2); + private static ObjectIdentifier aes128CBC_OID = + ObjectIdentifier.of(KnownOIDs.AES_128$CBC$NoPadding); + private static ObjectIdentifier aes192CBC_OID = + ObjectIdentifier.of(KnownOIDs.AES_192$CBC$NoPadding); + private static ObjectIdentifier aes256CBC_OID = + ObjectIdentifier.of(KnownOIDs.AES_256$CBC$NoPadding); // the PBES2 algorithm name private String pbes2AlgorithmName = null; @@ -155,7 +116,8 @@ abstract class PBES2Parameters extends AlgorithmParametersSpi { private AlgorithmParameterSpec cipherParam = null; // the key derivation function (default is HmacSHA1) - private ObjectIdentifier kdfAlgo_OID = hmacWithSHA1_OID; + private ObjectIdentifier kdfAlgo_OID = + ObjectIdentifier.of(KnownOIDs.HmacSHA1); // the encryption function private ObjectIdentifier cipherAlgo_OID = null; @@ -200,19 +162,11 @@ abstract class PBES2Parameters extends AlgorithmParametersSpi { switch (kdfAlgo) { case "HmacSHA1": - kdfAlgo_OID = hmacWithSHA1_OID; - break; case "HmacSHA224": - kdfAlgo_OID = hmacWithSHA224_OID; - break; case "HmacSHA256": - kdfAlgo_OID = hmacWithSHA256_OID; - break; case "HmacSHA384": - kdfAlgo_OID = hmacWithSHA384_OID; - break; case "HmacSHA512": - kdfAlgo_OID = hmacWithSHA512_OID; + kdfAlgo_OID = ObjectIdentifier.of(KnownOIDs.findMatch(kdfAlgo)); break; default: throw new NoSuchAlgorithmException( @@ -284,7 +238,7 @@ protected void engineInit(byte[] encoded) } cipherAlgo = parseES(pBES2_params.data.getDerValue()); - pbes2AlgorithmName = new StringBuilder().append("PBEWith") + this.pbes2AlgorithmName = new StringBuilder().append("PBEWith") .append(kdfAlgo).append("And").append(cipherAlgo).toString(); } @@ -335,21 +289,18 @@ private String parseKDF(DerValue keyDerivationFunc) throws IOException { } if (prf != null) { kdfAlgo_OID = prf.data.getOID(); - if (hmacWithSHA1_OID.equals(kdfAlgo_OID)) { - kdfAlgo = "HmacSHA1"; - } else if (hmacWithSHA224_OID.equals(kdfAlgo_OID)) { - kdfAlgo = "HmacSHA224"; - } else if (hmacWithSHA256_OID.equals(kdfAlgo_OID)) { - kdfAlgo = "HmacSHA256"; - } else if (hmacWithSHA384_OID.equals(kdfAlgo_OID)) { - kdfAlgo = "HmacSHA384"; - } else if (hmacWithSHA512_OID.equals(kdfAlgo_OID)) { - kdfAlgo = "HmacSHA512"; - } else { + KnownOIDs o = KnownOIDs.findMatch(kdfAlgo_OID.toString()); + if (o == null || (!o.stdName().equals("HmacSHA1") && + !o.stdName().equals("HmacSHA224") && + !o.stdName().equals("HmacSHA256") && + !o.stdName().equals("HmacSHA384") && + !o.stdName().equals("HmacSHA512"))) { throw new IOException("PBE parameter parsing error: " + "expecting the object identifier for a HmacSHA key " + "derivation function"); } + kdfAlgo = o.stdName(); + if (prf.data.available() != 0) { // parameter is 'NULL' for all HmacSHA KDFs DerValue parameter = prf.data.getDerValue(); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java index 414e2c5c6c4..815968bf3f0 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java @@ -32,8 +32,7 @@ import java.util.HashMap; import java.util.List; import static sun.security.util.SecurityConstants.PROVIDER_VER; -import static sun.security.provider.SunEntries.createAliases; -import static sun.security.provider.SunEntries.createAliasesWithOid; +import static sun.security.util.SecurityProviderConstants.*; /** * The "SunJCE" Cryptographic Service Provider. @@ -99,9 +98,22 @@ private static class SecureRandomHolder { } static SecureRandom getRandom() { return SecureRandomHolder.RANDOM; } - private void ps(String type, String algo, String cn, - List aliases, HashMap attrs) { - putService(new Provider.Service(this, type, algo, cn, aliases, attrs)); + // ps: putService + private void ps(String type, String algo, String cn) { + putService(new Provider.Service(this, type, algo, cn, null, null)); + } + + private void ps(String type, String algo, String cn, List als, + HashMap attrs) { + putService(new Provider.Service(this, type, algo, cn, als, + attrs)); + } + + // psA: putService with default aliases + private void psA(String type, String algo, String cn, + HashMap attrs) { + putService(new Provider.Service(this, type, algo, cn, getAliases(algo), + attrs)); } public SunJCE() { @@ -127,57 +139,6 @@ public Void run() { } void putEntries() { - // common aliases and oids - List aesAliases = createAliases("Rijndael"); - List desEdeAliases = createAliases("TripleDES"); - List arcFourAliases = createAliases("RC4"); - List sunTlsMSAliases = createAliases( - "SunTls12MasterSecret", "SunTlsExtendedMasterSecret" - ); - List sunTlsKMAliases = createAliases("SunTls12KeyMaterial"); - List sunTlsRsaPMSAliases = createAliases("SunTls12RsaPremasterSecret"); - - String aes128Oid = "2.16.840.1.101.3.4.1."; - String aes192Oid = "2.16.840.1.101.3.4.1.2"; - String aes256Oid = "2.16.840.1.101.3.4.1.4"; - - List pkcs12RC4_128Aliases = - createAliasesWithOid("1.2.840.113549.1.12.1.1"); - - List pkcs12RC4_40Aliases = - createAliasesWithOid("1.2.840.113549.1.12.1.2"); - - List pkcs12DESedeAliases = - createAliasesWithOid("1.2.840.113549.1.12.1.3"); - - List pkcs12RC2_128Aliases = - createAliasesWithOid("1.2.840.113549.1.12.1.5"); - - List pkcs12RC2_40Aliases = - createAliasesWithOid("1.2.840.113549.1.12.1.6"); - - List pkcs5MD5_DESAliases = - createAliasesWithOid("1.2.840.113549.1.5.3", "PBE"); - - List pkcs5PBKDF2Aliases = - createAliasesWithOid("1.2.840.113549.1.5.12"); - - List pkcs5PBES2Aliases = - createAliasesWithOid("1.2.840.113549.1.5.13"); - - List diffieHellmanAliases = - createAliasesWithOid("1.2.840.113549.1.3.1", "DH"); - - List chachaPolyAliases = - createAliasesWithOid("1.2.840.113549.1.9.16.3.18"); - - String macOidBase = "1.2.840.113549.2."; - List macSHA1Aliases = createAliasesWithOid(macOidBase + "7"); - List macSHA224Aliases = createAliasesWithOid(macOidBase + "8"); - List macSHA256Aliases = createAliasesWithOid(macOidBase + "9"); - List macSHA384Aliases = createAliasesWithOid(macOidBase + "10"); - List macSHA512Aliases = createAliasesWithOid(macOidBase + "11"); - // reuse attribute map and reset before each reuse HashMap attrs = new HashMap<>(3); attrs.put("SupportedModes", "ECB"); @@ -212,8 +173,8 @@ void putEntries() { attrs.put("SupportedKeyFormats", "RAW"); ps("Cipher", "DES", "com.sun.crypto.provider.DESCipher", null, attrs); - ps("Cipher", "DESede", "com.sun.crypto.provider.DESedeCipher", - desEdeAliases, attrs); + psA("Cipher", "DESede", "com.sun.crypto.provider.DESedeCipher", + attrs); ps("Cipher", "Blowfish", "com.sun.crypto.provider.BlowfishCipher", null, attrs); @@ -224,58 +185,58 @@ void putEntries() { attrs.put("SupportedModes", BLOCK_MODES128); attrs.put("SupportedPaddings", BLOCK_PADS); attrs.put("SupportedKeyFormats", "RAW"); - ps("Cipher", "AES", "com.sun.crypto.provider.AESCipher$General", - aesAliases, attrs); + psA("Cipher", "AES", + "com.sun.crypto.provider.AESCipher$General", attrs); attrs.clear(); attrs.put("SupportedKeyFormats", "RAW"); - ps("Cipher", "AES_128/ECB/NoPadding", + psA("Cipher", "AES_128/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding", - createAliasesWithOid(aes128Oid+"1"), attrs); - ps("Cipher", "AES_128/CBC/NoPadding", + attrs); + psA("Cipher", "AES_128/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CBC_NoPadding", - createAliasesWithOid(aes128Oid+"2"), attrs); - ps("Cipher", "AES_128/OFB/NoPadding", + attrs); + psA("Cipher", "AES_128/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_OFB_NoPadding", - createAliasesWithOid(aes128Oid+"3"), attrs); - ps("Cipher", "AES_128/CFB/NoPadding", + attrs); + psA("Cipher", "AES_128/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding", - createAliasesWithOid(aes128Oid+"4"), attrs); - ps("Cipher", "AES_128/GCM/NoPadding", + attrs); + psA("Cipher", "AES_128/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding", - createAliasesWithOid(aes128Oid+"6"), attrs); + attrs); - ps("Cipher", "AES_192/ECB/NoPadding", + psA("Cipher", "AES_192/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding", - createAliasesWithOid(aes192Oid+"1"), attrs); - ps("Cipher", "AES_192/CBC/NoPadding", + attrs); + psA("Cipher", "AES_192/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CBC_NoPadding", - createAliasesWithOid(aes192Oid+"2"), attrs); - ps("Cipher", "AES_192/OFB/NoPadding", + attrs); + psA("Cipher", "AES_192/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_OFB_NoPadding", - createAliasesWithOid(aes192Oid+"3"), attrs); - ps("Cipher", "AES_192/CFB/NoPadding", + attrs); + psA("Cipher", "AES_192/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding", - createAliasesWithOid(aes192Oid+"4"), attrs); - ps("Cipher", "AES_192/GCM/NoPadding", + attrs); + psA("Cipher", "AES_192/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding", - createAliasesWithOid(aes192Oid+"6"), attrs); + attrs); - ps("Cipher", "AES_256/ECB/NoPadding", + psA("Cipher", "AES_256/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding", - createAliasesWithOid(aes256Oid+"1"), attrs); - ps("Cipher", "AES_256/CBC/NoPadding", + attrs); + psA("Cipher", "AES_256/CBC/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CBC_NoPadding", - createAliasesWithOid(aes256Oid+"2"), attrs); - ps("Cipher", "AES_256/OFB/NoPadding", + attrs); + psA("Cipher", "AES_256/OFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_OFB_NoPadding", - createAliasesWithOid(aes256Oid+"3"), attrs); - ps("Cipher", "AES_256/CFB/NoPadding", + attrs); + psA("Cipher", "AES_256/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding", - createAliasesWithOid(aes256Oid+"4"), attrs); - ps("Cipher", "AES_256/GCM/NoPadding", + attrs); + psA("Cipher", "AES_256/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding", - createAliasesWithOid(aes256Oid+"6"), attrs); + attrs); attrs.clear(); attrs.put("SupportedModes", "CBC"); @@ -288,148 +249,131 @@ void putEntries() { attrs.put("SupportedModes", "ECB"); attrs.put("SupportedPaddings", "NOPADDING"); attrs.put("SupportedKeyFormats", "RAW"); - ps("Cipher", "ARCFOUR", "com.sun.crypto.provider.ARCFOURCipher", - arcFourAliases, attrs); + psA("Cipher", "ARCFOUR", + "com.sun.crypto.provider.ARCFOURCipher", attrs); ps("Cipher", "AESWrap", "com.sun.crypto.provider.AESWrapCipher$General", null, attrs); - ps("Cipher", "AESWrap_128", + psA("Cipher", "AESWrap_128", "com.sun.crypto.provider.AESWrapCipher$AES128", - createAliasesWithOid(aes128Oid+"5"), attrs); - ps("Cipher", "AESWrap_192", + attrs); + psA("Cipher", "AESWrap_192", "com.sun.crypto.provider.AESWrapCipher$AES192", - createAliasesWithOid(aes192Oid+"5"), attrs); - ps("Cipher", "AESWrap_256", + attrs); + psA("Cipher", "AESWrap_256", "com.sun.crypto.provider.AESWrapCipher$AES256", - createAliasesWithOid(aes256Oid+"5"), attrs); + attrs); attrs.clear(); attrs.put("SupportedKeyFormats", "RAW"); ps("Cipher", "ChaCha20", "com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Only", null, attrs); - ps("Cipher", "ChaCha20-Poly1305", + psA("Cipher", "ChaCha20-Poly1305", "com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", - chachaPolyAliases, attrs); + attrs); // PBES1 - ps("Cipher", "PBEWithMD5AndDES", + psA("Cipher", "PBEWithMD5AndDES", "com.sun.crypto.provider.PBEWithMD5AndDESCipher", - pkcs5MD5_DESAliases, null); + null); ps("Cipher", "PBEWithMD5AndTripleDES", - "com.sun.crypto.provider.PBEWithMD5AndTripleDESCipher", - null, null); - ps("Cipher", "PBEWithSHA1AndDESede", + "com.sun.crypto.provider.PBEWithMD5AndTripleDESCipher"); + psA("Cipher", "PBEWithSHA1AndDESede", "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede", - pkcs12DESedeAliases, null); - ps("Cipher", "PBEWithSHA1AndRC2_40", + null); + psA("Cipher", "PBEWithSHA1AndRC2_40", "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_40", - pkcs12RC2_40Aliases, null); - ps("Cipher", "PBEWithSHA1AndRC2_128", + null); + psA("Cipher", "PBEWithSHA1AndRC2_128", "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_128", - pkcs12RC2_128Aliases, null); - ps("Cipher", "PBEWithSHA1AndRC4_40", + null); + psA("Cipher", "PBEWithSHA1AndRC4_40", "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_40", - pkcs12RC4_40Aliases, null); + null); - ps("Cipher", "PBEWithSHA1AndRC4_128", + psA("Cipher", "PBEWithSHA1AndRC4_128", "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_128", - pkcs12RC4_128Aliases, null); + null); // PBES2 ps("Cipher", "PBEWithHmacSHA1AndAES_128", - "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_128"); ps("Cipher", "PBEWithHmacSHA224AndAES_128", - "com.sun.crypto.provider.PBES2Core$HmacSHA224AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA224AndAES_128"); ps("Cipher", "PBEWithHmacSHA256AndAES_128", - "com.sun.crypto.provider.PBES2Core$HmacSHA256AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA256AndAES_128"); ps("Cipher", "PBEWithHmacSHA384AndAES_128", - "com.sun.crypto.provider.PBES2Core$HmacSHA384AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA384AndAES_128"); ps("Cipher", "PBEWithHmacSHA512AndAES_128", - "com.sun.crypto.provider.PBES2Core$HmacSHA512AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA512AndAES_128"); ps("Cipher", "PBEWithHmacSHA1AndAES_256", - "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA1AndAES_256"); ps("Cipher", "PBEWithHmacSHA224AndAES_256", - "com.sun.crypto.provider.PBES2Core$HmacSHA224AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA224AndAES_256"); ps("Cipher", "PBEWithHmacSHA256AndAES_256", - "com.sun.crypto.provider.PBES2Core$HmacSHA256AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA256AndAES_256"); ps("Cipher", "PBEWithHmacSHA384AndAES_256", - "com.sun.crypto.provider.PBES2Core$HmacSHA384AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA384AndAES_256"); ps("Cipher", "PBEWithHmacSHA512AndAES_256", - "com.sun.crypto.provider.PBES2Core$HmacSHA512AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Core$HmacSHA512AndAES_256"); /* * Key(pair) Generator engines */ ps("KeyGenerator", "DES", - "com.sun.crypto.provider.DESKeyGenerator", - null, null); - ps("KeyGenerator", "DESede", + "com.sun.crypto.provider.DESKeyGenerator"); + psA("KeyGenerator", "DESede", "com.sun.crypto.provider.DESedeKeyGenerator", - desEdeAliases, null); + null); ps("KeyGenerator", "Blowfish", - "com.sun.crypto.provider.BlowfishKeyGenerator", - null, null); - ps("KeyGenerator", "AES", + "com.sun.crypto.provider.BlowfishKeyGenerator"); + psA("KeyGenerator", "AES", "com.sun.crypto.provider.AESKeyGenerator", - aesAliases, null); + null); ps("KeyGenerator", "RC2", - "com.sun.crypto.provider.KeyGeneratorCore$RC2KeyGenerator", - null, null); - ps("KeyGenerator", "ARCFOUR", + "com.sun.crypto.provider.KeyGeneratorCore$RC2KeyGenerator"); + psA("KeyGenerator", "ARCFOUR", "com.sun.crypto.provider.KeyGeneratorCore$ARCFOURKeyGenerator", - arcFourAliases, null); + null); ps("KeyGenerator", "ChaCha20", - "com.sun.crypto.provider.KeyGeneratorCore$ChaCha20KeyGenerator", - null, null); + "com.sun.crypto.provider.KeyGeneratorCore$ChaCha20KeyGenerator"); ps("KeyGenerator", "HmacMD5", - "com.sun.crypto.provider.HmacMD5KeyGenerator", - null, null); + "com.sun.crypto.provider.HmacMD5KeyGenerator"); - ps("KeyGenerator", "HmacSHA1", - "com.sun.crypto.provider.HmacSHA1KeyGenerator", - macSHA1Aliases, null); - ps("KeyGenerator", "HmacSHA224", + psA("KeyGenerator", "HmacSHA1", + "com.sun.crypto.provider.HmacSHA1KeyGenerator", null); + psA("KeyGenerator", "HmacSHA224", "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA224", - macSHA224Aliases, null); - ps("KeyGenerator", "HmacSHA256", + null); + psA("KeyGenerator", "HmacSHA256", "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA256", - macSHA256Aliases, null); - ps("KeyGenerator", "HmacSHA384", + null); + psA("KeyGenerator", "HmacSHA384", "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA384", - macSHA384Aliases, null); - ps("KeyGenerator", "HmacSHA512", + null); + psA("KeyGenerator", "HmacSHA512", "com.sun.crypto.provider.KeyGeneratorCore$HmacSHA2KG$SHA512", - macSHA512Aliases, null); + null); - ps("KeyPairGenerator", "DiffieHellman", + psA("KeyPairGenerator", "DiffieHellman", "com.sun.crypto.provider.DHKeyPairGenerator", - diffieHellmanAliases, null); + null); /* * Algorithm parameter generation engines */ - ps("AlgorithmParameterGenerator", + psA("AlgorithmParameterGenerator", "DiffieHellman", "com.sun.crypto.provider.DHParameterGenerator", - diffieHellmanAliases, null); + null); /* * Key Agreement engines @@ -437,142 +381,120 @@ void putEntries() { attrs.clear(); attrs.put("SupportedKeyClasses", "javax.crypto.interfaces.DHPublicKey" + "|javax.crypto.interfaces.DHPrivateKey"); - ps("KeyAgreement", "DiffieHellman", + psA("KeyAgreement", "DiffieHellman", "com.sun.crypto.provider.DHKeyAgreement", - diffieHellmanAliases, attrs); + attrs); /* * Algorithm Parameter engines */ - ps("AlgorithmParameters", "DiffieHellman", - "com.sun.crypto.provider.DHParameters", - diffieHellmanAliases, null); + psA("AlgorithmParameters", "DiffieHellman", + "com.sun.crypto.provider.DHParameters", null); ps("AlgorithmParameters", "DES", - "com.sun.crypto.provider.DESParameters", - null, null); + "com.sun.crypto.provider.DESParameters"); - ps("AlgorithmParameters", "DESede", - "com.sun.crypto.provider.DESedeParameters", - desEdeAliases, null); + psA("AlgorithmParameters", "DESede", + "com.sun.crypto.provider.DESedeParameters", null); - ps("AlgorithmParameters", "PBEWithMD5AndDES", + psA("AlgorithmParameters", "PBEWithMD5AndDES", "com.sun.crypto.provider.PBEParameters", - pkcs5MD5_DESAliases, null); + null); ps("AlgorithmParameters", "PBEWithMD5AndTripleDES", - "com.sun.crypto.provider.PBEParameters", - null, null); + "com.sun.crypto.provider.PBEParameters"); - ps("AlgorithmParameters", "PBEWithSHA1AndDESede", + psA("AlgorithmParameters", "PBEWithSHA1AndDESede", "com.sun.crypto.provider.PBEParameters", - pkcs12DESedeAliases, null); + null); - ps("AlgorithmParameters", "PBEWithSHA1AndRC2_40", + psA("AlgorithmParameters", "PBEWithSHA1AndRC2_40", "com.sun.crypto.provider.PBEParameters", - pkcs12RC2_40Aliases, null); + null); - ps("AlgorithmParameters", "PBEWithSHA1AndRC2_128", + psA("AlgorithmParameters", "PBEWithSHA1AndRC2_128", "com.sun.crypto.provider.PBEParameters", - pkcs12RC2_128Aliases, null); + null); - ps("AlgorithmParameters", "PBEWithSHA1AndRC4_40", + psA("AlgorithmParameters", "PBEWithSHA1AndRC4_40", "com.sun.crypto.provider.PBEParameters", - pkcs12RC4_40Aliases, null); + null); - ps("AlgorithmParameters", "PBEWithSHA1AndRC4_128", + psA("AlgorithmParameters", "PBEWithSHA1AndRC4_128", "com.sun.crypto.provider.PBEParameters", - pkcs12RC4_128Aliases, null); + null); - ps("AlgorithmParameters", "PBES2", + psA("AlgorithmParameters", "PBES2", "com.sun.crypto.provider.PBES2Parameters$General", - pkcs5PBES2Aliases, null); + null); ps("AlgorithmParameters", "PBEWithHmacSHA1AndAES_128", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_128"); ps("AlgorithmParameters", "PBEWithHmacSHA224AndAES_128", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_128"); ps("AlgorithmParameters", "PBEWithHmacSHA256AndAES_128", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_128"); ps("AlgorithmParameters", "PBEWithHmacSHA384AndAES_128", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_128"); ps("AlgorithmParameters", "PBEWithHmacSHA512AndAES_128", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_128", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_128"); ps("AlgorithmParameters", "PBEWithHmacSHA1AndAES_256", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA1AndAES_256"); ps("AlgorithmParameters", "PBEWithHmacSHA224AndAES_256", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA224AndAES_256"); ps("AlgorithmParameters", "PBEWithHmacSHA256AndAES_256", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA256AndAES_256"); ps("AlgorithmParameters", "PBEWithHmacSHA384AndAES_256", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA384AndAES_256"); ps("AlgorithmParameters", "PBEWithHmacSHA512AndAES_256", - "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_256", - null, null); + "com.sun.crypto.provider.PBES2Parameters$HmacSHA512AndAES_256"); ps("AlgorithmParameters", "Blowfish", - "com.sun.crypto.provider.BlowfishParameters", - null, null); + "com.sun.crypto.provider.BlowfishParameters"); - ps("AlgorithmParameters", "AES", - "com.sun.crypto.provider.AESParameters", - aesAliases, null); + psA("AlgorithmParameters", "AES", + "com.sun.crypto.provider.AESParameters", null); ps("AlgorithmParameters", "GCM", - "com.sun.crypto.provider.GCMParameters", - null, null); + "com.sun.crypto.provider.GCMParameters"); ps("AlgorithmParameters", "RC2", - "com.sun.crypto.provider.RC2Parameters", - null, null); + "com.sun.crypto.provider.RC2Parameters"); ps("AlgorithmParameters", "OAEP", - "com.sun.crypto.provider.OAEPParameters", - null, null); + "com.sun.crypto.provider.OAEPParameters"); - ps("AlgorithmParameters", "ChaCha20-Poly1305", - "com.sun.crypto.provider.ChaCha20Poly1305Parameters", - chachaPolyAliases, null); + psA("AlgorithmParameters", "ChaCha20-Poly1305", + "com.sun.crypto.provider.ChaCha20Poly1305Parameters", null); /* * Key factories */ - ps("KeyFactory", "DiffieHellman", + psA("KeyFactory", "DiffieHellman", "com.sun.crypto.provider.DHKeyFactory", - diffieHellmanAliases, null); + null); /* * Secret-key factories */ ps("SecretKeyFactory", "DES", - "com.sun.crypto.provider.DESKeyFactory", - null, null); + "com.sun.crypto.provider.DESKeyFactory"); - ps("SecretKeyFactory", "DESede", - "com.sun.crypto.provider.DESedeKeyFactory", - desEdeAliases, null); + psA("SecretKeyFactory", "DESede", + "com.sun.crypto.provider.DESedeKeyFactory", null); - ps("SecretKeyFactory", "PBEWithMD5AndDES", + psA("SecretKeyFactory", "PBEWithMD5AndDES", "com.sun.crypto.provider.PBEKeyFactory$PBEWithMD5AndDES", - pkcs5MD5_DESAliases, null); + null); /* * Internal in-house crypto algorithm used for @@ -581,85 +503,70 @@ void putEntries() { * algorithm. */ ps("SecretKeyFactory", "PBEWithMD5AndTripleDES", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithMD5AndTripleDES", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithMD5AndTripleDES"); - ps("SecretKeyFactory", "PBEWithSHA1AndDESede", + psA("SecretKeyFactory", "PBEWithSHA1AndDESede", "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndDESede", - pkcs12DESedeAliases, null); + null); - ps("SecretKeyFactory", "PBEWithSHA1AndRC2_40", + psA("SecretKeyFactory", "PBEWithSHA1AndRC2_40", "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_40", - pkcs12RC2_40Aliases, null); + null); - ps("SecretKeyFactory", "PBEWithSHA1AndRC2_128", + psA("SecretKeyFactory", "PBEWithSHA1AndRC2_128", "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC2_128", - pkcs12RC2_128Aliases, null); + null); - ps("SecretKeyFactory", "PBEWithSHA1AndRC4_40", + psA("SecretKeyFactory", "PBEWithSHA1AndRC4_40", "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_40", - pkcs12RC4_40Aliases,null); + null); - ps("SecretKeyFactory", "PBEWithSHA1AndRC4_128", + psA("SecretKeyFactory", "PBEWithSHA1AndRC4_128", "com.sun.crypto.provider.PBEKeyFactory$PBEWithSHA1AndRC4_128", - pkcs12RC4_128Aliases, null); + null); ps("SecretKeyFactory", "PBEWithHmacSHA1AndAES_128", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_128", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_128"); ps("SecretKeyFactory", "PBEWithHmacSHA224AndAES_128", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_128", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_128"); ps("SecretKeyFactory", "PBEWithHmacSHA256AndAES_128", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_128", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_128"); ps("SecretKeyFactory", "PBEWithHmacSHA384AndAES_128", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_128", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_128"); ps("SecretKeyFactory", "PBEWithHmacSHA512AndAES_128", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_128", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_128"); ps("SecretKeyFactory", "PBEWithHmacSHA1AndAES_256", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_256", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA1AndAES_256"); ps("SecretKeyFactory", "PBEWithHmacSHA224AndAES_256", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_256", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA224AndAES_256"); ps("SecretKeyFactory", "PBEWithHmacSHA256AndAES_256", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_256", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA256AndAES_256"); ps("SecretKeyFactory", "PBEWithHmacSHA384AndAES_256", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_256", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA384AndAES_256"); ps("SecretKeyFactory", "PBEWithHmacSHA512AndAES_256", - "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_256", - null, null); + "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_256"); // PBKDF2 - ps("SecretKeyFactory", "PBKDF2WithHmacSHA1", + psA("SecretKeyFactory", "PBKDF2WithHmacSHA1", "com.sun.crypto.provider.PBKDF2Core$HmacSHA1", - pkcs5PBKDF2Aliases, null); + null); ps("SecretKeyFactory", "PBKDF2WithHmacSHA224", - "com.sun.crypto.provider.PBKDF2Core$HmacSHA224", - null, null); + "com.sun.crypto.provider.PBKDF2Core$HmacSHA224"); ps("SecretKeyFactory", "PBKDF2WithHmacSHA256", - "com.sun.crypto.provider.PBKDF2Core$HmacSHA256", - null, null); + "com.sun.crypto.provider.PBKDF2Core$HmacSHA256"); ps("SecretKeyFactory", "PBKDF2WithHmacSHA384", - "com.sun.crypto.provider.PBKDF2Core$HmacSHA384", - null, null); + "com.sun.crypto.provider.PBKDF2Core$HmacSHA384"); ps("SecretKeyFactory", "PBKDF2WithHmacSHA512", - "com.sun.crypto.provider.PBKDF2Core$HmacSHA512", - null, null); + "com.sun.crypto.provider.PBKDF2Core$HmacSHA512"); /* * MAC @@ -667,23 +574,20 @@ void putEntries() { attrs.clear(); attrs.put("SupportedKeyFormats", "RAW"); ps("Mac", "HmacMD5", "com.sun.crypto.provider.HmacMD5", null, attrs); - ps("Mac", "HmacSHA1", "com.sun.crypto.provider.HmacSHA1", - macSHA1Aliases, attrs); - ps("Mac", "HmacSHA224", "com.sun.crypto.provider.HmacCore$HmacSHA224", - macSHA224Aliases, attrs); - ps("Mac", "HmacSHA256", "com.sun.crypto.provider.HmacCore$HmacSHA256", - macSHA256Aliases, attrs); - ps("Mac", "HmacSHA384", "com.sun.crypto.provider.HmacCore$HmacSHA384", - macSHA384Aliases, attrs); - ps("Mac", "HmacSHA512", "com.sun.crypto.provider.HmacCore$HmacSHA512", - macSHA512Aliases, attrs); - // TODO: aliases with OIDs - ps("Mac", "HmacSHA512/224", - "com.sun.crypto.provider.HmacCore$HmacSHA512_224", - null, attrs); - ps("Mac", "HmacSHA512/256", - "com.sun.crypto.provider.HmacCore$HmacSHA512_256", - null, attrs); + psA("Mac", "HmacSHA1", "com.sun.crypto.provider.HmacSHA1", + attrs); + psA("Mac", "HmacSHA224", + "com.sun.crypto.provider.HmacCore$HmacSHA224", attrs); + psA("Mac", "HmacSHA256", + "com.sun.crypto.provider.HmacCore$HmacSHA256", attrs); + psA("Mac", "HmacSHA384", + "com.sun.crypto.provider.HmacCore$HmacSHA384", attrs); + psA("Mac", "HmacSHA512", + "com.sun.crypto.provider.HmacCore$HmacSHA512", attrs); + psA("Mac", "HmacSHA512/224", + "com.sun.crypto.provider.HmacCore$HmacSHA512_224", attrs); + psA("Mac", "HmacSHA512/256", + "com.sun.crypto.provider.HmacCore$HmacSHA512_256", attrs); ps("Mac", "HmacPBESHA1", "com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA1", null, attrs); @@ -727,8 +631,7 @@ void putEntries() { * KeyStore */ ps("KeyStore", "JCEKS", - "com.sun.crypto.provider.JceKeyStore", - null, null); + "com.sun.crypto.provider.JceKeyStore"); /* * SSL/TLS mechanisms @@ -739,24 +642,22 @@ void putEntries() { * mechanisms, and it will cause calls to come here. */ ps("KeyGenerator", "SunTlsPrf", - "com.sun.crypto.provider.TlsPrfGenerator$V10", - null, null); + "com.sun.crypto.provider.TlsPrfGenerator$V10"); ps("KeyGenerator", "SunTls12Prf", - "com.sun.crypto.provider.TlsPrfGenerator$V12", - null, null); + "com.sun.crypto.provider.TlsPrfGenerator$V12"); ps("KeyGenerator", "SunTlsMasterSecret", "com.sun.crypto.provider.TlsMasterSecretGenerator", - createAliases("SunTls12MasterSecret", - "SunTlsExtendedMasterSecret"), null); + List.of("SunTls12MasterSecret", "SunTlsExtendedMasterSecret"), + null); ps("KeyGenerator", "SunTlsKeyMaterial", "com.sun.crypto.provider.TlsKeyMaterialGenerator", - createAliases("SunTls12KeyMaterial"), null); + List.of("SunTls12KeyMaterial"), null); ps("KeyGenerator", "SunTlsRsaPremasterSecret", "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator", - createAliases("SunTls12RsaPremasterSecret"), null); + List.of("SunTls12RsaPremasterSecret"), null); } // Return the instance of this class or create one if needed. diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index a2acf6fc946..8b1575298e9 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,10 +54,14 @@ * http://www.unicode.org. *

* The Java SE 11 Platform uses character information from version 10.0 - * of the Unicode Standard, with an extension. The Java SE 11 Platform allows - * an implementation of class {@code Character} to use the Japanese Era - * code point, {@code U+32FF}, from the first version of the Unicode Standard - * after 10.0 that assigns the code point. Consequently, the behavior of + * of the Unicode Standard, with two extensions. First, the Java SE 11 Platform + * allows an implementation of class {@code Character} to use the code points + * in the range of {@code U+9FEB} to {@code U+9FEF} from the Unicode Standard + * version 11.0, in order for the class to allow the "Implementation Level 2" + * of the Chinese GB18030-2022 standard. Second, the Java SE 11 Platform + * allows an implementation of class {@code Character} to use the Japanese Era + * code point, {@code U+32FF}, from the Unicode Standard version 12.1. + * Consequently, the behavior of * fields and methods of class {@code Character} may vary across * implementations of the Java SE 11 Platform when processing the * aforementioned code point ( outside of version 10.0 ), except for @@ -5402,8 +5406,8 @@ public static enum UnicodeScript { 0x3400, // 3400..4DB5; HAN 0x4DB6, // 4DB6..4DBF; UNKNOWN 0x4DC0, // 4DC0..4DFF; COMMON - 0x4E00, // 4E00..9FEA; HAN - 0x9FEB, // 9FEB..9FFF; UNKNOWN + 0x4E00, // 4E00..9FEF; HAN + 0x9FF0, // 9FF0..9FFF; UNKNOWN 0xA000, // A000..A48C; YI 0xA48D, // A48D..A48F; UNKNOWN 0xA490, // A490..A4C6; YI @@ -6919,8 +6923,8 @@ public static enum UnicodeScript { HAN, // 3400..4DB5 UNKNOWN, // 4DB6..4DBF COMMON, // 4DC0..4DFF - HAN, // 4E00..9FEA - UNKNOWN, // 9FEB..9FFF + HAN, // 4E00..9FEF + UNKNOWN, // 9FF0..9FFF YI, // A000..A48C UNKNOWN, // A48D..A48F YI, // A490..A4C6 diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index fa51f79aa01..d1a298b1ebf 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -406,6 +406,11 @@ private static String nameAndId(ClassLoader ld) { return nid; } + // Returns nameAndId string for exception message printing + String nameAndId() { + return nameAndId; + } + /** * Creates a new class loader of the specified name and using the * specified parent class loader for delegation. diff --git a/src/java.base/share/classes/java/lang/ProcessBuilder.java b/src/java.base/share/classes/java/lang/ProcessBuilder.java index 0b145b4e918..266a3a641ff 100644 --- a/src/java.base/share/classes/java/lang/ProcessBuilder.java +++ b/src/java.base/share/classes/java/lang/ProcessBuilder.java @@ -1097,8 +1097,8 @@ private Process start(Redirect[] redirects) throws IOException { String dir = directory == null ? null : directory.toString(); - for (int i = 1; i < cmdarray.length; i++) { - if (cmdarray[i].indexOf('\u0000') >= 0) { + for (String s : cmdarray) { + if (s.indexOf('\u0000') >= 0) { throw new IOException("invalid null character in command"); } } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 315b7825555..80044096f9d 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -631,6 +631,9 @@ public static native void arraycopy(Object src, int srcPos, * Java Runtime Environment specification version, whose value is * the {@linkplain Runtime.Version#feature feature} element of the * {@linkplain Runtime#version() runtime version} + * {@code java.specification.maintenance.version} + * Java Runtime Environment specification maintenance version, + * may be interpreted as a positive integer (optional, see below) * {@code java.specification.vendor} * Java Runtime Environment specification vendor * {@code java.specification.name} @@ -667,6 +670,16 @@ public static native void arraycopy(Object src, int srcPos, * * *

+ * The {@code java.specification.maintenance.version} property is + * defined if the specification implemented by this runtime at the + * time of its construction had undergone a maintenance + * release. When defined, its value identifies that + * maintenance release. To indicate the first maintenance release + * this property will have the value {@code "1"}, to indicate the + * second maintenance release this property will have the value + * {@code "2"}, and so on. + *

* Multiple paths in a system property value are separated by the path * separator character of the platform. *

@@ -2209,6 +2222,9 @@ public byte[] getBytesUTF8NoRepl(String s) { return StringCoding.getBytesUTF8NoRepl(s); } + public String getLoaderNameID(ClassLoader loader) { + return loader.nameAndId(); + } }); } } diff --git a/src/java.base/share/classes/java/lang/reflect/Proxy.java b/src/java.base/share/classes/java/lang/reflect/Proxy.java index 456527dd19b..268b6da1772 100644 --- a/src/java.base/share/classes/java/lang/reflect/Proxy.java +++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java @@ -44,6 +44,8 @@ import jdk.internal.loader.BootLoader; import jdk.internal.module.Modules; +import jdk.internal.misc.JavaLangAccess; +import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; import jdk.internal.reflect.CallerSensitive; @@ -469,6 +471,7 @@ private static void checkProxyAccess(Class caller, */ private static final class ProxyBuilder { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); // prefix for all proxy class names private static final String proxyClassNamePrefix = "$Proxy"; @@ -856,7 +859,7 @@ private static void ensureVisible(ClassLoader ld, Class c) { } if (type != c) { throw new IllegalArgumentException(c.getName() + - " referenced from a method is not visible from class loader"); + " referenced from a method is not visible from class loader: " + JLA.getLoaderNameID(ld)); } } diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index 19f94fdc106..c739ddf89fb 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -926,6 +926,7 @@ private static final class PlatformNameService implements NameService { public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException { + validate(host); return impl.lookupAllHostAddr(host); } @@ -1315,6 +1316,7 @@ private static InetAddress[] getAllByName(String host, InetAddress reqAddr) return ret; } + validate(host); boolean ipv6Expected = false; if (host.charAt(0) == '[') { // This is supposed to be an IPv6 literal @@ -1322,44 +1324,45 @@ private static InetAddress[] getAllByName(String host, InetAddress reqAddr) host = host.substring(1, host.length() -1); ipv6Expected = true; } else { - // This was supposed to be a IPv6 address, but it's not! - throw new UnknownHostException(host + ": invalid IPv6 address"); + // This was supposed to be a IPv6 literal, but it's not + throw invalidIPv6LiteralException(host, false); } } - // if host is an IP address, we won't do further lookup + // Check and try to parse host string as an IP address literal if (IPAddressUtil.digit(host.charAt(0), 16) != -1 || (host.charAt(0) == ':')) { - byte[] addr; + byte[] addr = null; int numericZone = -1; String ifname = null; - // see if it is IPv4 address - try { - addr = IPAddressUtil.validateNumericFormatV4(host); - } catch (IllegalArgumentException iae) { - var uhe = new UnknownHostException(host); - uhe.initCause(iae); - throw uhe; + + if (!ipv6Expected) { + // check if it is IPv4 address only if host is not wrapped in '[]' + try { + addr = IPAddressUtil.validateNumericFormatV4(host); + } catch (IllegalArgumentException iae) { + var uhe = new UnknownHostException(host); + uhe.initCause(iae); + throw uhe; + } } if (addr == null) { - // This is supposed to be an IPv6 literal - // Check if a numeric or string zone id is present + // Try to parse host string as an IPv6 literal + // Check if a numeric or string zone id is present first int pos; - if ((pos=host.indexOf ('%')) != -1) { - numericZone = checkNumericZone (host); + if ((pos = host.indexOf('%')) != -1) { + numericZone = checkNumericZone(host); if (numericZone == -1) { /* remainder of string must be an ifname */ - ifname = host.substring (pos+1); + ifname = host.substring(pos + 1); } } - if ((addr = IPAddressUtil.textToNumericFormatV6(host)) == null && host.contains(":")) { - throw new UnknownHostException(host + ": invalid IPv6 address"); + if ((addr = IPAddressUtil.textToNumericFormatV6(host)) == null && + (host.contains(":") || ipv6Expected)) { + throw invalidIPv6LiteralException(host, ipv6Expected); } - } else if (ipv6Expected) { - // Means an IPv4 litteral between brackets! - throw new UnknownHostException("["+host+"]"); } - InetAddress[] ret = new InetAddress[1]; if(addr != null) { + InetAddress[] ret = new InetAddress[1]; if (addr.length == Inet4Address.INADDRSZ) { if (numericZone != -1 || ifname != null) { // IPv4-mapped address must not contain zone-id @@ -1376,12 +1379,18 @@ private static InetAddress[] getAllByName(String host, InetAddress reqAddr) return ret; } } else if (ipv6Expected) { - // We were expecting an IPv6 Litteral, but got something else - throw new UnknownHostException("["+host+"]"); + // We were expecting an IPv6 Literal since host string starts + // and ends with square brackets, but we got something else. + throw invalidIPv6LiteralException(host, true); } return getAllByName0(host, reqAddr, true, true); } + private static UnknownHostException invalidIPv6LiteralException(String host, boolean wrapInBrackets) { + String hostString = wrapInBrackets ? "[" + host + "]" : host; + return new UnknownHostException(hostString + ": invalid IPv6 address literal"); + } + /** * Returns the loopback address. *

@@ -1781,6 +1790,12 @@ private void writeObject (ObjectOutputStream s) throws pf.put("family", holder().getFamily()); s.writeFields(); } + + private static void validate(String host) throws UnknownHostException { + if (host.indexOf(0) != -1) { + throw new UnknownHostException("NUL character not allowed in hostname"); + } + } } /* diff --git a/src/java.base/share/classes/java/net/URI.java b/src/java.base/share/classes/java/net/URI.java index 863bb771350..7d1bf935363 100644 --- a/src/java.base/share/classes/java/net/URI.java +++ b/src/java.base/share/classes/java/net/URI.java @@ -3193,6 +3193,7 @@ private int parseAuthority(int start, int n) boolean serverChars; boolean regChars; + boolean skipParseException; if (scan(p, n, "]") > p) { // contains a literal IPv6 address, therefore % is allowed @@ -3208,15 +3209,28 @@ private int parseAuthority(int start, int n) return n; } + // When parsing a URI, skip creating exception objects if the server-based + // authority is not required and the registry parse is successful. + // + skipParseException = (!requireServerAuthority && regChars); if (serverChars) { // Might be (probably is) a server-based authority, so attempt // to parse it as such. If the attempt fails, try to treat it // as a registry-based authority. try { - q = parseServer(p, n); - if (q < n) - failExpecting("end of authority", q); - authority = input.substring(p, n); + q = parseServer(p, n, skipParseException); + if (q < n) { + if (skipParseException) { + userInfo = null; + host = null; + port = -1; + q = p; + } else { + failExpecting("end of authority", q); + } + } else { + authority = input.substring(p, n); + } } catch (URISyntaxException x) { // Undo results of failed parse userInfo = null; @@ -3254,7 +3268,7 @@ private int parseAuthority(int start, int n) // [@][:] // - private int parseServer(int start, int n) + private int parseServer(int start, int n, boolean skipParseException) throws URISyntaxException { int p = start; @@ -3294,7 +3308,7 @@ private int parseServer(int start, int n) } else { q = parseIPv4Address(p, n); if (q <= p) - q = parseHostname(p, n); + q = parseHostname(p, n, skipParseException); p = q; } @@ -3311,7 +3325,10 @@ private int parseServer(int start, int n) } p = q; } + } else if (p < n && skipParseException) { + return p; } + if (p < n) failExpecting("port number", p); @@ -3416,7 +3433,7 @@ private int parseIPv4Address(int start, int n) { // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum // toplabel = alpha | alpha *( alphanum | "-" ) alphanum // - private int parseHostname(int start, int n) + private int parseHostname(int start, int n, boolean skipParseException) throws URISyntaxException { int p = start; @@ -3444,9 +3461,12 @@ private int parseHostname(int start, int n) p = q; } while (p < n); - if ((p < n) && !at(p, n, ':')) + if ((p < n) && !at(p, n, ':')) { + if (skipParseException) { + return p; + } fail("Illegal character in hostname", p); - + } if (l < 0) failExpecting("hostname", start); diff --git a/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java b/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java index 0be6b0de33a..af8bdd545aa 100644 --- a/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java +++ b/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * 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 @@ import java.security.spec.AlgorithmParameterSpec; import java.util.Objects; +import sun.security.jca.JCAUtil; /** * The {@code AlgorithmParameterGenerator} class is used to generate a @@ -308,7 +309,7 @@ public final Provider getProvider() { * @param size the size (number of bits). */ public final void init(int size) { - paramGenSpi.engineInit(size, new SecureRandom()); + paramGenSpi.engineInit(size, JCAUtil.getDefSecureRandom()); } /** @@ -339,7 +340,7 @@ public final void init(int size, SecureRandom random) { */ public final void init(AlgorithmParameterSpec genParamSpec) throws InvalidAlgorithmParameterException { - paramGenSpi.engineInit(genParamSpec, new SecureRandom()); + paramGenSpi.engineInit(genParamSpec, JCAUtil.getDefSecureRandom()); } /** diff --git a/src/java.base/share/classes/java/security/KeyPairGenerator.java b/src/java.base/share/classes/java/security/KeyPairGenerator.java index eee081ae51e..4c564989482 100644 --- a/src/java.base/share/classes/java/security/KeyPairGenerator.java +++ b/src/java.base/share/classes/java/security/KeyPairGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -373,7 +373,7 @@ void disableFailover() { * supported by this KeyPairGenerator object. */ public void initialize(int keysize) { - initialize(keysize, JCAUtil.getSecureRandom()); + initialize(keysize, JCAUtil.getDefSecureRandom()); } /** @@ -433,7 +433,7 @@ public void initialize(int keysize, SecureRandom random) { */ public void initialize(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { - initialize(params, JCAUtil.getSecureRandom()); + initialize(params, JCAUtil.getDefSecureRandom()); } /** diff --git a/src/java.base/share/classes/java/security/PKCS12Attribute.java b/src/java.base/share/classes/java/security/PKCS12Attribute.java index 4a8ebfb64c0..ffce09318e3 100644 --- a/src/java.base/share/classes/java/security/PKCS12Attribute.java +++ b/src/java.base/share/classes/java/security/PKCS12Attribute.java @@ -76,7 +76,7 @@ public PKCS12Attribute(String name, String value) { // Validate name ObjectIdentifier type; try { - type = new ObjectIdentifier(name); + type = ObjectIdentifier.of(name); } catch (IOException e) { throw new IllegalArgumentException("Incorrect format: name", e); } diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index ad5182e1e7c..b46de492113 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -51,6 +51,9 @@ *

Additional default values of security properties are read from a * system-specific location, if available.

* + * @implNote If the properties file fails to load, the JDK implementation will + * throw an unspecified error when initializing the {@code Security} class. + * * @author Benjamin Renaud * @since 1.1 */ @@ -103,117 +106,27 @@ public Void run() { private static void initialize() { props = new Properties(); - boolean loadedProps = false; boolean overrideAll = false; boolean systemSecPropsEnabled = false; // first load the system properties file // to determine the value of security.overridePropertiesFile File propFile = securityPropFile("java.security"); - if (propFile.exists()) { - InputStream is = null; - try { - FileInputStream fis = new FileInputStream(propFile); - is = new BufferedInputStream(fis); - props.load(is); - loadedProps = true; - - if (sdebug != null) { - sdebug.println("reading security properties file: " + - propFile); - sdebug.println(props.toString()); - } - } catch (IOException e) { - if (sdebug != null) { - sdebug.println("unable to load security properties from " + - propFile); - e.printStackTrace(); - } - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException ioe) { - if (sdebug != null) { - sdebug.println("unable to close input stream"); - } - } - } - } + boolean success = loadProps(propFile, null, false); + if (!success) { + throw new InternalError("Error loading java.security file"); } if ("true".equalsIgnoreCase(props.getProperty ("security.overridePropertiesFile"))) { String extraPropFile = System.getProperty - ("java.security.properties"); + ("java.security.properties"); if (extraPropFile != null && extraPropFile.startsWith("=")) { overrideAll = true; extraPropFile = extraPropFile.substring(1); } - - if (overrideAll) { - props = new Properties(); - if (sdebug != null) { - sdebug.println - ("overriding other security properties files!"); - } - } - - // now load the user-specified file so its values - // will win if they conflict with the earlier values - if (extraPropFile != null) { - BufferedInputStream bis = null; - try { - URL propURL; - - extraPropFile = PropertyExpander.expand(extraPropFile); - propFile = new File(extraPropFile); - if (propFile.exists()) { - propURL = new URL - ("file:" + propFile.getCanonicalPath()); - } else { - propURL = new URL(extraPropFile); - } - bis = new BufferedInputStream(propURL.openStream()); - props.load(bis); - loadedProps = true; - - if (sdebug != null) { - sdebug.println("reading security properties file: " + - propURL); - if (overrideAll) { - sdebug.println - ("overriding other security properties files!"); - } - } - } catch (Exception e) { - if (sdebug != null) { - sdebug.println - ("unable to load security properties from " + - extraPropFile); - e.printStackTrace(); - } - } finally { - if (bis != null) { - try { - bis.close(); - } catch (IOException ioe) { - if (sdebug != null) { - sdebug.println("unable to close input stream"); - } - } - } - } - } - } - - if (!loadedProps) { - initializeStatic(); - if (sdebug != null) { - sdebug.println("unable to load security properties " + - "-- using defaults"); - } + loadProps(null, extraPropFile, overrideAll); } boolean sysUseProps = Boolean.valueOf(System.getProperty(SYS_PROP_SWITCH, "false")); @@ -235,9 +148,7 @@ private static void initialize() { } } - // FIPS support depends on the contents of java.security so - // ensure it has loaded first - if (loadedProps && systemSecPropsEnabled) { + if (systemSecPropsEnabled) { boolean shouldEnable; String sysProp = System.getProperty("com.redhat.fips"); if (sysProp == null) { @@ -273,17 +184,60 @@ private static void initialize() { } } - /* - * Initialize to default values, if /lib/java.security - * is not found. - */ - private static void initializeStatic() { - props.put("security.provider.1", "sun.security.provider.Sun"); - props.put("security.provider.2", "sun.security.rsa.SunRsaSign"); - props.put("security.provider.3", "com.sun.net.ssl.internal.ssl.Provider"); - props.put("security.provider.4", "com.sun.crypto.provider.SunJCE"); - props.put("security.provider.5", "sun.security.jgss.SunProvider"); - props.put("security.provider.6", "com.sun.security.sasl.Provider"); + static boolean loadProps(File masterFile, String extraPropFile, boolean overrideAll) { + InputStream is = null; + try { + if (masterFile != null && masterFile.exists()) { + is = new FileInputStream(masterFile); + } else if (extraPropFile != null) { + extraPropFile = PropertyExpander.expand(extraPropFile); + File propFile = new File(extraPropFile); + URL propURL; + if (propFile.exists()) { + propURL = new URL + ("file:" + propFile.getCanonicalPath()); + } else { + propURL = new URL(extraPropFile); + } + + is = propURL.openStream(); + if (overrideAll) { + props = new Properties(); + if (sdebug != null) { + sdebug.println + ("overriding other security properties files!"); + } + } + } else { + // unexpected + return false; + } + props.load(is); + if (sdebug != null) { + // ExceptionInInitializerError if masterFile.getName() is + // called here (NPE!). Leave as is (and few lines down) + sdebug.println("reading security properties file: " + + masterFile == null ? extraPropFile : "java.security"); + } + return true; + } catch (IOException | PropertyExpander.ExpandException e) { + if (sdebug != null) { + sdebug.println("unable to load security properties from " + + masterFile == null ? extraPropFile : "java.security"); + e.printStackTrace(); + } + return false; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ioe) { + if (sdebug != null) { + sdebug.println("unable to close input stream"); + } + } + } + } } /** diff --git a/src/java.base/share/classes/java/security/SystemConfigurator.java b/src/java.base/share/classes/java/security/SystemConfigurator.java index 90f6dd2ebc0..49bf17ea17f 100644 --- a/src/java.base/share/classes/java/security/SystemConfigurator.java +++ b/src/java.base/share/classes/java/security/SystemConfigurator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Red Hat, Inc. + * Copyright (c) 2019, 2023, Red Hat, Inc. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -77,26 +77,9 @@ public Void run() { * security.useSystemPropertiesFile is true. */ static boolean configureSysProps(Properties props) { - boolean systemSecPropsLoaded = false; - - try (BufferedInputStream bis = - new BufferedInputStream( - new FileInputStream(CRYPTO_POLICIES_JAVA_CONFIG))) { - props.load(bis); - systemSecPropsLoaded = true; - if (sdebug != null) { - sdebug.println("reading system security properties file " + - CRYPTO_POLICIES_JAVA_CONFIG); - sdebug.println(props.toString()); - } - } catch (IOException e) { - if (sdebug != null) { - sdebug.println("unable to load security properties from " + - CRYPTO_POLICIES_JAVA_CONFIG); - e.printStackTrace(); - } - } - return systemSecPropsLoaded; + // now load the system file, if it exists, so its values + // will win if they conflict with the earlier values + return Security.loadProps(null, CRYPTO_POLICIES_JAVA_CONFIG, false); } /* diff --git a/src/java.base/share/classes/java/security/cert/CertificateFactory.java b/src/java.base/share/classes/java/security/cert/CertificateFactory.java index ae4e8bd05c4..40870fce541 100644 --- a/src/java.base/share/classes/java/security/cert/CertificateFactory.java +++ b/src/java.base/share/classes/java/security/cert/CertificateFactory.java @@ -26,14 +26,14 @@ package java.security.cert; import java.io.InputStream; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; import java.security.Provider; import java.security.Security; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; import sun.security.jca.*; import sun.security.jca.GetInstance.Instance; @@ -352,7 +352,9 @@ public final String getType() { public final Certificate generateCertificate(InputStream inStream) throws CertificateException { - return certFacSpi.engineGenerateCertificate(inStream); + Certificate c = certFacSpi.engineGenerateCertificate(inStream); + JCAUtil.tryCommitCertEvent(c); + return c; } /** diff --git a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java index 0bac9a64870..bd8bdb11fb8 100644 --- a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java +++ b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -243,7 +243,7 @@ private void readObject(ObjectInputStream ois) boolean critical = ois.readBoolean(); byte[] extVal = IOUtils.readExactlyNBytes(ois, ois.readInt()); Extension ext = sun.security.x509.Extension.newExtension - (new ObjectIdentifier(oid), critical, extVal); + (ObjectIdentifier.of(oid), critical, extVal); extensions.put(oid, ext); } } diff --git a/src/java.base/share/classes/java/security/cert/X509CertSelector.java b/src/java.base/share/classes/java/security/cert/X509CertSelector.java index 70c77258ad0..2db9cc255dd 100644 --- a/src/java.base/share/classes/java/security/cert/X509CertSelector.java +++ b/src/java.base/share/classes/java/security/cert/X509CertSelector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,11 +31,7 @@ import java.util.*; import javax.security.auth.x500.X500Principal; -import sun.security.util.HexDumpEncoder; -import sun.security.util.Debug; -import sun.security.util.DerInputStream; -import sun.security.util.DerValue; -import sun.security.util.ObjectIdentifier; +import sun.security.util.*; import sun.security.x509.*; /** @@ -88,7 +84,7 @@ public class X509CertSelector implements CertSelector { private static final Debug debug = Debug.getInstance("certpath"); private static final ObjectIdentifier ANY_EXTENDED_KEY_USAGE = - ObjectIdentifier.newInternal(new int[] {2, 5, 29, 37, 0}); + ObjectIdentifier.of(KnownOIDs.anyExtendedKeyUsage); static { CertPathHelperImpl.initialize(); @@ -506,7 +502,7 @@ public void setSubjectPublicKeyAlgID(String oid) throws IOException { if (oid == null) { subjectPublicKeyAlgID = null; } else { - subjectPublicKeyAlgID = new ObjectIdentifier(oid); + subjectPublicKeyAlgID = ObjectIdentifier.of(oid); } } @@ -622,7 +618,7 @@ public void setExtendedKeyUsage(Set keyPurposeSet) throws IOException { Collections.unmodifiableSet(new HashSet<>(keyPurposeSet)); keyPurposeOIDSet = new HashSet<>(); for (String s : this.keyPurposeSet) { - keyPurposeOIDSet.add(new ObjectIdentifier(s)); + keyPurposeOIDSet.add(ObjectIdentifier.of(s)); } } } @@ -1105,8 +1101,8 @@ public void setPolicy(Set certPolicySet) throws IOException { if (!(o instanceof String)) { throw new IOException("non String in certPolicySet"); } - polIdVector.add(new CertificatePolicyId(new ObjectIdentifier( - (String)o))); + polIdVector.add(new CertificatePolicyId + (ObjectIdentifier.of((String)o))); } // If everything went OK, make the changes policySet = tempSet; diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 267bd3ee9cd..b8a40634faf 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -4234,8 +4234,10 @@ public int parse(DateTimeParseContext context, CharSequence text, int position) if (length >= position + 3 && context.charEquals(text.charAt(position + 2), 'C')) { // There are localized zone texts that start with "UTC", e.g. // "UTC\u221210:00" (MINUS SIGN instead of HYPHEN-MINUS) in French. - // Exclude those ZoneText cases. - if (!(this instanceof ZoneTextPrinterParser)) { + // Exclude those cases. + if (length == position + 3 || + context.charEquals(text.charAt(position + 3), '+') || + context.charEquals(text.charAt(position + 3), '-')) { return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO); } } else { diff --git a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java index 4c8e9b159fc..4015d5697d5 100644 --- a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java +++ b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java @@ -1807,6 +1807,8 @@ public boolean block() { * interrupted. */ private Object waitingGet(boolean interruptible) { + if (interruptible && Thread.interrupted()) + return null; Signaller q = null; boolean queued = false; Object r; @@ -1818,25 +1820,25 @@ private Object waitingGet(boolean interruptible) { } else if (!queued) queued = tryPushStack(q); + else if (interruptible && q.interrupted) { + q.thread = null; + cleanStack(); + return null; + } else { try { ForkJoinPool.managedBlock(q); } catch (InterruptedException ie) { // currently cannot happen q.interrupted = true; } - if (q.interrupted && interruptible) - break; } } - if (q != null && queued) { + if (q != null) { q.thread = null; - if (!interruptible && q.interrupted) + if (q.interrupted) Thread.currentThread().interrupt(); - if (r == null) - cleanStack(); } - if (r != null || (r = result) != null) - postComplete(); + postComplete(); return r; } @@ -1845,45 +1847,49 @@ else if (!queued) * throws TimeoutException on timeout. */ private Object timedGet(long nanos) throws TimeoutException { - if (Thread.interrupted()) - return null; - if (nanos > 0L) { - long d = System.nanoTime() + nanos; - long deadline = (d == 0L) ? 1L : d; // avoid 0 - Signaller q = null; - boolean queued = false; - Object r; - while ((r = result) == null) { // similar to untimed - if (q == null) { - q = new Signaller(true, nanos, deadline); - if (Thread.currentThread() instanceof ForkJoinWorkerThread) - ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q); - } - else if (!queued) - queued = tryPushStack(q); - else if (q.nanos <= 0L) - break; - else { - try { - ForkJoinPool.managedBlock(q); - } catch (InterruptedException ie) { - q.interrupted = true; - } - if (q.interrupted) - break; - } + long d = System.nanoTime() + nanos; + long deadline = (d == 0L) ? 1L : d; // avoid 0 + boolean interrupted = false, queued = false; + Signaller q = null; + Object r = null; + for (;;) { // order of checking interrupt, result, timeout matters + if (interrupted || (interrupted = Thread.interrupted())) + break; + else if ((r = result) != null) + break; + else if (nanos <= 0L) + break; + else if (q == null) { + q = new Signaller(true, nanos, deadline); + if (Thread.currentThread() instanceof ForkJoinWorkerThread) + ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q); } - if (q != null && queued) { - q.thread = null; - if (r == null) - cleanStack(); + else if (!queued) + queued = tryPushStack(q); + else { + try { + ForkJoinPool.managedBlock(q); + interrupted = q.interrupted; + nanos = q.nanos; + } catch (InterruptedException ie) { + interrupted = true; + } } - if (r != null || (r = result) != null) - postComplete(); - if (r != null || (q != null && q.interrupted)) - return r; } - throw new TimeoutException(); + if (q != null) { + q.thread = null; + if (r == null) + cleanStack(); + } + if (r != null) { + if (interrupted) + Thread.currentThread().interrupt(); + postComplete(); + return r; + } else if (interrupted) + return null; + else + throw new TimeoutException(); } /* ------------- public methods -------------- */ diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index 56c52fb4ffa..bb969b5d2bd 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -2852,22 +2852,20 @@ private final void unlockRoot() { * Possibly blocks awaiting root lock. */ private final void contendedLock() { - boolean waiting = false; + Thread current = Thread.currentThread(), w; for (int s;;) { if (((s = lockState) & ~WAITER) == 0) { if (U.compareAndSetInt(this, LOCKSTATE, s, WRITER)) { - if (waiting) - waiter = null; + if (waiter == current) + U.compareAndSetObject(this, WAITERTHREAD, current, null); return; } } - else if ((s & WAITER) == 0) { - if (U.compareAndSetInt(this, LOCKSTATE, s, s | WAITER)) { - waiting = true; - waiter = Thread.currentThread(); - } - } - else if (waiting) + else if ((s & WAITER) == 0) + U.compareAndSetInt(this, LOCKSTATE, s, s | WAITER); + else if ((w = waiter) == null) + U.compareAndSetObject(this, WAITERTHREAD, null, current); + else if (w == current) LockSupport.park(this); } } @@ -3287,6 +3285,8 @@ static boolean checkInvariants(TreeNode t) { private static final Unsafe U = Unsafe.getUnsafe(); private static final long LOCKSTATE = U.objectFieldOffset(TreeBin.class, "lockState"); + private static final long WAITERTHREAD + = U.objectFieldOffset(TreeBin.class, "waiter"); } /* ----------------Table Traversal -------------- */ diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java index c902a53d950..cb7e308e0d7 100644 --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -153,8 +153,6 @@ class JarFile extends ZipFile { private static final boolean MULTI_RELEASE_ENABLED; private static final boolean MULTI_RELEASE_FORCED; private static final ThreadLocal isInitializing = new ThreadLocal<>(); - // The maximum size of array to allocate. Some VMs reserve some header words in an array. - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private SoftReference manRef; private JarEntry manEntry; @@ -807,8 +805,11 @@ private void initializeVerifier() { private byte[] getBytes(ZipEntry ze) throws IOException { try (InputStream is = super.getInputStream(ze)) { long uncompressedSize = ze.getSize(); - if (uncompressedSize > MAX_ARRAY_SIZE) { - throw new IOException("Unsupported size: " + uncompressedSize); + if (uncompressedSize > SignatureFileVerifier.MAX_SIG_FILE_SIZE) { + throw new IOException("Unsupported size: " + uncompressedSize + + " for JarEntry " + ze.getName() + + ". Allowed max size: " + + SignatureFileVerifier.MAX_SIG_FILE_SIZE + " bytes"); } int len = (int)uncompressedSize; int bytesRead; diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 4e90503dd1b..85da9318815 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -66,6 +66,8 @@ import jdk.internal.ref.CleanerFactory; import jdk.internal.vm.annotation.Stable; import sun.nio.cs.UTF_8; +import sun.security.action.GetBooleanAction; +import java.security.AccessController; import static java.util.zip.ZipConstants64.*; import static java.util.zip.ZipUtils.*; @@ -117,6 +119,13 @@ class ZipFile implements ZipConstants, Closeable { */ public static final int OPEN_READ = 0x1; + /** + * Flag which specifies whether the validation of the Zip64 extra + * fields should be disabled + */ + private static final boolean disableZip64ExtraFieldValidation = + AccessController.doPrivileged + (new GetBooleanAction("jdk.util.zip.disableZip64ExtraFieldValidation")); /** * Mode flag to open a zip file and mark it for deletion. The file will be * deleted some time between the moment that it is opened and the moment @@ -1211,6 +1220,119 @@ private int addEntry(int index, int hash, int next, int pos) { entries[index++] = pos; return index; } + + /** + * Validate the Zip64 Extra block fields + * @param startingOffset Extra Field starting offset within the CEN + * @param extraFieldLen Length of this Extra field + * @throws ZipException If an error occurs validating the Zip64 Extra + * block + */ + private void checkExtraFields(int cenPos, int startingOffset, + int extraFieldLen) throws ZipException { + // Extra field Length cannot exceed 65,535 bytes per the PKWare + // APP.note 4.4.11 + if (extraFieldLen > 0xFFFF) { + zerror("invalid extra field length"); + } + // CEN Offset where this Extra field ends + int extraEndOffset = startingOffset + extraFieldLen; + if (extraEndOffset > cen.length) { + zerror("Invalid CEN header (extra data field size too long)"); + } + int currentOffset = startingOffset; + while (currentOffset < extraEndOffset) { + int tag = get16(cen, currentOffset); + currentOffset += Short.BYTES; + + int tagBlockSize = get16(cen, currentOffset); + int tagBlockEndingOffset = currentOffset + tagBlockSize; + + // The ending offset for this tag block should not go past the + // offset for the end of the extra field + if (tagBlockEndingOffset > extraEndOffset) { + zerror("Invalid CEN header (invalid zip64 extra data field size)"); + } + currentOffset += Short.BYTES; + + if (tag == ZIP64_EXTID) { + // Get the compressed size; + long csize = CENSIZ(cen, cenPos); + // Get the uncompressed size; + long size = CENLEN(cen, cenPos); + checkZip64ExtraFieldValues(currentOffset, tagBlockSize, + csize, size); + } + currentOffset += tagBlockSize; + } + } + + /** + * Validate the Zip64 Extended Information Extra Field (0x0001) block + * size and that the uncompressed size and compressed size field + * values are not negative. + * Note: As we do not use the LOC offset or Starting disk number + * field value we will not validate them + * @param off the starting offset for the Zip64 field value + * @param blockSize the size of the Zip64 Extended Extra Field + * @param csize CEN header compressed size value + * @param size CEN header uncompressed size value + * @throws ZipException if an error occurs + */ + private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, + long size) + throws ZipException { + byte[] cen = this.cen; + // Validate the Zip64 Extended Information Extra Field (0x0001) + // length. + if (!isZip64ExtBlockSizeValid(blockSize)) { + zerror("Invalid CEN header (invalid zip64 extra data field size)"); + } + // Check the uncompressed size is not negative + // Note we do not need to check blockSize is >= 8 as + // we know its length is at least 8 from the call to + // isZip64ExtBlockSizeValid() + if ((size == ZIP64_MAGICVAL)) { + if(get64(cen, off) < 0) { + zerror("Invalid zip64 extra block size value"); + } + } + // Check the compressed size is not negative + if ((csize == ZIP64_MAGICVAL) && (blockSize >= 16)) { + if (get64(cen, off + 8) < 0) { + zerror("Invalid zip64 extra block compressed size value"); + } + } + } + + /** + * Validate the size and contents of a Zip64 extended information field + * The order of the Zip64 fields is fixed, but the fields MUST + * only appear if the corresponding LOC or CEN field is set to 0xFFFF: + * or 0xFFFFFFFF: + * Uncompressed Size - 8 bytes + * Compressed Size - 8 bytes + * LOC Header offset - 8 bytes + * Disk Start Number - 4 bytes + * See PKWare APP.Note Section 4.5.3 for more details + * + * @param blockSize the Zip64 Extended Information Extra Field size + * @return true if the extra block size is valid; false otherwise + */ + private static boolean isZip64ExtBlockSizeValid(int blockSize) { + /* + * As the fields must appear in order, the block size indicates which + * fields to expect: + * 8 - uncompressed size + * 16 - uncompressed size, compressed size + * 24 - uncompressed size, compressed sise, LOC Header offset + * 28 - uncompressed size, compressed sise, LOC Header offset, + * and Disk start number + */ + int i = blockSize; + return i == 8 || i == 16 || i == 24 || i == 28 ? true : false; + } + private int getEntryHash(int index) { return entries[index]; } private int getEntryNext(int index) { return entries[index + 1]; } private int getEntryPos(int index) { return entries[index + 2]; } @@ -1571,6 +1693,13 @@ private void initCEN(int knownTotal, ZipCoder zc) throws IOException { } else { checkEncoding(zc, cen, pos + CENHDR, nlen); } + if (elen > 0 && !disableZip64ExtraFieldValidation) { + long extraStartingOffset = pos + CENHDR + nlen; + if ((int)extraStartingOffset != extraStartingOffset) { + zerror("invalid CEN header (bad extra offset)"); + } + checkExtraFields(pos, (int)extraStartingOffset, elen); + } // Record the CEN offset and the name hash in our hash cell. hash = hashN(cen, pos + CENHDR, nlen); hsh = (hash & 0x7fffffff) % tablelen; diff --git a/src/java.base/share/classes/javax/crypto/Cipher.java b/src/java.base/share/classes/javax/crypto/Cipher.java index fded2e1c12a..b6194a8715b 100644 --- a/src/java.base/share/classes/javax/crypto/Cipher.java +++ b/src/java.base/share/classes/javax/crypto/Cipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1220,7 +1220,7 @@ private static String getOpmodeString(int opmode) { * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Key key) throws InvalidKeyException { - init(opmode, key, JceSecurity.RANDOM); + init(opmode, key, JCAUtil.getDefSecureRandom()); } /** @@ -1361,7 +1361,7 @@ public final void init(int opmode, Key key, SecureRandom random) public final void init(int opmode, Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { - init(opmode, key, params, JceSecurity.RANDOM); + init(opmode, key, params, JCAUtil.getDefSecureRandom()); } /** @@ -1504,7 +1504,7 @@ public final void init(int opmode, Key key, AlgorithmParameterSpec params, public final void init(int opmode, Key key, AlgorithmParameters params) throws InvalidKeyException, InvalidAlgorithmParameterException { - init(opmode, key, params, JceSecurity.RANDOM); + init(opmode, key, params, JCAUtil.getDefSecureRandom()); } /** @@ -1652,7 +1652,7 @@ public final void init(int opmode, Key key, AlgorithmParameters params, public final void init(int opmode, Certificate certificate) throws InvalidKeyException { - init(opmode, certificate, JceSecurity.RANDOM); + init(opmode, certificate, JCAUtil.getDefSecureRandom()); } /** diff --git a/src/java.base/share/classes/javax/crypto/JceSecurity.java.template b/src/java.base/share/classes/javax/crypto/JceSecurity.java.template index fa89313cc1b..157224796ae 100644 --- a/src/java.base/share/classes/javax/crypto/JceSecurity.java.template +++ b/src/java.base/share/classes/javax/crypto/JceSecurity.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,11 +74,8 @@ import sun.security.util.Debug; final class JceSecurity { - private static final Debug debug = Debug.getInstance("jca"); - static final SecureRandom RANDOM = new SecureRandom(); - // The defaultPolicy and exemptPolicy will be set up // in the static initializer. private static CryptoPermissions defaultPolicy = null; @@ -228,7 +225,11 @@ final class JceSecurity { // return whether this provider is properly signed and can be used by JCE static boolean canUseProvider(Provider p) { - return getVerificationResult(p) == null; + Exception e = getVerificationResult(p); + if (debug != null && e != null) { + debug.println("Provider verification result: " + e); + } + return e == null; } // dummy object to represent null diff --git a/src/java.base/share/classes/javax/crypto/KeyAgreement.java b/src/java.base/share/classes/javax/crypto/KeyAgreement.java index 3f77eddccc8..ad38418363b 100644 --- a/src/java.base/share/classes/javax/crypto/KeyAgreement.java +++ b/src/java.base/share/classes/javax/crypto/KeyAgreement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -446,7 +446,7 @@ public final Provider getProvider() { * has an incompatible algorithm type. */ public final void init(Key key) throws InvalidKeyException { - init(key, JceSecurity.RANDOM); + init(key, JCAUtil.getDefSecureRandom()); } /** @@ -514,7 +514,7 @@ public final void init(Key key, SecureRandom random) public final void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { - init(key, params, JceSecurity.RANDOM); + init(key, params, JCAUtil.getDefSecureRandom()); } private String getProviderName() { diff --git a/src/java.base/share/classes/javax/crypto/KeyGenerator.java b/src/java.base/share/classes/javax/crypto/KeyGenerator.java index b0d4de7775c..2f8af71cec5 100644 --- a/src/java.base/share/classes/javax/crypto/KeyGenerator.java +++ b/src/java.base/share/classes/javax/crypto/KeyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -450,7 +450,7 @@ public final void init(SecureRandom random) { public final void init(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { - init(params, JceSecurity.RANDOM); + init(params, JCAUtil.getDefSecureRandom()); } /** @@ -514,7 +514,7 @@ public final void init(AlgorithmParameterSpec params, SecureRandom random) * supported. */ public final void init(int keysize) { - init(keysize, JceSecurity.RANDOM); + init(keysize, JCAUtil.getDefSecureRandom()); } /** diff --git a/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java b/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java index f0b0dc33832..7f70c613d49 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java @@ -210,10 +210,9 @@ protected SSLParameters engineGetDefaultSSLParameters() { */ protected SSLParameters engineGetSupportedSSLParameters() { SSLSocket socket = getDefaultSocket(); - SSLParameters params = new SSLParameters(); + SSLParameters params = socket.getSSLParameters(); params.setCipherSuites(socket.getSupportedCipherSuites()); params.setProtocols(socket.getSupportedProtocols()); return params; } - } diff --git a/src/java.base/share/classes/jdk/internal/event/X509CertificateEvent.java b/src/java.base/share/classes/jdk/internal/event/X509CertificateEvent.java index b145abceb69..5b668ca2a05 100644 --- a/src/java.base/share/classes/jdk/internal/event/X509CertificateEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/X509CertificateEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,15 @@ */ public final class X509CertificateEvent extends Event { + private static final X509CertificateEvent EVENT = new X509CertificateEvent(); + + /** + * Returns {@code true} if event is enabled, {@code false} otherwise. + */ + public static boolean isTurnedOn() { + return EVENT.isEnabled(); + } + public String algorithm; public String serialNumber; public String subject; diff --git a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java index 2fb19ecd513..5e3a00c9858 100644 --- a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java @@ -305,4 +305,10 @@ public interface JavaLangAccess { * @throws IllegalArgumentException for malformed surrogates */ byte[] getBytesUTF8NoRepl(String s); + + /** + * Returns '' @ if classloader has a name + * explicitly set otherwise @ + */ + String getLoaderNameID(ClassLoader loader); } diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index eefb5bb712b..e8b7dd4693a 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,6 +87,7 @@ import jdk.internal.module.Modules; import jdk.internal.platform.Container; import jdk.internal.platform.Metrics; +import sun.util.calendar.ZoneInfoFile; public final class LauncherHelper { @@ -280,6 +281,8 @@ private static void printLocale() { Locale.getDefault(Category.DISPLAY).getDisplayName()); ostream.println(INDENT + "default format locale = " + Locale.getDefault(Category.FORMAT).getDisplayName()); + ostream.println(INDENT + "tzdata version = " + + ZoneInfoFile.getVersion()); printLocales(); ostream.println(); } diff --git a/src/java.base/share/classes/sun/net/www/HeaderParser.java b/src/java.base/share/classes/sun/net/www/HeaderParser.java index a4b2ab50497..e6d7435a458 100644 --- a/src/java.base/share/classes/sun/net/www/HeaderParser.java +++ b/src/java.base/share/classes/sun/net/www/HeaderParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.net.www; import java.util.Iterator; +import java.util.OptionalInt; /* This is useful for the nightmare of parsing multi-part HTTP/RFC822 headers * sensibly: @@ -246,6 +247,19 @@ public int findInt(String k, int Default) { return Default; } } + + public OptionalInt findInt(String k) { + try { + String s = findValue(k); + if (s == null) { + return OptionalInt.empty(); + } + return OptionalInt.of(Integer.parseInt(s)); + } catch (Throwable t) { + return OptionalInt.empty(); + } + } + /* public static void main(String[] a) throws Exception { System.out.print("enter line to parse> "); diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java index be1b7916762..3d0deb319cd 100644 --- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java +++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java @@ -29,6 +29,7 @@ import java.net.*; import java.util.Locale; import java.util.Objects; +import java.util.OptionalInt; import java.util.Properties; import sun.net.NetworkClient; import sun.net.ProgressSource; @@ -123,6 +124,7 @@ private static int getDefaultPort(String proto) { * 0: the server specified no keep alive headers * -1: the server provided "Connection: keep-alive" but did not specify a * a particular time in a "Keep-Alive:" headers + * -2: the server provided "Connection: keep-alive" and timeout=0 * Positive values are the number of seconds specified by the server * in a "Keep-Alive" header */ @@ -860,7 +862,23 @@ private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, Http responses.findValue("Keep-Alive")); /* default should be larger in case of proxy */ keepAliveConnections = p.findInt("max", usingProxy?50:5); - keepAliveTimeout = p.findInt("timeout", -1); + if (keepAliveConnections < 0) { + keepAliveConnections = usingProxy?50:5; + } + OptionalInt timeout = p.findInt("timeout"); + if (timeout.isEmpty()) { + keepAliveTimeout = -1; + } else { + keepAliveTimeout = timeout.getAsInt(); + if (keepAliveTimeout < 0) { + // if the server specified a negative (invalid) value + // then we set to -1, which is equivalent to no value + keepAliveTimeout = -1; + } else if (keepAliveTimeout == 0) { + // handled specially to mean close connection immediately + keepAliveTimeout = -2; + } + } } } else if (b[7] != '0') { /* diff --git a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java index 10766f4ccf0..e717d5b8698 100644 --- a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java +++ b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java @@ -113,38 +113,45 @@ public KeepAliveCache() {} * @param url The URL contains info about the host and port * @param http The HttpClient to be cached */ - public synchronized void put(final URL url, Object obj, HttpClient http) { - boolean startThread = (keepAliveTimer == null); - if (!startThread) { - if (!keepAliveTimer.isAlive()) { - startThread = true; - } - } - if (startThread) { - clear(); - /* Unfortunately, we can't always believe the keep-alive timeout we got - * back from the server. If I'm connected through a Netscape proxy - * to a server that sent me a keep-alive - * time of 15 sec, the proxy unilaterally terminates my connection - * The robustness to get around this is in HttpClient.parseHTTP() - */ - final KeepAliveCache cache = this; - AccessController.doPrivileged(new PrivilegedAction<>() { - public Void run() { - keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache); - keepAliveTimer.setDaemon(true); - keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2); - keepAliveTimer.start(); - return null; + public void put(final URL url, Object obj, HttpClient http) { + // this method may need to close an HttpClient, either because + // it is not cacheable, or because the cache is at its capacity. + // In the latter case, we close the least recently used client. + // The client to close is stored in oldClient, and is closed + // after cacheLock is released. + HttpClient oldClient = null; + synchronized (this) { + boolean startThread = (keepAliveTimer == null); + if (!startThread) { + if (!keepAliveTimer.isAlive()) { + startThread = true; } - }); - } + } + if (startThread) { + clear(); + /* Unfortunately, we can't always believe the keep-alive timeout we got + * back from the server. If I'm connected through a Netscape proxy + * to a server that sent me a keep-alive + * time of 15 sec, the proxy unilaterally terminates my connection + * The robustness to get around this is in HttpClient.parseHTTP() + */ + final KeepAliveCache cache = this; + AccessController.doPrivileged(new PrivilegedAction<>() { + public Void run() { + keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache); + keepAliveTimer.setDaemon(true); + keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2); + keepAliveTimer.start(); + return null; + } + }); + } - KeepAliveKey key = new KeepAliveKey(url, obj); - ClientVector v = super.get(key); + KeepAliveKey key = new KeepAliveKey(url, obj); + ClientVector v = super.get(key); - if (v == null) { - int keepAliveTimeout = http.getKeepAliveTimeout(); + if (v == null) { + int keepAliveTimeout = http.getKeepAliveTimeout(); if (keepAliveTimeout == 0) { keepAliveTimeout = getUserKeepAlive(http.getUsingProxy()); if (keepAliveTimeout == -1) { @@ -157,19 +164,26 @@ public Void run() { // different default for server and proxy keepAliveTimeout = http.getUsingProxy() ? 60 : 5; } + } else if (keepAliveTimeout == -2) { + keepAliveTimeout = 0; } // at this point keepAliveTimeout is the number of seconds to keep // alive, which could be 0, if the user specified 0 for the property assert keepAliveTimeout >= 0; if (keepAliveTimeout == 0) { - http.closeServer(); + oldClient = http; } else { v = new ClientVector(keepAliveTimeout * 1000); v.put(http); super.put(key, v); } - } else { - v.put(http); + } else { + oldClient = v.put(http); + } + } + // close after releasing locks + if (oldClient != null) { + oldClient.closeServer(); } } @@ -219,6 +233,7 @@ public void run() { try { Thread.sleep(LIFETIME); } catch (InterruptedException e) {} + List closeList = null; // Remove all outdated HttpClients. synchronized (this) { @@ -228,15 +243,18 @@ public void run() { for (KeepAliveKey key : keySet()) { ClientVector v = get(key); synchronized (v) { - KeepAliveEntry e = v.peek(); + KeepAliveEntry e = v.peekLast(); while (e != null) { if ((currentTime - e.idleStartTime) > v.nap) { - v.poll(); - e.hc.closeServer(); + v.pollLast(); + if (closeList == null) { + closeList = new ArrayList<>(); + } + closeList.add(e.hc); } else { break; } - e = v.peek(); + e = v.peekLast(); } if (v.isEmpty()) { @@ -249,6 +267,12 @@ public void run() { removeVector(key); } } + // close connections outside cacheLock + if (closeList != null) { + for (HttpClient hc : closeList) { + hc.closeServer(); + } + } } while (!isEmpty()); } @@ -266,8 +290,8 @@ private void readObject(ObjectInputStream stream) } } -/* FILO order for recycling HttpClients, should run in a thread - * to time them out. If > maxConns are in use, block. +/* LIFO order for reusing HttpClients. Most recent entries at the front. + * If > maxConns are in use, discard oldest. */ class ClientVector extends ArrayDeque { private static final long serialVersionUID = -8680532108106489459L; @@ -280,36 +304,37 @@ class ClientVector extends ArrayDeque { } synchronized HttpClient get() { - if (isEmpty()) { + // check the most recent connection, use if still valid + KeepAliveEntry e = peekFirst(); + if (e == null) { return null; } - // Loop until we find a connection that has not timed out - HttpClient hc = null; long currentTime = System.currentTimeMillis(); - do { - KeepAliveEntry e = pop(); - if ((currentTime - e.idleStartTime) > nap) { - e.hc.closeServer(); - } else { - hc = e.hc; - if (KeepAliveCache.logger.isLoggable(PlatformLogger.Level.FINEST)) { - String msg = "cached HttpClient was idle for " + if ((currentTime - e.idleStartTime) > nap) { + return null; // all connections stale - will be cleaned up later + } else { + pollFirst(); + if (KeepAliveCache.logger.isLoggable(PlatformLogger.Level.FINEST)) { + String msg = "cached HttpClient was idle for " + Long.toString(currentTime - e.idleStartTime); - KeepAliveCache.logger.finest(msg); - } + KeepAliveCache.logger.finest(msg); } - } while ((hc == null) && (!isEmpty())); - return hc; + return e.hc; + } } /* return a still valid, unused HttpClient */ - synchronized void put(HttpClient h) { + synchronized HttpClient put(HttpClient h) { + HttpClient staleClient = null; + assert KeepAliveCache.getMaxConnections() > 0; if (size() >= KeepAliveCache.getMaxConnections()) { - h.closeServer(); // otherwise the connection remains in limbo - } else { - push(new KeepAliveEntry(h, System.currentTimeMillis())); + // remove oldest connection + staleClient = removeLast().hc; } + addFirst(new KeepAliveEntry(h, System.currentTimeMillis())); + // close after releasing the locks + return staleClient; } /* remove an HttpClient */ @@ -337,10 +362,10 @@ private void readObject(ObjectInputStream stream) } class KeepAliveKey { - private String protocol = null; - private String host = null; - private int port = 0; - private Object obj = null; // additional key, such as socketfactory + private final String protocol; + private final String host; + private final int port; + private final Object obj; // additional key, such as socketfactory /** * Constructor @@ -381,8 +406,8 @@ public int hashCode() { } class KeepAliveEntry { - HttpClient hc; - long idleStartTime; + final HttpClient hc; + final long idleStartTime; KeepAliveEntry(HttpClient hc, long idleStartTime) { this.hc = hc; diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java index f4547a2c47a..1262d075015 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java @@ -517,4 +517,13 @@ private synchronized void writeObject(java.io.ObjectOutputStream s) s2 = new String (pw.getPassword()); s.defaultWriteObject (); } + + /** + * Releases any system or cryptographic resources. + * It is up to implementors to override disposeContext() + * to take necessary action. + */ + public void disposeContext() { + // do nothing + } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 18103847f88..85eee2c3bb6 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -1953,6 +1953,12 @@ private synchronized InputStream getInputStream0() throws IOException { if (serverAuthKey != null) { AuthenticationInfo.endAuthRequest(serverAuthKey); } + if (proxyAuthentication != null) { + proxyAuthentication.disposeContext(); + } + if (serverAuthentication != null) { + serverAuthentication.disposeContext(); + } } } @@ -2182,6 +2188,9 @@ public synchronized void doTunneling() throws IOException { if (proxyAuthKey != null) { AuthenticationInfo.endAuthRequest(proxyAuthKey); } + if (proxyAuthentication != null) { + proxyAuthentication.disposeContext(); + } } // restore original request headers @@ -2280,7 +2289,8 @@ private void setPreemptiveProxyAuthentication(MessageHeader requests) throws IOE * the connection. */ @SuppressWarnings("fallthrough") - private AuthenticationInfo getHttpProxyAuthentication (AuthenticationHeader authhdr) { + private AuthenticationInfo getHttpProxyAuthentication (AuthenticationHeader authhdr) + throws IOException { /* get authorization from authenticator */ AuthenticationInfo ret = null; String raw = authhdr.raw(); @@ -2376,6 +2386,7 @@ public InetAddress run() authenticator, host, null, port, url.getProtocol(), "", scheme, url, RequestorType.PROXY); + validateNTLMCredentials(a); } /* If we are not trying transparent authentication then * we need to have a PasswordAuthentication instance. For @@ -2426,6 +2437,7 @@ public InetAddress run() } if (ret != null) { if (!ret.setHeaders(this, p, raw)) { + ret.disposeContext(); ret = null; } } @@ -2443,7 +2455,8 @@ public InetAddress run() * preferred. */ @SuppressWarnings("fallthrough") - private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) { + private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) + throws IOException { /* get authorization from authenticator */ AuthenticationInfo ret = null; String raw = authhdr.raw(); @@ -2549,6 +2562,7 @@ private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr authenticator, url.getHost(), addr, port, url.getProtocol(), "", scheme, url, RequestorType.SERVER); + validateNTLMCredentials(a); } /* If we are not trying transparent authentication then @@ -2592,6 +2606,7 @@ private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr if (ret != null ) { if (!ret.setHeaders(this, p, raw)) { + ret.disposeContext(); ret = null; } } @@ -2618,6 +2633,7 @@ private void checkResponseCredentials (boolean inClose) throws IOException { DigestAuthentication da = (DigestAuthentication) currentProxyCredentials; da.checkResponse (raw, method, getRequestURI()); + currentProxyCredentials.disposeContext(); currentProxyCredentials = null; } } @@ -2628,6 +2644,7 @@ private void checkResponseCredentials (boolean inClose) throws IOException { DigestAuthentication da = (DigestAuthentication) currentServerCredentials; da.checkResponse (raw, method, url); + currentServerCredentials.disposeContext(); currentServerCredentials = null; } } @@ -3841,6 +3858,27 @@ public void close() throws IOException { } } } + + // ensure there are no null characters in username or password + private static void validateNTLMCredentials(PasswordAuthentication pw) + throws IOException { + + if (pw == null) { + return; + } + char[] password = pw.getPassword(); + if (password != null) { + for (int i=0; i= 1 && + "2000".equals(GetPropertyAction.privilegedGetProperty("jdk.charset.GB18030", "")); + public GB18030() { - super("GB18030", $ALIASES$); + super("GB18030", StandardCharsets.aliases_GB18030()); } public boolean contains(Charset cs) { @@ -1046,7 +1054,8 @@ public CharsetEncoder newEncoder() { "\u1E26\u1E27\u1E28\u1E29\u1E2A\u1E2B\u1E2C\u1E2D"+ "\u1E2E\u1E2F\u1E30\u1E31\u1E32\u1E33\u1E34\u1E35"+ "\u1E36\u1E37\u1E38\u1E39\u1E3A\u1E3B\u1E3C\u1E3D"+ - "\u1E3E\u1E3F\u1E40\u1E41\u1E42\u1E43\u1E44\u1E45"+ + (IS_2000 ? "\u1E3E\u1E3F\u1E40\u1E41\u1E42\u1E43\u1E44\u1E45" : + "\u1E3E\uE7C7\u1E40\u1E41\u1E42\u1E43\u1E44\u1E45")+ "\u1E46\u1E47\u1E48\u1E49\u1E4A\u1E4B\u1E4C\u1E4D"+ "\u1E4E\u1E4F\u1E50\u1E51\u1E52\u1E53\u1E54\u1E55"+ "\u1E56\u1E57\u1E58\u1E59\u1E5A\u1E5B\u1E5C\u1E5D"+ @@ -2502,8 +2511,10 @@ public CharsetEncoder newEncoder() { "\u4DF5\u4DF6\u4DF7\u4DF8\u4DF9\u4DFA\u4DFB\u4DFC"+ "\u4DFD\u4DFE\u4DFF\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ - "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ - "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + (IS_2000 ? "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" : + "\uFFFD\uE81E\uE826\uE82B\uE82C\uE832\uE843\uE854"+ + "\uE864\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD")+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ @@ -2766,8 +2777,10 @@ public CharsetEncoder newEncoder() { "\uFDF4\uFDF5\uFDF6\uFDF7\uFDF8\uFDF9\uFDFA\uFDFB"+ "\uFDFC\uFDFD\uFDFE\uFDFF\uFE00\uFE01\uFE02\uFE03"+ "\uFE04\uFE05\uFE06\uFE07\uFE08\uFE09\uFE0A\uFE0B"+ - "\uFE0C\uFE0D\uFE0E\uFE0F\uFE10\uFE11\uFE12\uFE13"+ - "\uFE14\uFE15\uFE16\uFE17\uFE18\uFE19\uFE1A\uFE1B"+ + (IS_2000 ? "\uFE0C\uFE0D\uFE0E\uFE0F\uFE10\uFE11\uFE12\uFE13"+ + "\uFE14\uFE15\uFE16\uFE17\uFE18\uFE19\uFE1A\uFE1B" : + "\uFE0C\uFE0D\uFE0E\uFE0F\uE78D\uE78F\uE78E\uE790"+ + "\uE791\uE792\uE793\uE794\uE795\uE796\uFE1A\uFE1B")+ "\uFE1C\uFE1D\uFE1E\uFE1F\uFE20\uFE21\uFE22\uFE23"+ "\uFE24\uFE25\uFE26\uFE27\uFE28\uFE29\uFE2A\uFE2B"+ "\uFE2C\uFE2D\uFE2E\uFE2F\uFE32\uFE45\uFE46\uFE47"+ @@ -3773,10 +3786,14 @@ public CharsetEncoder newEncoder() { "\uE78A\uE78B\uE78C\u03B1\u03B2\u03B3\u03B4\u03B5"+ "\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB\u03BC\u03BD"+ "\u03BE\u03BF\u03C0\u03C1\u03C3\u03C4\u03C5\u03C6"+ - "\u03C7\u03C8\u03C9\uE78D\uE78E\uE78F\uE790\uE791"+ - "\uE792\uE793\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40"+ - "\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44\uE794\uE795"+ - "\uFE3B\uFE3C\uFE37\uFE38\uFE31\uE796\uFE33\uFE34"+ + (IS_2000 ? "\u03C7\u03C8\u03C9\uE78D\uE78E\uE78F\uE790\uE791"+ + "\uE792\uE793\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40"+ + "\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44\uE794\uE795"+ + "\uFE3B\uFE3C\uFE37\uFE38\uFE31\uE796\uFE33\uFE34" : + "\u03C7\u03C8\u03C9\uFE10\uFE12\uFE11\uFE13\uFE14"+ + "\uFE15\uFE16\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40"+ + "\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44\uFE17\uFE18"+ + "\uFE3B\uFE3C\uFE37\uFE38\uFE31\uFE19\uFE33\uFE34")+ "\uE797\uE798\uE799\uE79A\uE79B\uE79C\uE79D\uE79E"+ "\uE79F\uE706\uE707\uE708\uE709\uE70A\uE70B\uE70C"+ "\uE70D\uE70E\uE70F\uE710\uE711\uE712\uE713\uE714"+ @@ -3817,7 +3834,8 @@ public CharsetEncoder newEncoder() { "\uE7C6\u0101\u00E1\u01CE\u00E0\u0113\u00E9\u011B"+ "\u00E8\u012B\u00ED\u01D0\u00EC\u014D\u00F3\u01D2"+ "\u00F2\u016B\u00FA\u01D4\u00F9\u01D6\u01D8\u01DA"+ - "\u01DC\u00FC\u00EA\u0251\uE7C7\u0144\u0148\u01F9"+ + (IS_2000 ? "\u01DC\u00FC\u00EA\u0251\uE7C7\u0144\u0148\u01F9" : + "\u01DC\u00FC\u00EA\u0251\u1E3F\u0144\u0148\u01F9")+ "\u0261\uE7C9\uE7CA\uE7CB\uE7CC\u3105\u3106\u3107"+ "\u3108\u3109\u310A\u310B\u310C\u310D\u310E\u310F"+ "\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117"+ @@ -5868,16 +5886,22 @@ public CharsetEncoder newEncoder() { "\uE466\uE467\uFA0C\uFA0D\uFA0E\uFA0F\uFA11\uFA13"+ "\uFA14\uFA18\uFA1F\uFA20\uFA21\uFA23\uFA24\uFA27"+ "\uFA28\uFA29\u2E81\uE816\uE817\uE818\u2E84\u3473"+ - "\u3447\u2E88\u2E8B\uE81E\u359E\u361A\u360E\u2E8C"+ - "\u2E97\u396E\u3918\uE826\u39CF\u39DF\u3A73\u39D0"+ - "\uE82B\uE82C\u3B4E\u3C6E\u3CE0\u2EA7\uE831\uE832"+ + (IS_2000 ? "\u3447\u2E88\u2E8B\uE81E\u359E\u361A\u360E\u2E8C"+ + "\u2E97\u396E\u3918\uE826\u39CF\u39DF\u3A73\u39D0"+ + "\uE82B\uE82C\u3B4E\u3C6E\u3CE0\u2EA7\uE831\uE832" : + "\u3447\u2E88\u2E8B\u9FB4\u359E\u361A\u360E\u2E8C"+ + "\u2E97\u396E\u3918\u9FB5\u39CF\u39DF\u3A73\u39D0"+ + "\u9FB6\u9FB7\u3B4E\u3C6E\u3CE0\u2EA7\uE831\u9FB8")+ "\u2EAA\u4056\u415F\u2EAE\u4337\u2EB3\u2EB6\u2EB7"+ "\uE83B\u43B1\u43AC\u2EBB\u43DD\u44D6\u4661\u464C"+ - "\uE843\uFFFD\u4723\u4729\u477C\u478D\u2ECA\u4947"+ + (IS_2000 ? "\uE843\uFFFD\u4723\u4729\u477C\u478D\u2ECA\u4947" : + "\u9FB9\uFFFD\u4723\u4729\u477C\u478D\u2ECA\u4947")+ "\u497A\u497D\u4982\u4983\u4985\u4986\u499F\u499B"+ - "\u49B7\u49B6\uE854\uE855\u4CA3\u4C9F\u4CA0\u4CA1"+ + (IS_2000 ? "\u49B7\u49B6\uE854\uE855\u4CA3\u4C9F\u4CA0\u4CA1" : + "\u49B7\u49B6\u9FBA\uE855\u4CA3\u4C9F\u4CA0\u4CA1")+ "\u4C77\u4CA2\u4D13\u4D14\u4D15\u4D16\u4D17\u4D18"+ - "\u4D19\u4DAE\uE864\uE468\uE469\uE46A\uE46B\uE46C"+ + (IS_2000 ? "\u4D19\u4DAE\uE864\uE468\uE469\uE46A\uE46B\uE46C" : + "\u4D19\u4DAE\u9FBB\uE468\uE469\uE46A\uE46B\uE46C")+ "\uE46D\uE46E\uE46F\uE470\uE471\uE472\uE473\uE474"+ "\uE475\uE476\uE477\uE478\uE479\uE47A\uE47B\uE47C"+ "\uE47D\uE47E\uE47F\uE480\uE481\uE482\uE483\uE484"+ @@ -6895,7 +6919,8 @@ public CharsetEncoder newEncoder() { "\u3D02\u3D03\u3D04\u3D05\u3D06\u3D07\u3D08\u3D09"+ "\u3D0A\u3D0B\u3D0C\u3D0D\u3D0E\u3D0F\u3D10\u3D11"+ "\u3D12\u3D13\u3D14\u3D15\u3D16\u3D17\u3D18\u3D19"+ - "\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E\u3D1F\u3D20\u3D21"+ + (IS_2000 ? "\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E\u3D1F\u3D20\u3D21" : + "\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E\u3D1F\u3D20\uA8BC")+ "\u3D22\u3D23\u3D24\u3D25\u3D26\u3D27\u3D28\u3D29"+ "\u3D2A\u3D2B\u3D2C\u3D2D\u3D2E\u3D2F\u3D30\u3D31"+ "\u3D32\u3D33\u3D34\u3D35\u3D36\u3D37\u3D38\u3D39"+ @@ -11054,8 +11079,10 @@ public CharsetEncoder newEncoder() { "\uFD93\uC1FA\uB9A8\uEDE8\uFD94\uFD95\uFD96\uB9EA"+ "\uD9DF\uFD97\uFD98\uFD99\uFD9A\uFD9B\u6A63\u6A64"+ "\u6A65\u6A66\u6A67\u6A68\u6A69\u6A6A\u6A6B\u6A6C"+ - "\u6A6D\u6A6E\u6A6F\u6A70\u6A71\u6A72\u6A73\u6A74"+ - "\u6A75\u6A76\u6A77\u6A78\u6A79\u6A7A\u6A7B\u6A7C"+ + (IS_2000 ? "\u6A6D\u6A6E\u6A6F\u6A70\u6A71\u6A72\u6A73\u6A74"+ + "\u6A75\u6A76\u6A77\u6A78\u6A79\u6A7A\u6A7B\u6A7C" : + "\u6A6D\u6A6E\u6A6F\u6A70\uFE59\uFE61\uFE66\uFE67"+ + "\uFE6D\uFE7E\uFE90\uFEA0\u6A79\u6A7A\u6A7B\u6A7C")+ "\u6A7D\u6A7E\u6A7F\u6A80\u6A81\u6A82\u6A83\u6A84"+ "\u6A85\u6A86\u6A87\u6A88\u6A89\u6A8A\u6A8B\u6A8C"+ "\u6A8D\u6A8E\u6A8F\u6A90\u6A91\u6A92\u6A93\u6A94"+ @@ -11467,14 +11494,17 @@ public CharsetEncoder newEncoder() { "\uA2FD\uA2FE\uA4F4\uA4F5\uA4F6\uA4F7\uA4F8\uA4F9"+ "\uA4FA\uA4FB\uA4FC\uA4FD\uA4FE\uA5F7\uA5F8\uA5F9"+ "\uA5FA\uA5FB\uA5FC\uA5FD\uA5FE\uA6B9\uA6BA\uA6BB"+ - "\uA6BC\uA6BD\uA6BE\uA6BF\uA6C0\uA6D9\uA6DA\uA6DB"+ - "\uA6DC\uA6DD\uA6DE\uA6DF\uA6EC\uA6ED\uA6F3\uA6F6"+ + (IS_2000 ? "\uA6BC\uA6BD\uA6BE\uA6BF\uA6C0\uA6D9\uA6DA\uA6DB"+ + "\uA6DC\uA6DD\uA6DE\uA6DF\uA6EC\uA6ED\uA6F3\uA6F6" : + "\uA6BC\uA6BD\uA6BE\uA6BF\uA6C0\u35E7\u35E9\u35E8"+ + "\u35EA\u35EB\u35EC\u35ED\u35EE\u35EF\u35F0\uA6F6")+ "\uA6F7\uA6F8\uA6F9\uA6FA\uA6FB\uA6FC\uA6FD\uA6FE"+ "\uA7C2\uA7C3\uA7C4\uA7C5\uA7C6\uA7C7\uA7C8\uA7C9"+ "\uA7CA\uA7CB\uA7CC\uA7CD\uA7CE\uA7CF\uA7D0\uA7F2"+ "\uA7F3\uA7F4\uA7F5\uA7F6\uA7F7\uA7F8\uA7F9\uA7FA"+ "\uA7FB\uA7FC\uA7FD\uA7FE\uA896\uA897\uA898\uA899"+ - "\uA89A\uA89B\uA89C\uA89D\uA89E\uA89F\uA8A0\uA8BC"+ + (IS_2000 ? "\uA89A\uA89B\uA89C\uA89D\uA89E\uA89F\uA8A0\uA8BC" : + "\uA89A\uA89B\uA89C\uA89D\uA89E\uA89F\uA8A0\u3D21")+ "\u2001\uA8C1\uA8C2\uA8C3\uA8C4\uA8EA\uA8EB\uA8EC"+ "\uA8ED\uA8EE\uA8EF\uA8F0\uA8F1\uA8F2\uA8F3\uA8F4"+ "\uA8F5\uA8F6\uA8F7\uA8F8\uA8F9\uA8FA\uA8FB\uA8FC"+ @@ -11485,16 +11515,23 @@ public CharsetEncoder newEncoder() { "\uA9A3\uA9F0\uA9F1\uA9F2\uA9F3\uA9F4\uA9F5\uA9F6"+ "\uA9F7\uA9F8\uA9F9\uA9FA\uA9FB\uA9FC\uA9FD\uA9FE"+ "\uD7FA\uD7FB\uD7FC\uD7FD\uD7FE\u200F\uFE51\uFE52"+ - "\uFE53\u2010\u2011\u2012\u2013\u2014\uFE59\u2015"+ - "\u2016\u2017\u2018\u2019\u201A\u201B\uFE61\u201C"+ - "\u201D\u201E\u201F\uFE66\uFE67\u2020\u2021\u2022"+ - "\u2023\uFE6C\uFE6D\u2024\u2025\u2026\u2027\u2028"+ + (IS_2000 ? "\uFE53\u2010\u2011\u2012\u2013\u2014\uFE59\u2015"+ + "\u2016\u2017\u2018\u2019\u201A\u201B\uFE61\u201C"+ + "\u201D\u201E\u201F\uFE66\uFE67\u2020\u2021\u2022"+ + "\u2023\uFE6C\uFE6D\u2024\u2025\u2026\u2027\u2028" : + "\uFE53\u2010\u2011\u2012\u2013\u2014\u6A71\u2015"+ + "\u2016\u2017\u2018\u2019\u201A\u201B\u6A72\u201C"+ + "\u201D\u201E\u201F\u6A73\u6A74\u2020\u2021\u2022"+ + "\u2023\uFE6C\u6A75\u2024\u2025\u2026\u2027\u2028")+ "\u2029\u202A\u202B\uFE76\u202C\u202D\u202E\u202F"+ - "\u2030\u2031\u2032\uFE7E\u2033\u2034\u2035\u2036"+ + (IS_2000 ? "\u2030\u2031\u2032\uFE7E\u2033\u2034\u2035\u2036" : + "\u2030\u2031\u2032\u6A76\u2033\u2034\u2035\u2036")+ "\u2037\u2038\u2039\u203A\u203B\u203C\u203D\u203E"+ - "\u203F\u2040\u2041\u2042\uFE90\uFE91\u2043\u2044"+ + (IS_2000 ? "\u203F\u2040\u2041\u2042\uFE90\uFE91\u2043\u2044" : + "\u203F\u2040\u2041\u2042\u6A77\uFE91\u2043\u2044")+ "\u2045\u2046\u2047\u2048\u2049\u204A\u204B\u204C"+ - "\u204D\u204E\u204F\u2050\uFEA0\u2051\u2052\u2053"+ + (IS_2000 ? "\u204D\u204E\u204F\u2050\uFEA0\u2051\u2052\u2053" : + "\u204D\u204E\u204F\u2050\u6A78\u2051\u2052\u2053")+ "\u2054\u2055\u2056\u2057\u2058\u2059\u205A\u205B"+ "\u205C\u205D\u205E\u205F\u2060\u2061\u2062\u2063"+ "\u2064\u2065\u2066\u2067\u2068\u2069\u206A\u206B"+ @@ -12192,8 +12229,10 @@ public CharsetEncoder newEncoder() { "\u24E3\u24E4\u24E5\u24E6\u24E7\u24E8\u24E9\u24EA"+ "\u24EB\u24EC\u24ED\u24EE\u24EF\u24F0\u24F1\u24F2"+ "\u24F3\u24F4\u24F5\u24F6\u24F7\u24F8\u24F9\u24FA"+ - "\u24FB\u24FC\u24FD\u24FE\u24FF\u2500\u2501\u2502"+ - "\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250A"+ + (IS_2000 ? "\u24FB\u24FC\u24FD\u24FE\u24FF\u2500\u2501\u2502"+ + "\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250A" : + "\uA6D9\uA6DB\uA6DA\uA6DC\uA6DD\uA6DE\uA6DF\uA6EC"+ + "\uA6ED\uA6F3\u2505\u2506\u2507\u2508\u2509\u250A")+ "\u250B\u250C\u250D\u250E\u250F\u2510\u2511\u2512"+ "\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251A"+ "\uA955\uA6F2\u251B\uA6F4\uA6F5\uA6E0\uA6E1\uA6F0"+ @@ -12410,8 +12449,13 @@ else if (byte2 == 0x7f || byte2 == 0xff || // BMP Ranges if (offset <= 0x4A62) da[dp++] = getChar(offset); - else if (offset > 0x4A62 && offset <= 0x82BC) - da[dp++] = (char)(offset + 0x5543); + else if (offset > 0x4A62 && offset <= 0x82BC) { + if (offset >= 0x4A71 && offset <= 0x4A78 && !IS_2000) { + da[dp++] = getChar(offset); + } else { + da[dp++] = (char)(offset + 0x5543); + } + } else if (offset >= 0x82BD && offset <= 0x830D) da[dp++] = getChar(offset); else if (offset >= 0x830D && offset <= 0x93A8) @@ -12513,8 +12557,13 @@ else if (byte2 == 0x7f || byte2 == 0xff || // BMP Ranges if (offset <= 0x4A62) dst.put(getChar(offset)); - else if (offset > 0x4A62 && offset <= 0x82BC) - dst.put((char)(offset + 0x5543)); + else if (offset > 0x4A62 && offset <= 0x82BC) { + if (offset >= 0x4A71 && offset <= 0x4A78 && !IS_2000) { + dst.put(getChar(offset)); + } else { + dst.put((char)(offset + 0x5543)); + } + } else if (offset >= 0x82BD && offset <= 0x830D) dst.put(getChar(offset)); else if (offset >= 0x830D && offset <= 0x93A8) @@ -12627,15 +12676,33 @@ else if (c <= 0xA4C6 || c >= 0xE000) { condensedKey = (hiByte - 0x20) * 256 + loByte; - if (c >= 0xE000 && c < 0xF900) + if (c >= 0xE000 && c < 0xF900) { + if (IS_2000) { condensedKey += 0x82BD; + } else { + switch (c) { + case 0xE7C7: + case 0xE81E: + case 0xE826: + case 0xE82B: + case 0xE82C: + case 0xE832: + case 0xE843: + case 0xE854: + case 0xE864: + break; + default: + condensedKey += 0x82BD; + }; + } + } else if (c >= 0xF900) - condensedKey += 0x93A9; + condensedKey += 0x93A9; if (hiByte > 0x80) - currentState = GB18030_DOUBLE_BYTE; + currentState = GB18030_DOUBLE_BYTE; else - currentState = GB18030_FOUR_BYTE; + currentState = GB18030_FOUR_BYTE; } else if (c >= 0xA4C7 && c <= 0xD7FF) { condensedKey = c - 0x5543; @@ -12678,7 +12745,7 @@ else if (c >= 0xA4C7 && c <= 0xD7FF) { } sp += inputSize; } - return CoderResult.UNDERFLOW; + return CoderResult.UNDERFLOW; } finally { src.position(sp - src.arrayOffset()); dst.position(dp - dst.arrayOffset()); @@ -12718,15 +12785,33 @@ else if (c <= 0xA4C6 || c >= 0xE000) { condensedKey = (hiByte - 0x20) * 256 + loByte; - if (c >= 0xE000 && c < 0xF900) + if (c >= 0xE000 && c < 0xF900) { + if (IS_2000) { condensedKey += 0x82BD; + } else { + switch (c) { + case 0xE7C7: + case 0xE81E: + case 0xE826: + case 0xE82B: + case 0xE82C: + case 0xE832: + case 0xE843: + case 0xE854: + case 0xE864: + break; + default: + condensedKey += 0x82BD; + }; + } + } else if (c >= 0xF900) - condensedKey += 0x93A9; + condensedKey += 0x93A9; if (hiByte > 0x80) - currentState = GB18030_DOUBLE_BYTE; + currentState = GB18030_DOUBLE_BYTE; else - currentState = GB18030_FOUR_BYTE; + currentState = GB18030_FOUR_BYTE; } else if (c >= 0xA4C7 && c <= 0xD7FF) { condensedKey = c - 0x5543; diff --git a/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template b/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template index 8f673f9ea2e..aa242491179 100644 --- a/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template +++ b/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -59,8 +59,13 @@ public class StandardCharsets extends CharsetProvider { } private String canonicalize(String csn) { - String acn = aliasMap().get(csn); - return (acn != null) ? acn : csn; + if (csn.startsWith("gb18030-")) { + return csn.equals("gb18030-2022") && !GB18030.IS_2000 || + csn.equals("gb18030-2000") && GB18030.IS_2000 ? "gb18030" : csn; + } else { + String acn = aliasMap().get(csn); + return (acn != null) ? acn : csn; + } } private Map aliasMap() { diff --git a/src/java.base/share/classes/sun/security/jca/JCAUtil.java b/src/java.base/share/classes/sun/security/jca/JCAUtil.java index 59375cc220b..de02a1e6562 100644 --- a/src/java.base/share/classes/sun/security/jca/JCAUtil.java +++ b/src/java.base/share/classes/sun/security/jca/JCAUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * 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,8 +26,14 @@ package sun.security.jca; import java.lang.ref.*; - import java.security.*; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import jdk.internal.event.EventHelper; +import jdk.internal.event.X509CertificateEvent; +import sun.security.util.KeyUtil; /** * Collection of static utility methods used by the security framework. @@ -59,6 +65,8 @@ private static class CachedSecureRandomHolder { public static SecureRandom instance = new SecureRandom(); } + private static volatile SecureRandom def = null; + /** * Get a SecureRandom instance. This method should be used by JDK * internal code in favor of calling "new SecureRandom()". That needs to @@ -69,4 +77,69 @@ public static SecureRandom getSecureRandom() { return CachedSecureRandomHolder.instance; } + // called by sun.security.jca.Providers class when provider list is changed + static void clearDefSecureRandom() { + def = null; + } + + /** + * Get the default SecureRandom instance. This method is the + * optimized version of "new SecureRandom()" which re-uses the default + * SecureRandom impl if the provider table is the same. + */ + public static SecureRandom getDefSecureRandom() { + SecureRandom result = def; + if (result == null) { + synchronized (JCAUtil.class) { + result = def; + if (result == null) { + def = result = new SecureRandom(); + } + } + } + return result; + + } + + public static void tryCommitCertEvent(Certificate cert) { + if ((X509CertificateEvent.isTurnedOn() || EventHelper.isLoggingSecurity())) { + if (cert instanceof X509Certificate) { + X509Certificate x509 = (X509Certificate) cert; + PublicKey pKey = x509.getPublicKey(); + String algId = x509.getSigAlgName(); + String serNum = x509.getSerialNumber().toString(16); + String subject = x509.getSubjectX500Principal().toString(); + String issuer = x509.getIssuerX500Principal().toString(); + String keyType = pKey.getAlgorithm(); + int length = KeyUtil.getKeySize(pKey); + int hashCode = x509.hashCode(); + long beginDate = x509.getNotBefore().getTime(); + long endDate = x509.getNotAfter().getTime(); + if (X509CertificateEvent.isTurnedOn()) { + X509CertificateEvent xce = new X509CertificateEvent(); + xce.algorithm = algId; + xce.serialNumber = serNum; + xce.subject = subject; + xce.issuer = issuer; + xce.keyType = keyType; + xce.keyLength = length; + xce.certificateId = hashCode; + xce.validFrom = beginDate; + xce.validUntil = endDate; + xce.commit(); + } + if (EventHelper.isLoggingSecurity()) { + EventHelper.logX509CertificateEvent(algId, + serNum, + subject, + issuer, + keyType, + length, + hashCode, + beginDate, + endDate); + } + } + } + } } diff --git a/src/java.base/share/classes/sun/security/jca/Providers.java b/src/java.base/share/classes/sun/security/jca/Providers.java index 817c8f2af06..7256308c12c 100644 --- a/src/java.base/share/classes/sun/security/jca/Providers.java +++ b/src/java.base/share/classes/sun/security/jca/Providers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -150,6 +150,7 @@ public static void setProviderList(ProviderList newList) { } else { changeThreadProviderList(newList); } + JCAUtil.clearDefSecureRandom(); } /** diff --git a/src/java.base/share/classes/sun/security/pkcs/ContentInfo.java b/src/java.base/share/classes/sun/security/pkcs/ContentInfo.java index d3cdf3aead0..44256b83980 100644 --- a/src/java.base/share/classes/sun/security/pkcs/ContentInfo.java +++ b/src/java.base/share/classes/sun/security/pkcs/ContentInfo.java @@ -38,50 +38,36 @@ public class ContentInfo { // pkcs7 pre-defined content types - private static int[] pkcs7 = {1, 2, 840, 113549, 1, 7}; - private static int[] data = {1, 2, 840, 113549, 1, 7, 1}; - private static int[] sdata = {1, 2, 840, 113549, 1, 7, 2}; - private static int[] edata = {1, 2, 840, 113549, 1, 7, 3}; - private static int[] sedata = {1, 2, 840, 113549, 1, 7, 4}; - private static int[] ddata = {1, 2, 840, 113549, 1, 7, 5}; - private static int[] crdata = {1, 2, 840, 113549, 1, 7, 6}; - private static int[] nsdata = {2, 16, 840, 1, 113730, 2, 5}; - // timestamp token (id-ct-TSTInfo) from RFC 3161 - private static int[] tstInfo = {1, 2, 840, 113549, 1, 9, 16, 1, 4}; + public static ObjectIdentifier PKCS7_OID = + ObjectIdentifier.of(KnownOIDs.PKCS7); + public static ObjectIdentifier DATA_OID = + ObjectIdentifier.of(KnownOIDs.Data); + public static ObjectIdentifier SIGNED_DATA_OID = + ObjectIdentifier.of(KnownOIDs.SignedData); + public static ObjectIdentifier ENVELOPED_DATA_OID = + ObjectIdentifier.of(KnownOIDs.EnvelopedData); + public static ObjectIdentifier SIGNED_AND_ENVELOPED_DATA_OID = + ObjectIdentifier.of(KnownOIDs.SignedAndEnvelopedData); + public static ObjectIdentifier DIGESTED_DATA_OID = + ObjectIdentifier.of(KnownOIDs.DigestedData); + public static ObjectIdentifier ENCRYPTED_DATA_OID = + ObjectIdentifier.of(KnownOIDs.EncryptedData); + // this is for backwards-compatibility with JDK 1.1.x - private static final int[] OLD_SDATA = {1, 2, 840, 1113549, 1, 7, 2}; - private static final int[] OLD_DATA = {1, 2, 840, 1113549, 1, 7, 1}; - public static ObjectIdentifier PKCS7_OID; - public static ObjectIdentifier DATA_OID; - public static ObjectIdentifier SIGNED_DATA_OID; - public static ObjectIdentifier ENVELOPED_DATA_OID; - public static ObjectIdentifier SIGNED_AND_ENVELOPED_DATA_OID; - public static ObjectIdentifier DIGESTED_DATA_OID; - public static ObjectIdentifier ENCRYPTED_DATA_OID; - public static ObjectIdentifier OLD_SIGNED_DATA_OID; - public static ObjectIdentifier OLD_DATA_OID; - public static ObjectIdentifier NETSCAPE_CERT_SEQUENCE_OID; - public static ObjectIdentifier TIMESTAMP_TOKEN_INFO_OID; - - static { - PKCS7_OID = ObjectIdentifier.newInternal(pkcs7); - DATA_OID = ObjectIdentifier.newInternal(data); - SIGNED_DATA_OID = ObjectIdentifier.newInternal(sdata); - ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(edata); - SIGNED_AND_ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(sedata); - DIGESTED_DATA_OID = ObjectIdentifier.newInternal(ddata); - ENCRYPTED_DATA_OID = ObjectIdentifier.newInternal(crdata); - OLD_SIGNED_DATA_OID = ObjectIdentifier.newInternal(OLD_SDATA); - OLD_DATA_OID = ObjectIdentifier.newInternal(OLD_DATA); - /** - * The ASN.1 systax for the Netscape Certificate Sequence - * data type is defined - * - * here. - */ - NETSCAPE_CERT_SEQUENCE_OID = ObjectIdentifier.newInternal(nsdata); - TIMESTAMP_TOKEN_INFO_OID = ObjectIdentifier.newInternal(tstInfo); - } + public static ObjectIdentifier OLD_SIGNED_DATA_OID = + ObjectIdentifier.of(KnownOIDs.JDK_OLD_SignedData); + public static ObjectIdentifier OLD_DATA_OID = + ObjectIdentifier.of(KnownOIDs.JDK_OLD_Data); + + // The ASN.1 systax for the Netscape Certificate Sequence data type is + // defined at: + // http://wp.netscape.com/eng/security/comm4-cert-download.html + public static ObjectIdentifier NETSCAPE_CERT_SEQUENCE_OID = + ObjectIdentifier.of(KnownOIDs.NETSCAPE_CertSequence); + + // timestamp token (id-ct-TSTInfo) from RFC 3161 + public static ObjectIdentifier TIMESTAMP_TOKEN_INFO_OID = + ObjectIdentifier.of(KnownOIDs.TimeStampTokenInfo); ObjectIdentifier contentType; DerValue content; // OPTIONAL diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java index 042dfd13333..603167e2279 100644 --- a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java +++ b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java @@ -36,6 +36,7 @@ import java.security.cert.CertificateFactory; import java.security.*; +import sun.security.jca.JCAUtil; import sun.security.timestamp.*; import sun.security.util.*; import sun.security.x509.AlgorithmId; @@ -69,23 +70,6 @@ public class PKCS7 { private Principal[] certIssuerNames; - /* - * Random number generator for creating nonce values - * (Lazy initialization) - */ - private static class SecureRandomHolder { - static final SecureRandom RANDOM; - static { - SecureRandom tmp = null; - try { - tmp = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - // should not happen - } - RANDOM = tmp; - } - } - /* * Object identifier for the timestamping key purpose. */ @@ -885,11 +869,9 @@ private static byte[] generateTimestampToken(Timestamper tsa, } // Generate a nonce - BigInteger nonce = null; - if (SecureRandomHolder.RANDOM != null) { - nonce = new BigInteger(64, SecureRandomHolder.RANDOM); - tsQuery.setNonce(nonce); - } + BigInteger nonce = new BigInteger(64, JCAUtil.getDefSecureRandom()); + tsQuery.setNonce(nonce); + tsQuery.requestCertificate(true); TSResponse tsReply = tsa.generateTimestamp(tsQuery); diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java b/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java index c6a1bc1778a..fb2fa121f14 100644 --- a/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java +++ b/src/java.base/share/classes/sun/security/pkcs/PKCS9Attribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,9 +28,7 @@ import java.io.IOException; import java.io.OutputStream; import java.security.cert.CertificateException; -import java.util.Locale; import java.util.Date; -import java.util.Hashtable; import sun.security.x509.CertificateExtensions; import sun.security.util.*; diff --git a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index be8722d8cdf..684c68c72ce 100644 --- a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -64,17 +64,11 @@ import javax.security.auth.x500.X500Principal; import sun.security.action.GetPropertyAction; -import sun.security.util.Debug; -import sun.security.util.DerInputStream; -import sun.security.util.DerOutputStream; -import sun.security.util.DerValue; -import sun.security.util.ObjectIdentifier; +import sun.security.util.*; import sun.security.pkcs.ContentInfo; -import sun.security.util.SecurityProperties; import sun.security.x509.AlgorithmId; import sun.security.pkcs.EncryptedPrivateKeyInfo; import sun.security.provider.JavaKeyStore.JKS; -import sun.security.util.KeyStoreDelegator; /** @@ -133,43 +127,42 @@ public DualFormatPKCS12() { private static final int MAX_ITERATION_COUNT = 5000000; private static final int SALT_LEN = 20; - // friendlyName, localKeyId, trustedKeyUsage - private static final String[] CORE_ATTRIBUTES = { - "1.2.840.113549.1.9.20", - "1.2.840.113549.1.9.21", - "2.16.840.1.113894.746875.1.1" + private static final KnownOIDs[] CORE_ATTRIBUTES = { + KnownOIDs.FriendlyName, + KnownOIDs.LocalKeyID, + KnownOIDs.ORACLE_TrustedKeyUsage }; private static final Debug debug = Debug.getInstance("pkcs12"); - private static final int[] keyBag = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; - private static final int[] certBag = {1, 2, 840, 113549, 1, 12, 10, 1, 3}; - private static final int[] secretBag = {1, 2, 840, 113549, 1, 12, 10, 1, 5}; + private static final ObjectIdentifier PKCS8ShroudedKeyBag_OID = + ObjectIdentifier.of(KnownOIDs.PKCS8ShroudedKeyBag); + private static final ObjectIdentifier CertBag_OID = + ObjectIdentifier.of(KnownOIDs.CertBag); + private static final ObjectIdentifier SecretBag_OID = + ObjectIdentifier.of(KnownOIDs.SecretBag); + + private static final ObjectIdentifier PKCS9FriendlyName_OID = + ObjectIdentifier.of(KnownOIDs.FriendlyName); + private static final ObjectIdentifier PKCS9LocalKeyId_OID = + ObjectIdentifier.of(KnownOIDs.LocalKeyID); + private static final ObjectIdentifier PKCS9CertType_OID = + ObjectIdentifier.of(KnownOIDs.CertTypeX509); + private static final ObjectIdentifier pbes2_OID = + ObjectIdentifier.of(KnownOIDs.PBES2); - private static final int[] pkcs9Name = {1, 2, 840, 113549, 1, 9, 20}; - private static final int[] pkcs9KeyId = {1, 2, 840, 113549, 1, 9, 21}; - - private static final int[] pkcs9certType = {1, 2, 840, 113549, 1, 9, 22, 1}; - - private static final int[] pbes2 = {1, 2, 840, 113549, 1, 5, 13}; - // TODO: temporary Oracle OID /* - * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894) - * jdk(746875) crypto(1) id-at-trustedKeyUsage(1) } + * Temporary Oracle OID + * + * {joint-iso-itu-t(2) country(16) us(840) organization(1) + * oracle(113894) jdk(746875) crypto(1) id-at-trustedKeyUsage(1)} */ - private static final int[] TrustedKeyUsage = - {2, 16, 840, 1, 113894, 746875, 1, 1}; - private static final int[] AnyExtendedKeyUsage = {2, 5, 29, 37, 0}; - - private static final ObjectIdentifier PKCS8ShroudedKeyBag_OID; - private static final ObjectIdentifier CertBag_OID; - private static final ObjectIdentifier SecretBag_OID; - private static final ObjectIdentifier PKCS9FriendlyName_OID; - private static final ObjectIdentifier PKCS9LocalKeyId_OID; - private static final ObjectIdentifier PKCS9CertType_OID; - private static final ObjectIdentifier pbes2_OID; - private static final ObjectIdentifier TrustedKeyUsage_OID; - private static final ObjectIdentifier[] AnyUsage; + private static final ObjectIdentifier TrustedKeyUsage_OID = + ObjectIdentifier.of(KnownOIDs.ORACLE_TrustedKeyUsage); + + private static final ObjectIdentifier[] AnyUsage = new ObjectIdentifier[] { + ObjectIdentifier.of(KnownOIDs.anyExtendedKeyUsage) + }; private int counter = 0; @@ -198,23 +191,6 @@ public DualFormatPKCS12() { // the source of randomness private SecureRandom random; - static { - try { - PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag); - CertBag_OID = new ObjectIdentifier(certBag); - SecretBag_OID = new ObjectIdentifier(secretBag); - PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name); - PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId); - PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType); - pbes2_OID = new ObjectIdentifier(pbes2); - TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage); - AnyUsage = new ObjectIdentifier[]{ - new ObjectIdentifier(AnyExtendedKeyUsage)}; - } catch (IOException ioe) { - throw new AssertionError("OID not initialized", ioe); - } - } - // A keystore entry and associated attributes private static class Entry { Date date; // the creation date of this entry @@ -1655,9 +1631,9 @@ private byte[] getBagAttributes(String alias, byte[] keyId, for (KeyStore.Entry.Attribute attribute : attributes) { String attributeName = attribute.getName(); // skip friendlyName, localKeyId and trustedKeyUsage - if (CORE_ATTRIBUTES[0].equals(attributeName) || - CORE_ATTRIBUTES[1].equals(attributeName) || - CORE_ATTRIBUTES[2].equals(attributeName)) { + if (CORE_ATTRIBUTES[0].value().equals(attributeName) || + CORE_ATTRIBUTES[1].value().equals(attributeName) || + CORE_ATTRIBUTES[2].value().equals(attributeName)) { continue; } attrs.write(((PKCS12Attribute) attribute).getEncoded()); diff --git a/src/java.base/share/classes/sun/security/provider/KeyProtector.java b/src/java.base/share/classes/sun/security/provider/KeyProtector.java index 18405cca9c3..0404247621a 100644 --- a/src/java.base/share/classes/sun/security/provider/KeyProtector.java +++ b/src/java.base/share/classes/sun/security/provider/KeyProtector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ import sun.security.pkcs.EncryptedPrivateKeyInfo; import sun.security.x509.AlgorithmId; import sun.security.util.ObjectIdentifier; +import sun.security.util.KnownOIDs; import sun.security.util.DerValue; /** @@ -106,9 +107,6 @@ final class KeyProtector { private static final String DIGEST_ALG = "SHA"; private static final int DIGEST_LEN = 20; - // defined by JavaSoft - private static final String KEY_PROTECTOR_OID = "1.3.6.1.4.1.42.2.17.1.1"; - // The password used for protecting/recovering keys passed through this // key protector. We store it as a byte array, so that we can digest it. private byte[] passwdBytes; @@ -214,7 +212,8 @@ public byte[] protect(Key key) throws KeyStoreException // EncryptedPrivateKeyInfo, and returns its encoding AlgorithmId encrAlg; try { - encrAlg = new AlgorithmId(new ObjectIdentifier(KEY_PROTECTOR_OID)); + encrAlg = new AlgorithmId(ObjectIdentifier.of + (KnownOIDs.JAVASOFT_JDKKeyProtector)); return new EncryptedPrivateKeyInfo(encrAlg,encrKey).getEncoded(); } catch (IOException ioe) { throw new KeyStoreException(ioe.getMessage()); @@ -236,7 +235,8 @@ public Key recover(EncryptedPrivateKeyInfo encrInfo) // do we support the algorithm? AlgorithmId encrAlg = encrInfo.getAlgorithm(); - if (!(encrAlg.getOID().toString().equals(KEY_PROTECTOR_OID))) { + if (!(encrAlg.getOID().toString().equals + (KnownOIDs.JAVASOFT_JDKKeyProtector.value()))) { throw new UnrecoverableKeyException("Unsupported key protection " + "algorithm"); } diff --git a/src/java.base/share/classes/sun/security/provider/PolicyFile.java b/src/java.base/share/classes/sun/security/provider/PolicyFile.java index 2ac339e75c3..d2b46ab3c2a 100644 --- a/src/java.base/share/classes/sun/security/provider/PolicyFile.java +++ b/src/java.base/share/classes/sun/security/provider/PolicyFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -605,6 +605,9 @@ public Void run() { pe.add(new PropertyPermission ("java.specification.version", SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.specification.maintenance.version", + SecurityConstants.PROPERTY_READ_ACTION)); pe.add(new PropertyPermission ("java.specification.vendor", SecurityConstants.PROPERTY_READ_ACTION)); diff --git a/src/java.base/share/classes/sun/security/provider/SunEntries.java b/src/java.base/share/classes/sun/security/provider/SunEntries.java index 759f7d8e529..6fdc344b321 100644 --- a/src/java.base/share/classes/sun/security/provider/SunEntries.java +++ b/src/java.base/share/classes/sun/security/provider/SunEntries.java @@ -32,6 +32,8 @@ import jdk.internal.util.StaticProperty; import sun.security.action.GetPropertyAction; +import sun.security.util.SecurityProviderConstants; +import static sun.security.util.SecurityProviderConstants.getAliases; /** * Defines the entries of the SUN provider. @@ -80,18 +82,6 @@ public final class SunEntries { // the default algo used by SecureRandom class for new SecureRandom() calls public static final String DEF_SECURE_RANDOM_ALGO; - // create an aliases List from the specified aliases - public static List createAliases(String ... aliases) { - return Arrays.asList(aliases); - } - - // create an aliases List from the specified oid followed by other aliases - public static List createAliasesWithOid(String ... oids) { - String[] result = Arrays.copyOf(oids, oids.length + 1); - result[result.length - 1] = "OID." + oids[0]; - return Arrays.asList(result); - } - SunEntries(Provider p) { services = new LinkedHashSet<>(50, 0.9f); @@ -106,22 +96,20 @@ public static List createAliasesWithOid(String ... oids) { attrs.put("ThreadSafe", "true"); if (NativePRNG.isAvailable()) { add(p, "SecureRandom", "NativePRNG", - "sun.security.provider.NativePRNG", - null, attrs); + "sun.security.provider.NativePRNG", attrs); } if (NativePRNG.Blocking.isAvailable()) { add(p, "SecureRandom", "NativePRNGBlocking", - "sun.security.provider.NativePRNG$Blocking", null, attrs); + "sun.security.provider.NativePRNG$Blocking", attrs); } if (NativePRNG.NonBlocking.isAvailable()) { add(p, "SecureRandom", "NativePRNGNonBlocking", - "sun.security.provider.NativePRNG$NonBlocking", null, attrs); + "sun.security.provider.NativePRNG$NonBlocking", attrs); } attrs.put("ImplementedIn", "Software"); - add(p, "SecureRandom", "DRBG", "sun.security.provider.DRBG", - null, attrs); + add(p, "SecureRandom", "DRBG", "sun.security.provider.DRBG", attrs); add(p, "SecureRandom", "SHA1PRNG", - "sun.security.provider.SecureRandom", null, attrs); + "sun.security.provider.SecureRandom", attrs); /* * Signature engines @@ -134,37 +122,28 @@ public static List createAliasesWithOid(String ... oids) { attrs.put("KeySize", "1024"); // for NONE and SHA1 DSA signatures - add(p, "Signature", "SHA1withDSA", - "sun.security.provider.DSA$SHA1withDSA", - createAliasesWithOid("1.2.840.10040.4.3", "DSA", "DSS", - "SHA/DSA", "SHA-1/DSA", "SHA1/DSA", "SHAwithDSA", - "DSAWithSHA1", "1.3.14.3.2.13", "1.3.14.3.2.27"), attrs); - add(p, "Signature", "NONEwithDSA", "sun.security.provider.DSA$RawDSA", - createAliases("RawDSA"), attrs); + addWithAlias(p, "Signature", "SHA1withDSA", + "sun.security.provider.DSA$SHA1withDSA", attrs); + addWithAlias(p, "Signature", "NONEwithDSA", + "sun.security.provider.DSA$RawDSA", attrs); attrs.put("KeySize", "2048"); // for SHA224 and SHA256 DSA signatures - add(p, "Signature", "SHA224withDSA", - "sun.security.provider.DSA$SHA224withDSA", - createAliasesWithOid("2.16.840.1.101.3.4.3.1"), attrs); - add(p, "Signature", "SHA256withDSA", - "sun.security.provider.DSA$SHA256withDSA", - createAliasesWithOid("2.16.840.1.101.3.4.3.2"), attrs); + addWithAlias(p, "Signature", "SHA224withDSA", + "sun.security.provider.DSA$SHA224withDSA", attrs); + addWithAlias(p, "Signature", "SHA256withDSA", + "sun.security.provider.DSA$SHA256withDSA", attrs); attrs.remove("KeySize"); add(p, "Signature", "SHA1withDSAinP1363Format", - "sun.security.provider.DSA$SHA1withDSAinP1363Format", - null, null); + "sun.security.provider.DSA$SHA1withDSAinP1363Format"); add(p, "Signature", "NONEwithDSAinP1363Format", - "sun.security.provider.DSA$RawDSAinP1363Format", - null, null); + "sun.security.provider.DSA$RawDSAinP1363Format"); add(p, "Signature", "SHA224withDSAinP1363Format", - "sun.security.provider.DSA$SHA224withDSAinP1363Format", - null, null); + "sun.security.provider.DSA$SHA224withDSAinP1363Format"); add(p, "Signature", "SHA256withDSAinP1363Format", - "sun.security.provider.DSA$SHA256withDSAinP1363Format", - null, null); + "sun.security.provider.DSA$SHA256withDSAinP1363Format"); /* * Key Pair Generator engines @@ -173,85 +152,75 @@ public static List createAliasesWithOid(String ... oids) { attrs.put("ImplementedIn", "Software"); attrs.put("KeySize", "2048"); // for DSA KPG and APG only - String dsaOid = "1.2.840.10040.4.1"; - List dsaAliases = createAliasesWithOid(dsaOid, "1.3.14.3.2.12"); String dsaKPGImplClass = "sun.security.provider.DSAKeyPairGenerator$"; dsaKPGImplClass += (useLegacyDSA? "Legacy" : "Current"); - add(p, "KeyPairGenerator", "DSA", dsaKPGImplClass, dsaAliases, attrs); + addWithAlias(p, "KeyPairGenerator", "DSA", dsaKPGImplClass, attrs); /* * Algorithm Parameter Generator engines */ - add(p, "AlgorithmParameterGenerator", "DSA", - "sun.security.provider.DSAParameterGenerator", dsaAliases, - attrs); + addWithAlias(p, "AlgorithmParameterGenerator", "DSA", + "sun.security.provider.DSAParameterGenerator", attrs); attrs.remove("KeySize"); /* * Algorithm Parameter engines */ - add(p, "AlgorithmParameters", "DSA", - "sun.security.provider.DSAParameters", dsaAliases, attrs); + addWithAlias(p, "AlgorithmParameters", "DSA", + "sun.security.provider.DSAParameters", attrs); /* * Key factories */ - add(p, "KeyFactory", "DSA", "sun.security.provider.DSAKeyFactory", - dsaAliases, attrs); + addWithAlias(p, "KeyFactory", "DSA", + "sun.security.provider.DSAKeyFactory", attrs); /* * Digest engines */ - add(p, "MessageDigest", "MD2", "sun.security.provider.MD2", null, attrs); - add(p, "MessageDigest", "MD5", "sun.security.provider.MD5", null, attrs); - add(p, "MessageDigest", "SHA", "sun.security.provider.SHA", - createAliasesWithOid("1.3.14.3.2.26", "SHA-1", "SHA1"), attrs); - - String sha2BaseOid = "2.16.840.1.101.3.4.2"; - add(p, "MessageDigest", "SHA-224", "sun.security.provider.SHA2$SHA224", - createAliasesWithOid(sha2BaseOid + ".4"), attrs); - add(p, "MessageDigest", "SHA-256", "sun.security.provider.SHA2$SHA256", - createAliasesWithOid(sha2BaseOid + ".1"), attrs); - add(p, "MessageDigest", "SHA-384", "sun.security.provider.SHA5$SHA384", - createAliasesWithOid(sha2BaseOid + ".2"), attrs); - add(p, "MessageDigest", "SHA-512", "sun.security.provider.SHA5$SHA512", - createAliasesWithOid(sha2BaseOid + ".3"), attrs); - add(p, "MessageDigest", "SHA-512/224", - "sun.security.provider.SHA5$SHA512_224", - createAliasesWithOid(sha2BaseOid + ".5"), attrs); - add(p, "MessageDigest", "SHA-512/256", - "sun.security.provider.SHA5$SHA512_256", - createAliasesWithOid(sha2BaseOid + ".6"), attrs); - add(p, "MessageDigest", "SHA3-224", "sun.security.provider.SHA3$SHA224", - createAliasesWithOid(sha2BaseOid + ".7"), attrs); - add(p, "MessageDigest", "SHA3-256", "sun.security.provider.SHA3$SHA256", - createAliasesWithOid(sha2BaseOid + ".8"), attrs); - add(p, "MessageDigest", "SHA3-384", "sun.security.provider.SHA3$SHA384", - createAliasesWithOid(sha2BaseOid + ".9"), attrs); - add(p, "MessageDigest", "SHA3-512", "sun.security.provider.SHA3$SHA512", - createAliasesWithOid(sha2BaseOid + ".10"), attrs); + add(p, "MessageDigest", "MD2", "sun.security.provider.MD2", attrs); + add(p, "MessageDigest", "MD5", "sun.security.provider.MD5", attrs); + addWithAlias(p, "MessageDigest", "SHA-1", "sun.security.provider.SHA", + attrs); + + addWithAlias(p, "MessageDigest", "SHA-224", + "sun.security.provider.SHA2$SHA224", attrs); + addWithAlias(p, "MessageDigest", "SHA-256", + "sun.security.provider.SHA2$SHA256", attrs); + addWithAlias(p, "MessageDigest", "SHA-384", + "sun.security.provider.SHA5$SHA384", attrs); + addWithAlias(p, "MessageDigest", "SHA-512", + "sun.security.provider.SHA5$SHA512", attrs); + addWithAlias(p, "MessageDigest", "SHA-512/224", + "sun.security.provider.SHA5$SHA512_224", attrs); + addWithAlias(p, "MessageDigest", "SHA-512/256", + "sun.security.provider.SHA5$SHA512_256", attrs); + addWithAlias(p, "MessageDigest", "SHA3-224", + "sun.security.provider.SHA3$SHA224", attrs); + addWithAlias(p, "MessageDigest", "SHA3-256", + "sun.security.provider.SHA3$SHA256", attrs); + addWithAlias(p, "MessageDigest", "SHA3-384", + "sun.security.provider.SHA3$SHA384", attrs); + addWithAlias(p, "MessageDigest", "SHA3-512", + "sun.security.provider.SHA3$SHA512", attrs); /* * Certificates */ - add(p, "CertificateFactory", "X.509", - "sun.security.provider.X509Factory", - createAliases("X509"), attrs); + addWithAlias(p, "CertificateFactory", "X.509", + "sun.security.provider.X509Factory", attrs); /* * KeyStore */ add(p, "KeyStore", "PKCS12", - "sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", - null, null); + "sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12"); add(p, "KeyStore", "JKS", - "sun.security.provider.JavaKeyStore$DualFormatJKS", - null, attrs); + "sun.security.provider.JavaKeyStore$DualFormatJKS", attrs); add(p, "KeyStore", "CaseExactJKS", - "sun.security.provider.JavaKeyStore$CaseExactJKS", - null, attrs); + "sun.security.provider.JavaKeyStore$CaseExactJKS", attrs); add(p, "KeyStore", "DKS", "sun.security.provider.DomainKeyStore$DKS", - null, attrs); + attrs); /* @@ -259,22 +228,21 @@ public static List createAliasesWithOid(String ... oids) { */ add(p, "CertStore", "Collection", "sun.security.provider.certpath.CollectionCertStore", - null, attrs); + attrs); add(p, "CertStore", "com.sun.security.IndexedCollection", "sun.security.provider.certpath.IndexedCollectionCertStore", - null, attrs); + attrs); /* * Policy */ - add(p, "Policy", "JavaPolicy", "sun.security.provider.PolicySpiFile", - null, null); + add(p, "Policy", "JavaPolicy", "sun.security.provider.PolicySpiFile"); /* * Configuration */ add(p, "Configuration", "JavaLoginConfig", - "sun.security.provider.ConfigFile$Spi", null, null); + "sun.security.provider.ConfigFile$Spi"); /* * CertPathBuilder and CertPathValidator @@ -285,19 +253,29 @@ public static List createAliasesWithOid(String ... oids) { add(p, "CertPathBuilder", "PKIX", "sun.security.provider.certpath.SunCertPathBuilder", - null, attrs); + attrs); add(p, "CertPathValidator", "PKIX", "sun.security.provider.certpath.PKIXCertPathValidator", - null, attrs); + attrs); } Iterator iterator() { return services.iterator(); } + private void add(Provider p, String type, String algo, String cn) { + services.add(new Provider.Service(p, type, algo, cn, null, null)); + } + private void add(Provider p, String type, String algo, String cn, - List aliases, HashMap attrs) { - services.add(new Provider.Service(p, type, algo, cn, aliases, attrs)); + HashMap attrs) { + services.add(new Provider.Service(p, type, algo, cn, null, attrs)); + } + + private void addWithAlias(Provider p, String type, String algo, String cn, + HashMap attrs) { + services.add(new Provider.Service(p, type, algo, cn, + getAliases(algo), attrs)); } private LinkedHashSet services; diff --git a/src/java.base/share/classes/sun/security/provider/X509Factory.java b/src/java.base/share/classes/sun/security/provider/X509Factory.java index 47466820838..f30647126f4 100644 --- a/src/java.base/share/classes/sun/security/provider/X509Factory.java +++ b/src/java.base/share/classes/sun/security/provider/X509Factory.java @@ -26,12 +26,9 @@ package sun.security.provider; import java.io.*; -import java.security.PublicKey; import java.util.*; import java.security.cert.*; -import jdk.internal.event.EventHelper; -import jdk.internal.event.X509CertificateEvent; import sun.security.util.KeyUtil; import sun.security.util.Pem; import sun.security.x509.*; @@ -104,8 +101,6 @@ public Certificate engineGenerateCertificate(InputStream is) } cert = new X509CertImpl(encoding); addToCache(certCache, cert.getEncodedInternal(), cert); - // record cert details if necessary - commitEvent(cert); return cert; } else { throw new IOException("Empty input"); @@ -473,7 +468,7 @@ public Collection engineGenerateCRLs( } } catch (ParsingException e) { while (data != null) { - coll.add(new X509CertImpl(data)); + coll.add(X509CertImpl.newX509CertImpl(data)); data = readOneBlock(pbis); } } @@ -766,43 +761,4 @@ private static int readBERInternal(InputStream is, } return tag; } - - private void commitEvent(X509CertImpl info) { - X509CertificateEvent xce = new X509CertificateEvent(); - if (xce.shouldCommit() || EventHelper.isLoggingSecurity()) { - PublicKey pKey = info.getPublicKey(); - String algId = info.getSigAlgName(); - String serNum = info.getSerialNumber().toString(16); - String subject = info.getSubjectDN().getName(); - String issuer = info.getIssuerDN().getName(); - String keyType = pKey.getAlgorithm(); - int length = KeyUtil.getKeySize(pKey); - int hashCode = info.hashCode(); - long beginDate = info.getNotBefore().getTime(); - long endDate = info.getNotAfter().getTime(); - if (xce.shouldCommit()) { - xce.algorithm = algId; - xce.serialNumber = serNum; - xce.subject = subject; - xce.issuer = issuer; - xce.keyType = keyType; - xce.keyLength = length; - xce.certificateId = hashCode; - xce.validFrom = beginDate; - xce.validUntil = endDate; - xce.commit(); - } - if (EventHelper.isLoggingSecurity()) { - EventHelper.logX509CertificateEvent(algId, - serNum, - subject, - issuer, - keyType, - length, - hashCode, - beginDate, - endDate); - } - } - } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/AdjacencyList.java b/src/java.base/share/classes/sun/security/provider/certpath/AdjacencyList.java index 9039d4fe75b..905a1d3e13c 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/AdjacencyList.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/AdjacencyList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,7 +87,7 @@ public class AdjacencyList { // the actual set of steps the AdjacencyList represents private ArrayList mStepList; - // the original list, just for the toString method + // the original list private List> mOrigList; /** @@ -114,6 +114,13 @@ public Iterator iterator() { return Collections.unmodifiableList(mStepList).iterator(); } + /** + * Returns the number of attempted paths (useful for debugging). + */ + public int numAttemptedPaths() { + return mOrigList.size(); + } + /** * Recursive, private method which actually builds the step list from * the given adjacency list. Follow is the parent BuildStep diff --git a/src/java.base/share/classes/sun/security/provider/certpath/Builder.java b/src/java.base/share/classes/sun/security/provider/certpath/Builder.java index 36e7accbe12..d73ed0f8bb1 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/Builder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/Builder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -412,8 +412,7 @@ Set getMatchingPolicies() { /** * Search the specified CertStores and add all certificates matching - * selector to resultCerts. Self-signed certs are not useful here - * and therefore ignored. + * selector to resultCerts. * * If the targetCert criterion of the selector is set, only that cert * is examined and the CertStores are not searched. @@ -432,8 +431,7 @@ boolean addMatchingCerts(X509CertSelector selector, X509Certificate targetCert = selector.getCertificate(); if (targetCert != null) { // no need to search CertStores - if (selector.match(targetCert) && !X509CertImpl.isSelfSigned - (targetCert, buildParams.sigProvider())) { + if (selector.match(targetCert)) { if (debug != null) { debug.println("Builder.addMatchingCerts: " + "adding target cert" + @@ -452,11 +450,8 @@ boolean addMatchingCerts(X509CertSelector selector, Collection certs = store.getCertificates(selector); for (Certificate cert : certs) { - if (!X509CertImpl.isSelfSigned - ((X509Certificate)cert, buildParams.sigProvider())) { - if (resultCerts.add((X509Certificate)cert)) { - add = true; - } + if (resultCerts.add((X509Certificate)cert)) { + add = true; } } if (!checkAll && add) { diff --git a/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java b/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java index 56ecabfc4f3..2afb2f9a85f 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,7 @@ import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.AuthorityKeyIdentifierExtension; import static sun.security.x509.PKIXExtensions.*; +import sun.security.x509.SubjectAlternativeNameExtension; import sun.security.x509.X500Name; import sun.security.x509.X509CertImpl; @@ -294,9 +295,7 @@ private void getMatchingCACerts(ForwardState currentState, "\n Issuer: " + trustedCert.getIssuerX500Principal()); } - if (caCerts.add(trustedCert) && !searchAllCertStores) { - return; - } + caCerts.add(trustedCert); } } @@ -675,8 +674,7 @@ public int compare(X509Certificate oCert1, X509Certificate oCert2) { * only be executed in a reverse direction are deferred until the * complete path has been built. * - * Trust anchor certs are not validated, but are used to verify the - * signature and revocation status of the previous cert. + * Trust anchor certs are not validated. * * If the last certificate is being verified (the one whose subject * matches the target subject, then steps in 6.1.4 of the PKIX @@ -707,17 +705,15 @@ void verifyCert(X509Certificate cert, State currentState, currState.untrustedChecker.check(cert, Collections.emptySet()); /* - * check for looping - abort a loop if we encounter the same - * certificate twice + * Abort if we encounter the same certificate or a certificate with + * the same public key, subject DN, and subjectAltNames as a cert + * that is already in path. */ - if (certPathList != null) { - for (X509Certificate cpListCert : certPathList) { - if (cert.equals(cpListCert)) { - if (debug != null) { - debug.println("loop detected!!"); - } - throw new CertPathValidatorException("loop detected"); - } + for (X509Certificate cpListCert : certPathList) { + if (repeated(cpListCert, cert)) { + throw new CertPathValidatorException( + "cert with repeated subject, public key, and " + + "subjectAltNames detected"); } } @@ -796,21 +792,48 @@ void verifyCert(X509Certificate cert, State currentState, */ KeyChecker.verifyCAKeyUsage(cert); } + } - /* - * the following checks are performed even when the cert - * is a trusted cert, since we are only extracting the - * subjectDN, and publicKey from the cert - * in order to verify a previous cert - */ + /** + * Return true if two certificates are equal or have the same subject, + * public key, and subject alternative names. + */ + private static boolean repeated( + X509Certificate currCert, X509Certificate nextCert) { + if (currCert.equals(nextCert)) { + return true; + } + return (currCert.getSubjectX500Principal().equals( + nextCert.getSubjectX500Principal()) && + currCert.getPublicKey().equals(nextCert.getPublicKey()) && + altNamesEqual(currCert, nextCert)); + } - /* - * Check signature only if no key requiring key parameters has been - * encountered. - */ - if (!currState.keyParamsNeeded()) { - (currState.cert).verify(cert.getPublicKey(), - buildParams.sigProvider()); + /** + * Return true if two certificates have the same subject alternative names. + */ + private static boolean altNamesEqual( + X509Certificate currCert, X509Certificate nextCert) { + X509CertImpl curr, next; + try { + curr = X509CertImpl.toImpl(currCert); + next = X509CertImpl.toImpl(nextCert); + } catch (CertificateException ce) { + return false; + } + + SubjectAlternativeNameExtension currAltNameExt = + curr.getSubjectAlternativeNameExtension(); + SubjectAlternativeNameExtension nextAltNameExt = + next.getSubjectAlternativeNameExtension(); + if (currAltNameExt != null) { + if (nextAltNameExt == null) { + return false; + } + return Arrays.equals(currAltNameExt.getExtensionValue(), + nextAltNameExt.getExtensionValue()); + } else { + return (nextAltNameExt == null); } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java b/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java index 2dc9e208e92..9d7af9b169b 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/ForwardState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,10 +80,8 @@ class ForwardState implements State { /* The list of user-defined checkers that support forward checking */ ArrayList forwardCheckers; - /* Flag indicating if key needing to inherit key parameters has been - * encountered. - */ - boolean keyParamsNeededFlag = false; + /* Flag indicating if last cert in path is self-issued */ + boolean selfIssued; /** * Returns a boolean flag indicating if the state is initial @@ -96,18 +94,6 @@ public boolean isInitial() { return init; } - /** - * Return boolean flag indicating whether a public key that needs to inherit - * key parameters has been encountered. - * - * @return boolean true if key needing to inherit parameters has been - * encountered; false otherwise. - */ - @Override - public boolean keyParamsNeeded() { - return keyParamsNeededFlag; - } - /** * Display state for debugging purposes */ @@ -118,10 +104,10 @@ public String toString() { sb.append("\n issuerDN of last cert: ").append(issuerDN); sb.append("\n traversedCACerts: ").append(traversedCACerts); sb.append("\n init: ").append(String.valueOf(init)); - sb.append("\n keyParamsNeeded: ").append - (String.valueOf(keyParamsNeededFlag)); sb.append("\n subjectNamesTraversed: \n").append (subjectNamesTraversed); + sb.append("\n selfIssued: ").append + (String.valueOf(selfIssued)); sb.append("]\n"); return sb.toString(); } @@ -166,18 +152,14 @@ public void updateState(X509Certificate cert) X509CertImpl icert = X509CertImpl.toImpl(cert); - /* see if certificate key has null parameters */ - if (PKIX.isDSAPublicKeyWithoutParams(icert.getPublicKey())) { - keyParamsNeededFlag = true; - } - /* update certificate */ this.cert = icert; /* update issuer DN */ issuerDN = cert.getIssuerX500Principal(); - if (!X509CertImpl.isSelfIssued(cert)) { + selfIssued = X509CertImpl.isSelfIssued(cert); + if (!selfIssued) { /* * update traversedCACerts only if this is a non-self-issued @@ -190,7 +172,7 @@ public void updateState(X509Certificate cert) /* update subjectNamesTraversed only if this is the EE cert or if this cert is not self-issued */ - if (init || !X509CertImpl.isSelfIssued(cert)){ + if (init || !selfIssued) { X500Principal subjName = cert.getSubjectX500Principal(); subjectNamesTraversed.add(X500Name.asX500Name(subjName)); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java b/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java index 2cff9ead31e..f8b77988666 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java @@ -135,7 +135,7 @@ public enum ResponseStatus { private static final Debug debug = Debug.getInstance("certpath"); private static final boolean dump = debug != null && Debug.isOn("ocsp"); private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID = - ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1}); + ObjectIdentifier.of(KnownOIDs.OCSPBasicResponse); private static final int CERT_STATUS_GOOD = 0; private static final int CERT_STATUS_REVOKED = 1; private static final int CERT_STATUS_UNKNOWN = 2; @@ -144,9 +144,6 @@ public enum ResponseStatus { private static final int NAME_TAG = 1; private static final int KEY_TAG = 2; - // Object identifier for the OCSPSigning key purpose - private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9"; - // Default maximum clock skew in milliseconds (15 minutes) // allowed when checking validity of OCSP responses private static final int DEFAULT_MAX_CLOCK_SKEW = 900000; @@ -357,7 +354,7 @@ public OCSPResponse(byte[] bytes) throws IOException { try { for (int i = 0; i < derCerts.length; i++) { X509CertImpl cert = - new X509CertImpl(derCerts[i].toByteArray()); + X509CertImpl.newX509CertImpl(derCerts[i].toByteArray()); certs.add(cert); if (debug != null) { @@ -495,7 +492,7 @@ void verify(List certIds, IssuerInfo issuerInfo, try { List keyPurposes = signerCert.getExtendedKeyUsage(); if (keyPurposes == null || - !keyPurposes.contains(KP_OCSP_SIGNING_OID)) { + !keyPurposes.contains(KnownOIDs.OCSPSigning.value())) { throw new CertPathValidatorException( "Responder's certificate not valid for signing " + "OCSP responses"); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java b/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java index 02425e86add..de3923bb3bc 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java @@ -228,13 +228,13 @@ private static PKIXCertPathValidatorResult validate(TrustAnchor anchor, X509ValidationEvent xve = new X509ValidationEvent(); if (xve.shouldCommit() || EventHelper.isLoggingSecurity()) { int[] certIds = params.certificates().stream() - .mapToInt(x -> x.hashCode()) + .mapToInt(Certificate::hashCode) .toArray(); - int anchorCertId = - anchor.getTrustedCert().hashCode(); + int anchorCertId = (anchorCert != null) ? + anchorCert.hashCode() : anchor.getCAPublicKey().hashCode(); if (xve.shouldCommit()) { xve.certificateId = anchorCertId; - int certificatePos = 1; //anchor cert + int certificatePos = 1; // most trusted CA xve.certificatePosition = certificatePos; xve.validationCounter = validationCounter.incrementAndGet(); xve.commit(); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java b/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java index 1e8f3294570..6d8e842419a 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/RevocationChecker.java @@ -46,6 +46,7 @@ import sun.security.x509.*; import static sun.security.x509.PKIXExtensions.*; import sun.security.util.Debug; +import sun.security.util.KnownOIDs; class RevocationChecker extends PKIXRevocationChecker { @@ -723,7 +724,7 @@ private void checkOCSP(X509Certificate cert, // verify the response byte[] nonce = null; for (Extension ext : ocspExtensions) { - if (ext.getId().equals("1.3.6.1.5.5.7.48.1.2")) { + if (ext.getId().equals(KnownOIDs.OCSPNonceExt.value())) { nonce = ext.getValue(); } } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/State.java b/src/java.base/share/classes/sun/security/provider/certpath/State.java index 93a153fec38..3292d654880 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/State.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/State.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,12 +62,4 @@ public void updateState(X509Certificate cert) * @return boolean flag indicating if the state is initial (just starting) */ public boolean isInitial(); - - /** - * Returns a boolean flag indicating if a key lacking necessary key - * algorithm parameters has been encountered. - * - * @return boolean flag indicating if key lacking parameters encountered. - */ - public boolean keyParamsNeeded(); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java b/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java index c1c03d86b7e..fd4eb9543e9 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ import sun.security.provider.certpath.PKIX.BuilderParams; import static sun.security.x509.PKIXExtensions.*; +import sun.security.x509.X509CertImpl; import sun.security.util.Debug; /** @@ -130,18 +131,21 @@ private PKIXCertPathBuilderResult build() throws CertPathBuilderException { List> adjList = new ArrayList<>(); PKIXCertPathBuilderResult result = buildCertPath(false, adjList); if (result == null) { - if (debug != null) { - debug.println("SunCertPathBuilder.engineBuild: 2nd pass; " + + if (buildParams.certStores().size() > 1 || Builder.USE_AIA) { + if (debug != null) { + debug.println("SunCertPathBuilder.engineBuild: 2nd pass; " + "try building again searching all certstores"); + } + // try again + adjList.clear(); + result = buildCertPath(true, adjList); + if (result != null) { + return result; + } } - // try again - adjList.clear(); - result = buildCertPath(true, adjList); - if (result == null) { - throw new SunCertPathBuilderException("unable to find valid " - + "certification path to requested target", - new AdjacencyList(adjList)); - } + throw new SunCertPathBuilderException("unable to find valid " + + "certification path to requested target", + new AdjacencyList(adjList)); } return result; } @@ -270,8 +274,8 @@ private void depthFirstSearchForward(X500Principal dN, /* * For each cert in the collection, verify anything * that hasn't been checked yet (signature, revocation, etc) - * and check for loops. Call depthFirstSearchForward() - * recursively for each good cert. + * and check for certs with repeated public key and subject. + * Call depthFirstSearchForward() recursively for each good cert. */ vertices: @@ -346,26 +350,24 @@ private void depthFirstSearchForward(X500Principal dN, checkers.add(new AlgorithmChecker(builder.trustAnchor, buildParams.timestamp(), buildParams.variant())); - BasicChecker basicChecker = null; - if (nextState.keyParamsNeeded()) { - PublicKey rootKey = cert.getPublicKey(); - if (builder.trustAnchor.getTrustedCert() == null) { - rootKey = builder.trustAnchor.getCAPublicKey(); - if (debug != null) - debug.println( - "SunCertPathBuilder.depthFirstSearchForward " + - "using buildParams public key: " + - rootKey.toString()); - } - TrustAnchor anchor = new TrustAnchor - (cert.getSubjectX500Principal(), rootKey, null); + PublicKey rootKey = cert.getPublicKey(); + if (builder.trustAnchor.getTrustedCert() == null) { + rootKey = builder.trustAnchor.getCAPublicKey(); + if (debug != null) + debug.println( + "SunCertPathBuilder.depthFirstSearchForward " + + "using buildParams public key: " + + rootKey.toString()); + } + TrustAnchor anchor = new TrustAnchor + (cert.getSubjectX500Principal(), rootKey, null); - // add the basic checker - basicChecker = new BasicChecker(anchor, buildParams.date(), + // add the basic checker + BasicChecker basicChecker = new BasicChecker(anchor, + buildParams.date(), buildParams.sigProvider(), true); - checkers.add(basicChecker); - } + checkers.add(basicChecker); buildParams.setCertPath(cf.generateCertPath(appendedCerts)); @@ -511,6 +513,14 @@ private void depthFirstSearchForward(X500Principal dN, policyTreeResult = policyChecker.getPolicyTree(); return; } else { + // If successive certs are self-issued, don't continue search + // on this branch. + if (currentState.selfIssued && X509CertImpl.isSelfIssued(cert)) { + if (debug != null) { + debug.println("Successive certs are self-issued"); + } + return; + } builder.addCertToPath(cert, cpList); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/X509CertificatePair.java b/src/java.base/share/classes/sun/security/provider/certpath/X509CertificatePair.java index ea139dd061d..35c64147e88 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/X509CertificatePair.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/X509CertificatePair.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -240,7 +240,7 @@ private void parse(DerValue val) } opt = opt.data.getDerValue(); forward = X509Factory.intern - (new X509CertImpl(opt.toByteArray())); + (X509CertImpl.newX509CertImpl(opt.toByteArray())); } break; case TAG_REVERSE: @@ -251,7 +251,7 @@ private void parse(DerValue val) } opt = opt.data.getDerValue(); reverse = X509Factory.intern - (new X509CertImpl(opt.toByteArray())); + (X509CertImpl.newX509CertImpl(opt.toByteArray())); } break; default: diff --git a/src/java.base/share/classes/sun/security/rsa/PSSParameters.java b/src/java.base/share/classes/sun/security/rsa/PSSParameters.java index 22da2303923..163cdfcd126 100644 --- a/src/java.base/share/classes/sun/security/rsa/PSSParameters.java +++ b/src/java.base/share/classes/sun/security/rsa/PSSParameters.java @@ -99,7 +99,7 @@ protected void engineInit(byte[] encoded) throws IOException { } else if (d.isContextSpecific((byte) 0x01)) { // mgf algid AlgorithmId val = AlgorithmId.parse(d.data.getDerValue()); - if (!val.getOID().equals(AlgorithmId.mgf1_oid)) { + if (!val.getOID().equals(AlgorithmId.MGF1_oid)) { throw new IOException("Only MGF1 mgf is supported"); } @@ -247,7 +247,7 @@ public static byte[] getEncoded(PSSParameterSpec spec) throws IOException { if (!mgfDigestId.getOID().equals(AlgorithmId.SHA_oid)) { tmp2 = new DerOutputStream(); - tmp2.putOID(AlgorithmId.mgf1_oid); + tmp2.putOID(AlgorithmId.MGF1_oid); mgfDigestId.encode(tmp2); tmp3 = new DerOutputStream(); tmp3.write(DerValue.tag_Sequence, tmp2); diff --git a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java index 927044c0c6c..eb8a7388616 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * 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,8 +32,7 @@ import java.security.spec.*; import sun.security.action.GetPropertyAction; -import sun.security.x509.AlgorithmId; -import static sun.security.rsa.RSAUtil.KeyType; +import sun.security.rsa.RSAUtil.KeyType; /** * KeyFactory for RSA keys, e.g. "RSA", "RSASSA-PSS". @@ -44,13 +43,15 @@ * between the following: * * For public keys: - * . PublicKey with an X.509 encoding + * . RSA PublicKey with an X.509 encoding + * . RSA PublicKey with an PKCS#1 encoding * . RSAPublicKey * . RSAPublicKeySpec * . X509EncodedKeySpec * * For private keys: - * . PrivateKey with a PKCS#8 encoding + * . RSA PrivateKey with a PKCS#8 encoding + * . RSA PrivateKey with a PKCS#1 encoding * . RSAPrivateKey * . RSAPrivateCrtKey * . RSAPrivateKeySpec @@ -96,8 +97,8 @@ static RSAKeyFactory getInstance(KeyType type) { return new RSAKeyFactory(type); } - // Internal utility method for checking key algorithm - private static void checkKeyAlgo(Key key, String expectedAlg) + // pkg-private utility method for checking key algorithm + static void checkKeyAlgo(Key key, String expectedAlg) throws InvalidKeyException { String keyAlg = key.getAlgorithm(); if (keyAlg == null || !(keyAlg.equalsIgnoreCase(expectedAlg))) { @@ -211,7 +212,7 @@ protected Key engineTranslateKey(Key key) throws InvalidKeyException { throw new InvalidKeyException("Key must not be null"); } // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(key, type.keyAlgo()); + checkKeyAlgo(key, type.keyAlgo); // no translation needed if the key is already our own impl if ((key instanceof RSAPrivateKeyImpl) || @@ -259,21 +260,17 @@ private PublicKey translatePublicKey(PublicKey key) RSAPublicKey rsaKey = (RSAPublicKey)key; try { return new RSAPublicKeyImpl( - RSAUtil.createAlgorithmId(type, rsaKey.getParams()), + type, rsaKey.getParams(), rsaKey.getModulus(), rsaKey.getPublicExponent()); } catch (ProviderException e) { // catch providers that incorrectly implement RSAPublicKey throw new InvalidKeyException("Invalid key", e); } - } else if ("X.509".equals(key.getFormat())) { - RSAPublicKey translated = new RSAPublicKeyImpl(key.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(translated, type.keyAlgo()); - return translated; } else { - throw new InvalidKeyException("Public keys must be instance " - + "of RSAPublicKey or have X.509 encoding"); + // create new key based on the format and encoding of current 'key' + return RSAPublicKeyImpl.newKey(type, key.getFormat(), + key.getEncoded()); } } @@ -284,7 +281,7 @@ private PrivateKey translatePrivateKey(PrivateKey key) RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key; try { return new RSAPrivateCrtKeyImpl( - RSAUtil.createAlgorithmId(type, rsaKey.getParams()), + type, rsaKey.getParams(), rsaKey.getModulus(), rsaKey.getPublicExponent(), rsaKey.getPrivateExponent(), @@ -302,7 +299,7 @@ private PrivateKey translatePrivateKey(PrivateKey key) RSAPrivateKey rsaKey = (RSAPrivateKey)key; try { return new RSAPrivateKeyImpl( - RSAUtil.createAlgorithmId(type, rsaKey.getParams()), + type, rsaKey.getParams(), rsaKey.getModulus(), rsaKey.getPrivateExponent() ); @@ -310,15 +307,9 @@ private PrivateKey translatePrivateKey(PrivateKey key) // catch providers that incorrectly implement RSAPrivateKey throw new InvalidKeyException("Invalid key", e); } - } else if ("PKCS#8".equals(key.getFormat())) { - RSAPrivateKey translated = - RSAPrivateCrtKeyImpl.newKey(key.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(translated, type.keyAlgo()); - return translated; } else { - throw new InvalidKeyException("Private keys must be instance " - + "of RSAPrivate(Crt)Key or have PKCS#8 encoding"); + return RSAPrivateCrtKeyImpl.newKey(type, key.getFormat(), + key.getEncoded()); } } @@ -326,16 +317,13 @@ private PrivateKey translatePrivateKey(PrivateKey key) private PublicKey generatePublic(KeySpec keySpec) throws GeneralSecurityException { if (keySpec instanceof X509EncodedKeySpec) { - X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec; - RSAPublicKey generated = new RSAPublicKeyImpl(x509Spec.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(generated, type.keyAlgo()); - return generated; + return RSAPublicKeyImpl.newKey(type, "X.509", + ((X509EncodedKeySpec)keySpec).getEncoded()); } else if (keySpec instanceof RSAPublicKeySpec) { RSAPublicKeySpec rsaSpec = (RSAPublicKeySpec)keySpec; try { return new RSAPublicKeyImpl( - RSAUtil.createAlgorithmId(type, rsaSpec.getParams()), + type, rsaSpec.getParams(), rsaSpec.getModulus(), rsaSpec.getPublicExponent() ); @@ -352,16 +340,13 @@ private PublicKey generatePublic(KeySpec keySpec) private PrivateKey generatePrivate(KeySpec keySpec) throws GeneralSecurityException { if (keySpec instanceof PKCS8EncodedKeySpec) { - PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec; - RSAPrivateKey generated = RSAPrivateCrtKeyImpl.newKey(pkcsSpec.getEncoded()); - // ensure the key algorithm matches the current KeyFactory instance - checkKeyAlgo(generated, type.keyAlgo()); - return generated; + return RSAPrivateCrtKeyImpl.newKey(type, "PKCS#8", + ((PKCS8EncodedKeySpec)keySpec).getEncoded()); } else if (keySpec instanceof RSAPrivateCrtKeySpec) { RSAPrivateCrtKeySpec rsaSpec = (RSAPrivateCrtKeySpec)keySpec; try { return new RSAPrivateCrtKeyImpl( - RSAUtil.createAlgorithmId(type, rsaSpec.getParams()), + type, rsaSpec.getParams(), rsaSpec.getModulus(), rsaSpec.getPublicExponent(), rsaSpec.getPrivateExponent(), @@ -378,7 +363,7 @@ private PrivateKey generatePrivate(KeySpec keySpec) RSAPrivateKeySpec rsaSpec = (RSAPrivateKeySpec)keySpec; try { return new RSAPrivateKeyImpl( - RSAUtil.createAlgorithmId(type, rsaSpec.getParams()), + type, rsaSpec.getParams(), rsaSpec.getModulus(), rsaSpec.getPrivateExponent() ); @@ -396,7 +381,8 @@ protected T engineGetKeySpec(Key key, Class keySpec) try { // convert key to one of our keys // this also verifies that the key is a valid RSA key and ensures - // that the encoding is X.509/PKCS#8 for public/private keys + // that the encoding is X.509/PKCS#8 or PKCS#1 for public/private + // keys key = engineTranslateKey(key); } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); diff --git a/src/java.base/share/classes/sun/security/rsa/RSAKeyPairGenerator.java b/src/java.base/share/classes/sun/security/rsa/RSAKeyPairGenerator.java index 4d2f9952368..6bd041b60f1 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAKeyPairGenerator.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAKeyPairGenerator.java @@ -32,10 +32,10 @@ import java.security.spec.RSAKeyGenParameterSpec; import sun.security.jca.JCAUtil; +import sun.security.rsa.RSAUtil.KeyType; + import static sun.security.util.SecurityProviderConstants.DEF_RSA_KEY_SIZE; import static sun.security.util.SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE; -import sun.security.x509.AlgorithmId; -import static sun.security.rsa.RSAUtil.KeyType; /** * RSA keypair generation. Standard algorithm, minimum key length 512 bit. @@ -55,7 +55,7 @@ public abstract class RSAKeyPairGenerator extends KeyPairGeneratorSpi { private int keySize; private final KeyType type; - private AlgorithmId rsaId; + private AlgorithmParameterSpec keyParams; // PRNG to use private SecureRandom random; @@ -116,7 +116,7 @@ public void initialize(AlgorithmParameterSpec params, SecureRandom random) } try { - this.rsaId = RSAUtil.createAlgorithmId(type, tmpParams); + this.keyParams = RSAUtil.checkParamsAgainstType(type, tmpParams); } catch (ProviderException e) { throw new InvalidAlgorithmParameterException( "Invalid key parameters", e); @@ -177,9 +177,10 @@ public KeyPair generateKeyPair() { BigInteger coeff = q.modInverse(p); try { - PublicKey publicKey = new RSAPublicKeyImpl(rsaId, n, e); - PrivateKey privateKey = new RSAPrivateCrtKeyImpl( - rsaId, n, e, d, p, q, pe, qe, coeff); + PublicKey publicKey = new RSAPublicKeyImpl(type, keyParams, + n, e); + PrivateKey privateKey = new RSAPrivateCrtKeyImpl(type, + keyParams, n, e, d, p, q, pe, qe, coeff); return new KeyPair(publicKey, privateKey); } catch (InvalidKeyException exc) { // invalid key exception only thrown for keys < 512 bit, diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java index 7ba5fb93d64..df6b444ef7a 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,10 +34,9 @@ import sun.security.util.*; -import sun.security.x509.AlgorithmId; import sun.security.pkcs.PKCS8Key; -import static sun.security.rsa.RSAUtil.KeyType; +import sun.security.rsa.RSAUtil.KeyType; /** * RSA private key implementation for "RSA", "RSASSA-PSS" algorithms in CRT form. @@ -66,27 +65,52 @@ public final class RSAPrivateCrtKeyImpl private BigInteger qe; // prime exponent q private BigInteger coeff; // CRT coeffcient + private transient KeyType type; + // Optional parameters associated with this RSA key // specified in the encoding of its AlgorithmId. // Must be null for "RSA" keys. - private AlgorithmParameterSpec keyParams; + private transient AlgorithmParameterSpec keyParams; /** - * Generate a new key from its encoding. Returns a CRT key if possible - * and a non-CRT key otherwise. Used by RSAKeyFactory. + * Generate a new RSAPrivate(Crt)Key from the specified type, + * format and encoding. Returns a CRT key if possible and a non-CRT + * key otherwise. + * Also used by SunPKCS11 provider. */ - public static RSAPrivateKey newKey(byte[] encoded) - throws InvalidKeyException { - RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded); - // check all CRT-specific components are available, if any one - // missing, return a non-CRT key instead - if (checkComponents(key)) { - return key; - } else { - return new RSAPrivateKeyImpl( - key.algid, - key.getModulus(), - key.getPrivateExponent()); + public static RSAPrivateKey newKey(KeyType type, String format, + byte[] encoded) throws InvalidKeyException { + switch (format) { + case "PKCS#8": + RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded); + RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo); + // check all CRT-specific components are available, if any one + // missing, return a non-CRT key instead + if (checkComponents(key)) { + return key; + } else { + return new RSAPrivateKeyImpl(key.type, key.keyParams, + key.getModulus(), key.getPrivateExponent()); + } + case "PKCS#1": + try { + BigInteger[] comps = parseASN1(encoded); + if ((comps[1].signum() == 0) || (comps[3].signum() == 0) || + (comps[4].signum() == 0) || (comps[5].signum() == 0) || + (comps[6].signum() == 0) || (comps[7].signum() == 0)) { + return new RSAPrivateKeyImpl(type, null, comps[0], + comps[2]); + } else { + return new RSAPrivateCrtKeyImpl(type, null, comps[0], + comps[1], comps[2], comps[3], comps[4], comps[5], + comps[6], comps[7]); + } + } catch (IOException ioe) { + throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe); + } + default: + throw new InvalidKeyException("Unsupported RSA Private(Crt)Key " + + "format: " + format); } } @@ -113,14 +137,13 @@ public static RSAPrivateKey newKey(KeyType type, BigInteger p, BigInteger q, BigInteger pe, BigInteger qe, BigInteger coeff) throws InvalidKeyException { RSAPrivateKey key; - AlgorithmId rsaId = RSAUtil.createAlgorithmId(type, params); if ((e.signum() == 0) || (p.signum() == 0) || (q.signum() == 0) || (pe.signum() == 0) || (qe.signum() == 0) || (coeff.signum() == 0)) { // if any component is missing, return a non-CRT key - return new RSAPrivateKeyImpl(rsaId, n, d); + return new RSAPrivateKeyImpl(type, params, n, d); } else { - return new RSAPrivateCrtKeyImpl(rsaId, n, e, d, + return new RSAPrivateCrtKeyImpl(type, params, n, e, d, p, q, pe, qe, coeff); } } @@ -128,7 +151,7 @@ public static RSAPrivateKey newKey(KeyType type, /** * Construct a key from its encoding. Called from newKey above. */ - RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException { + private RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException { if (encoded == null || encoded.length == 0) { throw new InvalidKeyException("Missing key encoding"); } @@ -136,8 +159,10 @@ public static RSAPrivateKey newKey(KeyType type, decode(encoded); RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); try { - // this will check the validity of params - this.keyParams = RSAUtil.getParamSpec(algid); + // check the validity of oid and params + Object[] o = RSAUtil.getTypeAndParamSpec(algid); + this.type = (KeyType) o[0]; + this.keyParams = (AlgorithmParameterSpec) o[1]; } catch (ProviderException e) { throw new InvalidKeyException(e); } @@ -147,7 +172,7 @@ public static RSAPrivateKey newKey(KeyType type, * Construct a RSA key from its components. Used by the * RSAKeyFactory and the RSAKeyPairGenerator. */ - RSAPrivateCrtKeyImpl(AlgorithmId rsaId, + RSAPrivateCrtKeyImpl(KeyType type, AlgorithmParameterSpec keyParams, BigInteger n, BigInteger e, BigInteger d, BigInteger p, BigInteger q, BigInteger pe, BigInteger qe, BigInteger coeff) throws InvalidKeyException { @@ -161,11 +186,19 @@ public static RSAPrivateKey newKey(KeyType type, this.pe = pe; this.qe = qe; this.coeff = coeff; - this.keyParams = RSAUtil.getParamSpec(rsaId); - // generate the encoding - algid = rsaId; try { + // validate and generate the algid encoding + algid = RSAUtil.createAlgorithmId(type, keyParams); + } catch (ProviderException exc) { + throw new InvalidKeyException(exc); + } + + this.type = type; + this.keyParams = keyParams; + + try { + // generate the key encoding DerOutputStream out = new DerOutputStream(); out.putInteger(0); // version must be 0 out.putInteger(n); @@ -188,7 +221,7 @@ public static RSAPrivateKey newKey(KeyType type, // see JCA doc @Override public String getAlgorithm() { - return algid.getName(); + return type.keyAlgo; } // see JCA doc @@ -248,9 +281,39 @@ public AlgorithmParameterSpec getParams() { // return a string representation of this key for debugging @Override public String toString() { - return "SunRsaSign " + getAlgorithm() + " private CRT key, " + n.bitLength() - + " bits" + "\n params: " + keyParams + "\n modulus: " + n - + "\n private exponent: " + d; + return "SunRsaSign " + type.keyAlgo + " private CRT key, " + + n.bitLength() + " bits" + "\n params: " + keyParams + + "\n modulus: " + n + "\n private exponent: " + d; + } + + // utility method for parsing DER encoding of RSA private keys in PKCS#1 + // format as defined in RFC 8017 Appendix A.1.2, i.e. SEQ of version, n, + // e, d, p, q, pe, qe, and coeff, and return the parsed components. + private static BigInteger[] parseASN1(byte[] raw) throws IOException { + DerValue derValue = new DerValue(raw); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + int version = derValue.data.getInteger(); + if (version != 0) { + throw new IOException("Version must be 0"); + } + + BigInteger[] result = new BigInteger[8]; // n, e, d, p, q, pe, qe, coeff + /* + * Some implementations do not correctly encode ASN.1 INTEGER values + * in 2's complement format, resulting in a negative integer when + * decoded. Correct the error by converting it to a positive integer. + * + * See CR 6255949 + */ + for (int i = 0; i < result.length; i++) { + result[i] = derValue.data.getPositiveBigInteger(); + } + if (derValue.data.available() != 0) { + throw new IOException("Extra data available"); + } + return result; } /** @@ -258,35 +321,15 @@ public String toString() { */ protected void parseKeyBits() throws InvalidKeyException { try { - DerInputStream in = new DerInputStream(key); - DerValue derValue = in.getDerValue(); - if (derValue.tag != DerValue.tag_Sequence) { - throw new IOException("Not a SEQUENCE"); - } - DerInputStream data = derValue.data; - int version = data.getInteger(); - if (version != 0) { - throw new IOException("Version must be 0"); - } - - /* - * Some implementations do not correctly encode ASN.1 INTEGER values - * in 2's complement format, resulting in a negative integer when - * decoded. Correct the error by converting it to a positive integer. - * - * See CR 6255949 - */ - n = data.getPositiveBigInteger(); - e = data.getPositiveBigInteger(); - d = data.getPositiveBigInteger(); - p = data.getPositiveBigInteger(); - q = data.getPositiveBigInteger(); - pe = data.getPositiveBigInteger(); - qe = data.getPositiveBigInteger(); - coeff = data.getPositiveBigInteger(); - if (derValue.data.available() != 0) { - throw new IOException("Extra data available"); - } + BigInteger[] comps = parseASN1(key); + n = comps[0]; + e = comps[1]; + d = comps[2]; + p = comps[3]; + q = comps[4]; + pe = comps[5]; + qe = comps[6]; + coeff = comps[7]; } catch (IOException e) { throw new InvalidKeyException("Invalid RSA private key", e); } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java index 792018d4f08..af639a2535c 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * 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,9 +33,10 @@ import java.security.interfaces.*; import sun.security.util.*; -import sun.security.x509.AlgorithmId; import sun.security.pkcs.PKCS8Key; +import sun.security.rsa.RSAUtil.KeyType; + /** * RSA private key implementation for "RSA", "RSASSA-PSS" algorithms in non-CRT * form (modulus, private exponent only). For CRT private keys, see @@ -57,26 +58,37 @@ public final class RSAPrivateKeyImpl extends PKCS8Key implements RSAPrivateKey { private final BigInteger n; // modulus private final BigInteger d; // private exponent + private transient final KeyType type; + // optional parameters associated with this RSA key // specified in the encoding of its AlgorithmId. // must be null for "RSA" keys. - private final AlgorithmParameterSpec keyParams; + private transient final AlgorithmParameterSpec keyParams; /** * Construct a key from its components. Used by the * RSAKeyFactory and the RSAKeyPairGenerator. */ - RSAPrivateKeyImpl(AlgorithmId rsaId, BigInteger n, BigInteger d) - throws InvalidKeyException { + RSAPrivateKeyImpl(KeyType type, AlgorithmParameterSpec keyParams, + BigInteger n, BigInteger d) throws InvalidKeyException { + RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), null); this.n = n; this.d = d; - this.keyParams = RSAUtil.getParamSpec(rsaId); - // generate the encoding - algid = rsaId; try { + // validate and generate the algid encoding + algid = RSAUtil.createAlgorithmId(type, keyParams); + } catch (ProviderException pe) { + throw new InvalidKeyException(pe); + } + + this.type = type; + this.keyParams = keyParams; + + try { + // generate the key encoding DerOutputStream out = new DerOutputStream(); out.putInteger(0); // version must be 0 out.putInteger(n); @@ -99,7 +111,7 @@ public final class RSAPrivateKeyImpl extends PKCS8Key implements RSAPrivateKey { // see JCA doc @Override public String getAlgorithm() { - return algid.getName(); + return type.keyAlgo; } // see JCA doc @@ -123,7 +135,7 @@ public AlgorithmParameterSpec getParams() { // return a string representation of this key for debugging @Override public String toString() { - return "Sun " + getAlgorithm() + " private key, " + n.bitLength() + return "Sun " + type.keyAlgo + " private key, " + n.bitLength() + " bits" + "\n params: " + keyParams + "\n modulus: " + n + "\n private exponent: " + d; } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java index 2489107a8c9..a12786e81c8 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,9 +34,8 @@ import sun.security.util.*; import sun.security.x509.X509Key; -import sun.security.x509.AlgorithmId; -import static sun.security.rsa.RSAUtil.KeyType; +import sun.security.rsa.RSAUtil.KeyType; /** * RSA public key implementation for "RSA", "RSASSA-PSS" algorithms. @@ -58,47 +57,76 @@ public final class RSAPublicKeyImpl extends X509Key implements RSAPublicKey { private BigInteger n; // modulus private BigInteger e; // public exponent + private transient KeyType type; + // optional parameters associated with this RSA key // specified in the encoding of its AlgorithmId // must be null for "RSA" keys. - private AlgorithmParameterSpec keyParams; + private transient AlgorithmParameterSpec keyParams; /** - * Generate a new RSAPublicKey from the specified encoding. - * Used by SunPKCS11 provider. + * Generate a new RSAPublicKey from the specified type, format, and + * encoding. + * Also used by SunPKCS11 provider. */ - public static RSAPublicKey newKey(byte[] encoded) - throws InvalidKeyException { - return new RSAPublicKeyImpl(encoded); + public static RSAPublicKey newKey(KeyType type, String format, + byte[] encoded) throws InvalidKeyException { + RSAPublicKey key; + switch (format) { + case "X.509": + key = new RSAPublicKeyImpl(encoded); + RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo); + break; + case "PKCS#1": + try { + BigInteger[] comps = parseASN1(encoded); + key = new RSAPublicKeyImpl(type, null, comps[0], comps[1]); + } catch (IOException ioe) { + throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe); + } + break; + default: + throw new InvalidKeyException("Unsupported RSA PublicKey format: " + + format); + } + return key; } /** * Generate a new RSAPublicKey from the specified type and components. - * Used by SunPKCS11 provider. + * Also used by SunPKCS11 provider. */ public static RSAPublicKey newKey(KeyType type, AlgorithmParameterSpec params, BigInteger n, BigInteger e) throws InvalidKeyException { - AlgorithmId rsaId = RSAUtil.createAlgorithmId(type, params); - return new RSAPublicKeyImpl(rsaId, n, e); + return new RSAPublicKeyImpl(type, params, n, e); } /** - * Construct a RSA key from AlgorithmId and its components. Used by + * Construct a RSA key from the specified type and components. Used by * RSAKeyFactory and RSAKeyPairGenerator. */ - RSAPublicKeyImpl(AlgorithmId rsaId, BigInteger n, BigInteger e) - throws InvalidKeyException { + RSAPublicKeyImpl(KeyType type, AlgorithmParameterSpec keyParams, + BigInteger n, BigInteger e) throws InvalidKeyException { + RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); checkExponentRange(n, e); this.n = n; this.e = e; - this.keyParams = RSAUtil.getParamSpec(rsaId); - // generate the encoding - algid = rsaId; try { + // validate and generate algid encoding + algid = RSAUtil.createAlgorithmId(type, keyParams); + } catch (ProviderException pe) { + throw new InvalidKeyException(pe); + } + + this.type = type; + this.keyParams = keyParams; + + try { + // generate the key encoding DerOutputStream out = new DerOutputStream(); out.putInteger(n); out.putInteger(e); @@ -113,9 +141,9 @@ public static RSAPublicKey newKey(KeyType type, } /** - * Construct a key from its encoding. Used by RSAKeyFactory. + * Construct a key from its encoding. */ - RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException { + private RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException { if (encoded == null || encoded.length == 0) { throw new InvalidKeyException("Missing key encoding"); } @@ -124,8 +152,10 @@ public static RSAPublicKey newKey(KeyType type, checkExponentRange(n, e); try { - // this will check the validity of params - this.keyParams = RSAUtil.getParamSpec(algid); + // check the validity of oid and params + Object[] o = RSAUtil.getTypeAndParamSpec(algid); + this.type = (KeyType) o[0]; + this.keyParams = (AlgorithmParameterSpec) o[1]; } catch (ProviderException e) { throw new InvalidKeyException(e); } @@ -148,7 +178,7 @@ static void checkExponentRange(BigInteger mod, BigInteger exp) // see JCA doc @Override public String getAlgorithm() { - return algid.getName(); + return type.keyAlgo; } // see JCA doc @@ -169,22 +199,30 @@ public AlgorithmParameterSpec getParams() { return keyParams; } + // utility method for parsing DER encoding of RSA public keys in PKCS#1 + // format as defined in RFC 8017 Appendix A.1.1, i.e. SEQ of n and e. + private static BigInteger[] parseASN1(byte[] raw) throws IOException { + DerValue derValue = new DerValue(raw); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + BigInteger[] result = new BigInteger[2]; // n, e + result[0] = derValue.data.getPositiveBigInteger(); + result[1] = derValue.data.getPositiveBigInteger(); + if (derValue.data.available() != 0) { + throw new IOException("Extra data available"); + } + return result; + } + /** * Parse the key. Called by X509Key. */ protected void parseKeyBits() throws InvalidKeyException { try { - DerInputStream in = new DerInputStream(getKey().toByteArray()); - DerValue derValue = in.getDerValue(); - if (derValue.tag != DerValue.tag_Sequence) { - throw new IOException("Not a SEQUENCE"); - } - DerInputStream data = derValue.data; - n = data.getPositiveBigInteger(); - e = data.getPositiveBigInteger(); - if (derValue.data.available() != 0) { - throw new IOException("Extra data available"); - } + BigInteger[] comps = parseASN1(getKey().toByteArray()); + n = comps[0]; + e = comps[1]; } catch (IOException e) { throw new InvalidKeyException("Invalid RSA public key", e); } @@ -193,7 +231,7 @@ protected void parseKeyBits() throws InvalidKeyException { // return a string representation of this key for debugging @Override public String toString() { - return "Sun " + getAlgorithm() + " public key, " + n.bitLength() + return "Sun " + type.keyAlgo + " public key, " + n.bitLength() + " bits" + "\n params: " + keyParams + "\n modulus: " + n + "\n public exponent: " + e; } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAUtil.java b/src/java.base/share/classes/sun/security/rsa/RSAUtil.java index d4a60a04545..1d2e991e1bd 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAUtil.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,52 +40,71 @@ public class RSAUtil { public enum KeyType { - RSA ("RSA"), - PSS ("RSASSA-PSS") + RSA ("RSA", AlgorithmId.RSAEncryption_oid, null), + PSS ("RSASSA-PSS", AlgorithmId.RSASSA_PSS_oid, PSSParameterSpec.class) ; - private final String algo; + final String keyAlgo; + final ObjectIdentifier oid; + final Class paramSpecCls; - KeyType(String keyAlgo) { - this.algo = keyAlgo; + KeyType(String keyAlgo, ObjectIdentifier oid, + Class paramSpecCls) { + this.keyAlgo = keyAlgo; + this.oid = oid; + this.paramSpecCls = paramSpecCls; } - public String keyAlgo() { - return algo; - } - public static KeyType lookup(String name) - throws InvalidKeyException, ProviderException { - if (name == null) { - throw new InvalidKeyException("Null key algorithm"); - } - for (KeyType kt : KeyType.values()) { - if (kt.keyAlgo().equalsIgnoreCase(name)) { - return kt; - } + + public static KeyType lookup(String name) throws ProviderException { + + requireNonNull(name, "Key algorithm should not be null"); + + // match loosely in order to work with 3rd party providers which + // may not follow the standard names + if (name.indexOf("PSS") != -1) { + return PSS; + } else if (name.indexOf("RSA") != -1) { + return RSA; + } else { // no match + throw new ProviderException("Unsupported algorithm " + name); } - // no match - throw new ProviderException("Unsupported algorithm " + name); } } - public static void checkParamsAgainstType(KeyType type, + private static void requireNonNull(Object obj, String msg) { + if (obj == null) throw new ProviderException(msg); + } + + public static AlgorithmParameterSpec checkParamsAgainstType(KeyType type, AlgorithmParameterSpec paramSpec) throws ProviderException { - switch (type) { - case RSA: - if (paramSpec != null) { - throw new ProviderException("null params expected for " + - type.keyAlgo()); - } - break; - case PSS: - if ((paramSpec != null) && - !(paramSpec instanceof PSSParameterSpec)) { - throw new ProviderException - ("PSSParmeterSpec expected for " + type.keyAlgo()); - } - break; - default: - throw new ProviderException - ("Unsupported RSA algorithm " + type); + + // currently no check for null parameter spec + // assumption is parameter spec is optional and can be null + if (paramSpec == null) return null; + + Class expCls = type.paramSpecCls; + if (expCls == null) { + throw new ProviderException("null params expected for " + + type.keyAlgo); + } else if (!expCls.isInstance(paramSpec)) { + throw new ProviderException + (expCls + " expected for " + type.keyAlgo); + } + return paramSpec; + } + + public static AlgorithmParameters getParams(KeyType type, + AlgorithmParameterSpec spec) throws ProviderException { + + if (spec == null) return null; + + try { + AlgorithmParameters params = + AlgorithmParameters.getInstance(type.keyAlgo); + params.init(spec); + return params; + } catch (NoSuchAlgorithmException | InvalidParameterSpecException ex) { + throw new ProviderException(ex); } } @@ -94,69 +113,53 @@ public static AlgorithmId createAlgorithmId(KeyType type, checkParamsAgainstType(type, paramSpec); - ObjectIdentifier oid = null; - AlgorithmParameters params = null; - try { - switch (type) { - case RSA: - oid = AlgorithmId.RSAEncryption_oid; - break; - case PSS: - if (paramSpec != null) { - params = AlgorithmParameters.getInstance(type.keyAlgo()); - params.init(paramSpec); - } - oid = AlgorithmId.RSASSA_PSS_oid; - break; - default: - throw new ProviderException - ("Unsupported RSA algorithm " + type); - } - AlgorithmId result; - if (params == null) { - result = new AlgorithmId(oid); - } else { - result = new AlgorithmId(oid, params); - } - return result; - } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { - // should not happen - throw new ProviderException(e); - } + ObjectIdentifier oid = type.oid; + AlgorithmParameters params = getParams(type, paramSpec); + return new AlgorithmId(oid, params); } - public static AlgorithmParameterSpec getParamSpec(AlgorithmId algid) - throws ProviderException { - if (algid == null) { - throw new ProviderException("AlgorithmId should not be null"); + public static AlgorithmParameterSpec getParamSpec( + AlgorithmParameters params) throws ProviderException { + + if (params == null) return null; + + String algName = params.getAlgorithm(); + + KeyType type = KeyType.lookup(algName); + Class specCls = type.paramSpecCls; + if (specCls == null) { + throw new ProviderException("No params accepted for " + + type.keyAlgo); + } + try { + return params.getParameterSpec(specCls); + } catch (InvalidParameterSpecException ex) { + throw new ProviderException(ex); } - return getParamSpec(algid.getParameters()); } - public static AlgorithmParameterSpec getParamSpec(AlgorithmParameters params) + public static Object[] getTypeAndParamSpec(AlgorithmId algid) throws ProviderException { - if (params == null) return null; + requireNonNull(algid, "AlgorithmId should not be null"); + + Object[] result = new Object[2]; + + String algName = algid.getName(); try { - String algName = params.getAlgorithm(); - KeyType type = KeyType.lookup(algName); - Class specCls; - switch (type) { - case RSA: - throw new ProviderException("No params accepted for " + - type.keyAlgo()); - case PSS: - specCls = PSSParameterSpec.class; - break; - default: - throw new ProviderException("Unsupported RSA algorithm: " + algName); - } - return params.getParameterSpec(specCls); + result[0] = KeyType.lookup(algName); } catch (ProviderException pe) { - // pass it up - throw pe; - } catch (Exception e) { - throw new ProviderException(e); + // accommodate RSA keys encoded with various RSA signature oids + // for backward compatibility + if (algName.indexOf("RSA") != -1) { + result[0] = KeyType.RSA; + } else { + // pass it up + throw pe; + } } + + result[1] = getParamSpec(algid.getParameters()); + return result; } } diff --git a/src/java.base/share/classes/sun/security/rsa/SunRsaSignEntries.java b/src/java.base/share/classes/sun/security/rsa/SunRsaSignEntries.java index a5cab587d28..f3edf4f25e1 100644 --- a/src/java.base/share/classes/sun/security/rsa/SunRsaSignEntries.java +++ b/src/java.base/share/classes/sun/security/rsa/SunRsaSignEntries.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ import java.util.*; import java.security.Provider; -import static sun.security.provider.SunEntries.createAliasesWithOid; +import static sun.security.util.SecurityProviderConstants.getAliases; /** * Defines the entries of the SunRsaSign provider. @@ -38,7 +38,14 @@ public final class SunRsaSignEntries { private void add(Provider p, String type, String algo, String cn, List aliases, HashMap attrs) { - services.add(new Provider.Service(p, type, algo, cn, aliases, attrs)); + services.add(new Provider.Service(p, type, algo, cn, + aliases, attrs)); + } + + private void addA(Provider p, String type, String algo, String cn, + HashMap attrs) { + services.add(new Provider.Service(p, type, algo, cn, + getAliases(algo), attrs)); } // extend LinkedHashSet for consistency with SunEntries @@ -47,13 +54,6 @@ public SunRsaSignEntries(Provider p) { services = new LinkedHashSet<>(20, 0.9f); // start populating content using the specified provider - - // common oids - String rsaOid = "1.2.840.113549.1.1"; - List rsaAliases = createAliasesWithOid(rsaOid); - List rsapssAliases = createAliasesWithOid(rsaOid + ".10"); - String sha1withRSAOid2 = "1.3.14.3.2.29"; - // common attribute map HashMap attrs = new HashMap<>(3); attrs.put("SupportedKeyClasses", @@ -62,50 +62,37 @@ public SunRsaSignEntries(Provider p) { add(p, "KeyFactory", "RSA", "sun.security.rsa.RSAKeyFactory$Legacy", - rsaAliases, null); + getAliases("PKCS1"), null); add(p, "KeyPairGenerator", "RSA", "sun.security.rsa.RSAKeyPairGenerator$Legacy", - rsaAliases, null); - add(p, "Signature", "MD2withRSA", - "sun.security.rsa.RSASignature$MD2withRSA", - createAliasesWithOid(rsaOid + ".2"), attrs); - add(p, "Signature", "MD5withRSA", - "sun.security.rsa.RSASignature$MD5withRSA", - createAliasesWithOid(rsaOid + ".4"), attrs); - add(p, "Signature", "SHA1withRSA", - "sun.security.rsa.RSASignature$SHA1withRSA", - createAliasesWithOid(rsaOid + ".5", sha1withRSAOid2), attrs); - add(p, "Signature", "SHA224withRSA", - "sun.security.rsa.RSASignature$SHA224withRSA", - createAliasesWithOid(rsaOid + ".14"), attrs); - add(p, "Signature", "SHA256withRSA", - "sun.security.rsa.RSASignature$SHA256withRSA", - createAliasesWithOid(rsaOid + ".11"), attrs); - add(p, "Signature", "SHA384withRSA", - "sun.security.rsa.RSASignature$SHA384withRSA", - createAliasesWithOid(rsaOid + ".12"), attrs); - add(p, "Signature", "SHA512withRSA", - "sun.security.rsa.RSASignature$SHA512withRSA", - createAliasesWithOid(rsaOid + ".13"), attrs); - add(p, "Signature", "SHA512/224withRSA", - "sun.security.rsa.RSASignature$SHA512_224withRSA", - createAliasesWithOid(rsaOid + ".15"), attrs); - add(p, "Signature", "SHA512/256withRSA", - "sun.security.rsa.RSASignature$SHA512_256withRSA", - createAliasesWithOid(rsaOid + ".16"), attrs); + getAliases("PKCS1"), null); + addA(p, "Signature", "MD2withRSA", + "sun.security.rsa.RSASignature$MD2withRSA", attrs); + addA(p, "Signature", "MD5withRSA", + "sun.security.rsa.RSASignature$MD5withRSA", attrs); + addA(p, "Signature", "SHA1withRSA", + "sun.security.rsa.RSASignature$SHA1withRSA", attrs); + addA(p, "Signature", "SHA224withRSA", + "sun.security.rsa.RSASignature$SHA224withRSA", attrs); + addA(p, "Signature", "SHA256withRSA", + "sun.security.rsa.RSASignature$SHA256withRSA", attrs); + addA(p, "Signature", "SHA384withRSA", + "sun.security.rsa.RSASignature$SHA384withRSA", attrs); + addA(p, "Signature", "SHA512withRSA", + "sun.security.rsa.RSASignature$SHA512withRSA", attrs); + addA(p, "Signature", "SHA512/224withRSA", + "sun.security.rsa.RSASignature$SHA512_224withRSA", attrs); + addA(p, "Signature", "SHA512/256withRSA", + "sun.security.rsa.RSASignature$SHA512_256withRSA", attrs); - add(p, "KeyFactory", "RSASSA-PSS", - "sun.security.rsa.RSAKeyFactory$PSS", - rsapssAliases, null); - add(p, "KeyPairGenerator", "RSASSA-PSS", - "sun.security.rsa.RSAKeyPairGenerator$PSS", - rsapssAliases, null); - add(p, "Signature", "RSASSA-PSS", - "sun.security.rsa.RSAPSSSignature", - rsapssAliases, attrs); - add(p, "AlgorithmParameters", "RSASSA-PSS", - "sun.security.rsa.PSSParameters", - rsapssAliases, null); + addA(p, "KeyFactory", "RSASSA-PSS", + "sun.security.rsa.RSAKeyFactory$PSS", attrs); + addA(p, "KeyPairGenerator", "RSASSA-PSS", + "sun.security.rsa.RSAKeyPairGenerator$PSS", attrs); + addA(p, "Signature", "RSASSA-PSS", + "sun.security.rsa.RSAPSSSignature", attrs); + addA(p, "AlgorithmParameters", "RSASSA-PSS", + "sun.security.rsa.PSSParameters", attrs); } public Iterator iterator() { diff --git a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java index 6accc74c6ba..d6662235029 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java @@ -327,45 +327,36 @@ public SSLPossession createPossession(HandshakeContext context) { } /* - * 768 bits ephemeral DH private keys were used to be used in + * 768 bit ephemeral DH private keys used to be used in * ServerKeyExchange except that exportable ciphers max out at 512 - * bits modulus values. We still adhere to this behavior in legacy + * bit modulus values. We still adhere to this behavior in legacy * mode (system property "jdk.tls.ephemeralDHKeySize" is defined * as "legacy"). * - * Old JDK (JDK 7 and previous) releases don't support DH keys - * bigger than 1024 bits. We have to consider the compatibility - * requirement. 1024 bits DH key is always used for non-exportable - * cipher suites in default mode (system property + * Only very old JDK releases don't support DH keys bigger than + * 1024 bits (JDK 1.5 and 6u/7u releases prior to adding support + * for DH keys > 1024 bits - see JDK-8062834). A 2048 bit + * DH key is always used for non-exportable cipher suites in + * default mode (when the system property * "jdk.tls.ephemeralDHKeySize" is not defined). * - * However, if applications want more stronger strength, setting - * system property "jdk.tls.ephemeralDHKeySize" to "matched" - * is a workaround to use ephemeral DH key which size matches the - * corresponding authentication key. For example, if the public key - * size of an authentication certificate is 2048 bits, then the - * ephemeral DH key size should be 2048 bits accordingly unless - * the cipher suite is exportable. This key sizing scheme keeps - * the cryptographic strength consistent between authentication - * keys and key-exchange keys. - * * Applications may also want to customize the ephemeral DH key * size to a fixed length for non-exportable cipher suites. This - * can be approached by setting system property + * can be done by setting the system property * "jdk.tls.ephemeralDHKeySize" to a valid positive integer between * 1024 and 8192 bits, inclusive. * - * Note that the minimum acceptable key size is 1024 bits except - * exportable cipher suites or legacy mode. + * Note that the minimum acceptable key size is 2048 bits except + * for exportable cipher suites or legacy mode. * * Note that per RFC 2246, the key size limit of DH is 512 bits for * exportable cipher suites. Because of the weakness, exportable * cipher suites are deprecated since TLS v1.1 and they are not * enabled by default in Oracle provider. The legacy behavior is - * reserved and 512 bits DH key is always used for exportable + * preserved and a 512 bit DH key is always used for exportable * cipher suites. */ - int keySize = exportable ? 512 : 1024; // default mode + int keySize = exportable ? 512 : 2048; // default mode if (!exportable) { if (useLegacyEphemeralDHKeys) { // legacy mode keySize = 768; @@ -391,7 +382,7 @@ public SSLPossession createPossession(HandshakeContext context) { // limit in the future when the compatibility and // interoperability impact is limited. keySize = ks <= 1024 ? 1024 : 2048; - } // Otherwise, anonymous cipher suites, 1024-bit is used. + } // Otherwise, anonymous cipher suites, 2048-bit is used. } else if (customizedDHKeySize > 0) { // customized mode keySize = customizedDHKeySize; } diff --git a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java index 13063444ee6..9e921e63633 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -169,7 +169,9 @@ private KeyUpdateKickstartProducer() { public byte[] produce(ConnectionContext context) throws IOException { PostHandshakeContext hc = (PostHandshakeContext)context; return handshakeProducer.produce(context, - new KeyUpdateMessage(hc, KeyUpdateRequest.REQUESTED)); + new KeyUpdateMessage(hc, hc.conContext.isInboundClosed() ? + KeyUpdateRequest.NOTREQUESTED : + KeyUpdateRequest.REQUESTED)); } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index c77c628158a..9daa0f7b07b 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -143,7 +143,7 @@ final class SSLConfiguration implements Cloneable { this.identificationProtocol = null; this.serverNames = Collections.emptyList(); this.sniMatchers = Collections.emptyList(); - this.preferLocalCipherSuites = false; + this.preferLocalCipherSuites = true; this.applicationProtocols = new String[0]; this.enableRetransmissions = sslContext.isDTLS(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java index a3db3ad0e10..42d6e7645e8 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -368,11 +368,11 @@ private HandshakeStatus tryToFinishHandshake(byte contentType) { */ private HandshakeStatus tryKeyUpdate( HandshakeStatus currentHandshakeStatus) throws IOException { - // Don't bother to kickstart if handshaking is in progress, or if the - // connection is not duplex-open. + // Don't bother to kickstart if handshaking is in progress, or if + // the write side of the connection is not open. We allow a half- + // duplex write-only connection for key updates. if ((conContext.handshakeContext == null) && !conContext.isOutboundClosed() && - !conContext.isInboundClosed() && !conContext.isBroken) { if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger key update"); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index 1222a4f4a17..4065391be72 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -640,8 +640,12 @@ private void duplexCloseOutput() throws IOException { if (!conContext.protocolVersion.useTLS13PlusSpec()) { hasCloseReceipt = true; } else { - // Use a user_canceled alert for TLS 1.3 duplex close. - useUserCanceled = true; + // Do not use user_canceled workaround if the other side has + // already half-closed the connection + if (!conContext.isInboundClosed()) { + // Use a user_canceled alert for TLS 1.3 duplex close. + useUserCanceled = true; + } } } else if (conContext.handshakeContext != null) { // initial handshake // Use user_canceled alert regardless the protocol versions. @@ -1538,11 +1542,11 @@ private Plaintext decode(ByteBuffer destination) throws IOException { * wrapped. */ private void tryKeyUpdate() throws IOException { - // Don't bother to kickstart if handshaking is in progress, or if the - // connection is not duplex-open. + // Don't bother to kickstart if handshaking is in progress, or if + // the write side of the connection is not open. We allow a half- + // duplex write-only connection for key updates. if ((conContext.handshakeContext == null) && !conContext.isOutboundClosed() && - !conContext.isInboundClosed() && !conContext.isBroken) { if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { SSLLogger.finest("trigger key update"); diff --git a/src/java.base/share/classes/sun/security/ssl/SunJSSE.java b/src/java.base/share/classes/sun/security/ssl/SunJSSE.java index de2a91a478c..891796f19bb 100644 --- a/src/java.base/share/classes/sun/security/ssl/SunJSSE.java +++ b/src/java.base/share/classes/sun/security/ssl/SunJSSE.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ import jdk.internal.misc.SharedSecrets; import sun.security.rsa.SunRsaSignEntries; import static sun.security.util.SecurityConstants.PROVIDER_VER; -import static sun.security.provider.SunEntries.createAliases; +import static sun.security.util.SecurityProviderConstants.*; /** * The JSSE provider. @@ -163,8 +163,8 @@ public Object run() { } private void ps(String type, String algo, String cn, - List aliases, HashMap attrs) { - putService(new Provider.Service(this, type, algo, cn, aliases, attrs)); + List a, HashMap attrs) { + putService(new Provider.Service(this, type, algo, cn, a, attrs)); } private void doRegister(boolean isfips) { @@ -182,17 +182,17 @@ private void doRegister(boolean isfips) { "sun.security.ssl.KeyManagerFactoryImpl$SunX509", null, null); ps("KeyManagerFactory", "NewSunX509", "sun.security.ssl.KeyManagerFactoryImpl$X509", - createAliases("PKIX"), null); + List.of("PKIX"), null); ps("TrustManagerFactory", "SunX509", "sun.security.ssl.TrustManagerFactoryImpl$SimpleFactory", null, null); ps("TrustManagerFactory", "PKIX", "sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", - createAliases("SunPKIX", "X509", "X.509"), null); + List.of("SunPKIX", "X509", "X.509"), null); ps("SSLContext", "TLSv1", "sun.security.ssl.SSLContextImpl$TLS10Context", - (isfips? null : createAliases("SSLv3")), null); + (isfips? null : List.of("SSLv3")), null); ps("SSLContext", "TLSv1.1", "sun.security.ssl.SSLContextImpl$TLS11Context", null, null); ps("SSLContext", "TLSv1.2", @@ -206,7 +206,7 @@ private void doRegister(boolean isfips) { } ps("SSLContext", "TLS", "sun.security.ssl.SSLContextImpl$TLSContext", - (isfips? null : createAliases("SSL")), null); + (isfips? null : List.of("SSL")), null); ps("SSLContext", "DTLSv1.0", "sun.security.ssl.SSLContextImpl$DTLS10Context", null, null); diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index 91266db6123..be3f480ae1f 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -201,7 +201,14 @@ void kickstart() throws IOException { throw new IllegalStateException("Client/Server mode not yet set."); } - if (outputRecord.isClosed() || inputRecord.isClosed() || isBroken) { + // The threshold for allowing the method to continue processing + // depends on whether we are doing a key update or kickstarting + // a handshake. In the former case, we only require the write-side + // to be open where a handshake would require a full duplex connection. + boolean isNotUsable = outputRecord.writeCipher.atKeyLimit() ? + (outputRecord.isClosed() || isBroken) : + (outputRecord.isClosed() || inputRecord.isClosed() || isBroken); + if (isNotUsable) { if (closeReason != null) { throw new SSLException( "Cannot kickstart, the connection is broken or closed", @@ -229,7 +236,7 @@ void kickstart() throws IOException { // // Need no kickstart message on server side unless the connection // has been established. - if(isNegotiated || sslConfig.isClientMode) { + if (isNegotiated || sslConfig.isClientMode) { handshakeContext.kickstart(); } } diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java index bf7ac162c1f..f5050c279fb 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java @@ -43,6 +43,7 @@ import javax.net.ssl.*; import sun.security.provider.certpath.AlgorithmChecker; import sun.security.validator.Validator; +import sun.security.util.KnownOIDs; /** * The new X509 key manager implementation. The main differences to the @@ -521,14 +522,19 @@ private static enum CheckType { // enum constant for "tls client" check // valid EKU for TLS client: any, tls_client - CLIENT(new HashSet(Arrays.asList(new String[] { - "2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))), + CLIENT(new HashSet(List.of( + KnownOIDs.anyExtendedKeyUsage.value(), + KnownOIDs.clientAuth.value() + ))), // enum constant for "tls server" check // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc - SERVER(new HashSet(Arrays.asList(new String[] { - "2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1", - "1.3.6.1.4.1.311.10.3.3" }))); + SERVER(new HashSet(List.of( + KnownOIDs.anyExtendedKeyUsage.value(), + KnownOIDs.serverAuth.value(), + KnownOIDs.NETSCAPE_ExportApproved.value(), + KnownOIDs.MICROSOFT_ExportApproved.value() + ))); // set of valid EKU values for this type final Set validEku; diff --git a/src/java.base/share/classes/sun/security/timestamp/TSRequest.java b/src/java.base/share/classes/sun/security/timestamp/TSRequest.java index 290981dc7e8..619f2be65ed 100644 --- a/src/java.base/share/classes/sun/security/timestamp/TSRequest.java +++ b/src/java.base/share/classes/sun/security/timestamp/TSRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -163,7 +163,7 @@ public byte[] encode() throws IOException { // encode optional elements if (policyId != null) { - request.putOID(new ObjectIdentifier(policyId)); + request.putOID(ObjectIdentifier.of(policyId)); } if (nonce != null) { request.putInteger(nonce); diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index 8bf24cdb9bb..c626e76edf5 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java @@ -4277,6 +4277,23 @@ private static Date getStartDate(String s) throws IOException { return c.getTime(); } + /** + * Match a command with a command set. The match can be exact, or + * partial, or case-insensitive. + * + * @param s the command provided by user + * @param list the legal command set represented by KnownOIDs enums. + * @return the position of a single match, or -1 if none matched + * @throws Exception if s is ambiguous + */ + private static int oneOf(String s, KnownOIDs... list) throws Exception { + String[] convertedList = new String[list.length]; + for (int i = 0; i < list.length; i++) { + convertedList[i] = list[i].stdName(); + } + return oneOf(s, convertedList); + } + /** * Match a command (may be abbreviated) with a command set. * @param s the command provided @@ -4395,7 +4412,7 @@ private ObjectIdentifier findOidForExtName(String type) case 5: return PKIXExtensions.SubjectInfoAccess_Id; case 6: return PKIXExtensions.AuthInfoAccess_Id; case 8: return PKIXExtensions.CRLDistributionPoints_Id; - default: return new ObjectIdentifier(type); + default: return ObjectIdentifier.of(type); } } @@ -4616,30 +4633,26 @@ private CertificateExtensions createV3Extensions( case 2: // EKU if(value != null) { Vector v = new Vector<>(); + KnownOIDs[] choices = { + KnownOIDs.anyExtendedKeyUsage, + KnownOIDs.serverAuth, + KnownOIDs.clientAuth, + KnownOIDs.codeSigning, + KnownOIDs.emailProtection, + KnownOIDs.KP_TimeStamping, + KnownOIDs.OCSPSigning + }; for (String s: value.split(",")) { - int p = oneOf(s, - "anyExtendedKeyUsage", - "serverAuth", //1 - "clientAuth", //2 - "codeSigning", //3 - "emailProtection", //4 - "", //5 - "", //6 - "", //7 - "timeStamping", //8 - "OCSPSigning" //9 - ); - if (p < 0) { - try { - v.add(new ObjectIdentifier(s)); - } catch (Exception e) { - throw new Exception(rb.getString( - "Unknown.extendedkeyUsage.type.") + s); - } - } else if (p == 0) { - v.add(new ObjectIdentifier("2.5.29.37.0")); - } else { - v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p)); + int p = oneOf(s, choices); + String o = s; + if (p >= 0) { + o = choices[p].value(); + } + try { + v.add(ObjectIdentifier.of(o)); + } catch (Exception e) { + throw new Exception(rb.getString( + "Unknown.extendedkeyUsage.type.") + s); } } setExt(result, new ExtendedKeyUsageExtension(isCritical, v)); @@ -4694,24 +4707,23 @@ private CertificateExtensions createV3Extensions( String m = item.substring(0, colonpos); String t = item.substring(colonpos+1, colonpos2); String v = item.substring(colonpos2+1); - int p = oneOf(m, - "", - "ocsp", //1 - "caIssuers", //2 - "timeStamping", //3 - "", - "caRepository" //5 - ); + KnownOIDs[] choices = { + KnownOIDs.OCSP, + KnownOIDs.caIssuers, + KnownOIDs.AD_TimeStamping, + KnownOIDs.caRepository + }; + int p = oneOf(m, choices); ObjectIdentifier oid; - if (p < 0) { + if (p >= 0) { + oid = ObjectIdentifier.of(choices[p]); + } else { try { - oid = new ObjectIdentifier(m); + oid = ObjectIdentifier.of(m); } catch (Exception e) { throw new Exception(rb.getString( "Unknown.AccessDescription.type.") + m); } - } else { - oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p); } accessDescriptions.add(new AccessDescription( oid, createGeneralName(t, v, exttype))); @@ -4748,7 +4760,7 @@ private CertificateExtensions createV3Extensions( } break; case -1: - ObjectIdentifier oid = new ObjectIdentifier(name); + ObjectIdentifier oid = ObjectIdentifier.of(name); byte[] data = null; if (value != null) { data = new byte[value.length() / 2 + 1]; diff --git a/src/java.base/share/classes/sun/security/util/ConstraintsParameters.java b/src/java.base/share/classes/sun/security/util/ConstraintsParameters.java index f7fc4bf3512..82ce9c97af2 100644 --- a/src/java.base/share/classes/sun/security/util/ConstraintsParameters.java +++ b/src/java.base/share/classes/sun/security/util/ConstraintsParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/java.base/share/classes/sun/security/util/CurveDB.java b/src/java.base/share/classes/sun/security/util/CurveDB.java index 7e6f62fe5a3..45dfb106a7a 100644 --- a/src/java.base/share/classes/sun/security/util/CurveDB.java +++ b/src/java.base/share/classes/sun/security/util/CurveDB.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ import java.security.spec.*; import java.util.*; -import java.util.regex.Pattern; /** * Repository for well-known Elliptic Curve parameters. It is used by both @@ -54,8 +53,6 @@ public class CurveDB { private static Collection specCollection; - public static final String SPLIT_PATTERN = ",|\\[|\\]"; - // Used by SunECEntries public static CollectiongetSupportedCurves() { return specCollection; @@ -117,9 +114,8 @@ private static BigInteger bi(String s) { return new BigInteger(s, 16); } - private static void add(String name, String soid, int type, String sfield, - String a, String b, String x, String y, String n, int h, - Pattern nameSplitPattern) { + private static void add(KnownOIDs o, int type, String sfield, + String a, String b, String x, String y, String n, int h) { BigInteger p = bi(sfield); ECField field; if ((type == P) || (type == PD)) { @@ -133,15 +129,16 @@ private static void add(String name, String soid, int type, String sfield, EllipticCurve curve = new EllipticCurve(field, bi(a), bi(b)); ECPoint g = new ECPoint(bi(x), bi(y)); - NamedCurve params = new NamedCurve(name, soid, curve, g, bi(n), h); - if (oidMap.put(soid, params) != null) { - throw new RuntimeException("Duplication oid: " + soid); + String oid = o.value(); + NamedCurve params = new NamedCurve(o, curve, g, bi(n), h); + if (oidMap.put(oid, params) != null) { + throw new RuntimeException("Duplication oid: " + oid); } - String[] commonNames = nameSplitPattern.split(name); - for (String commonName : commonNames) { - if (nameMap.put(commonName.trim(), params) != null) { - throw new RuntimeException("Duplication name: " + commonName); + for (String cn : params.getNameAndAliases()) { + if (nameMap.put(cn, + params) != null) { + throw new RuntimeException("Duplication name: " + cn); } } @@ -153,445 +150,424 @@ private static void add(String name, String soid, int type, String sfield, } } - private static class Holder { - private static final Pattern nameSplitPattern = Pattern.compile( - SPLIT_PATTERN); - } - - // Return all the names the EC curve could be using. - static String[] getNamesByOID(String oid) { - NamedCurve nc = oidMap.get(oid); - if (nc == null) { - return new String[0]; - } - String[] list = Holder.nameSplitPattern.split(nc.getName()); - int i = 0; - do { - list[i] = list[i].trim(); - } while (++i < list.length); - return list; - } - static { - Pattern nameSplitPattern = Holder.nameSplitPattern; - /* SEC2 prime curves */ - add("secp112r1", "1.3.132.0.6", P, + add(KnownOIDs.secp112r1, P, "DB7C2ABF62E35E668076BEAD208B", "DB7C2ABF62E35E668076BEAD2088", "659EF8BA043916EEDE8911702B22", "09487239995A5EE76B55F9C2F098", "A89CE5AF8724C0A23E0E0FF77500", "DB7C2ABF62E35E7628DFAC6561C5", - 1, nameSplitPattern); + 1); - add("secp112r2", "1.3.132.0.7", P, + add(KnownOIDs.secp112r2, P, "DB7C2ABF62E35E668076BEAD208B", "6127C24C05F38A0AAAF65C0EF02C", "51DEF1815DB5ED74FCC34C85D709", "4BA30AB5E892B4E1649DD0928643", "adcd46f5882e3747def36e956e97", "36DF0AAFD8B8D7597CA10520D04B", - 4, nameSplitPattern); + 4); - add("secp128r1", "1.3.132.0.28", P, + add(KnownOIDs.secp128r1, P, "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC", "E87579C11079F43DD824993C2CEE5ED3", "161FF7528B899B2D0C28607CA52C5B86", "CF5AC8395BAFEB13C02DA292DDED7A83", "FFFFFFFE0000000075A30D1B9038A115", - 1, nameSplitPattern); + 1); - add("secp128r2", "1.3.132.0.29", P, + add(KnownOIDs.secp128r2, P, "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", "D6031998D1B3BBFEBF59CC9BBFF9AEE1", "5EEEFCA380D02919DC2C6558BB6D8A5D", "7B6AA5D85E572983E6FB32A7CDEBC140", "27B6916A894D3AEE7106FE805FC34B44", "3FFFFFFF7FFFFFFFBE0024720613B5A3", - 4, nameSplitPattern); + 4); - add("secp160k1", "1.3.132.0.9", P, + add(KnownOIDs.secp160k1, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", "0000000000000000000000000000000000000000", "0000000000000000000000000000000000000007", "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", "938CF935318FDCED6BC28286531733C3F03C4FEE", "0100000000000000000001B8FA16DFAB9ACA16B6B3", - 1, nameSplitPattern); + 1); - add("secp160r1", "1.3.132.0.8", P, + add(KnownOIDs.secp160r1, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC", "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", "4A96B5688EF573284664698968C38BB913CBFC82", "23A628553168947D59DCC912042351377AC5FB32", "0100000000000000000001F4C8F927AED3CA752257", - 1, nameSplitPattern); + 1); - add("secp160r2", "1.3.132.0.30", P, + add(KnownOIDs.secp160r2, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70", "B4E134D3FB59EB8BAB57274904664D5AF50388BA", "52DCB034293A117E1F4FF11B30F7199D3144CE6D", "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E", "0100000000000000000000351EE786A818F3A1A16B", - 1, nameSplitPattern); + 1); - add("secp192k1", "1.3.132.0.31", P, + add(KnownOIDs.secp192k1, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", "000000000000000000000000000000000000000000000000", "000000000000000000000000000000000000000000000003", "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", - 1, nameSplitPattern); + 1); - add("secp192r1 [NIST P-192, X9.62 prime192v1]", "1.2.840.10045.3.1.1", PD, + add(KnownOIDs.secp192r1, PD, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", - 1, nameSplitPattern); + 1); - add("secp224k1", "1.3.132.0.32", P, + add(KnownOIDs.secp224k1, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", "00000000000000000000000000000000000000000000000000000000", "00000000000000000000000000000000000000000000000000000005", "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", "010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", - 1, nameSplitPattern); + 1); - add("secp224r1 [NIST P-224]", "1.3.132.0.33", PD, + add(KnownOIDs.secp224r1, PD, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", - 1, nameSplitPattern); + 1); - add("secp256k1", "1.3.132.0.10", P, + add(KnownOIDs.secp256k1, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000007", "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", - 1, nameSplitPattern); + 1); - add("secp256r1 [NIST P-256, X9.62 prime256v1]", "1.2.840.10045.3.1.7", PD, + add(KnownOIDs.secp256r1, PD, "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", - 1, nameSplitPattern); + 1); - add("secp384r1 [NIST P-384]", "1.3.132.0.34", PD, + add(KnownOIDs.secp384r1, PD, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", - 1, nameSplitPattern); + 1); - add("secp521r1 [NIST P-521]", "1.3.132.0.35", PD, + add(KnownOIDs.secp521r1, PD, "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", - 1, nameSplitPattern); + 1); /* ANSI X9.62 prime curves */ - add("X9.62 prime192v2", "1.2.840.10045.3.1.2", P, + add(KnownOIDs.prime192v2, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", "CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953", "EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A", "6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15", "FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31", - 1, nameSplitPattern); + 1); - add("X9.62 prime192v3", "1.2.840.10045.3.1.3", P, + add(KnownOIDs.prime192v3, P, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", "22123DC2395A05CAA7423DAECCC94760A7D462256BD56916", "7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896", "38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0", "FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13", - 1, nameSplitPattern); + 1); - add("X9.62 prime239v1", "1.2.840.10045.3.1.4", P, + add(KnownOIDs.prime239v1, P, "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", "6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A", "0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF", "7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE", "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B", - 1, nameSplitPattern); + 1); - add("X9.62 prime239v2", "1.2.840.10045.3.1.5", P, + add(KnownOIDs.prime239v2, P, "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", "617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C", "38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7", "5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA", "7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063", - 1, nameSplitPattern); + 1); - add("X9.62 prime239v3", "1.2.840.10045.3.1.6", P, + add(KnownOIDs.prime239v3, P, "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", "255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E", "6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A", "1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3", "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551", - 1, nameSplitPattern); + 1); /* SEC2 binary curves */ - add("sect113r1", "1.3.132.0.4", B, + add(KnownOIDs.sect113r1, B, "020000000000000000000000000201", "003088250CA6E7C7FE649CE85820F7", "00E8BEE4D3E2260744188BE0E9C723", "009D73616F35F4AB1407D73562C10F", "00A52830277958EE84D1315ED31886", "0100000000000000D9CCEC8A39E56F", - 2, nameSplitPattern); + 2); - add("sect113r2", "1.3.132.0.5", B, + add(KnownOIDs.sect113r2, B, "020000000000000000000000000201", "00689918DBEC7E5A0DD6DFC0AA55C7", "0095E9A9EC9B297BD4BF36E059184F", "01A57A6A7B26CA5EF52FCDB8164797", "00B3ADC94ED1FE674C06E695BABA1D", "010000000000000108789B2496AF93", - 2, nameSplitPattern); + 2); - add("sect131r1", "1.3.132.0.22", B, + add(KnownOIDs.sect131r1, B, "080000000000000000000000000000010D", "07A11B09A76B562144418FF3FF8C2570B8", "0217C05610884B63B9C6C7291678F9D341", "0081BAF91FDF9833C40F9C181343638399", "078C6E7EA38C001F73C8134B1B4EF9E150", "0400000000000000023123953A9464B54D", - 2, nameSplitPattern); + 2); - add("sect131r2", "1.3.132.0.23", B, + add(KnownOIDs.sect131r2, B, "080000000000000000000000000000010D", "03E5A88919D7CAFCBF415F07C2176573B2", "04B8266A46C55657AC734CE38F018F2192", "0356DCD8F2F95031AD652D23951BB366A8", "0648F06D867940A5366D9E265DE9EB240F", "0400000000000000016954A233049BA98F", - 2, nameSplitPattern); + 2); - add("sect163k1 [NIST K-163]", "1.3.132.0.1", BD, + add(KnownOIDs.sect163k1, BD, "0800000000000000000000000000000000000000C9", "000000000000000000000000000000000000000001", "000000000000000000000000000000000000000001", "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8", "0289070FB05D38FF58321F2E800536D538CCDAA3D9", "04000000000000000000020108A2E0CC0D99F8A5EF", - 2, nameSplitPattern); + 2); - add("sect163r1", "1.3.132.0.2", B, + add(KnownOIDs.sect163r1, B, "0800000000000000000000000000000000000000C9", "07B6882CAAEFA84F9554FF8428BD88E246D2782AE2", "0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9", "0369979697AB43897789566789567F787A7876A654", "00435EDB42EFAFB2989D51FEFCE3C80988F41FF883", "03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B", - 2, nameSplitPattern); + 2); - add("sect163r2 [NIST B-163]", "1.3.132.0.15", BD, + add(KnownOIDs.sect163r2, BD, "0800000000000000000000000000000000000000C9", "000000000000000000000000000000000000000001", "020A601907B8C953CA1481EB10512F78744A3205FD", "03F0EBA16286A2D57EA0991168D4994637E8343E36", "00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1", "040000000000000000000292FE77E70C12A4234C33", - 2, nameSplitPattern); + 2); - add("sect193r1", "1.3.132.0.24", B, + add(KnownOIDs.sect193r1, B, "02000000000000000000000000000000000000000000008001", "0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01", "00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814", "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1", "0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05", "01000000000000000000000000C7F34A778F443ACC920EBA49", - 2, nameSplitPattern); + 2); - add("sect193r2", "1.3.132.0.25", B, + add(KnownOIDs.sect193r2, B, "02000000000000000000000000000000000000000000008001", "0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B", "00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE", "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F", "01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C", "010000000000000000000000015AAB561B005413CCD4EE99D5", - 2, nameSplitPattern); + 2); - add("sect233k1 [NIST K-233]", "1.3.132.0.26", BD, + add(KnownOIDs.sect233k1, BD, "020000000000000000000000000000000000000004000000000000000001", "000000000000000000000000000000000000000000000000000000000000", "000000000000000000000000000000000000000000000000000000000001", "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126", "01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3", "008000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF", - 4, nameSplitPattern); + 4); - add("sect233r1 [NIST B-233]", "1.3.132.0.27", B, + add(KnownOIDs.sect233r1, B, "020000000000000000000000000000000000000004000000000000000001", "000000000000000000000000000000000000000000000000000000000001", "0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD", "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B", "01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052", "01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7", - 2, nameSplitPattern); + 2); - add("sect239k1", "1.3.132.0.3", B, + add(KnownOIDs.sect239k1, B, "800000000000000000004000000000000000000000000000000000000001", "000000000000000000000000000000000000000000000000000000000000", "000000000000000000000000000000000000000000000000000000000001", "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC", "76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA", "2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5", - 4, nameSplitPattern); + 4); - add("sect283k1 [NIST K-283]", "1.3.132.0.16", BD, + add(KnownOIDs.sect283k1, BD, "0800000000000000000000000000000000000000000000000000000000000000000010A1", "000000000000000000000000000000000000000000000000000000000000000000000000", "000000000000000000000000000000000000000000000000000000000000000000000001", "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836", "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259", "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61", - 4, nameSplitPattern); + 4); - add("sect283r1 [NIST B-283]", "1.3.132.0.17", B, + add(KnownOIDs.sect283r1, B, "0800000000000000000000000000000000000000000000000000000000000000000010A1", "000000000000000000000000000000000000000000000000000000000000000000000001", "027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5", "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053", "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4", "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307", - 2, nameSplitPattern); + 2); - add("sect409k1 [NIST K-409]", "1.3.132.0.36", BD, + add(KnownOIDs.sect409k1, BD, "02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746", "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B", "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF", - 4, nameSplitPattern); + 4); - add("sect409r1 [NIST B-409]", "1.3.132.0.37", B, + add(KnownOIDs.sect409r1, B, "02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F", "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7", "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706", "010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173", - 2, nameSplitPattern); + 2); - add("sect571k1 [NIST K-571]", "1.3.132.0.38", BD, + add(KnownOIDs.sect571k1, BD, "080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972", "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3", "020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001", - 4, nameSplitPattern); + 4); - add("sect571r1 [NIST B-571]", "1.3.132.0.39", B, + add(KnownOIDs.sect571r1, B, "080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A", "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19", "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B", "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47", - 2, nameSplitPattern); + 2); /* ANSI X9.62 binary curves */ - add("X9.62 c2tnb191v1", "1.2.840.10045.3.0.5", B, + add(KnownOIDs.c2tnb191v1, B, "800000000000000000000000000000000000000000000201", "2866537B676752636A68F56554E12640276B649EF7526267", "2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", "36B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D", "765BE73433B3F95E332932E70EA245CA2418EA0EF98018FB", "40000000000000000000000004A20E90C39067C893BBB9A5", - 2, nameSplitPattern); + 2); - add("X9.62 c2tnb191v2", "1.2.840.10045.3.0.6", B, + add(KnownOIDs.c2tnb191v2, B, "800000000000000000000000000000000000000000000201", "401028774D7777C7B7666D1366EA432071274F89FF01E718", "0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01", "3809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10", "17434386626D14F3DBF01760D9213A3E1CF37AEC437D668A", "20000000000000000000000050508CB89F652824E06B8173", - 4, nameSplitPattern); + 4); - add("X9.62 c2tnb191v3", "1.2.840.10045.3.0.7", B, + add(KnownOIDs.c2tnb191v3, B, "800000000000000000000000000000000000000000000201", "6C01074756099122221056911C77D77E77A777E7E7E77FCB", "71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8", "375D4CE24FDE434489DE8746E71786015009E66E38A926DD", "545A39176196575D985999366E6AD34CE0A77CD7127B06BE", "155555555555555555555555610C0B196812BFB6288A3EA3", - 6, nameSplitPattern); + 6); - add("X9.62 c2tnb239v1", "1.2.840.10045.3.0.11", B, + add(KnownOIDs.c2tnb239v1, B, "800000000000000000000000000000000000000000000000001000000001", "32010857077C5431123A46B808906756F543423E8D27877578125778AC76", "790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", "57927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D", "61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305", "2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447", - 4, nameSplitPattern); + 4); - add("X9.62 c2tnb239v2", "1.2.840.10045.3.0.12", B, + add(KnownOIDs.c2tnb239v2, B, "800000000000000000000000000000000000000000000000001000000001", "4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F", "5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B", "28F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205", "5667334C45AFF3B5A03BAD9DD75E2C71A99362567D5453F7FA6E227EC833", "1555555555555555555555555555553C6F2885259C31E3FCDF154624522D", - 6, nameSplitPattern); + 6); - add("X9.62 c2tnb239v3", "1.2.840.10045.3.0.13", B, + add(KnownOIDs.c2tnb239v3, B, "800000000000000000000000000000000000000000000000001000000001", "01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F", "6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40", "70F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92", "2E5A0EAF6E5E1305B9004DCE5C0ED7FE59A35608F33837C816D80B79F461", "0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF", - 0xA, nameSplitPattern); + 0xA); - add("X9.62 c2tnb359v1", "1.2.840.10045.3.0.18", B, + add(KnownOIDs.c2tnb359v1, B, "800000000000000000000000000000000000000000000000000000000000000000000000100000000000000001", "5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557", "2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988", "3C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097", "53D7E08529547048121E9C95F3791DD804963948F34FAE7BF44EA82365DC7868FE57E4AE2DE211305A407104BD", "01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B", - 0x4C, nameSplitPattern); + 0x4C); - add("X9.62 c2tnb431r1", "1.2.840.10045.3.0.20", B, + add(KnownOIDs.c2tnb431r1, B, "800000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000001", "1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F", "10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618", "120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7", "20D0AF8903A96F8D5FA2C255745D3C451B302C9346D9B7E485E7BCE41F6B591F3E8F6ADDCBB0BC4C2F947A7DE1A89B625D6A598B3760", "0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91", - 0x2760, nameSplitPattern); + 0x2760); /* ANSI X9.62 binary curves from the 1998 standard but forbidden * in the 2005 version of the standard. @@ -599,77 +575,77 @@ static String[] getNamesByOID(String oid) { * case we need to support them after all. */ /* - add("X9.62 c2pnb163v1", "1.2.840.10045.3.0.1", B, + add(KnownOIDs.c2pnb163v1, B, "080000000000000000000000000000000000000107", "072546B5435234A422E0789675F432C89435DE5242", "00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9", "07AF69989546103D79329FCC3D74880F33BBE803CB", "01EC23211B5966ADEA1D3F87F7EA5848AEF0B7CA9F", "0400000000000000000001E60FC8821CC74DAEAFC1", - 2, nameSplitPattern); + 2); - add("X9.62 c2pnb163v2", "1.2.840.10045.3.0.2", B, + add(KnownOIDs.c2pnb163v2, B, "080000000000000000000000000000000000000107", "0108B39E77C4B108BED981ED0E890E117C511CF072", "0667ACEB38AF4E488C407433FFAE4F1C811638DF20", "0024266E4EB5106D0A964D92C4860E2671DB9B6CC5", "079F684DDF6684C5CD258B3890021B2386DFD19FC5", "03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7", - 2, nameSplitPattern); + 2); - add("X9.62 c2pnb163v3", "1.2.840.10045.3.0.3", B, + add(KnownOIDs.c2pnb163v3, B, "080000000000000000000000000000000000000107", "07A526C63D3E25A256A007699F5447E32AE456B50E", "03F7061798EB99E238FD6F1BF95B48FEEB4854252B", "02F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB", "05B935590C155E17EA48EB3FF3718B893DF59A05D0", "03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309", - 2, nameSplitPattern); + 2); - add("X9.62 c2pnb176w1", "1.2.840.10045.3.0.4", B, + add(KnownOIDs.c2pnb176w1, B, "0100000000000000000000000000000000080000000007", "E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B", "5DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2", "8D16C2866798B600F9F08BB4A8E860F3298CE04A5798", "6FA4539C2DADDDD6BAB5167D61B436E1D92BB16A562C", "00010092537397ECA4F6145799D62B0A19CE06FE26AD", - 0xFF6E, nameSplitPattern); + 0xFF6E); - add("X9.62 c2pnb208w1", "1.2.840.10045.3.0.10", B, + add(KnownOIDs.c2pnb208w1, B, "010000000000000000000000000000000800000000000000000007", "0000000000000000000000000000000000000000000000000000", "C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E", "89FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A", "0F55B51A06E78E9AC38A035FF520D8B01781BEB1A6BB08617DE3", "000101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D", - 0xFE48, nameSplitPattern); + 0xFE48); - add("X9.62 c2pnb272w1", "1.2.840.10045.3.0.16", B, + add(KnownOIDs.c2pnb272w1, B, "010000000000000000000000000000000000000000000000000000010000000000000B", "91A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20", "7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7", "6108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D", "10C7695716851EEF6BA7F6872E6142FBD241B830FF5EFCACECCAB05E02005DDE9D23", "000100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521", - 0xFF06, nameSplitPattern); + 0xFF06); - add("X9.62 c2pnb304w1", "1.2.840.10045.3.0.17", B, + add(KnownOIDs.c2pnb304w1, B, "010000000000000000000000000000000000000000000000000000000000000000000000000807", "FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681", "BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE", "197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614", "E19FBEB76E0DA171517ECF401B50289BF014103288527A9B416A105E80260B549FDC1B92C03B", "000101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D", - 0xFE2E, nameSplitPattern); + 0xFE2E); - add("X9.62 c2pnb368w1", "1.2.840.10045.3.0.19", B, + add(KnownOIDs.c2pnb368w1, B, "0100000000000000000000000000000000000000000000000000000000000000000000002000000000000000000007", "E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D", "FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A", "1085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F", "7B3EB1BDDCBA62D5D8B2059B525797FC73822C59059C623A45FF3843CEE8F87CD1855ADAA81E2A0750B80FDA2310", "00010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967", - 0xFF70, nameSplitPattern); + 0xFF70); */ /* @@ -677,68 +653,68 @@ static String[] getNamesByOID(String oid) { * (Twisted curves are not included) */ - add("brainpoolP160r1", "1.3.36.3.3.2.8.1.1.1", P, + add(KnownOIDs.brainpoolP160r1, P, "E95E4A5F737059DC60DFC7AD95B3D8139515620F", "340E7BE2A280EB74E2BE61BADA745D97E8F7C300", "1E589A8595423412134FAA2DBDEC95C8D8675E58", "BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3", "1667CB477A1A8EC338F94741669C976316DA6321", "E95E4A5F737059DC60DF5991D45029409E60FC09", - 1, nameSplitPattern); + 1); - add("brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3", P, + add(KnownOIDs.brainpoolP192r1, P, "C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", "6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF", "469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", "C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6", "14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F", "C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", - 1, nameSplitPattern); + 1); - add("brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5", P, + add(KnownOIDs.brainpoolP224r1, P, "D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", "68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43", "2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", "0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D", "58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD", "D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", - 1, nameSplitPattern); + 1); - add("brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", P, + add(KnownOIDs.brainpoolP256r1, P, "A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", "7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", "26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", "8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", "547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", "A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", - 1, nameSplitPattern); + 1); - add("brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9", P, + add(KnownOIDs.brainpoolP320r1, P, "D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", "3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4", "520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", "43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E20611", "14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1", "D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", - 1, nameSplitPattern); + 1); - add("brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", P, + add(KnownOIDs.brainpoolP384r1, P, "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", "7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", "04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", "1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E", "8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", - 1, nameSplitPattern); + 1); - add("brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", P, + add(KnownOIDs.brainpoolP512r1, P, "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", "7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", "3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", "81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822", "7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", - 1, nameSplitPattern); + 1); specCollection = Collections.unmodifiableCollection(oidMap.values()); } diff --git a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java index 0d2346e19f8..dc320b85d7e 100644 --- a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java @@ -261,7 +261,7 @@ private static List getNamedCurveFromKey(Key key) { if (key instanceof ECKey) { NamedCurve nc = CurveDB.lookup(((ECKey)key).getParams()); return (nc == null ? List.of() - : Arrays.asList(CurveDB.getNamesByOID(nc.getObjectId()))); + : Arrays.asList(nc.getNameAndAliases())); } else if (key instanceof XECKey) { return List.of( ((NamedParameterSpec)((XECKey)key).getParams()).getName()); diff --git a/src/java.base/share/classes/sun/security/util/KnownOIDs.java b/src/java.base/share/classes/sun/security/util/KnownOIDs.java index 92ecb9adc0c..b5cc3b05f14 100644 --- a/src/java.base/share/classes/sun/security/util/KnownOIDs.java +++ b/src/java.base/share/classes/sun/security/util/KnownOIDs.java @@ -356,6 +356,11 @@ public enum KnownOIDs { boolean registerNames() { return false; } }, + OIW_SHA1withRSA_Odd("1.3.14.3.2.15", "SHA1withRSA") { + @Override + boolean registerNames() { return false; } + }, + SHA_1("1.3.14.3.2.26", "SHA-1", "SHA", "SHA1"), OIW_SHA1withDSA("1.3.14.3.2.27", "SHA1withDSA") { diff --git a/src/java.base/share/classes/sun/security/util/NamedCurve.java b/src/java.base/share/classes/sun/security/util/NamedCurve.java index 0a677aad414..f12aba4f9c7 100644 --- a/src/java.base/share/classes/sun/security/util/NamedCurve.java +++ b/src/java.base/share/classes/sun/security/util/NamedCurve.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. * 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.math.BigInteger; import java.security.spec.*; - +import java.util.Arrays; /** * Contains Elliptic Curve parameters. @@ -39,8 +39,8 @@ */ public final class NamedCurve extends ECParameterSpec { - // friendly name for toString() output - private final String name; + // friendly names with stdName followed by aliases + private final String[] nameAndAliases; // well known OID private final String oid; @@ -48,25 +48,28 @@ public final class NamedCurve extends ECParameterSpec { // encoded form (as NamedCurve identified via OID) private final byte[] encoded; - NamedCurve(String name, String oid, EllipticCurve curve, + NamedCurve(KnownOIDs ko, EllipticCurve curve, ECPoint g, BigInteger n, int h) { super(curve, g, n, h); - this.name = name; - this.oid = oid; + String[] aliases = ko.aliases(); + this.nameAndAliases = new String[aliases.length + 1]; + nameAndAliases[0] = ko.stdName(); + System.arraycopy(aliases, 0, nameAndAliases, 1, aliases.length); - DerOutputStream out = new DerOutputStream(); + this.oid = ko.value(); + DerOutputStream out = new DerOutputStream(); try { - out.putOID(new ObjectIdentifier(oid)); + out.putOID(ObjectIdentifier.of(ko)); } catch (IOException e) { throw new RuntimeException("Internal error", e); } - encoded = out.toByteArray(); } - public String getName() { - return name; + // returns the curve's standard name followed by its aliases + public String[] getNameAndAliases() { + return nameAndAliases; } public byte[] getEncoded() { @@ -78,6 +81,17 @@ public String getObjectId() { } public String toString() { - return name + " (" + oid + ")"; + StringBuilder sb = new StringBuilder(nameAndAliases[0]); + if (nameAndAliases.length > 1) { + sb.append(" ["); + int j = 1; + while (j < nameAndAliases.length - 1) { + sb.append(nameAndAliases[j++]); + sb.append(','); + } + sb.append(nameAndAliases[j] + "]"); + } + sb.append(" (" + oid + ")"); + return sb.toString(); } } diff --git a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java index b74d84e1df0..2d554057712 100644 --- a/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java +++ b/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java @@ -51,9 +51,7 @@ * @author Hemma Prafullchandra */ -public final -class ObjectIdentifier implements Serializable -{ +public final class ObjectIdentifier implements Serializable { /* * The maximum encoded OID length, excluding the ASN.1 encoding tag and * length. @@ -73,7 +71,6 @@ class ObjectIdentifier implements Serializable */ private static final int MAXIMUM_OID_SIZE = 4096; // 2^12 - /** * We use the DER value (no tag, no length) as the internal format * @serial @@ -119,6 +116,7 @@ class ObjectIdentifier implements Serializable * @serial */ private Object components = null; // path from root + /** * @serial */ @@ -163,15 +161,15 @@ private void writeObject(ObjectOutputStream os) static class HugeOidNotSupportedByOldJDK implements Serializable { private static final long serialVersionUID = 1L; - static HugeOidNotSupportedByOldJDK theOne = new HugeOidNotSupportedByOldJDK(); + static HugeOidNotSupportedByOldJDK theOne = + new HugeOidNotSupportedByOldJDK(); } /** * Constructs, from a string. This string should be of the form 1.23.56. * Validity check included. */ - public ObjectIdentifier (String oid) throws IOException - { + private ObjectIdentifier(String oid) throws IOException { int ch = '.'; int start = 0; int end = 0; @@ -267,8 +265,7 @@ public ObjectIdentifier(int[] values) throws IOException * @param in DER-encoded data holding an object ID * @exception IOException indicates a decoding error */ - public ObjectIdentifier (DerInputStream in) throws IOException - { + public ObjectIdentifier(DerInputStream in) throws IOException { byte type_id; int bufferEnd; @@ -281,7 +278,7 @@ public ObjectIdentifier (DerInputStream in) throws IOException * up so that we can use in.available() to check for the end of * this value in the data stream. */ - type_id = (byte) in.getByte (); + type_id = (byte)in.getByte(); if (type_id != DerValue.tag_ObjectId) throw new IOException ( "ObjectIdentifier() -- data isn't an object ID" @@ -306,8 +303,7 @@ public ObjectIdentifier (DerInputStream in) throws IOException * the tag and length have been removed/verified * Validity check NOT included. */ - ObjectIdentifier (DerInputBuffer buf) throws IOException - { + ObjectIdentifier(DerInputBuffer buf) throws IOException { DerInputStream in = new DerInputStream(buf); int len = in.available(); checkOidSize(len); @@ -361,6 +357,11 @@ public static ObjectIdentifier newInternal(int[] values) { private static ConcurrentHashMap oidTable = new ConcurrentHashMap<>(); + /** + * Returns an ObjectIdentifier instance for the specific String. + * + * If the String is not a valid OID string, an IOException is thrown. + */ public static ObjectIdentifier of(String oidStr) throws IOException { // check cache first ObjectIdentifier oid = oidTable.get(oidStr); @@ -393,8 +394,7 @@ public static ObjectIdentifier of(KnownOIDs o) { /* * n.b. the only public interface is DerOutputStream.putOID() */ - void encode (DerOutputStream out) throws IOException - { + void encode(DerOutputStream out) throws IOException { out.write (DerValue.tag_ObjectId, encoding); } @@ -435,17 +435,21 @@ private int[] toIntArray() { if ((encoding[i] & 0x80) == 0) { // one section [fromPos..i] if (i - fromPos + 1 > 4) { - BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8)); + BigInteger big = new BigInteger(1, pack(encoding, + fromPos, i-fromPos+1, 7, 8)); if (fromPos == 0) { result[which++] = 2; - BigInteger second = big.subtract(BigInteger.valueOf(80)); - if (second.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { + BigInteger second = + big.subtract(BigInteger.valueOf(80)); + if (second.compareTo( + BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { return null; } else { result[which++] = second.intValue(); } } else { - if (big.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { + if (big.compareTo( + BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { return null; } else { result[which++] = big.intValue(); @@ -500,7 +504,8 @@ public String toString() { sb.append('.'); } if (i - fromPos + 1 > 4) { // maybe big integer - BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8)); + BigInteger big = new BigInteger( + 1, pack(encoding, fromPos, i-fromPos+1, 7, 8)); if (fromPos == 0) { // first section encoded with more than 4 bytes, // must be 2.something @@ -541,7 +546,7 @@ public String toString() { /** * Repack all bits from input to output. On the both sides, only a portion * (from the least significant bit) of the 8 bits in a byte is used. This - * number is defined as the number of useful bits (NUB) for the array. All the + * number is defined as the number of useful bits (NUB) for the array. All * used bits from the input byte array and repacked into the output in the * exactly same order. The output bits are aligned so that the final bit of * the input (the least significant bit in the last byte), when repacked as @@ -563,7 +568,8 @@ public String toString() { * @param ow NUB for output * @return the repacked bytes */ - private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) { + private static byte[] pack(byte[] in, + int ioffset, int ilength, int iw, int ow) { assert (iw > 0 && iw <= 8): "input NUB must be between 1 and 8"; assert (ow > 0 && ow <= 8): "output NUB must be between 1 and 8"; @@ -585,12 +591,13 @@ private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) if (count > ow - opos%ow) { // free space available in output byte count = ow - opos%ow; // choose the smaller number } + // and move them! - out[opos/ow] |= // paste! - (((in[ioffset+ipos/iw]+256) // locate the byte (+256 so that it's never negative) - >> (iw-ipos%iw-count)) // move to the end of a byte - & ((1 << (count))-1)) // zero out all other bits - << (ow-opos%ow-count); // move to the output position + out[opos/ow] |= // paste! + (((in[ioffset+ipos/iw]+256) // locate the byte (+256 so that it's never negative) + >> (iw-ipos%iw-count)) & // move to the end of a byte + ((1 << (count))-1)) // zero out all other bits + << (ow-opos%ow-count); // move to the output position ipos += count; // advance opos += count; // advance } @@ -606,7 +613,8 @@ private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) * @param ooffset the starting position to paste * @return the number of bytes pasted */ - private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) { + private static int pack7Oid(byte[] in, + int ioffset, int ilength, byte[] out, int ooffset) { byte[] pack = pack(in, ioffset, ilength, 8, 7); int firstNonZero = pack.length-1; // paste at least one byte for (int i=pack.length-2; i>=0; i--) { @@ -615,7 +623,8 @@ private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int } pack[i] |= 0x80; } - System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero); + System.arraycopy(pack, firstNonZero, + out, ooffset, pack.length-firstNonZero); return pack.length-firstNonZero; } @@ -626,7 +635,8 @@ private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int * @param ooffset the starting position to paste * @return the number of bytes pasted */ - private static int pack8(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) { + private static int pack8(byte[] in, + int ioffset, int ilength, byte[] out, int ooffset) { byte[] pack = pack(in, ioffset, ilength, 7, 8); int firstNonZero = pack.length-1; // paste at least one byte for (int i=pack.length-2; i>=0; i--) { @@ -634,7 +644,8 @@ private static int pack8(byte[] in, int ioffset, int ilength, byte[] out, int oo firstNonZero = i; } } - System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero); + System.arraycopy(pack, firstNonZero, + out, ooffset, pack.length-firstNonZero); return pack.length-firstNonZero; } @@ -686,31 +697,39 @@ private static void check(byte[] encoding) throws IOException { } } } + private static void checkCount(int count) throws IOException { if (count < 2) { throw new IOException("ObjectIdentifier() -- " + "Must be at least two oid components "); } } + private static void checkFirstComponent(int first) throws IOException { if (first < 0 || first > 2) { throw new IOException("ObjectIdentifier() -- " + "First oid component is invalid "); } } - private static void checkFirstComponent(BigInteger first) throws IOException { + + private static void checkFirstComponent( + BigInteger first) throws IOException { if (first.signum() == -1 || first.compareTo(BigInteger.TWO) > 0) { throw new IOException("ObjectIdentifier() -- " + "First oid component is invalid "); } } - private static void checkSecondComponent(int first, int second) throws IOException { + + private static void checkSecondComponent( + int first, int second) throws IOException { if (second < 0 || first != 2 && second > 39) { throw new IOException("ObjectIdentifier() -- " + "Second oid component is invalid "); } } - private static void checkSecondComponent(int first, BigInteger second) throws IOException { + + private static void checkSecondComponent( + int first, BigInteger second) throws IOException { if (second.signum() == -1 || first != 2 && second.compareTo(BigInteger.valueOf(39)) == 1) { @@ -718,13 +737,16 @@ private static void checkSecondComponent(int first, BigInteger second) throws IO "Second oid component is invalid "); } } + private static void checkOtherComponent(int i, int num) throws IOException { if (num < 0) { throw new IOException("ObjectIdentifier() -- " + "oid component #" + (i+1) + " must be non-negative "); } } - private static void checkOtherComponent(int i, BigInteger num) throws IOException { + + private static void checkOtherComponent( + int i, BigInteger num) throws IOException { if (num.signum() == -1) { throw new IOException("ObjectIdentifier() -- " + "oid component #" + (i+1) + " must be non-negative "); diff --git a/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java b/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java index 37e6df348d4..26aad9ada0a 100644 --- a/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java +++ b/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java @@ -25,8 +25,11 @@ package sun.security.util; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.PatternSyntaxException; import java.security.InvalidParameterException; +import java.security.ProviderException; import javax.crypto.spec.DHParameterSpec; import sun.security.action.GetPropertyAction; @@ -35,11 +38,59 @@ * the JDK security/crypto providers. */ public final class SecurityProviderConstants { + // Cannot create one of these + private SecurityProviderConstants () {} + private static final Debug debug = Debug.getInstance("jca", "ProviderConfig"); - // Cannot create one of these - private SecurityProviderConstants () { + // cache for provider aliases; key is the standard algorithm name + // value is the associated aliases List + private static final ConcurrentHashMap> aliasesMap; + + // utility method for generating aliases list using the supplied + // 'oid' and 'extraAliases', then store into "aliasesMap" cache under the + // key 'stdName' + private static List store(String stdName, KnownOIDs oid, + String ... extraAliases) { + List value; + if (oid == null && extraAliases.length != 0) { + value = List.of(extraAliases); + } else { + value = new ArrayList<>(); + if (oid != null) { + value.add("OID." + oid.value()); + value.add(oid.value()); + String[] knownAliases = oid.aliases(); + if (knownAliases != null) { + for (String ka : knownAliases) { + value.add(ka); + } + } + } + for (String ea : extraAliases) { + value.add(ea); + } + } + aliasesMap.put(stdName, value); + return value; + } + + // returns an aliases List for the specified algorithm name o + // NOTE: exception is thrown if no aliases nor oid found, so + // only call this method if aliases are expected + public static List getAliases(String o) { + List res = aliasesMap.get(o); + if (res == null) { + KnownOIDs e = KnownOIDs.findMatch(o); + if (e != null) { + return store(o, e); + } + ProviderException pe = + new ProviderException("Cannot find aliases for " + o); + throw pe; + } + return res; } public static final int getDefDSASubprimeSize(int primeSize) { @@ -99,6 +150,7 @@ public static final int getDefDHPrivateExpSize(DHParameterSpec spec) { private static final String KEY_LENGTH_PROP = "jdk.security.defaultKeySize"; + static { String keyLengthStr = GetPropertyAction.privilegedGetProperty (KEY_LENGTH_PROP); @@ -169,5 +221,39 @@ public static final int getDefDHPrivateExpSize(DHParameterSpec spec) { DEF_RSASSA_PSS_KEY_SIZE = rsaSsaPssKeySize; DEF_DH_KEY_SIZE = dhKeySize; DEF_EC_KEY_SIZE = ecKeySize; + + // Set up aliases with default mappings + // This is needed when the mapping contains non-oid + // aliases + aliasesMap = new ConcurrentHashMap<>(); + + store("SHA1withDSA", KnownOIDs.SHA1withDSA, + KnownOIDs.OIW_JDK_SHA1withDSA.value(), + KnownOIDs.OIW_SHA1withDSA.value(), + "DSA", "SHA/DSA", "SHA-1/DSA", + "SHA1/DSA", "SHAwithDSA", "DSAWithSHA1"); + + store("DSA", KnownOIDs.DSA, KnownOIDs.OIW_DSA.value()); + + store("SHA1withRSA", KnownOIDs.SHA1withRSA, + KnownOIDs.OIW_SHA1withRSA.value()); + + store("SHA-1", KnownOIDs.SHA_1); + + store("PBEWithMD5AndDES", KnownOIDs.PBEWithMD5AndDES, "PBE"); + + store("DiffieHellman", KnownOIDs.DiffieHellman); + + store("AES", KnownOIDs.AES, "Rijndael"); + + store("EC", KnownOIDs.EC, "EllipticCurve"); + + store("X.509", null, "X509"); + store("NONEwithDSA", null, "RawDSA"); + store("DESede", null, "TripleDES"); + store("ARCFOUR", KnownOIDs.ARCFOUR); + // For backward compatility, refer to PKCS1 mapping for RSA + // KeyPairGenerator and KeyFactory + store("PKCS1", KnownOIDs.PKCS1, KnownOIDs.RSA.value()); } } diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index 9981bdd5a2b..cb477fc1340 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,6 +50,7 @@ import java.util.jar.JarFile; import java.util.jar.Manifest; +import sun.security.action.GetIntegerAction; import sun.security.jca.Providers; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; @@ -95,6 +96,12 @@ public class SignatureFileVerifier { /** ConstraintsParameters for checking disabled algorithms */ private JarConstraintsParameters params; + // the maximum allowed size in bytes for the signature-related files + public static final int MAX_SIG_FILE_SIZE = initializeMaxSigFileSize(); + + // The maximum size of array to allocate. Some VMs reserve some header words in an array. + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + /** * Create the named SignatureFileVerifier. * @@ -838,4 +845,24 @@ void updateSigners(CodeSigner[] newSigners, signerCache.add(cachedSigners); signers.put(name, cachedSigners); } + + private static int initializeMaxSigFileSize() { + /* + * System property "jdk.jar.maxSignatureFileSize" used to configure + * the maximum allowed number of bytes for the signature-related files + * in a JAR file. + */ + Integer tmp = GetIntegerAction.privilegedGetProperty( + "jdk.jar.maxSignatureFileSize", 8000000); + if (tmp < 0 || tmp > MAX_ARRAY_SIZE) { + if (debug != null) { + debug.println("Default signature file size 8000000 bytes " + + "is used as the specified size for the " + + "jdk.jar.maxSignatureFileSize system property " + + "is out of range: " + tmp); + } + tmp = 8000000; + } + return tmp; + } } diff --git a/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java b/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java index d3398cb2e90..03675d0b257 100644 --- a/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java +++ b/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ import java.util.*; import java.security.cert.*; - +import sun.security.util.KnownOIDs; import sun.security.x509.NetscapeCertTypeExtension; /** @@ -71,24 +71,32 @@ class EndEntityChecker { private static final String OID_EXTENDED_KEY_USAGE = SimpleValidator.OID_EXTENDED_KEY_USAGE; - private static final String OID_EKU_TLS_SERVER = "1.3.6.1.5.5.7.3.1"; + private static final String OID_EKU_TLS_SERVER = + KnownOIDs.serverAuth.value(); - private static final String OID_EKU_TLS_CLIENT = "1.3.6.1.5.5.7.3.2"; + private static final String OID_EKU_TLS_CLIENT = + KnownOIDs.clientAuth.value(); - private static final String OID_EKU_CODE_SIGNING = "1.3.6.1.5.5.7.3.3"; + private static final String OID_EKU_CODE_SIGNING = + KnownOIDs.codeSigning.value(); - private static final String OID_EKU_TIME_STAMPING = "1.3.6.1.5.5.7.3.8"; + private static final String OID_EKU_TIME_STAMPING = + KnownOIDs.KP_TimeStamping.value(); - private static final String OID_EKU_ANY_USAGE = "2.5.29.37.0"; + private static final String OID_EKU_ANY_USAGE = + KnownOIDs.anyExtendedKeyUsage.value(); // the Netscape Server-Gated-Cryptography EKU extension OID - private static final String OID_EKU_NS_SGC = "2.16.840.1.113730.4.1"; + private static final String OID_EKU_NS_SGC = + KnownOIDs.NETSCAPE_ExportApproved.value(); // the Microsoft Server-Gated-Cryptography EKU extension OID - private static final String OID_EKU_MS_SGC = "1.3.6.1.4.1.311.10.3.3"; + private static final String OID_EKU_MS_SGC = + KnownOIDs.MICROSOFT_ExportApproved.value(); // the recognized extension OIDs - private static final String OID_SUBJECT_ALT_NAME = "2.5.29.17"; + private static final String OID_SUBJECT_ALT_NAME = + KnownOIDs.SubjectAlternativeName.value(); private static final String NSCT_SSL_CLIENT = NetscapeCertTypeExtension.SSL_CLIENT; diff --git a/src/java.base/share/classes/sun/security/validator/SimpleValidator.java b/src/java.base/share/classes/sun/security/validator/SimpleValidator.java index 7b7068fa9e6..b7f72f8bdeb 100644 --- a/src/java.base/share/classes/sun/security/validator/SimpleValidator.java +++ b/src/java.base/share/classes/sun/security/validator/SimpleValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ import sun.security.util.DerValue; import sun.security.util.DerInputStream; import sun.security.util.ObjectIdentifier; +import sun.security.util.KnownOIDs; import sun.security.provider.certpath.AlgorithmChecker; import sun.security.provider.certpath.UntrustedChecker; @@ -60,24 +61,28 @@ public final class SimpleValidator extends Validator { // Constants for the OIDs we need - static final String OID_BASIC_CONSTRAINTS = "2.5.29.19"; + static final String OID_BASIC_CONSTRAINTS = + KnownOIDs.BasicConstraints.value(); - static final String OID_NETSCAPE_CERT_TYPE = "2.16.840.1.113730.1.1"; + static final String OID_NETSCAPE_CERT_TYPE = + KnownOIDs.NETSCAPE_CertType.value(); - static final String OID_KEY_USAGE = "2.5.29.15"; + static final String OID_KEY_USAGE = KnownOIDs.KeyUsage.value(); - static final String OID_EXTENDED_KEY_USAGE = "2.5.29.37"; + static final String OID_EXTENDED_KEY_USAGE = + KnownOIDs.extendedKeyUsage.value(); - static final String OID_EKU_ANY_USAGE = "2.5.29.37.0"; + static final String OID_EKU_ANY_USAGE = + KnownOIDs.anyExtendedKeyUsage.value(); static final ObjectIdentifier OBJID_NETSCAPE_CERT_TYPE = - NetscapeCertTypeExtension.NetscapeCertType_Id; + NetscapeCertTypeExtension.NetscapeCertType_Id; private static final String NSCT_SSL_CA = - NetscapeCertTypeExtension.SSL_CA; + NetscapeCertTypeExtension.SSL_CA; private static final String NSCT_CODE_SIGNING_CA = - NetscapeCertTypeExtension.OBJECT_SIGNING_CA; + NetscapeCertTypeExtension.OBJECT_SIGNING_CA; /** * The trusted certificates as: diff --git a/src/java.base/share/classes/sun/security/x509/AVA.java b/src/java.base/share/classes/sun/security/x509/AVA.java index b3a673756a0..77bc088af6d 100644 --- a/src/java.base/share/classes/sun/security/x509/AVA.java +++ b/src/java.base/share/classes/sun/security/x509/AVA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1237,7 +1237,7 @@ private boolean isCompliant(int standard) { return ak.oid; } } else { - return new ObjectIdentifier(oidString); + return ObjectIdentifier.of(oidString); } // no keyword found, check if OID string @@ -1255,7 +1255,7 @@ private boolean isCompliant(int standard) { if (number == false) { throw new IOException("Invalid keyword \"" + keyword + "\""); } - return new ObjectIdentifier(keyword); + return ObjectIdentifier.of(keyword); } /** diff --git a/src/java.base/share/classes/sun/security/x509/AccessDescription.java b/src/java.base/share/classes/sun/security/x509/AccessDescription.java index 45fa695e88c..c2843a971e7 100644 --- a/src/java.base/share/classes/sun/security/x509/AccessDescription.java +++ b/src/java.base/share/classes/sun/security/x509/AccessDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,16 +42,16 @@ public final class AccessDescription { private GeneralName accessLocation; public static final ObjectIdentifier Ad_OCSP_Id = - ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 1}); + ObjectIdentifier.of(KnownOIDs.OCSP); public static final ObjectIdentifier Ad_CAISSUERS_Id = - ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 2}); + ObjectIdentifier.of(KnownOIDs.caIssuers); public static final ObjectIdentifier Ad_TIMESTAMPING_Id = - ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 3}); + ObjectIdentifier.of(KnownOIDs.AD_TimeStamping); public static final ObjectIdentifier Ad_CAREPOSITORY_Id = - ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 5}); + ObjectIdentifier.of(KnownOIDs.caRepository); public AccessDescription(ObjectIdentifier accessMethod, GeneralName accessLocation) { this.accessMethod = accessMethod; diff --git a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java index 9f86115013c..ff8c3ea3299 100644 --- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java +++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java @@ -31,6 +31,7 @@ import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.security.*; import sun.security.rsa.PSSParameters; @@ -256,21 +257,31 @@ public final ObjectIdentifier getOID () { * returns the "full" signature algorithm (Ex: SHA256withECDSA) directly. */ public String getName() { - String algName = nameTable.get(algid); - if (algName != null) { - return algName; - } - if ((params != null) && algid.equals((Object)specifiedWithECDSA_oid)) { - try { - AlgorithmId paramsId = + String oidStr = algid.toString(); + // first check the list of support oids + KnownOIDs o = KnownOIDs.findMatch(oidStr); + if (o == KnownOIDs.SpecifiedSHA2withECDSA) { + if (params != null) { + try { + AlgorithmId paramsId = AlgorithmId.parse(new DerValue(encodedParams)); - String paramsName = paramsId.getName(); - algName = makeSigAlg(paramsName, "EC"); - } catch (IOException e) { - // ignore + String paramsName = paramsId.getName(); + return makeSigAlg(paramsName, "EC"); + } catch (IOException e) { + // ignore + } + } + } + if (o != null) { + return o.stdName(); + } else { + String n = aliasOidsTable().get(oidStr); + if (n != null) { + return n; + } else { + return algid.toString(); } } - return (algName == null) ? algid.toString() : algName; } public AlgorithmParameters getParameters() { @@ -292,7 +303,8 @@ public AlgorithmParameters getParameters() { * @return DER encoded parameters, or null not present. */ public byte[] getEncodedParams() throws IOException { - return (encodedParams == null || algid.equals(specifiedWithECDSA_oid)) + return (encodedParams == null || + algid.toString().equals(KnownOIDs.SpecifiedSHA2withECDSA.value())) ? null : encodedParams.clone(); } @@ -488,541 +500,142 @@ public static AlgorithmId get(AlgorithmParameters algparams) * used as a "KeyPairGenerator" algorithm. */ private static ObjectIdentifier algOID(String name) throws IOException { - // See if algname is in printable OID ("dot-dot") notation - if (name.indexOf('.') != -1) { - if (name.startsWith("OID.")) { - return new ObjectIdentifier(name.substring("OID.".length())); - } else { - return new ObjectIdentifier(name); - } + if (name.startsWith("OID.")) { + name = name.substring("OID.".length()); } - // Digesting algorithms - if (name.equalsIgnoreCase("MD5")) { - return AlgorithmId.MD5_oid; - } - if (name.equalsIgnoreCase("MD2")) { - return AlgorithmId.MD2_oid; - } - if (name.equalsIgnoreCase("SHA") || name.equalsIgnoreCase("SHA1") - || name.equalsIgnoreCase("SHA-1")) { - return AlgorithmId.SHA_oid; - } - if (name.equalsIgnoreCase("SHA-256") || - name.equalsIgnoreCase("SHA256")) { - return AlgorithmId.SHA256_oid; - } - if (name.equalsIgnoreCase("SHA-384") || - name.equalsIgnoreCase("SHA384")) { - return AlgorithmId.SHA384_oid; - } - if (name.equalsIgnoreCase("SHA-512") || - name.equalsIgnoreCase("SHA512")) { - return AlgorithmId.SHA512_oid; - } - if (name.equalsIgnoreCase("SHA-224") || - name.equalsIgnoreCase("SHA224")) { - return AlgorithmId.SHA224_oid; - } - if (name.equalsIgnoreCase("SHA-512/224") || - name.equalsIgnoreCase("SHA512/224")) { - return AlgorithmId.SHA512_224_oid; - } - if (name.equalsIgnoreCase("SHA-512/256") || - name.equalsIgnoreCase("SHA512/256")) { - return AlgorithmId.SHA512_256_oid; - } - // Various public key algorithms - if (name.equalsIgnoreCase("RSA")) { - return AlgorithmId.RSAEncryption_oid; - } - if (name.equalsIgnoreCase("RSASSA-PSS")) { - return AlgorithmId.RSASSA_PSS_oid; - } - if (name.equalsIgnoreCase("RSAES-OAEP")) { - return AlgorithmId.RSAES_OAEP_oid; - } - if (name.equalsIgnoreCase("Diffie-Hellman") - || name.equalsIgnoreCase("DH")) { - return AlgorithmId.DH_oid; - } - if (name.equalsIgnoreCase("DSA")) { - return AlgorithmId.DSA_oid; - } - if (name.equalsIgnoreCase("EC")) { - return EC_oid; - } - if (name.equalsIgnoreCase("ECDH")) { - return AlgorithmId.ECDH_oid; - } - - // Secret key algorithms - if (name.equalsIgnoreCase("AES")) { - return AlgorithmId.AES_oid; + KnownOIDs k = KnownOIDs.findMatch(name); + if (k != null) { + return ObjectIdentifier.of(k); } - // Common signature types - if (name.equalsIgnoreCase("MD5withRSA") - || name.equalsIgnoreCase("MD5/RSA")) { - return AlgorithmId.md5WithRSAEncryption_oid; - } - if (name.equalsIgnoreCase("MD2withRSA") - || name.equalsIgnoreCase("MD2/RSA")) { - return AlgorithmId.md2WithRSAEncryption_oid; - } - if (name.equalsIgnoreCase("SHAwithDSA") - || name.equalsIgnoreCase("SHA1withDSA") - || name.equalsIgnoreCase("SHA/DSA") - || name.equalsIgnoreCase("SHA1/DSA") - || name.equalsIgnoreCase("DSAWithSHA1") - || name.equalsIgnoreCase("DSS") - || name.equalsIgnoreCase("SHA-1/DSA")) { - return AlgorithmId.sha1WithDSA_oid; - } - if (name.equalsIgnoreCase("SHA224WithDSA")) { - return AlgorithmId.sha224WithDSA_oid; - } - if (name.equalsIgnoreCase("SHA256WithDSA")) { - return AlgorithmId.sha256WithDSA_oid; - } - if (name.equalsIgnoreCase("SHA1WithRSA") - || name.equalsIgnoreCase("SHA1/RSA")) { - return AlgorithmId.sha1WithRSAEncryption_oid; - } - if (name.equalsIgnoreCase("SHA256WithRSA")) { - return AlgorithmId.sha256WithRSAEncryption_oid; - } - if (name.equalsIgnoreCase("SHA384WithRSA")) { - return AlgorithmId.sha384WithRSAEncryption_oid; - } - if (name.equalsIgnoreCase("SHA512WithRSA")) { - return AlgorithmId.sha512WithRSAEncryption_oid; - } - if (name.equalsIgnoreCase("SHA1withECDSA") - || name.equalsIgnoreCase("ECDSA")) { - return AlgorithmId.sha1WithECDSA_oid; - } - if (name.equalsIgnoreCase("SHA224withECDSA")) { - return AlgorithmId.sha224WithECDSA_oid; - } - if (name.equalsIgnoreCase("SHA256withECDSA")) { - return AlgorithmId.sha256WithECDSA_oid; - } - if (name.equalsIgnoreCase("SHA384withECDSA")) { - return AlgorithmId.sha384WithECDSA_oid; - } - if (name.equalsIgnoreCase("SHA512withECDSA")) { - return AlgorithmId.sha512WithECDSA_oid; + // unknown algorithm oids + if (name.indexOf(".") == -1) { + // see if there is a matching oid string alias mapping from + // 3rd party providers + name = name.toUpperCase(Locale.ENGLISH); + String oidStr = aliasOidsTable().get(name); + if (oidStr != null) { + return ObjectIdentifier.of(oidStr); + } return null; + } else { + return ObjectIdentifier.of(name); } - - return oidTable().get(name.toUpperCase(Locale.ENGLISH)); - } - - private static ObjectIdentifier oid(int ... values) { - return ObjectIdentifier.newInternal(values); } - private static volatile Map oidTable; - private static final Map nameTable; + // oid string cache index'ed by algorithm name and oid strings + private static volatile Map aliasOidsTable; - /** Returns the oidTable, lazily initializing it on first access. */ - private static Map oidTable() - throws IOException { - // Double checked locking; safe because oidTable is volatile - Map tab; - if ((tab = oidTable) == null) { + // returns the aliasOidsTable, lazily initializing it on first access. + private static Map aliasOidsTable() { + // Double checked locking; safe because aliasOidsTable is volatile + Map tab = aliasOidsTable; + if (tab == null) { synchronized (AlgorithmId.class) { - if ((tab = oidTable) == null) - oidTable = tab = computeOidTable(); + if ((tab = aliasOidsTable) == null) { + aliasOidsTable = tab = collectOIDAliases(); + } } } return tab; } - /** Collects the algorithm names from the installed providers. */ - private static HashMap computeOidTable() - throws IOException { - HashMap tab = new HashMap<>(); + private static boolean isKnownProvider(Provider p) { + String pn = p.getName(); + String mn = p.getClass().getModule().getName(); + if (pn != null && mn != null) { + return ((mn.equals("java.base") && + (pn.equals("SUN") || pn.equals("SunRsaSign") || + pn.equals("SunJCE") || pn.equals("SunJSSE"))) || + (mn.equals("jdk.crypto.ec") && pn.equals("SunEC")) || + (mn.equals("jdk.crypto.mscapi") && pn.equals("SunMSCAPI")) || + (mn.equals("jdk.crypto.cryptoki") && + pn.startsWith("SunPKCS11"))); + } else { + return false; + } + } + + private static ConcurrentHashMap collectOIDAliases() { + ConcurrentHashMap t = new ConcurrentHashMap<>(); for (Provider provider : Security.getProviders()) { + // skip providers which are already using SecurityProviderConstants + // and KnownOIDs + if (isKnownProvider(provider)) { + continue; + } for (Object key : provider.keySet()) { String alias = (String)key; String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH); int index; if (upperCaseAlias.startsWith("ALG.ALIAS") && - (index=upperCaseAlias.indexOf("OID.", 0)) != -1) { + (index = upperCaseAlias.indexOf("OID.", 0)) != -1) { index += "OID.".length(); if (index == alias.length()) { // invalid alias entry break; } - String oidString = alias.substring(index); + String ostr = alias.substring(index); String stdAlgName = provider.getProperty(alias); if (stdAlgName != null) { stdAlgName = stdAlgName.toUpperCase(Locale.ENGLISH); } - if (stdAlgName != null && - tab.get(stdAlgName) == null) { - tab.put(stdAlgName, new ObjectIdentifier(oidString)); + // add the name->oid and oid->name mappings if none exists + if (KnownOIDs.findMatch(stdAlgName) == null) { + // not override earlier entries if it exists + t.putIfAbsent(stdAlgName, ostr); + } + if (KnownOIDs.findMatch(ostr) == null) { + // not override earlier entries if it exists + t.putIfAbsent(ostr, stdAlgName); } } } } - return tab; + return t; } - /*****************************************************************/ - - /* - * HASHING ALGORITHMS - */ - - /** - * Algorithm ID for the MD2 Message Digest Algorthm, from RFC 1319. - * OID = 1.2.840.113549.2.2 - */ public static final ObjectIdentifier MD2_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 2, 2}); + ObjectIdentifier.of(KnownOIDs.MD2); - /** - * Algorithm ID for the MD5 Message Digest Algorthm, from RFC 1321. - * OID = 1.2.840.113549.2.5 - */ public static final ObjectIdentifier MD5_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 2, 5}); + ObjectIdentifier.of(KnownOIDs.MD5); - /** - * Algorithm ID for the SHA1 Message Digest Algorithm, from FIPS 180-1. - * This is sometimes called "SHA", though that is often confusing since - * many people refer to FIPS 180 (which has an error) as defining SHA. - * OID = 1.3.14.3.2.26. Old SHA-0 OID: 1.3.14.3.2.18. - */ public static final ObjectIdentifier SHA_oid = - ObjectIdentifier.newInternal(new int[] {1, 3, 14, 3, 2, 26}); + ObjectIdentifier.of(KnownOIDs.SHA_1); public static final ObjectIdentifier SHA224_oid = - ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 4}); + ObjectIdentifier.of(KnownOIDs.SHA_224); public static final ObjectIdentifier SHA256_oid = - ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 1}); + ObjectIdentifier.of(KnownOIDs.SHA_256); public static final ObjectIdentifier SHA384_oid = - ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 2}); + ObjectIdentifier.of(KnownOIDs.SHA_384); public static final ObjectIdentifier SHA512_oid = - ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 3}); + ObjectIdentifier.of(KnownOIDs.SHA_512); public static final ObjectIdentifier SHA512_224_oid = - ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 5}); + ObjectIdentifier.of(KnownOIDs.SHA_512$224); public static final ObjectIdentifier SHA512_256_oid = - ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 6}); - - /* - * COMMON PUBLIC KEY TYPES - */ - private static final int[] DH_data = { 1, 2, 840, 113549, 1, 3, 1 }; - private static final int[] DH_PKIX_data = { 1, 2, 840, 10046, 2, 1 }; - private static final int[] DSA_OIW_data = { 1, 3, 14, 3, 2, 12 }; - private static final int[] DSA_PKIX_data = { 1, 2, 840, 10040, 4, 1 }; - private static final int[] RSA_data = { 2, 5, 8, 1, 1 }; - - public static final ObjectIdentifier DH_oid; - public static final ObjectIdentifier DH_PKIX_oid; - public static final ObjectIdentifier DSA_oid; - public static final ObjectIdentifier DSA_OIW_oid; - public static final ObjectIdentifier EC_oid = oid(1, 2, 840, 10045, 2, 1); - public static final ObjectIdentifier ECDH_oid = oid(1, 3, 132, 1, 12); - public static final ObjectIdentifier RSA_oid; - public static final ObjectIdentifier RSAEncryption_oid = - oid(1, 2, 840, 113549, 1, 1, 1); - public static final ObjectIdentifier RSAES_OAEP_oid = - oid(1, 2, 840, 113549, 1, 1, 7); - public static final ObjectIdentifier mgf1_oid = - oid(1, 2, 840, 113549, 1, 1, 8); - public static final ObjectIdentifier RSASSA_PSS_oid = - oid(1, 2, 840, 113549, 1, 1, 10); - - /* - * COMMON SECRET KEY TYPES - */ - public static final ObjectIdentifier AES_oid = - oid(2, 16, 840, 1, 101, 3, 4, 1); - - /* - * COMMON SIGNATURE ALGORITHMS - */ - private static final int[] md2WithRSAEncryption_data = - { 1, 2, 840, 113549, 1, 1, 2 }; - private static final int[] md5WithRSAEncryption_data = - { 1, 2, 840, 113549, 1, 1, 4 }; - private static final int[] sha1WithRSAEncryption_data = - { 1, 2, 840, 113549, 1, 1, 5 }; - private static final int[] sha1WithRSAEncryption_OIW_data = - { 1, 3, 14, 3, 2, 29 }; - private static final int[] sha224WithRSAEncryption_data = - { 1, 2, 840, 113549, 1, 1, 14 }; - private static final int[] sha256WithRSAEncryption_data = - { 1, 2, 840, 113549, 1, 1, 11 }; - private static final int[] sha384WithRSAEncryption_data = - { 1, 2, 840, 113549, 1, 1, 12 }; - private static final int[] sha512WithRSAEncryption_data = - { 1, 2, 840, 113549, 1, 1, 13 }; - - private static final int[] shaWithDSA_OIW_data = - { 1, 3, 14, 3, 2, 13 }; - private static final int[] sha1WithDSA_OIW_data = - { 1, 3, 14, 3, 2, 27 }; - private static final int[] dsaWithSHA1_PKIX_data = - { 1, 2, 840, 10040, 4, 3 }; - - public static final ObjectIdentifier md2WithRSAEncryption_oid; - public static final ObjectIdentifier md5WithRSAEncryption_oid; - public static final ObjectIdentifier sha1WithRSAEncryption_oid; - public static final ObjectIdentifier sha1WithRSAEncryption_OIW_oid; - public static final ObjectIdentifier sha224WithRSAEncryption_oid; - public static final ObjectIdentifier sha256WithRSAEncryption_oid; - public static final ObjectIdentifier sha384WithRSAEncryption_oid; - public static final ObjectIdentifier sha512WithRSAEncryption_oid; - public static final ObjectIdentifier sha512_224WithRSAEncryption_oid = - oid(1, 2, 840, 113549, 1, 1, 15); - public static final ObjectIdentifier sha512_256WithRSAEncryption_oid = - oid(1, 2, 840, 113549, 1, 1, 16);; - - public static final ObjectIdentifier shaWithDSA_OIW_oid; - public static final ObjectIdentifier sha1WithDSA_OIW_oid; - public static final ObjectIdentifier sha1WithDSA_oid; - public static final ObjectIdentifier sha224WithDSA_oid = - oid(2, 16, 840, 1, 101, 3, 4, 3, 1); - public static final ObjectIdentifier sha256WithDSA_oid = - oid(2, 16, 840, 1, 101, 3, 4, 3, 2); - - public static final ObjectIdentifier sha1WithECDSA_oid = - oid(1, 2, 840, 10045, 4, 1); - public static final ObjectIdentifier sha224WithECDSA_oid = - oid(1, 2, 840, 10045, 4, 3, 1); - public static final ObjectIdentifier sha256WithECDSA_oid = - oid(1, 2, 840, 10045, 4, 3, 2); - public static final ObjectIdentifier sha384WithECDSA_oid = - oid(1, 2, 840, 10045, 4, 3, 3); - public static final ObjectIdentifier sha512WithECDSA_oid = - oid(1, 2, 840, 10045, 4, 3, 4); - public static final ObjectIdentifier specifiedWithECDSA_oid = - oid(1, 2, 840, 10045, 4, 3); - - /** - * Algorithm ID for the PBE encryption algorithms from PKCS#5 and - * PKCS#12. - */ - public static final ObjectIdentifier pbeWithMD5AndDES_oid = - ObjectIdentifier.newInternal(new int[]{1, 2, 840, 113549, 1, 5, 3}); - public static final ObjectIdentifier pbeWithMD5AndRC2_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 6}); - public static final ObjectIdentifier pbeWithSHA1AndDES_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 10}); - public static final ObjectIdentifier pbeWithSHA1AndRC2_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 11}); - public static ObjectIdentifier pbeWithSHA1AndRC4_128_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 1}); - public static ObjectIdentifier pbeWithSHA1AndRC4_40_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 2}); - public static ObjectIdentifier pbeWithSHA1AndDESede_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 3}); - public static ObjectIdentifier pbeWithSHA1AndRC2_128_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 5}); - public static ObjectIdentifier pbeWithSHA1AndRC2_40_oid = - ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 6}); - - static { - /* - * Note the preferred OIDs are named simply with no "OIW" or - * "PKIX" in them, even though they may point to data from these - * specs; e.g. SHA_oid, DH_oid, DSA_oid, SHA1WithDSA_oid... - */ - /** - * Algorithm ID for Diffie Hellman Key agreement, from PKCS #3. - * Parameters include public values P and G, and may optionally specify - * the length of the private key X. Alternatively, algorithm parameters - * may be derived from another source such as a Certificate Authority's - * certificate. - * OID = 1.2.840.113549.1.3.1 - */ - DH_oid = ObjectIdentifier.newInternal(DH_data); - - /** - * Algorithm ID for the Diffie Hellman Key Agreement (DH), from RFC 3279. - * Parameters may include public values P and G. - * OID = 1.2.840.10046.2.1 - */ - DH_PKIX_oid = ObjectIdentifier.newInternal(DH_PKIX_data); - - /** - * Algorithm ID for the Digital Signing Algorithm (DSA), from the - * NIST OIW Stable Agreements part 12. - * Parameters may include public values P, Q, and G; or these may be - * derived from - * another source such as a Certificate Authority's certificate. - * OID = 1.3.14.3.2.12 - */ - DSA_OIW_oid = ObjectIdentifier.newInternal(DSA_OIW_data); - - /** - * Algorithm ID for the Digital Signing Algorithm (DSA), from RFC 3279. - * Parameters may include public values P, Q, and G; or these may be - * derived from another source such as a Certificate Authority's - * certificate. - * OID = 1.2.840.10040.4.1 - */ - DSA_oid = ObjectIdentifier.newInternal(DSA_PKIX_data); - - /** - * Algorithm ID for RSA keys used for any purpose, as defined in X.509. - * The algorithm parameter is a single value, the number of bits in the - * public modulus. - * OID = 2.5.8.1.1 - */ - RSA_oid = ObjectIdentifier.newInternal(RSA_data); + ObjectIdentifier.of(KnownOIDs.SHA_512$256); - /** - * Identifies a signing algorithm where an MD2 digest is encrypted - * using an RSA private key; defined in PKCS #1. Use of this - * signing algorithm is discouraged due to MD2 vulnerabilities. - * OID = 1.2.840.113549.1.1.2 - */ - md2WithRSAEncryption_oid = - ObjectIdentifier.newInternal(md2WithRSAEncryption_data); - - /** - * Identifies a signing algorithm where an MD5 digest is - * encrypted using an RSA private key; defined in PKCS #1. - * OID = 1.2.840.113549.1.1.4 - */ - md5WithRSAEncryption_oid = - ObjectIdentifier.newInternal(md5WithRSAEncryption_data); - - /** - * Identifies a signing algorithm where a SHA1 digest is - * encrypted using an RSA private key; defined by RSA DSI. - * OID = 1.2.840.113549.1.1.5 - */ - sha1WithRSAEncryption_oid = - ObjectIdentifier.newInternal(sha1WithRSAEncryption_data); + public static final ObjectIdentifier DSA_oid = + ObjectIdentifier.of(KnownOIDs.DSA); - /** - * Identifies a signing algorithm where a SHA1 digest is - * encrypted using an RSA private key; defined in NIST OIW. - * OID = 1.3.14.3.2.29 - */ - sha1WithRSAEncryption_OIW_oid = - ObjectIdentifier.newInternal(sha1WithRSAEncryption_OIW_data); - - /** - * Identifies a signing algorithm where a SHA224 digest is - * encrypted using an RSA private key; defined by PKCS #1. - * OID = 1.2.840.113549.1.1.14 - */ - sha224WithRSAEncryption_oid = - ObjectIdentifier.newInternal(sha224WithRSAEncryption_data); - - /** - * Identifies a signing algorithm where a SHA256 digest is - * encrypted using an RSA private key; defined by PKCS #1. - * OID = 1.2.840.113549.1.1.11 - */ - sha256WithRSAEncryption_oid = - ObjectIdentifier.newInternal(sha256WithRSAEncryption_data); + public static final ObjectIdentifier EC_oid = + ObjectIdentifier.of(KnownOIDs.EC); - /** - * Identifies a signing algorithm where a SHA384 digest is - * encrypted using an RSA private key; defined by PKCS #1. - * OID = 1.2.840.113549.1.1.12 - */ - sha384WithRSAEncryption_oid = - ObjectIdentifier.newInternal(sha384WithRSAEncryption_data); - - /** - * Identifies a signing algorithm where a SHA512 digest is - * encrypted using an RSA private key; defined by PKCS #1. - * OID = 1.2.840.113549.1.1.13 - */ - sha512WithRSAEncryption_oid = - ObjectIdentifier.newInternal(sha512WithRSAEncryption_data); - - /** - * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a - * SHA digest is signed using the Digital Signing Algorithm (DSA). - * This should not be used. - * OID = 1.3.14.3.2.13 - */ - shaWithDSA_OIW_oid = ObjectIdentifier.newInternal(shaWithDSA_OIW_data); + public static final ObjectIdentifier RSAEncryption_oid = + ObjectIdentifier.of(KnownOIDs.RSA); - /** - * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a - * SHA1 digest is signed using the Digital Signing Algorithm (DSA). - * OID = 1.3.14.3.2.27 - */ - sha1WithDSA_OIW_oid = ObjectIdentifier.newInternal(sha1WithDSA_OIW_data); + public static final ObjectIdentifier RSASSA_PSS_oid = + ObjectIdentifier.of(KnownOIDs.RSASSA_PSS); - /** - * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a - * SHA1 digest is signed using the Digital Signing Algorithm (DSA). - * OID = 1.2.840.10040.4.3 - */ - sha1WithDSA_oid = ObjectIdentifier.newInternal(dsaWithSHA1_PKIX_data); - - nameTable = new HashMap<>(); - nameTable.put(MD5_oid, "MD5"); - nameTable.put(MD2_oid, "MD2"); - nameTable.put(SHA_oid, "SHA-1"); - nameTable.put(SHA224_oid, "SHA-224"); - nameTable.put(SHA256_oid, "SHA-256"); - nameTable.put(SHA384_oid, "SHA-384"); - nameTable.put(SHA512_oid, "SHA-512"); - nameTable.put(SHA512_224_oid, "SHA-512/224"); - nameTable.put(SHA512_256_oid, "SHA-512/256"); - nameTable.put(RSAEncryption_oid, "RSA"); - nameTable.put(RSA_oid, "RSA"); - nameTable.put(DH_oid, "Diffie-Hellman"); - nameTable.put(DH_PKIX_oid, "Diffie-Hellman"); - nameTable.put(DSA_oid, "DSA"); - nameTable.put(DSA_OIW_oid, "DSA"); - nameTable.put(EC_oid, "EC"); - nameTable.put(ECDH_oid, "ECDH"); - - nameTable.put(AES_oid, "AES"); - - nameTable.put(sha1WithECDSA_oid, "SHA1withECDSA"); - nameTable.put(sha224WithECDSA_oid, "SHA224withECDSA"); - nameTable.put(sha256WithECDSA_oid, "SHA256withECDSA"); - nameTable.put(sha384WithECDSA_oid, "SHA384withECDSA"); - nameTable.put(sha512WithECDSA_oid, "SHA512withECDSA"); - nameTable.put(md5WithRSAEncryption_oid, "MD5withRSA"); - nameTable.put(md2WithRSAEncryption_oid, "MD2withRSA"); - nameTable.put(sha1WithDSA_oid, "SHA1withDSA"); - nameTable.put(sha1WithDSA_OIW_oid, "SHA1withDSA"); - nameTable.put(shaWithDSA_OIW_oid, "SHA1withDSA"); - nameTable.put(sha224WithDSA_oid, "SHA224withDSA"); - nameTable.put(sha256WithDSA_oid, "SHA256withDSA"); - nameTable.put(sha1WithRSAEncryption_oid, "SHA1withRSA"); - nameTable.put(sha1WithRSAEncryption_OIW_oid, "SHA1withRSA"); - nameTable.put(sha224WithRSAEncryption_oid, "SHA224withRSA"); - nameTable.put(sha256WithRSAEncryption_oid, "SHA256withRSA"); - nameTable.put(sha384WithRSAEncryption_oid, "SHA384withRSA"); - nameTable.put(sha512WithRSAEncryption_oid, "SHA512withRSA"); - nameTable.put(sha512_224WithRSAEncryption_oid, "SHA512/224withRSA"); - nameTable.put(sha512_256WithRSAEncryption_oid, "SHA512/256withRSA"); - nameTable.put(RSASSA_PSS_oid, "RSASSA-PSS"); - nameTable.put(RSAES_OAEP_oid, "RSAES-OAEP"); - - nameTable.put(pbeWithMD5AndDES_oid, "PBEWithMD5AndDES"); - nameTable.put(pbeWithMD5AndRC2_oid, "PBEWithMD5AndRC2"); - nameTable.put(pbeWithSHA1AndDES_oid, "PBEWithSHA1AndDES"); - nameTable.put(pbeWithSHA1AndRC2_oid, "PBEWithSHA1AndRC2"); - nameTable.put(pbeWithSHA1AndRC4_128_oid, "PBEWithSHA1AndRC4_128"); - nameTable.put(pbeWithSHA1AndRC4_40_oid, "PBEWithSHA1AndRC4_40"); - nameTable.put(pbeWithSHA1AndDESede_oid, "PBEWithSHA1AndDESede"); - nameTable.put(pbeWithSHA1AndRC2_128_oid, "PBEWithSHA1AndRC2_128"); - nameTable.put(pbeWithSHA1AndRC2_40_oid, "PBEWithSHA1AndRC2_40"); - } + public static final ObjectIdentifier MGF1_oid = + ObjectIdentifier.of(KnownOIDs.MGF1); /** * Creates a signature algorithm name from a digest algorithm diff --git a/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java b/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java index c8968448c7f..6f73a3af6d0 100644 --- a/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java +++ b/src/java.base/share/classes/sun/security/x509/ExtendedKeyUsageExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,9 +34,7 @@ import java.util.Map; import java.util.Vector; -import sun.security.util.DerValue; -import sun.security.util.DerOutputStream; -import sun.security.util.ObjectIdentifier; +import sun.security.util.*; /** * This class defines the Extended Key Usage Extension, which @@ -94,35 +92,6 @@ public class ExtendedKeyUsageExtension extends Extension public static final String NAME = "ExtendedKeyUsage"; public static final String USAGES = "usages"; - // OID defined in RFC 5280 Sections 4.2.1.12 - // more from http://www.alvestrand.no/objectid/1.3.6.1.5.5.7.3.html - private static final Map map = - new HashMap (); - - private static final int[] anyExtendedKeyUsageOidData = {2, 5, 29, 37, 0}; - private static final int[] serverAuthOidData = {1, 3, 6, 1, 5, 5, 7, 3, 1}; - private static final int[] clientAuthOidData = {1, 3, 6, 1, 5, 5, 7, 3, 2}; - private static final int[] codeSigningOidData = {1, 3, 6, 1, 5, 5, 7, 3, 3}; - private static final int[] emailProtectionOidData = {1, 3, 6, 1, 5, 5, 7, 3, 4}; - private static final int[] ipsecEndSystemOidData = {1, 3, 6, 1, 5, 5, 7, 3, 5}; - private static final int[] ipsecTunnelOidData = {1, 3, 6, 1, 5, 5, 7, 3, 6}; - private static final int[] ipsecUserOidData = {1, 3, 6, 1, 5, 5, 7, 3, 7}; - private static final int[] timeStampingOidData = {1, 3, 6, 1, 5, 5, 7, 3, 8}; - private static final int[] OCSPSigningOidData = {1, 3, 6, 1, 5, 5, 7, 3, 9}; - - static { - map.put(ObjectIdentifier.newInternal(anyExtendedKeyUsageOidData), "anyExtendedKeyUsage"); - map.put(ObjectIdentifier.newInternal(serverAuthOidData), "serverAuth"); - map.put(ObjectIdentifier.newInternal(clientAuthOidData), "clientAuth"); - map.put(ObjectIdentifier.newInternal(codeSigningOidData), "codeSigning"); - map.put(ObjectIdentifier.newInternal(emailProtectionOidData), "emailProtection"); - map.put(ObjectIdentifier.newInternal(ipsecEndSystemOidData), "ipsecEndSystem"); - map.put(ObjectIdentifier.newInternal(ipsecTunnelOidData), "ipsecTunnel"); - map.put(ObjectIdentifier.newInternal(ipsecUserOidData), "ipsecUser"); - map.put(ObjectIdentifier.newInternal(timeStampingOidData), "timeStamping"); - map.put(ObjectIdentifier.newInternal(OCSPSigningOidData), "OCSPSigning"); - }; - /** * Vector of KeyUsages for this object. */ @@ -209,11 +178,12 @@ public String toString() { usage += "\n "; } - String result = map.get(oid); - if (result != null) { - usage += result; + String res = oid.toString(); + KnownOIDs os = KnownOIDs.findMatch(res); + if (os != null) { + usage += os.stdName(); } else { - usage += oid.toString(); + usage += res; } first = false; } diff --git a/src/java.base/share/classes/sun/security/x509/GeneralSubtrees.java b/src/java.base/share/classes/sun/security/x509/GeneralSubtrees.java index 847f56aba86..8adafe20344 100644 --- a/src/java.base/share/classes/sun/security/x509/GeneralSubtrees.java +++ b/src/java.base/share/classes/sun/security/x509/GeneralSubtrees.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -270,8 +270,7 @@ private GeneralSubtree createWidestSubtree(GeneralNameInterface name) { newName = new GeneralName(new IPAddressName((byte[])null)); break; case GeneralNameInterface.NAME_OID: - newName = new GeneralName - (new OIDName(new ObjectIdentifier((int[])null))); + newName = new GeneralName(new OIDName("")); break; default: throw new IOException diff --git a/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java b/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java index a20a1f55803..6fa2ae16729 100644 --- a/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java +++ b/src/java.base/share/classes/sun/security/x509/InhibitAnyPolicyExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * 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,10 +29,7 @@ import java.io.OutputStream; import java.util.Enumeration; -import sun.security.util.Debug; -import sun.security.util.DerOutputStream; -import sun.security.util.DerValue; -import sun.security.util.ObjectIdentifier; +import sun.security.util.*; /** * This class represents the Inhibit Any-Policy Extension. @@ -75,14 +72,8 @@ public class InhibitAnyPolicyExtension extends Extension /** * Object identifier for "any-policy" */ - public static ObjectIdentifier AnyPolicy_Id; - static { - try { - AnyPolicy_Id = new ObjectIdentifier("2.5.29.32.0"); - } catch (IOException ioe) { - // Should not happen - } - } + public static ObjectIdentifier AnyPolicy_Id = + ObjectIdentifier.of(KnownOIDs.CE_CERT_POLICIES_ANY); /** * Attribute names. diff --git a/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java b/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java index ca4dc3c53c3..d455f00f8a8 100644 --- a/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java +++ b/src/java.base/share/classes/sun/security/x509/NetscapeCertTypeExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,20 +69,11 @@ public class NetscapeCertTypeExtension extends Extension public static final String S_MIME_CA = "s_mime_ca"; public static final String OBJECT_SIGNING_CA = "object_signing_ca"; - private static final int[] CertType_data = { 2, 16, 840, 1, 113730, 1, 1 }; - /** * Object identifier for the Netscape-Cert-Type extension. */ - public static ObjectIdentifier NetscapeCertType_Id; - - static { - try { - NetscapeCertType_Id = new ObjectIdentifier(CertType_data); - } catch (IOException ioe) { - // should not happen - } - } + public static ObjectIdentifier NetscapeCertType_Id = + ObjectIdentifier.of(KnownOIDs.NETSCAPE_CertType); private boolean[] bitString; diff --git a/src/java.base/share/classes/sun/security/x509/OIDMap.java b/src/java.base/share/classes/sun/security/x509/OIDMap.java index a39493e279a..54a8d9146c8 100644 --- a/src/java.base/share/classes/sun/security/x509/OIDMap.java +++ b/src/java.base/share/classes/sun/security/x509/OIDMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,9 +102,6 @@ private OIDMap() { private static final String OCSPNOCHECK = ROOT + "." + OCSPNoCheckExtension.NAME; - private static final int[] NetscapeCertType_data = - { 2, 16, 840, 1, 113730, 1, 1 }; - /** Map ObjectIdentifier(oid) -> OIDInfo(info) */ private static final Map oidMap; @@ -138,8 +135,8 @@ private OIDMap() { "sun.security.x509.AuthorityKeyIdentifierExtension"); addInternal(POLICY_CONSTRAINTS, PKIXExtensions.PolicyConstraints_Id, "sun.security.x509.PolicyConstraintsExtension"); - addInternal(NETSCAPE_CERT, ObjectIdentifier.newInternal - (new int[] {2,16,840,1,113730,1,1}), + addInternal(NETSCAPE_CERT, + ObjectIdentifier.of(KnownOIDs.NETSCAPE_CertType), "sun.security.x509.NetscapeCertTypeExtension"); addInternal(CERT_POLICIES, PKIXExtensions.CertificatePolicies_Id, "sun.security.x509.CertificatePoliciesExtension"); @@ -230,7 +227,7 @@ public static void addAttribute(String name, String oid, Class clazz) throws CertificateException { ObjectIdentifier objId; try { - objId = new ObjectIdentifier(oid); + objId = ObjectIdentifier.of(oid); } catch (IOException ioe) { throw new CertificateException ("Invalid Object identifier: " + oid); diff --git a/src/java.base/share/classes/sun/security/x509/OIDName.java b/src/java.base/share/classes/sun/security/x509/OIDName.java index 6db9d11e2d3..4377fc3ee71 100644 --- a/src/java.base/share/classes/sun/security/x509/OIDName.java +++ b/src/java.base/share/classes/sun/security/x509/OIDName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ public OIDName(ObjectIdentifier oid) { */ public OIDName(String name) throws IOException { try { - oid = new ObjectIdentifier(name); + oid = ObjectIdentifier.of(name); } catch (Exception e) { throw new IOException("Unable to create OIDName: " + e); } diff --git a/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java b/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java index bf4f8fdf80c..754d3d0e726 100644 --- a/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java +++ b/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package sun.security.x509; -import java.io.*; import sun.security.util.*; @@ -48,163 +47,151 @@ * @author Hemma Prafullchandra */ public class PKIXExtensions { - // The object identifiers - private static final int[] AuthorityKey_data = { 2, 5, 29, 35 }; - private static final int[] SubjectKey_data = { 2, 5, 29, 14 }; - private static final int[] KeyUsage_data = { 2, 5, 29, 15 }; - private static final int[] PrivateKeyUsage_data = { 2, 5, 29, 16 }; - private static final int[] CertificatePolicies_data = { 2, 5, 29, 32 }; - private static final int[] PolicyMappings_data = { 2, 5, 29, 33 }; - private static final int[] SubjectAlternativeName_data = { 2, 5, 29, 17 }; - private static final int[] IssuerAlternativeName_data = { 2, 5, 29, 18 }; - private static final int[] SubjectDirectoryAttributes_data = { 2, 5, 29, 9 }; - private static final int[] BasicConstraints_data = { 2, 5, 29, 19 }; - private static final int[] NameConstraints_data = { 2, 5, 29, 30 }; - private static final int[] PolicyConstraints_data = { 2, 5, 29, 36 }; - private static final int[] CRLDistributionPoints_data = { 2, 5, 29, 31 }; - private static final int[] CRLNumber_data = { 2, 5, 29, 20 }; - private static final int[] IssuingDistributionPoint_data = { 2, 5, 29, 28 }; - private static final int[] DeltaCRLIndicator_data = { 2, 5, 29, 27 }; - private static final int[] ReasonCode_data = { 2, 5, 29, 21 }; - private static final int[] HoldInstructionCode_data = { 2, 5, 29, 23 }; - private static final int[] InvalidityDate_data = { 2, 5, 29, 24 }; - private static final int[] ExtendedKeyUsage_data = { 2, 5, 29, 37 }; - private static final int[] InhibitAnyPolicy_data = { 2, 5, 29, 54 }; - private static final int[] CertificateIssuer_data = { 2, 5, 29, 29 }; - private static final int[] AuthInfoAccess_data = { 1, 3, 6, 1, 5, 5, 7, 1, 1}; - private static final int[] SubjectInfoAccess_data = { 1, 3, 6, 1, 5, 5, 7, 1, 11}; - private static final int[] FreshestCRL_data = { 2, 5, 29, 46 }; - private static final int[] OCSPNoCheck_data = { 1, 3, 6, 1, 5, 5, 7, - 48, 1, 5}; - - // Additional extensions under the PKIX arc that are not necessarily - // used in X.509 Certificates or CRLs. - private static final int OCSPNonce_data [] = { 1, 3, 6, 1, 5, 5, 7, - 48, 1, 2}; - /** * Identifies the particular public key used to sign the certificate. */ - public static final ObjectIdentifier AuthorityKey_Id; + public static final ObjectIdentifier AuthorityKey_Id = + ObjectIdentifier.of(KnownOIDs.AuthorityKeyID); /** * Identifies the particular public key used in an application. */ - public static final ObjectIdentifier SubjectKey_Id; + public static final ObjectIdentifier SubjectKey_Id = + ObjectIdentifier.of(KnownOIDs.SubjectKeyID); /** * Defines the purpose of the key contained in the certificate. */ - public static final ObjectIdentifier KeyUsage_Id; + public static final ObjectIdentifier KeyUsage_Id = + ObjectIdentifier.of(KnownOIDs.KeyUsage); /** * Allows the certificate issuer to specify a different validity period * for the private key than the certificate. */ - public static final ObjectIdentifier PrivateKeyUsage_Id; + public static final ObjectIdentifier PrivateKeyUsage_Id = + ObjectIdentifier.of(KnownOIDs.PrivateKeyUsage); /** * Contains the sequence of policy information terms. */ - public static final ObjectIdentifier CertificatePolicies_Id; + public static final ObjectIdentifier CertificatePolicies_Id = + ObjectIdentifier.of(KnownOIDs.CertificatePolicies); /** * Lists pairs of object identifiers of policies considered equivalent by * the issuing CA to the subject CA. */ - public static final ObjectIdentifier PolicyMappings_Id; + public static final ObjectIdentifier PolicyMappings_Id = + ObjectIdentifier.of(KnownOIDs.PolicyMappings); /** * Allows additional identities to be bound to the subject of the * certificate. */ - public static final ObjectIdentifier SubjectAlternativeName_Id; + public static final ObjectIdentifier SubjectAlternativeName_Id = + ObjectIdentifier.of(KnownOIDs.SubjectAlternativeName); /** * Allows additional identities to be associated with the certificate * issuer. */ - public static final ObjectIdentifier IssuerAlternativeName_Id; + public static final ObjectIdentifier IssuerAlternativeName_Id = + ObjectIdentifier.of(KnownOIDs.IssuerAlternativeName); /** * Identifies additional directory attributes. * This extension is always non-critical. */ - public static final ObjectIdentifier SubjectDirectoryAttributes_Id; + public static final ObjectIdentifier SubjectDirectoryAttributes_Id = + ObjectIdentifier.of(KnownOIDs.SubjectDirectoryAttributes); /** * Identifies whether the subject of the certificate is a CA and how deep * a certification path may exist through that CA. */ - public static final ObjectIdentifier BasicConstraints_Id; + public static final ObjectIdentifier BasicConstraints_Id = + ObjectIdentifier.of(KnownOIDs.BasicConstraints); /** * Provides for permitted and excluded subtrees that place restrictions * on names that may be included within a certificate issued by a given CA. */ - public static final ObjectIdentifier NameConstraints_Id; + public static final ObjectIdentifier NameConstraints_Id = + ObjectIdentifier.of(KnownOIDs.NameConstraints); /** * Used to either prohibit policy mapping or limit the set of policies * that can be in subsequent certificates. */ - public static final ObjectIdentifier PolicyConstraints_Id; + public static final ObjectIdentifier PolicyConstraints_Id = + ObjectIdentifier.of(KnownOIDs.PolicyConstraints); /** * Identifies how CRL information is obtained. */ - public static final ObjectIdentifier CRLDistributionPoints_Id; + public static final ObjectIdentifier CRLDistributionPoints_Id = + ObjectIdentifier.of(KnownOIDs.CRLDistributionPoints); /** * Conveys a monotonically increasing sequence number for each CRL * issued by a given CA. */ - public static final ObjectIdentifier CRLNumber_Id; + public static final ObjectIdentifier CRLNumber_Id = + ObjectIdentifier.of(KnownOIDs.CRLNumber); /** * Identifies the CRL distribution point for a particular CRL. */ - public static final ObjectIdentifier IssuingDistributionPoint_Id; + public static final ObjectIdentifier IssuingDistributionPoint_Id = + ObjectIdentifier.of(KnownOIDs.IssuingDistributionPoint); /** * Identifies the delta CRL. */ - public static final ObjectIdentifier DeltaCRLIndicator_Id; + public static final ObjectIdentifier DeltaCRLIndicator_Id = + ObjectIdentifier.of(KnownOIDs.DeltaCRLIndicator); /** * Identifies the reason for the certificate revocation. */ - public static final ObjectIdentifier ReasonCode_Id; + public static final ObjectIdentifier ReasonCode_Id = + ObjectIdentifier.of(KnownOIDs.ReasonCode); /** * This extension provides a registered instruction identifier indicating * the action to be taken, after encountering a certificate that has been * placed on hold. */ - public static final ObjectIdentifier HoldInstructionCode_Id; + public static final ObjectIdentifier HoldInstructionCode_Id = + ObjectIdentifier.of(KnownOIDs.HoldInstructionCode); /** * Identifies the date on which it is known or suspected that the private * key was compromised or that the certificate otherwise became invalid. */ - public static final ObjectIdentifier InvalidityDate_Id; + public static final ObjectIdentifier InvalidityDate_Id = + ObjectIdentifier.of(KnownOIDs.InvalidityDate); /** * Identifies one or more purposes for which the certified public key * may be used, in addition to or in place of the basic purposes * indicated in the key usage extension field. */ - public static final ObjectIdentifier ExtendedKeyUsage_Id; + public static final ObjectIdentifier ExtendedKeyUsage_Id = + ObjectIdentifier.of(KnownOIDs.extendedKeyUsage); /** * Specifies whether any-policy policy OID is permitted */ - public static final ObjectIdentifier InhibitAnyPolicy_Id; + public static final ObjectIdentifier InhibitAnyPolicy_Id = + ObjectIdentifier.of(KnownOIDs.InhibitAnyPolicy); /** * Identifies the certificate issuer associated with an entry in an * indirect CRL. */ - public static final ObjectIdentifier CertificateIssuer_Id; + public static final ObjectIdentifier CertificateIssuer_Id = + ObjectIdentifier.of(KnownOIDs.CertificateIssuer); /** * This extension indicates how to access CA information and services for @@ -212,73 +199,33 @@ public class PKIXExtensions { * This information may be used for on-line certification validation * services. */ - public static final ObjectIdentifier AuthInfoAccess_Id; + public static final ObjectIdentifier AuthInfoAccess_Id = + ObjectIdentifier.of(KnownOIDs.AuthInfoAccess); /** * This extension indicates how to access CA information and services for * the subject of the certificate in which the extension appears. */ - public static final ObjectIdentifier SubjectInfoAccess_Id; + public static final ObjectIdentifier SubjectInfoAccess_Id = + ObjectIdentifier.of(KnownOIDs.SubjectInfoAccess); /** * Identifies how delta CRL information is obtained. */ - public static final ObjectIdentifier FreshestCRL_Id; + public static final ObjectIdentifier FreshestCRL_Id = + ObjectIdentifier.of(KnownOIDs.FreshestCRL); /** * Identifies the OCSP client can trust the responder for the * lifetime of the responder's certificate. */ - public static final ObjectIdentifier OCSPNoCheck_Id; + public static final ObjectIdentifier OCSPNoCheck_Id = + ObjectIdentifier.of(KnownOIDs.OCSPNoCheck); /** * This extension is used to provide nonce data for OCSP requests * or responses. */ - public static final ObjectIdentifier OCSPNonce_Id; - - static { - AuthorityKey_Id = ObjectIdentifier.newInternal(AuthorityKey_data); - SubjectKey_Id = ObjectIdentifier.newInternal(SubjectKey_data); - KeyUsage_Id = ObjectIdentifier.newInternal(KeyUsage_data); - PrivateKeyUsage_Id = ObjectIdentifier.newInternal(PrivateKeyUsage_data); - CertificatePolicies_Id = - ObjectIdentifier.newInternal(CertificatePolicies_data); - PolicyMappings_Id = ObjectIdentifier.newInternal(PolicyMappings_data); - SubjectAlternativeName_Id = - ObjectIdentifier.newInternal(SubjectAlternativeName_data); - IssuerAlternativeName_Id = - ObjectIdentifier.newInternal(IssuerAlternativeName_data); - ExtendedKeyUsage_Id = ObjectIdentifier.newInternal(ExtendedKeyUsage_data); - InhibitAnyPolicy_Id = ObjectIdentifier.newInternal(InhibitAnyPolicy_data); - SubjectDirectoryAttributes_Id = - ObjectIdentifier.newInternal(SubjectDirectoryAttributes_data); - BasicConstraints_Id = - ObjectIdentifier.newInternal(BasicConstraints_data); - ReasonCode_Id = ObjectIdentifier.newInternal(ReasonCode_data); - HoldInstructionCode_Id = - ObjectIdentifier.newInternal(HoldInstructionCode_data); - InvalidityDate_Id = ObjectIdentifier.newInternal(InvalidityDate_data); - - NameConstraints_Id = ObjectIdentifier.newInternal(NameConstraints_data); - PolicyConstraints_Id = - ObjectIdentifier.newInternal(PolicyConstraints_data); - CRLDistributionPoints_Id = - ObjectIdentifier.newInternal(CRLDistributionPoints_data); - CRLNumber_Id = - ObjectIdentifier.newInternal(CRLNumber_data); - IssuingDistributionPoint_Id = - ObjectIdentifier.newInternal(IssuingDistributionPoint_data); - DeltaCRLIndicator_Id = - ObjectIdentifier.newInternal(DeltaCRLIndicator_data); - CertificateIssuer_Id = - ObjectIdentifier.newInternal(CertificateIssuer_data); - AuthInfoAccess_Id = - ObjectIdentifier.newInternal(AuthInfoAccess_data); - SubjectInfoAccess_Id = - ObjectIdentifier.newInternal(SubjectInfoAccess_data); - FreshestCRL_Id = ObjectIdentifier.newInternal(FreshestCRL_data); - OCSPNoCheck_Id = ObjectIdentifier.newInternal(OCSPNoCheck_data); - OCSPNonce_Id = ObjectIdentifier.newInternal(OCSPNonce_data); - } + public static final ObjectIdentifier OCSPNonce_Id = + ObjectIdentifier.of(KnownOIDs.OCSPNonceExt); } diff --git a/src/java.base/share/classes/sun/security/x509/X500Name.java b/src/java.base/share/classes/sun/security/x509/X500Name.java index 9bf9c55fb61..701ee40b31a 100644 --- a/src/java.base/share/classes/sun/security/x509/X500Name.java +++ b/src/java.base/share/classes/sun/security/x509/X500Name.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1102,104 +1102,83 @@ private String generateRFC1779DN(Map oidMap) { * Includes all those specified in RFC 5280 as MUST or SHOULD * be recognized */ - private static final int[] commonName_data = { 2, 5, 4, 3 }; - private static final int[] SURNAME_DATA = { 2, 5, 4, 4 }; - private static final int[] SERIALNUMBER_DATA = { 2, 5, 4, 5 }; - private static final int[] countryName_data = { 2, 5, 4, 6 }; - private static final int[] localityName_data = { 2, 5, 4, 7 }; - private static final int[] stateName_data = { 2, 5, 4, 8 }; - private static final int[] streetAddress_data = { 2, 5, 4, 9 }; - private static final int[] orgName_data = { 2, 5, 4, 10 }; - private static final int[] orgUnitName_data = { 2, 5, 4, 11 }; - private static final int[] title_data = { 2, 5, 4, 12 }; - private static final int[] GIVENNAME_DATA = { 2, 5, 4, 42 }; - private static final int[] INITIALS_DATA = { 2, 5, 4, 43 }; - private static final int[] GENERATIONQUALIFIER_DATA = { 2, 5, 4, 44 }; - private static final int[] DNQUALIFIER_DATA = { 2, 5, 4, 46 }; - - private static final int[] ipAddress_data = { 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 }; - private static final int[] DOMAIN_COMPONENT_DATA = - { 0, 9, 2342, 19200300, 100, 1, 25 }; - private static final int[] userid_data = - { 0, 9, 2342, 19200300, 100, 1, 1 }; - // OID for the "CN=" attribute, denoting a person's common name. public static final ObjectIdentifier commonName_oid = - ObjectIdentifier.newInternal(commonName_data); + ObjectIdentifier.of(KnownOIDs.CommonName); + + // OID for the "SURNAME=" attribute, denoting a person's surname. + public static final ObjectIdentifier SURNAME_OID = + ObjectIdentifier.of(KnownOIDs.Surname); // OID for the "SERIALNUMBER=" attribute, denoting a serial number for. // a name. Do not confuse with PKCS#9 issuerAndSerialNumber or the // certificate serial number. public static final ObjectIdentifier SERIALNUMBER_OID = - ObjectIdentifier.newInternal(SERIALNUMBER_DATA); + ObjectIdentifier.of(KnownOIDs.SerialNumber); // OID for the "C=" attribute, denoting a country. public static final ObjectIdentifier countryName_oid = - ObjectIdentifier.newInternal(countryName_data); + ObjectIdentifier.of(KnownOIDs.CountryName); // OID for the "L=" attribute, denoting a locality (such as a city). public static final ObjectIdentifier localityName_oid = - ObjectIdentifier.newInternal(localityName_data); - - // OID for the "O=" attribute, denoting an organization name. - public static final ObjectIdentifier orgName_oid = - ObjectIdentifier.newInternal(orgName_data); - - // OID for the "OU=" attribute, denoting an organizational unit name. - public static final ObjectIdentifier orgUnitName_oid = - ObjectIdentifier.newInternal(orgUnitName_data); + ObjectIdentifier.of(KnownOIDs.LocalityName); // OID for the "S=" attribute, denoting a state (such as Delaware). public static final ObjectIdentifier stateName_oid = - ObjectIdentifier.newInternal(stateName_data); + ObjectIdentifier.of(KnownOIDs.StateName); // OID for the "STREET=" attribute, denoting a street address. public static final ObjectIdentifier streetAddress_oid = - ObjectIdentifier.newInternal(streetAddress_data); + ObjectIdentifier.of(KnownOIDs.StreetAddress); - // OID for the "T=" attribute, denoting a person's title. - public static final ObjectIdentifier title_oid = - ObjectIdentifier.newInternal(title_data); + // OID for the "O=" attribute, denoting an organization name. + public static final ObjectIdentifier orgName_oid = + ObjectIdentifier.of(KnownOIDs.OrgName); - // OID for the "DNQUALIFIER=" or "DNQ=" attribute, denoting DN - // disambiguating information. - public static final ObjectIdentifier DNQUALIFIER_OID = - ObjectIdentifier.newInternal(DNQUALIFIER_DATA); + // OID for the "OU=" attribute, denoting an organizational unit name. + public static final ObjectIdentifier orgUnitName_oid = + ObjectIdentifier.of(KnownOIDs.OrgUnitName); - // OID for the "SURNAME=" attribute, denoting a person's surname. - public static final ObjectIdentifier SURNAME_OID = - ObjectIdentifier.newInternal(SURNAME_DATA); + // OID for the "T=" attribute, denoting a person's title. + public static final ObjectIdentifier title_oid = + ObjectIdentifier.of(KnownOIDs.Title); // OID for the "GIVENNAME=" attribute, denoting a person's given name. public static final ObjectIdentifier GIVENNAME_OID = - ObjectIdentifier.newInternal(GIVENNAME_DATA); + ObjectIdentifier.of(KnownOIDs.GivenName); // OID for the "INITIALS=" attribute, denoting a person's initials. public static final ObjectIdentifier INITIALS_OID = - ObjectIdentifier.newInternal(INITIALS_DATA); + ObjectIdentifier.of(KnownOIDs.Initials); // OID for the "GENERATION=" attribute, denoting Jr., II, etc. public static final ObjectIdentifier GENERATIONQUALIFIER_OID = - ObjectIdentifier.newInternal(GENERATIONQUALIFIER_DATA); + ObjectIdentifier.of(KnownOIDs.GenerationQualifier); + + // OID for the "DNQUALIFIER=" or "DNQ=" attribute, denoting DN + // disambiguating information. + public static final ObjectIdentifier DNQUALIFIER_OID = + ObjectIdentifier.of(KnownOIDs.DNQualifier); // OIDs from other sources which show up in X.500 names we // expect to deal with often. // // OID for "IP=" IP address attributes, used with SKIP. public static final ObjectIdentifier ipAddress_oid = - ObjectIdentifier.newInternal(ipAddress_data); + ObjectIdentifier.of(KnownOIDs.SkipIPAddress); // Domain component OID from RFC 1274, RFC 2247, RFC 5280. // - // OID for "DC=" domain component attributes, used with DNSNames in DN + // OID for "DC=" domain component attributes.used with DNSNames in DN // format. public static final ObjectIdentifier DOMAIN_COMPONENT_OID = - ObjectIdentifier.newInternal(DOMAIN_COMPONENT_DATA); + ObjectIdentifier.of(KnownOIDs.UCL_DomainComponent); // OID for "UID=" denoting a user id, defined in RFCs 1274 & 2798. public static final ObjectIdentifier userid_oid = - ObjectIdentifier.newInternal(userid_data); + ObjectIdentifier.of(KnownOIDs.UCL_UserID); /** * Return constraint type:
    diff --git a/src/java.base/share/classes/sun/security/x509/X509CRLEntryImpl.java b/src/java.base/share/classes/sun/security/x509/X509CRLEntryImpl.java index 54e6016b740..aa6ef1ff348 100644 --- a/src/java.base/share/classes/sun/security/x509/X509CRLEntryImpl.java +++ b/src/java.base/share/classes/sun/security/x509/X509CRLEntryImpl.java @@ -252,7 +252,8 @@ public CRLReason getRevocationReason() { */ public static CRLReason getRevocationReason(X509CRLEntry crlEntry) { try { - byte[] ext = crlEntry.getExtensionValue("2.5.29.21"); + byte[] ext = crlEntry.getExtensionValue + (KnownOIDs.ReasonCode.value()); if (ext == null) { return null; } @@ -402,11 +403,11 @@ public byte[] getExtensionValue(String oid) { if (extensions == null) return null; try { - String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); + String extAlias = OIDMap.getName(ObjectIdentifier.of(oid)); Extension crlExt = null; if (extAlias == null) { // may be unknown - ObjectIdentifier findOID = new ObjectIdentifier(oid); + ObjectIdentifier findOID = ObjectIdentifier.of(oid); Extension ex = null; ObjectIdentifier inCertOID; for (Enumeration e = extensions.getElements(); diff --git a/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java b/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java index 6e50047f41e..da438f71890 100644 --- a/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java +++ b/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java @@ -1036,11 +1036,11 @@ public byte[] getExtensionValue(String oid) { if (extensions == null) return null; try { - String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); + String extAlias = OIDMap.getName(ObjectIdentifier.of(oid)); Extension crlExt = null; if (extAlias == null) { // may be unknown - ObjectIdentifier findOID = new ObjectIdentifier(oid); + ObjectIdentifier findOID = ObjectIdentifier.of(oid); Extension ex = null; ObjectIdentifier inCertOID; for (Enumeration e = extensions.getElements(); diff --git a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java index 90d9cfaf20b..30bceaf0ae8 100644 --- a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java +++ b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java @@ -42,6 +42,7 @@ import javax.security.auth.x500.X500Principal; +import sun.security.jca.JCAUtil; import sun.security.util.*; import sun.security.provider.X509Factory; @@ -130,14 +131,6 @@ public class X509CertImpl extends X509Certificate implements DerEncoder { protected AlgorithmId algId = null; protected byte[] signature = null; - // recognized extension OIDS - private static final String KEY_USAGE_OID = "2.5.29.15"; - private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37"; - private static final String BASIC_CONSTRAINT_OID = "2.5.29.19"; - private static final String SUBJECT_ALT_NAME_OID = "2.5.29.17"; - private static final String ISSUER_ALT_NAME_OID = "2.5.29.18"; - private static final String AUTH_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.1"; - // number of standard key usage bits. private static final int NUM_STANDARD_KEY_USAGE = 9; @@ -312,6 +305,13 @@ public X509CertImpl(DerValue derVal) throws CertificateException { } } + // helper method to record certificate, if necessary, after construction + public static X509CertImpl newX509CertImpl(byte[] certData) throws CertificateException { + var cert = new X509CertImpl(certData); + JCAUtil.tryCommitCertEvent(cert); + return cert; + } + /** * Appends the certificate to an output stream. * @@ -1425,7 +1425,7 @@ public Extension getUnparseableExtension(ObjectIdentifier oid) { */ public byte[] getExtensionValue(String oid) { try { - ObjectIdentifier findOID = new ObjectIdentifier(oid); + ObjectIdentifier findOID = ObjectIdentifier.of(oid); String extAlias = OIDMap.getName(findOID); Extension certExt = null; CertificateExtensions exts = (CertificateExtensions)info.get( @@ -1528,7 +1528,8 @@ public synchronized List getExtendedKeyUsage() public static List getExtendedKeyUsage(X509Certificate cert) throws CertificateParsingException { try { - byte[] ext = cert.getExtensionValue(EXTENDED_KEY_USAGE_OID); + byte[] ext = cert.getExtensionValue + (KnownOIDs.extendedKeyUsage.value()); if (ext == null) return null; DerValue val = new DerValue(ext); @@ -1698,7 +1699,8 @@ public synchronized Collection> getSubjectAlternativeNames() public static Collection> getSubjectAlternativeNames(X509Certificate cert) throws CertificateParsingException { try { - byte[] ext = cert.getExtensionValue(SUBJECT_ALT_NAME_OID); + byte[] ext = cert.getExtensionValue + (KnownOIDs.SubjectAlternativeName.value()); if (ext == null) { return null; } @@ -1761,7 +1763,8 @@ public synchronized Collection> getIssuerAlternativeNames() public static Collection> getIssuerAlternativeNames(X509Certificate cert) throws CertificateParsingException { try { - byte[] ext = cert.getExtensionValue(ISSUER_ALT_NAME_OID); + byte[] ext = cert.getExtensionValue + (KnownOIDs.IssuerAlternativeName.value()); if (ext == null) { return null; } diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java index 1dc82561f23..9fdc741af9a 100644 --- a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java +++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java @@ -608,33 +608,16 @@ private static ZoneInfo getZoneInfo(String zoneId, params[9] = toSTZTime[endRule.timeDefinition]; dstSavings = (startRule.offsetAfter - startRule.offsetBefore) * 1000; - // Note: known mismatching -> Asia/Amman + // Note: known mismatching -> Africa/Cairo // ZoneInfo : startDayOfWeek=5 <= Thursday - // startTime=86400000 <= 24 hours - // This: startDayOfWeek=6 - // startTime=0 - // Similar workaround needs to be applied to Africa/Cairo and - // its endDayOfWeek and endTime - // Below is the workarounds, it probably slows down everyone a little - if (params[2] == 6 && params[3] == 0 && - (zoneId.equals("Asia/Amman"))) { - params[2] = 5; - params[3] = 86400000; + // startTime=86400000 <= 24:00 + // This: startDayOfWeek=6 <= Friday + // startTime=0 <= 0:00 + if (zoneId.equals("Africa/Cairo") && + params[7] == Calendar.FRIDAY && params[8] == 0) { + params[7] = Calendar.THURSDAY; + params[8] = SECONDS_PER_DAY * 1000; } - // Additional check for startDayOfWeek=6 and starTime=86400000 - // is needed for Asia/Amman; - if (params[2] == 7 && params[3] == 0 && - (zoneId.equals("Asia/Amman"))) { - params[2] = 6; // Friday - params[3] = 86400000; // 24h - } - //endDayOfWeek and endTime workaround - if (params[7] == 6 && params[8] == 0 && - (zoneId.equals("Africa/Cairo"))) { - params[7] = 5; - params[8] = 86400000; - } - } else if (nTrans > 0) { // only do this if there is something in table already if (lastyear < LASTYEAR) { // ZoneInfo has an ending entry for 2037 @@ -907,7 +890,6 @@ private static class ZoneOffsetTransitionRule { this.dow = dowByte == 0 ? -1 : dowByte; this.secondOfDay = timeByte == 31 ? in.readInt() : timeByte * 3600; this.timeDefinition = (data & (3 << 12)) >>> 12; - this.standardOffset = stdByte == 255 ? in.readInt() : (stdByte - 128) * 900; this.offsetBefore = beforeByte == 3 ? in.readInt() : standardOffset + beforeByte * 1800; this.offsetAfter = afterByte == 3 ? in.readInt() : standardOffset + afterByte * 1800; diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java index 6d78d77f64d..f97888a6977 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,7 +82,7 @@ protected String[] getDisplayNameArray(String id, Locale locale) { } if (namesSuper != null) { - // CLDR's resource bundle has an translated entry for this id. + // CLDR's resource bundle has a translated entry for this id. // Fix up names if needed, either missing or no-inheritance namesSuper[INDEX_TZID] = id; @@ -91,7 +91,7 @@ protected String[] getDisplayNameArray(String id, Locale locale) { case "": // Fill in empty elements deriveFallbackName(namesSuper, i, locale, - !TimeZone.getTimeZone(id).useDaylightTime()); + TimeZone.getTimeZone(id).toZoneId().getRules().isFixedOffset()); break; case NO_INHERITANCE_MARKER: // CLDR's "no inheritance marker" @@ -129,7 +129,7 @@ protected String[][] getZoneStrings(Locale locale) { // Derive fallback time zone name according to LDML's logic private void deriveFallbackNames(String[] names, Locale locale) { - boolean noDST = !TimeZone.getTimeZone(names[0]).useDaylightTime(); + boolean noDST = TimeZone.getTimeZone(names[0]).toZoneId().getRules().isFixedOffset(); for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) { deriveFallbackName(names, i, locale, noDST); @@ -149,13 +149,12 @@ private void deriveFallbackName(String[] names, int index, Locale locale, boolea return; } - // Check parent locale first + // Check parent locales first if (!exists(names, index)) { - CLDRLocaleProviderAdapter clpa = (CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR); - var cands = clpa.getCandidateLocales("", locale); - if (cands.size() > 1) { - var parentLoc = cands.get(1); // immediate parent locale - String[] parentNames = super.getDisplayNameArray(id, parentLoc); + var cands = ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR)) + .getCandidateLocales("", locale); + for (int i = 1; i < cands.size() ; i++) { + String[] parentNames = super.getDisplayNameArray(id, cands.get(i)); if (parentNames != null && !parentNames[index].isEmpty()) { names[index] = parentNames[index]; return; @@ -163,18 +162,20 @@ private void deriveFallbackName(String[] names, int index, Locale locale, boolea } } + // Type Fallback + if (noDST && typeFallback(names, index)) { + return; + } + // Check if COMPAT can substitute the name - if (LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) { + if (!exists(names, index) && + LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) { String[] compatNames = (String[])LocaleProviderAdapter.forJRE() - .getLocaleResources(mapChineseLocale(locale)) - .getTimeZoneNames(id); + .getLocaleResources(mapChineseLocale(locale)) + .getTimeZoneNames(id); if (compatNames != null) { - for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) { - // Assumes COMPAT has no empty slots - if (i == index || !exists(names, i)) { - names[i] = compatNames[i]; - } - } + // Assumes COMPAT has no empty slots + names[index] = compatNames[index]; return; } } @@ -184,11 +185,6 @@ private void deriveFallbackName(String[] names, int index, Locale locale, boolea return; } - // Type Fallback - if (noDST && typeFallback(names, index)) { - return; - } - // last resort names[index] = toGMTFormat(id, index == INDEX_DST_LONG || index == INDEX_DST_SHORT, @@ -234,6 +230,11 @@ private boolean typeFallback(String[] names, int index) { } private boolean regionFormatFallback(String[] names, int index, Locale l) { + if (index % 2 == 0) { + // ignore short names + return false; + } + String id = names[INDEX_TZID]; LocaleResources lr = LocaleProviderAdapter.forType(Type.CLDR).getLocaleResources(l); ResourceBundle fd = lr.getJavaTimeFormatData(); diff --git a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java index bf7918659ae..2763ac30ca7 100644 --- a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java +++ b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -845,9 +845,7 @@ protected final Object[][] getContents() { {"Europe/Jersey", GMTBST}, {"Europe/Kaliningrad", EET}, {"Europe/Kiev", EET}, - {"Europe/Kirov", new String[] {"Kirov Standard Time", "GMT+03:00", - "Kirov Daylight Time", "GMT+03:00", - "Kirov Time", "GMT+03:00"}}, + {"Europe/Kirov", MSK}, {"Europe/Lisbon", WET}, {"Europe/Ljubljana", CET}, {"Europe/London", GMTBST}, diff --git a/src/java.base/share/conf/security/java.policy b/src/java.base/share/conf/security/java.policy index 1554541d126..bac4a949d4c 100644 --- a/src/java.base/share/conf/security/java.policy +++ b/src/java.base/share/conf/security/java.policy @@ -30,6 +30,8 @@ grant { permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; + permission java.util.PropertyPermission + "java.specification.maintenance.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 957cd78a552..b03bd9f896c 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -22,6 +22,9 @@ # the command line, set the key security.overridePropertiesFile # to false in the master security properties file. It is set to true # by default. +# +# If this properties file fails to load, the JDK implementation will throw +# an unspecified error when initializing the java.security.Security class. # In this file, various security properties are set for use by # java.security classes. This is where users can statically register @@ -930,7 +933,8 @@ jdk.tls.legacyAlgorithms= \ # Note: This property is currently used by OpenJDK's JSSE implementation. It # is not guaranteed to be examined and used by other implementations. # -jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37 +jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37, \ + ChaCha20-Poly1305 KeyUpdate 2^37 # # Cryptographic Jurisdiction Policy defaults diff --git a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c index 8c0bc11680d..e7b6a21fc92 100644 --- a/src/java.base/share/native/libjava/System.c +++ b/src/java.base/share/native/libjava/System.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -197,6 +197,8 @@ Java_java_lang_System_initProperties(JNIEnv *env, jclass cla, jobject props) PUTPROP(props, "java.specification.version", VERSION_SPECIFICATION); + PUTPROP(props, "java.specification.maintenance.version", + "2"); PUTPROP(props, "java.specification.name", "Java Platform API Specification"); PUTPROP(props, "java.specification.vendor", diff --git a/src/java.base/share/native/libjava/jni_util.c b/src/java.base/share/native/libjava/jni_util.c index 8b0df176c1d..430a01af210 100644 --- a/src/java.base/share/native/libjava/jni_util.c +++ b/src/java.base/share/native/libjava/jni_util.c @@ -23,6 +23,7 @@ * questions. */ +#include #include #include @@ -35,8 +36,13 @@ * such as "z:" need to be appended with a "." so we * must allocate at least 4 bytes to allow room for * this expansion. See 4235353 for details. + * This macro returns NULL if the requested size is + * negative, or the size is INT_MAX as the macro adds 1 + * that overflows into negative value. */ -#define MALLOC_MIN4(len) ((char *)malloc((len) + 1 < 4 ? 4 : (len) + 1)) +#define MALLOC_MIN4(len) ((unsigned)(len) >= INT_MAX ? \ + NULL : \ + ((char *)malloc((len) + 1 < 4 ? 4 : (len) + 1))) /** * Throw a Java exception by name. Similar to SignalError. @@ -945,17 +951,10 @@ getStringUTF8(JNIEnv *env, jstring jstr) } } - // Check `jint` overflow - if (rlen < 0) { - (*env)->ReleasePrimitiveArrayCritical(env, value, str, 0); - JNU_ThrowOutOfMemoryError(env, "requested array size exceeds VM limit"); - return NULL; - } - result = MALLOC_MIN4(rlen); if (result == NULL) { (*env)->ReleasePrimitiveArrayCritical(env, value, str, 0); - JNU_ThrowOutOfMemoryError(env, 0); + JNU_ThrowOutOfMemoryError(env, "requested array size exceeds VM limit"); return NULL; } diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index 4f900888bfd..e56102820ef 100644 --- a/src/java.base/share/native/libverify/check_code.c +++ b/src/java.base/share/native/libverify/check_code.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -471,7 +471,8 @@ static void CCout_of_memory (context_type *); /* Because we can longjmp any time, we need to be very careful about * remembering what needs to be freed. */ -static void check_and_push(context_type *context, const void *ptr, int kind); +static void check_and_push_malloc_block(context_type *context, void *ptr); +static void check_and_push_string_utf(context_type *context, const char *ptr); static void pop_and_free(context_type *context); static int signature_to_args_size(const char *method_signature); @@ -607,7 +608,7 @@ class_to_ID(context_type *context, jclass cb, jboolean loadable) unsigned short *pID; const char *name = JVM_GetClassNameUTF(env, cb); - check_and_push(context, name, VM_STRING_UTF); + check_and_push_string_utf(context, name); hash = class_hash_fun(name); pID = &(class_hash->table[hash % HASH_TABLE_SIZE]); while (*pID) { @@ -954,10 +955,10 @@ read_all_code(context_type* context, jclass cb, int num_methods, int i; lengths = malloc(sizeof(int) * num_methods); - check_and_push(context, lengths, VM_MALLOC_BLK); + check_and_push_malloc_block(context, lengths); code = malloc(sizeof(unsigned char*) * num_methods); - check_and_push(context, code, VM_MALLOC_BLK); + check_and_push_malloc_block(context, code); *(lengths_addr) = lengths; *(code_addr) = code; @@ -966,7 +967,7 @@ read_all_code(context_type* context, jclass cb, int num_methods, lengths[i] = JVM_GetMethodIxByteCodeLength(context->env, cb, i); if (lengths[i] > 0) { code[i] = malloc(sizeof(unsigned char) * (lengths[i] + 1)); - check_and_push(context, code[i], VM_MALLOC_BLK); + check_and_push_malloc_block(context, code[i]); JVM_GetMethodIxByteCode(context->env, cb, i, code[i]); } else { code[i] = NULL; @@ -1320,7 +1321,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) /* Make sure the constant pool item is the right type. */ verify_constant_pool_type(context, key, kind); methodname = JVM_GetCPMethodNameUTF(env, cb, key); - check_and_push(context, methodname, VM_STRING_UTF); + check_and_push_string_utf(context, methodname); is_constructor = !strcmp(methodname, ""); is_internal = methodname[0] == '<'; pop_and_free(context); @@ -1369,7 +1370,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) unsigned int args2; const char *signature = JVM_GetCPMethodSignatureUTF(env, context->class, key); - check_and_push(context, signature, VM_STRING_UTF); + check_and_push_string_utf(context, signature); args1 = signature_to_args_size(signature) + 1; args2 = code[offset + 3]; if (args1 != args2) { @@ -1667,7 +1668,7 @@ initialize_exception_table(context_type *context) classname = JVM_GetCPClassNameUTF(env, context->class, einfo.catchType); - check_and_push(context, classname, VM_STRING_UTF); + check_and_push_string_utf(context, classname); stack_item->item = make_class_info_from_name(context, classname); if (!isAssignableTo(context, stack_item->item, @@ -1817,7 +1818,7 @@ initialize_dataflow(context_type *context) } } signature = JVM_GetMethodIxSignatureUTF(env, cb, mi); - check_and_push(context, signature, VM_STRING_UTF); + check_and_push_string_utf(context, signature); /* Fill in each of the arguments into the registers. */ for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) { char fieldchar = signature_to_fieldtype(context, &p, &full_info); @@ -2060,7 +2061,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac context->class, operand); char *ip = buffer; - check_and_push(context, signature, VM_STRING_UTF); + check_and_push_string_utf(context, signature); #ifdef DEBUG if (verify_verbose) { print_formatted_fieldname(context, operand); @@ -2086,7 +2087,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac operand); char *ip = buffer; const char *p; - check_and_push(context, signature, VM_STRING_UTF); + check_and_push_string_utf(context, signature); #ifdef DEBUG if (verify_verbose) { print_formatted_methodname(context, operand); @@ -2386,7 +2387,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac operand); int item; const char *p; - check_and_push(context, signature, VM_STRING_UTF); + check_and_push_string_utf(context, signature); if (opcode == JVM_OPC_invokestatic) { item = 0; } else if (opcode == JVM_OPC_invokeinit) { @@ -2768,7 +2769,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta const char *signature = JVM_GetCPFieldSignatureUTF(context->env, context->class, operand); - check_and_push(context, signature, VM_STRING_UTF); + check_and_push_string_utf(context, signature); #ifdef DEBUG if (verify_verbose) { print_formatted_fieldname(context, operand); @@ -2790,7 +2791,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta context->class, operand); const char *result_signature; - check_and_push(context, signature, VM_STRING_UTF); + check_and_push_string_utf(context, signature); result_signature = get_result_signature(signature); if (result_signature++ == NULL) { CCerror(context, "Illegal signature %s", signature); @@ -3630,7 +3631,7 @@ cp_index_to_class_fullinfo(context_type *context, int cp_index, int kind) CCerror(context, "Internal error #5"); } - check_and_push(context, classname, VM_STRING_UTF); + check_and_push_string_utf(context, classname); if (classname[0] == JVM_SIGNATURE_ARRAY) { /* This make recursively call us, in case of a class array */ signature_to_fieldtype(context, &classname, &result); @@ -3828,10 +3829,11 @@ signature_to_fieldtype(context_type *context, result = 0; break; } - length = finish - p; + assert(finish >= p); + length = (int)(finish - p); if (length + 1 > (int)sizeof(buffer_space)) { - buffer = calloc(length + 1, sizeof(char)); - check_and_push(context, buffer, VM_MALLOC_BLK); + buffer = malloc(length + 1); + check_and_push_malloc_block(context, buffer); } memcpy(buffer, p, length); buffer[length] = '\0'; @@ -4150,7 +4152,7 @@ static void free_block(void *ptr, int kind) } } -static void check_and_push(context_type *context, const void *ptr, int kind) +static void check_and_push_common(context_type *context, void *ptr, int kind) { alloc_stack_type *p; if (ptr == 0) @@ -4162,16 +4164,24 @@ static void check_and_push(context_type *context, const void *ptr, int kind) p = malloc(sizeof(alloc_stack_type)); if (p == 0) { /* Make sure we clean up. */ - free_block((void *)ptr, kind); + free_block(ptr, kind); CCout_of_memory(context); } } p->kind = kind; - p->ptr = (void *)ptr; + p->ptr = ptr; p->next = context->allocated_memory; context->allocated_memory = p; } +static void check_and_push_malloc_block(context_type *context, void *ptr) { + check_and_push_common(context, ptr, VM_MALLOC_BLK); +} + +static void check_and_push_string_utf(context_type *context, const char *ptr) { + check_and_push_common(context, (void *)ptr, VM_STRING_UTF); +} + static void pop_and_free(context_type *context) { alloc_stack_type *p = context->allocated_memory; diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java b/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java index c25f837a6cc..9adbc2747f5 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java @@ -87,6 +87,10 @@ static Path fromUri(UnixFileSystem fs, URI uri) { throw new IllegalArgumentException("Bad escape"); b = (byte)c; } + if (b == '/' && rlen > 0 && result[rlen-1] == '/') { + // skip redundant slashes + continue; + } result[rlen++] = b; } if (rlen != result.length) diff --git a/src/java.base/unix/native/libnet/Inet4AddressImpl.c b/src/java.base/unix/native/libnet/Inet4AddressImpl.c index 59f920fc097..0e4e3f91a3c 100644 --- a/src/java.base/unix/native/libnet/Inet4AddressImpl.c +++ b/src/java.base/unix/native/libnet/Inet4AddressImpl.c @@ -361,8 +361,8 @@ ping4(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif, struct ip *ip; struct sockaddr_in sa_recv; jchar pid; - struct timeval tv; - size_t plen = ICMP_ADVLENMIN + sizeof(tv); + struct timeval tv = { 0, 0 }; + const size_t plen = ICMP_MINLEN + sizeof(tv); setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); @@ -434,7 +434,7 @@ ping4(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif, ip = (struct ip *)recvbuf; hlen = ((jint)(unsigned int)(ip->ip_hl)) << 2; // check if we received enough data - if (n < (jint)(hlen + sizeof(struct icmp))) { + if (n < (jint)(hlen + plen)) { continue; } icmp = (struct icmp *)(recvbuf + hlen); diff --git a/src/java.base/unix/native/libnet/Inet6AddressImpl.c b/src/java.base/unix/native/libnet/Inet6AddressImpl.c index bdbfd794a8d..e81ed8e0f29 100644 --- a/src/java.base/unix/native/libnet/Inet6AddressImpl.c +++ b/src/java.base/unix/native/libnet/Inet6AddressImpl.c @@ -564,7 +564,7 @@ ping6(JNIEnv *env, jint fd, SOCKETADDRESS *sa, SOCKETADDRESS *netif, struct icmp6_hdr *icmp6; struct sockaddr_in6 sa_recv; jchar pid; - struct timeval tv; + struct timeval tv = { 0, 0 }; size_t plen = sizeof(struct icmp6_hdr) + sizeof(tv); #if defined(__linux__) diff --git a/src/java.base/windows/conf/tzmappings b/src/java.base/windows/conf/tzmappings deleted file mode 100644 index 28917ae0cc9..00000000000 --- a/src/java.base/windows/conf/tzmappings +++ /dev/null @@ -1,226 +0,0 @@ -# -# This file describes mapping information between Windows and Java -# time zones. -# Format: Each line should include a colon separated fields of Windows -# time zone registry key, time zone mapID, locale (which is most -# likely used in the time zone), and Java time zone ID. Blank lines -# and lines that start with '#' are ignored. Data lines must be sorted -# by mapID (ASCII order). -# -# NOTE -# This table format is not a public interface of any Java -# platforms. No applications should depend on this file in any form. -# -# This table has been generated by a program and should not be edited -# manually. -# -Romance:-1,64::Europe/Paris: -Romance Standard Time:-1,64::Europe/Paris: -Warsaw:-1,65::Europe/Warsaw: -Central Europe:-1,66::Europe/Prague: -Central Europe Standard Time:-1,66::Europe/Prague: -Prague Bratislava:-1,66::Europe/Prague: -W. Central Africa Standard Time:-1,66:AO:Africa/Luanda: -FLE:-1,67:FI:Europe/Helsinki: -FLE Standard Time:-1,67:FI:Europe/Helsinki: -GFT:-1,67::Europe/Athens: -GFT Standard Time:-1,67::Europe/Athens: -GTB:-1,67::Europe/Athens: -GTB Standard Time:-1,67::Europe/Athens: -Israel:-1,70::Asia/Jerusalem: -Israel Standard Time:-1,70::Asia/Jerusalem: -Arab:-1,71::Asia/Riyadh: -Arab Standard Time:-1,71::Asia/Riyadh: -Arabic Standard Time:-1,71:IQ:Asia/Baghdad: -E. Africa:-1,71:KE:Africa/Nairobi: -E. Africa Standard Time:-1,71:KE:Africa/Nairobi: -Saudi Arabia:-1,71::Asia/Riyadh: -Saudi Arabia Standard Time:-1,71::Asia/Riyadh: -Iran:-1,72::Asia/Tehran: -Iran Standard Time:-1,72::Asia/Tehran: -Afghanistan:-1,73::Asia/Kabul: -Afghanistan Standard Time:-1,73::Asia/Kabul: -India:-1,74::Asia/Calcutta: -India Standard Time:-1,74::Asia/Calcutta: -Myanmar Standard Time:-1,74::Asia/Rangoon: -Nepal Standard Time:-1,74::Asia/Katmandu: -Sri Lanka:-1,74:LK:Asia/Colombo: -Sri Lanka Standard Time:-1,74:LK:Asia/Colombo: -Beijing:-1,75::Asia/Shanghai: -China:-1,75::Asia/Shanghai: -China Standard Time:-1,75::Asia/Shanghai: -AUS Central:-1,76::Australia/Darwin: -AUS Central Standard Time:-1,76::Australia/Darwin: -Cen. Australia:-1,76::Australia/Adelaide: -Cen. Australia Standard Time:-1,76::Australia/Adelaide: -Vladivostok:-1,77::Asia/Vladivostok: -Vladivostok Standard Time:-1,77::Asia/Vladivostok: -West Pacific:-1,77:GU:Pacific/Guam: -West Pacific Standard Time:-1,77:GU:Pacific/Guam: -E. South America:-1,80::America/Sao_Paulo: -E. South America Standard Time:-1,80::America/Sao_Paulo: -Greenland Standard Time:-1,80:GL:America/Godthab: -Newfoundland:-1,81::America/St_Johns: -Newfoundland Standard Time:-1,81::America/St_Johns: -Pacific SA:-1,82::America/Santiago: -Pacific SA Standard Time:-1,82::America/Santiago: -SA Western:-1,82:BO:America/La_Paz: -SA Western Standard Time:-1,82:BO:America/La_Paz: -SA Pacific:-1,83::America/Bogota: -SA Pacific Standard Time:-1,83::America/Bogota: -US Eastern:-1,84::America/Indianapolis: -US Eastern Standard Time:-1,84::America/Indianapolis: -Central America Standard Time:-1,85::America/Regina: -Mexico:-1,85::America/Mexico_City: -Mexico Standard Time:-1,85::America/Mexico_City: -Canada Central:-1,86::America/Regina: -Canada Central Standard Time:-1,86::America/Regina: -US Mountain:-1,87::America/Phoenix: -US Mountain Standard Time:-1,87::America/Phoenix: -GMT:0,1::Europe/London: -GMT Standard Time:0,1::Europe/London: -Ekaterinburg:10,11::Asia/Yekaterinburg: -Ekaterinburg Standard Time:10,11::Asia/Yekaterinburg: -West Asia:10,11:UZ:Asia/Tashkent: -West Asia Standard Time:10,11:UZ:Asia/Tashkent: -Central Asia:12,13::Asia/Almaty: -Central Asia Standard Time:12,13::Asia/Almaty: -N. Central Asia Standard Time:12,13::Asia/Novosibirsk: -Bangkok:14,15::Asia/Bangkok: -Bangkok Standard Time:14,15::Asia/Bangkok: -North Asia Standard Time:14,15::Asia/Krasnoyarsk: -SE Asia:14,15::Asia/Bangkok: -SE Asia Standard Time:14,15::Asia/Bangkok: -North Asia East Standard Time:16,17:RU:Asia/Irkutsk: -Singapore:16,17:SG:Asia/Singapore: -Singapore Standard Time:16,17:SG:Asia/Singapore: -Taipei:16,17::Asia/Taipei: -Taipei Standard Time:16,17::Asia/Taipei: -W. Australia:16,17:AU:Australia/Perth: -W. Australia Standard Time:16,17:AU:Australia/Perth: -Korea:18,19:KR:Asia/Seoul: -Korea Standard Time:18,19:KR:Asia/Seoul: -Tokyo:18,19::Asia/Tokyo: -Tokyo Standard Time:18,19::Asia/Tokyo: -Yakutsk:18,19:RU:Asia/Yakutsk: -Yakutsk Standard Time:18,19:RU:Asia/Yakutsk: -Central European:2,3:CS:Europe/Belgrade: -Central European Standard Time:2,3:CS:Europe/Belgrade: -W. Europe:2,3::Europe/Berlin: -W. Europe Standard Time:2,3::Europe/Berlin: -Tasmania:20,-1::Australia/Hobart: -Tasmania Standard Time:20,-1::Australia/Hobart: -AUS Eastern:20,21::Australia/Sydney: -AUS Eastern Standard Time:20,21::Australia/Sydney: -E. Australia:20,21::Australia/Brisbane: -E. Australia Standard Time:20,21::Australia/Brisbane: -Sydney Standard Time:20,21::Australia/Sydney: -Tasmania Standard Time:20,65::Australia/Hobart: -Central Pacific:22,23::Pacific/Guadalcanal: -Central Pacific Standard Time:22,23::Pacific/Guadalcanal: -Dateline:24,25::GMT-1200: -Dateline Standard Time:24,25::GMT-1200: -Fiji:24,25::Pacific/Fiji: -Fiji Standard Time:24,25::Pacific/Fiji: -Samoa:26,27::Pacific/Apia: -Samoa Standard Time:26,27::Pacific/Apia: -Hawaiian:28,29::Pacific/Honolulu: -Hawaiian Standard Time:28,29::Pacific/Honolulu: -Alaskan:30,31::America/Anchorage: -Alaskan Standard Time:30,31::America/Anchorage: -Pacific:32,33::America/Los_Angeles: -Pacific Standard Time:32,33::America/Los_Angeles: -Mexico Standard Time 2:34,35:MX:America/Chihuahua: -Mountain:34,35::America/Denver: -Mountain Standard Time:34,35::America/Denver: -Central:36,37::America/Chicago: -Central Standard Time:36,37::America/Chicago: -Eastern:38,39::America/New_York: -Eastern Standard Time:38,39::America/New_York: -E. Europe:4,5::EET: -E. Europe Standard Time:4,5::EET: -Egypt:4,68::Africa/Cairo: -Egypt Standard Time:4,68::Africa/Cairo: -South Africa:4,69::Africa/Harare: -South Africa Standard Time:4,69::Africa/Harare: -Atlantic:40,41::America/Halifax: -Atlantic Standard Time:40,41::America/Halifax: -SA Eastern:42,43:GF:America/Cayenne: -SA Eastern Standard Time:42,43:GF:America/Cayenne: -Mid-Atlantic:44,45::Atlantic/South_Georgia: -Mid-Atlantic Standard Time:44,45::Atlantic/South_Georgia: -Azores:46,47::Atlantic/Azores: -Azores Standard Time:46,47::Atlantic/Azores: -Cape Verde Standard Time:46,47::Atlantic/Cape_Verde: -Russian:6,7::Europe/Moscow: -Russian Standard Time:6,7::Europe/Moscow: -New Zealand:78,79::Pacific/Auckland: -New Zealand Standard Time:78,79::Pacific/Auckland: -Tonga Standard Time:78,79::Pacific/Tongatapu: -Arabian:8,9::Asia/Muscat: -Arabian Standard Time:8,9::Asia/Muscat: -Caucasus:8,9:AM:Asia/Yerevan: -Caucasus Standard Time:8,9:AM:Asia/Yerevan: -GMT Standard Time:88,89::GMT: -Greenwich:88,89::GMT: -Greenwich Standard Time:88,89::GMT: -Aleutian Standard Time:900,900:US:America/Adak: -Altai Standard Time:901,901::Asia/Barnaul: -Argentina Standard Time:902,902::America/Buenos_Aires: -Armenian Standard Time:903,903:AM:Asia/Yerevan: -Astrakhan Standard Time:904,904::Europe/Astrakhan: -Aus Central W. Standard Time:905,905::Australia/Eucla: -Azerbaijan Standard Time:906,906:AZ:Asia/Baku: -Bahia Standard Time:907,907::America/Bahia: -Bangladesh Standard Time:908,908::Asia/Dhaka: -Belarus Standard Time:909,909:BY:Europe/Minsk: -Bougainville Standard Time:910,910::Pacific/Bougainville: -Central Brazilian Standard Time:911,911:BR:America/Cuiaba: -Central Standard Time (Mexico):912,912::America/Mexico_City: -Chatham Islands Standard Time:913,913::Pacific/Chatham: -Cuba Standard Time:914,914:CU:America/Havana: -Easter Island Standard Time:915,915:CL:Pacific/Easter: -Eastern Standard Time (Mexico):916,916::America/Cancun: -Georgian Standard Time:917,917:GE:Asia/Tbilisi: -Haiti Standard Time:918,918:HT:America/Port-au-Prince: -Jordan Standard Time:919,919:JO:Asia/Amman: -Kaliningrad Standard Time:920,920:RU:Europe/Kaliningrad: -Kamchatka Standard Time:921,921:RU:Asia/Kamchatka: -Libya Standard Time:922,922:LY:Africa/Tripoli: -Line Islands Standard Time:923,923::Pacific/Kiritimati: -Lord Howe Standard Time:924,924::Australia/Lord_Howe: -Magadan Standard Time:925,925::Asia/Magadan: -Marquesas Standard Time:926,926::Pacific/Marquesas: -Mauritius Standard Time:927,927:MU:Indian/Mauritius: -Middle East Standard Time:928,928:LB:Asia/Beirut: -Montevideo Standard Time:929,929:UY:America/Montevideo: -Morocco Standard Time:930,930:MA:Africa/Casablanca: -Mountain Standard Time (Mexico):931,931:MX:America/Chihuahua: -Namibia Standard Time:932,932:NA:Africa/Windhoek: -Norfolk Standard Time:933,933::Pacific/Norfolk: -North Korea Standard Time:934,934:KP:Asia/Pyongyang: -Pacific Standard Time (Mexico):935,935:MX:America/Tijuana: -Pakistan Standard Time:936,936::Asia/Karachi: -Paraguay Standard Time:937,937:PY:America/Asuncion: -Russia Time Zone 10:938,938::Asia/Srednekolymsk: -Russia Time Zone 11:939,939::Asia/Anadyr: -Russia Time Zone 3:940,940::Europe/Samara: -Saint Pierre Standard Time:941,941:PM:America/Miquelon: -Sakhalin Standard Time:942,942::Asia/Sakhalin: -Syria Standard Time:943,943:SY:Asia/Damascus: -Tocantins Standard Time:944,944::America/Araguaina: -Tomsk Standard Time:945,945::Asia/Tomsk: -Transbaikal Standard Time:946,946::Asia/Chita: -Turkey Standard Time:947,947::Asia/Istanbul: -Turks And Caicos Standard Time:948,948:TC:America/Grand_Turk: -UTC+12:949,949::GMT+1200: -UTC-02:950,950::GMT-0200: -UTC-08:951,951::GMT-0800: -UTC-09:952,952::GMT-0900: -UTC-11:953,953::GMT-1100: -UTC:954,954::UTC: -Ulaanbaatar Standard Time:955,955::Asia/Ulaanbaatar: -Venezuela Standard Time:956,956::America/Caracas: -W. Mongolia Standard Time:957,957::Asia/Hovd: -West Bank Standard Time:958,958::Asia/Gaza: -Western Brazilian Standard Time:959,959:BR:America/Rio_Branco: diff --git a/src/java.base/windows/native/libjava/TimeZone_md.c b/src/java.base/windows/native/libjava/TimeZone_md.c index d46b3c3b57d..061600c87a3 100644 --- a/src/java.base/windows/native/libjava/TimeZone_md.c +++ b/src/java.base/windows/native/libjava/TimeZone_md.c @@ -36,6 +36,7 @@ #define MAX_ZONE_CHAR 256 #define MAX_MAPID_LENGTH 32 +#define MAX_REGION_LENGTH 4 #define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" #define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones" @@ -145,7 +146,7 @@ static void customZoneName(LONG bias, char *buffer) { /* * Gets the current time zone entry in the "Time Zones" registry. */ -static int getWinTimeZone(char *winZoneName, char *winMapID) +static int getWinTimeZone(char *winZoneName) { DYNAMIC_TIME_ZONE_INFORMATION dtzi; DWORD timeType; @@ -231,7 +232,6 @@ static int getWinTimeZone(char *winZoneName, char *winMapID) WCHAR stdNameInReg[MAX_ZONE_CHAR]; TziValue tempTzi; WCHAR *stdNamePtr = tzi.StandardName; - DWORD valueSize; int onlyMapID; timeType = GetTimeZoneInformation(&tzi); @@ -372,24 +372,7 @@ static int getWinTimeZone(char *winZoneName, char *winMapID) (void) RegCloseKey(hSubKey); } - /* - * Get the "MapID" value of the registry to be able to eliminate - * duplicated key names later. - */ - valueSize = MAX_MAPID_LENGTH; - ret = RegQueryValueExA(hSubKey, "MapID", NULL, &valueType, winMapID, &valueSize); - (void) RegCloseKey(hSubKey); (void) RegCloseKey(hKey); - - if (ret != ERROR_SUCCESS) { - /* - * Vista doesn't have mapID. VALUE_UNKNOWN should be returned - * only for Windows NT. - */ - if (onlyMapID == 1) { - return VALUE_UNKNOWN; - } - } } return VALUE_KEY; @@ -410,24 +393,17 @@ static int getWinTimeZone(char *winZoneName, char *winMapID) * Index values for the mapping table. */ #define TZ_WIN_NAME 0 -#define TZ_MAPID 1 -#define TZ_REGION 2 -#define TZ_JAVA_NAME 3 +#define TZ_REGION 1 +#define TZ_JAVA_NAME 2 -#define TZ_NITEMS 4 /* number of items (fields) */ +#define TZ_NITEMS 3 /* number of items (fields) */ /* * Looks up the mapping table (tzmappings) and returns a Java time * zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is * returned. - * - * value_type is one of the following values: - * VALUE_KEY for exact key matching - * VALUE_MAPID for MapID (this is - * required for the old Windows, such as NT 4.0 SP3). */ -static char *matchJavaTZ(const char *java_home_dir, int value_type, char *tzName, - char *mapID) +static char *matchJavaTZ(const char *java_home_dir, char *tzName) { int line; int IDmatched = 0; @@ -436,9 +412,22 @@ static char *matchJavaTZ(const char *java_home_dir, int value_type, char *tzName char *items[TZ_NITEMS]; char *mapFileName; char lineBuffer[MAX_ZONE_CHAR * 4]; - int noMapID = *mapID == '\0'; /* no mapID on Vista and later */ int offset = 0; const char* errorMessage = "unknown error"; + char region[MAX_REGION_LENGTH]; + + // Get the user's location + if (GetGeoInfo(GetUserGeoID(GEOCLASS_NATION), + GEO_ISO2, region, MAX_REGION_LENGTH, 0) == 0) { + // If GetGeoInfo fails, fallback to LCID's country + LCID lcid = GetUserDefaultLCID(); + if (GetLocaleInfo(lcid, + LOCALE_SISO3166CTRYNAME, region, MAX_REGION_LENGTH) == 0 && + GetLocaleInfo(lcid, + LOCALE_SISO3166CTRYNAME2, region, MAX_REGION_LENGTH) == 0) { + region[0] = '\0'; + } + } mapFileName = malloc(strlen(java_home_dir) + strlen(MAPPINGS_FILE) + 1); if (mapFileName == NULL) { @@ -494,28 +483,20 @@ static char *matchJavaTZ(const char *java_home_dir, int value_type, char *tzName goto illegal_format; } - if (noMapID || strcmp(mapID, items[TZ_MAPID]) == 0) { + /* + * We need to scan items until the + * exact match is found or the end of data is detected. + */ + if (strcmp(items[TZ_WIN_NAME], tzName) == 0) { /* - * When there's no mapID, we need to scan items until the - * exact match is found or the end of data is detected. + * Found the time zone in the mapping table. + * Check the region code and select the appropriate entry */ - if (!noMapID) { - IDmatched = 1; - } - if (strcmp(items[TZ_WIN_NAME], tzName) == 0) { - /* - * Found the time zone in the mapping table. - */ + if (strcmp(items[TZ_REGION], region) == 0 || + strcmp(items[TZ_REGION], "001") == 0) { javaTZName = _strdup(items[TZ_JAVA_NAME]); break; } - } else { - if (IDmatched == 1) { - /* - * No need to look up the mapping table further. - */ - break; - } } } fclose(fp); @@ -535,19 +516,16 @@ static char *matchJavaTZ(const char *java_home_dir, int value_type, char *tzName char *findJavaTZ_md(const char *java_home_dir) { char winZoneName[MAX_ZONE_CHAR]; - char winMapID[MAX_MAPID_LENGTH]; char *std_timezone = NULL; int result; - winMapID[0] = 0; - result = getWinTimeZone(winZoneName, winMapID); + result = getWinTimeZone(winZoneName); if (result != VALUE_UNKNOWN) { if (result == VALUE_GMTOFFSET) { std_timezone = _strdup(winZoneName); } else { - std_timezone = matchJavaTZ(java_home_dir, result, - winZoneName, winMapID); + std_timezone = matchJavaTZ(java_home_dir, winZoneName); if (std_timezone == NULL) { std_timezone = getGMTOffsetID(); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m index 896e11cf568..4945420678e 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m @@ -136,15 +136,16 @@ - (BOOL) checkPasteboardWithoutNotification:(id)application { jint nElements = (*env)->GetArrayLength(env, inTypes); NSMutableArray *formatArray = [NSMutableArray arrayWithCapacity:nElements]; jlong *elements = (*env)->GetPrimitiveArrayCritical(env, inTypes, NULL); + if (elements != NULL) { + for (i = 0; i < nElements; i++) { + NSString *pbFormat = formatForIndex(elements[i]); + if (pbFormat) + [formatArray addObject:pbFormat]; + } - for (i = 0; i < nElements; i++) { - NSString *pbFormat = formatForIndex(elements[i]); - if (pbFormat) - [formatArray addObject:pbFormat]; + (*env)->ReleasePrimitiveArrayCritical(env, inTypes, elements, JNI_ABORT); + [[CClipboard sharedClipboard] declareTypes:formatArray withOwner:inJavaClip jniEnv:env]; } - - (*env)->ReleasePrimitiveArrayCritical(env, inTypes, elements, JNI_ABORT); - [[CClipboard sharedClipboard] declareTypes:formatArray withOwner:inJavaClip jniEnv:env]; JNI_COCOA_EXIT(env); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m index 55df129960f..089637d67bd 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m @@ -47,10 +47,6 @@ #define k_JAVA_ROBOT_WHEEL_COUNT 1 -#if !defined(kCGBitmapByteOrder32Host) -#define kCGBitmapByteOrder32Host 0 -#endif - // In OS X, left and right mouse button share the same click count. // That is, if one starts clicking the left button rapidly and then // switches to the right button, then the click count will continue @@ -350,7 +346,7 @@ static inline void autoDelay(BOOL isMove) { 8, picWidth * sizeof(jint), picColorSpace, kCGBitmapByteOrder32Host | - kCGImageAlphaPremultipliedFirst); + kCGImageAlphaNoneSkipFirst); CGColorSpaceRelease(picColorSpace); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h index d0312504e10..6eac8458f6c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,14 +29,6 @@ #import #import "JNIUtilities.h" -// these flags are not defined on Tiger on PPC, so we need to make them a no-op -#if !defined(kCGBitmapByteOrder32Host) -#define kCGBitmapByteOrder32Host 0 -#endif -#if !defined(kCGBitmapByteOrder16Host) -#define kCGBitmapByteOrder16Host 0 -#endif - // NOTE : Modify the printSurfaceDataDiagnostics API if you change this enum enum SDRenderType { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 7dcdb276ea1..7e092c0bd4d 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -48,7 +48,8 @@ + (void) initializeRolesMap { /* * Here we should keep all the mapping between the accessibility roles and implementing classes */ - rolesMap = [[NSMutableDictionary alloc] initWithCapacity:29]; + rolesMap = [[NSMutableDictionary alloc] initWithCapacity:35]; + [rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"]; [rolesMap setObject:@"ImageAccessibility" forKey:@"icon"]; @@ -58,9 +59,16 @@ + (void) initializeRolesMap { [rolesMap setObject:@"StaticTextAccessibility" forKey:@"label"]; [rolesMap setObject:@"RadiobuttonAccessibility" forKey:@"radiobutton"]; [rolesMap setObject:@"CheckboxAccessibility" forKey:@"checkbox"]; - // [rolesMap setObject:@"SliderAccessibility" forKey:@"slider"]; + //[rolesMap setObject:@"SliderAccessibility" forKey:@"slider"]; [rolesMap setObject:@"ScrollAreaAccessibility" forKey:@"scrollpane"]; [rolesMap setObject:@"ScrollBarAccessibility" forKey:@"scrollbar"]; + [rolesMap setObject:@"GroupAccessibility" forKey:@"awtcomponent"]; + [rolesMap setObject:@"GroupAccessibility" forKey:@"canvas"]; + [rolesMap setObject:@"GroupAccessibility" forKey:@"groupbox"]; + [rolesMap setObject:@"GroupAccessibility" forKey:@"internalframe"]; + [rolesMap setObject:@"GroupAccessibility" forKey:@"swingcomponent"]; + [rolesMap setObject:@"ToolbarAccessibility" forKey:@"toolbar"]; + /* * All the components below should be ignored by the accessibility subsystem, diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.h new file mode 100644 index 00000000000..2b4540f2892 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "JavaComponentAccessibility.h" +#import "CommonComponentAccessibility.h" + +#import + +@interface GroupAccessibility : CommonComponentAccessibility { + +}; + +- (NSArray * _Nullable)accessibilityChildren; +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m new file mode 100644 index 00000000000..ac0b5a80b4c --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/GroupAccessibility.m @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "GroupAccessibility.h" +#import "JNIUtilities.h" +#import "ThreadUtilities.h" +/* + * This is the protocol for the components that contain children. + * Basic logic of accessibilityChildren might be overridden in the specific implementing + * classes reflecting the logic of the class. + */ +@implementation GroupAccessibility + +/* + * Return all non-ignored children. + */ +- (NSArray *)accessibilityChildren { + JNIEnv *env = [ThreadUtilities getJNIEnv]; + + NSArray *children = [JavaComponentAccessibility childrenOfParent:self + withEnv:env + withChildrenCode:JAVA_AX_ALL_CHILDREN + allowIgnored:NO]; + + if ([children count] == 0) { + return nil; + } else { + return children; + } +} + +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.h index e1724542408..39cc1227ec3 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.h @@ -34,7 +34,7 @@ @interface StaticTextAccessibility : CommonTextAccessibility { }; -- (nullable NSString *)accessibilityAttributedString:(NSRange)range; +- (nullable NSString *)accessibilityAttributedStringForRange:(NSRange)range; - (nullable NSString *)accessibilityValue; - (NSRange)accessibilityVisibleCharacterRange; @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m index 470f0799c38..a22cb0bd14e 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m @@ -27,7 +27,7 @@ @implementation StaticTextAccessibility -- (nullable NSString *)accessibilityAttributedString:(NSRange)range +- (nullable NSString *)accessibilityAttributedStringForRange:(NSRange)range { return [self accessibilityStringForRangeAttribute:range]; } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ToolbarAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ToolbarAccessibility.h new file mode 100644 index 00000000000..22bc39e9632 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ToolbarAccessibility.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "JavaComponentAccessibility.h" +#import "CommonComponentAccessibility.h" + +#import + +@interface ToolbarAccessibility : CommonComponentAccessibility { + +}; +- (NSString * _Nonnull)accessibilityRole; +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ToolbarAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ToolbarAccessibility.m new file mode 100644 index 00000000000..f089c5b9345 --- /dev/null +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ToolbarAccessibility.m @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "ToolbarAccessibility.h" + +/* + * Implementation of the accessibility peer for the Toolbar role + */ +@implementation ToolbarAccessibility + +- (NSString * _Nonnull)accessibilityRole +{ + return [self accessibilityRoleAttribute]; +} +@end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m index 393d33bdbe6..e0cefbe01dc 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m @@ -58,12 +58,14 @@ { jint *glyphCodeInts = (*env)->GetPrimitiveArrayCritical(env, glyphs, 0); - CTS_GetGlyphsAsIntsForCharacters(awtFont, unicodes, - cgGlyphs, glyphCodeInts, count); + if (glyphCodeInts != NULL) { + CTS_GetGlyphsAsIntsForCharacters(awtFont, unicodes, + cgGlyphs, glyphCodeInts, count); - // Do not use JNI_COMMIT, as that will not free the buffer copy - // when +ProtectJavaHeap is on. - (*env)->ReleasePrimitiveArrayCritical(env, glyphs, glyphCodeInts, 0); + // Do not use JNI_COMMIT, as that will not free the buffer copy + // when +ProtectJavaHeap is on. + (*env)->ReleasePrimitiveArrayCritical(env, glyphs, glyphCodeInts, 0); + } } static inline void diff --git a/src/java.desktop/macosx/native/libosxui/JRSUIController.m b/src/java.desktop/macosx/native/libosxui/JRSUIController.m index b484319b06e..f7bb8e60e3f 100644 --- a/src/java.desktop/macosx/native/libosxui/JRSUIController.m +++ b/src/java.desktop/macosx/native/libosxui/JRSUIController.m @@ -277,11 +277,13 @@ static inline jint doPaintCGContext(CGContextRef cgRef, jlong controlPtr, jlong CGRect partBounds = JRSUIControlGetScrollBarPartBounds(control, frame, part); jdouble *rect = (*env)->GetPrimitiveArrayCritical(env, rectArray, NULL); - rect[0] = partBounds.origin.x; - rect[1] = partBounds.origin.y; - rect[2] = partBounds.size.width; - rect[3] = partBounds.size.height; - (*env)->ReleasePrimitiveArrayCritical(env, rectArray, rect, 0); + if (rect != NULL) { + rect[0] = partBounds.origin.x; + rect[1] = partBounds.origin.y; + rect[2] = partBounds.size.width; + rect[3] = partBounds.size.height; + (*env)->ReleasePrimitiveArrayCritical(env, rectArray, rect, 0); + } } /* diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java index f8a49d96857..2801376b34e 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java @@ -618,8 +618,10 @@ else if (size == 124) height = Math.abs(height); } - if (metadata.compression == BI_RGB) { - long imageDataSize = ((long)width * height * (bitsPerPixel / 8)); + if (metadata.compression == BI_RGB && + metadata.paletteSize == 0 && + metadata.bitsPerPixel >= 16) { + long imageDataSize = (((long)width * height * bitsPerPixel) / 8); if (imageDataSize > (bitmapFileSize - bitmapOffset)) { throw new IIOException(I18N.getString("BMPImageReader9")); } diff --git a/src/java.desktop/share/classes/java/awt/Font.java b/src/java.desktop/share/classes/java/awt/Font.java index e8d6f001083..58b2b110d7e 100644 --- a/src/java.desktop/share/classes/java/awt/Font.java +++ b/src/java.desktop/share/classes/java/awt/Font.java @@ -2601,8 +2601,10 @@ public Rectangle2D getStringBounds(char [] chars, // quick check for simple text, assume GV ok to use if simple boolean simple = values == null || - (values.getKerning() == 0 && values.getLigatures() == 0 && - values.getBaselineTransform() == null); + (values.getKerning() == 0 + && values.getLigatures() == 0 + && values.getTracking() == 0 + && values.getBaselineTransform() == null); if (simple) { simple = ! FontUtilities.isComplexText(chars, beginIndex, limit); } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicHTML.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicHTML.java index 85304d92e34..a9350a9cee3 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicHTML.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicHTML.java @@ -33,6 +33,7 @@ import javax.swing.text.*; import javax.swing.text.html.*; +import sun.swing.SwingAccessor; import sun.swing.SwingUtilities2; /** @@ -215,7 +216,7 @@ public static void updateRenderer(JComponent c, String text) { View value = null; View oldValue = (View)c.getClientProperty(BasicHTML.propertyKey); Boolean htmlDisabled = (Boolean) c.getClientProperty(htmlDisable); - if (htmlDisabled != Boolean.TRUE && BasicHTML.isHTMLString(text)) { + if (!(Boolean.TRUE.equals(htmlDisabled)) && BasicHTML.isHTMLString(text)) { value = BasicHTML.createHTMLView(c, text); } if (value != oldValue && oldValue != null) { @@ -371,15 +372,36 @@ public ViewFactory getViewFactory() { */ static class BasicHTMLViewFactory extends HTMLEditorKit.HTMLFactory { public View create(Element elem) { - View view = super.create(elem); + View view = null; + try { + setAllowHTMLObject(); + view = super.create(elem); + } finally { + clearAllowHTMLObject(); + } if (view instanceof ImageView) { ((ImageView)view).setLoadsSynchronously(true); } return view; } - } + private static Boolean useOV = null; + + @SuppressWarnings("removal") + private static void setAllowHTMLObject() { + if (useOV == null) { + useOV = java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction( + "swing.html.object")); + }; + SwingAccessor.setAllowHTMLObject(useOV); + } + + private static void clearAllowHTMLObject() { + SwingAccessor.setAllowHTMLObject(null); + } + } /** * The subclass of HTMLDocument that is used as the model. getForeground diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java index 02150052124..9616e8eea78 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java @@ -588,6 +588,18 @@ public void load(InputStream input, Class resourceBase) throws * new URL(synthFile, path). Refer to * Synth File Format for more * information. + *

    + * Whilst this API may be safe for loading local resources that are + * delivered with a {@code LookAndFeel} or application, and so have an + * equal level of trust with application code, using it to load from + * remote resources, particularly any which may have a lower level of + * trust, is strongly discouraged. + * The alternative mechanisms to load styles from an {@code InputStream} + * {@linkplain #load(InputStream, Class)} + * using resources co-located with the application or by providing a + * {@code SynthStyleFactory} to + * {@linkplain #setStyleFactory setStyleFactory(SynthStyleFactory)} + * are preferred. * * @param url the URL to load the set of * SynthStyle from diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html b/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html index 35e16e2076e..956554702c2 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html @@ -70,6 +70,8 @@

    File Format

    This example loads the look and feel from an input stream, using the specified class as the resource base to resolve paths. +

    +

    It is also possible to load a look and feel from an arbitrary URL as in the following example.

    @@ -94,6 +96,11 @@

    File Format

  • Remote JAR file, e.g. jar:http://host/synth-laf.jar!/laf.xml
+

Note: Synth's file format allows for the definition of code to be executed. + Loading any code from a remote location should be used only + with extreme caution from a trusted source over a secure connection. + It is strongly discouraged for an application or a LookAndFeel to do so. +

While the DTD for synth is specified, the parser is not validating. Parsing will fail only if a necessary attribute is not diff --git a/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java b/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java index e2f1966eefc..27573932efd 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/HTMLEditorKit.java @@ -41,6 +41,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import javax.swing.text.html.parser.ParserDelegator; +import sun.swing.SwingAccessor; /** * The Swing JEditorPane text component supports different kinds @@ -1306,7 +1307,11 @@ public View create(Element elem) { (kind == HTML.Tag.TEXTAREA)) { return new FormView(elem); } else if (kind == HTML.Tag.OBJECT) { - return new ObjectView(elem); + if (SwingAccessor.getAllowHTMLObject()) { + return new ObjectView(elem); + } else { + return new ObjectView(elem, false); + } } else if (kind == HTML.Tag.FRAMESET) { if (elem.getAttributes().isDefined(HTML.Attribute.ROWS)) { return new FrameSetView(elem, View.Y_AXIS); diff --git a/src/java.desktop/share/classes/javax/swing/text/html/ObjectView.java b/src/java.desktop/share/classes/javax/swing/text/html/ObjectView.java index b99254c75db..0bc0ea73d97 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/ObjectView.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/ObjectView.java @@ -71,6 +71,8 @@ */ public class ObjectView extends ComponentView { + private boolean createComp = true; // default + /** * Creates a new ObjectView object. * @@ -80,6 +82,11 @@ public ObjectView(Element elem) { super(elem); } + ObjectView(Element elem, boolean createComp) { + super(elem); + this.createComp = createComp; + } + /** * Create the component. The classid is used * as a specification of the classname, which @@ -87,6 +94,9 @@ public ObjectView(Element elem) { */ @SuppressWarnings("deprecation") protected Component createComponent() { + if (!createComp) { + return getUnloadableRepresentation(); + } AttributeSet attr = getElement().getAttributes(); String classname = (String) attr.getAttribute(HTML.Attribute.CLASSID); try { diff --git a/src/java.desktop/share/classes/sun/awt/FontConfiguration.java b/src/java.desktop/share/classes/sun/awt/FontConfiguration.java index 00169de3bbf..ec12b812b7e 100644 --- a/src/java.desktop/share/classes/sun/awt/FontConfiguration.java +++ b/src/java.desktop/share/classes/sun/awt/FontConfiguration.java @@ -1252,15 +1252,24 @@ public String getFileNameFromPlatformName(String platformName) { return filenamesMap.get(platformName); } + private static final String fontconfigErrorMessage = + "Fontconfig head is null, check your fonts or fonts configuration"; + /** * Returns a configuration specific path to be appended to the font * search path. */ public String getExtraFontPath() { + if (head == null) { + throw new RuntimeException(fontconfigErrorMessage); + } return getString(head[INDEX_appendedfontpath]); } public String getVersion() { + if (head == null) { + throw new RuntimeException(fontconfigErrorMessage); + } return getString(head[INDEX_version]); } diff --git a/src/java.desktop/share/classes/sun/awt/image/ImageRepresentation.java b/src/java.desktop/share/classes/sun/awt/image/ImageRepresentation.java index 3634fac5e3c..68bdc87dff5 100644 --- a/src/java.desktop/share/classes/sun/awt/image/ImageRepresentation.java +++ b/src/java.desktop/share/classes/sun/awt/image/ImageRepresentation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,24 +27,20 @@ import java.awt.Color; import java.awt.Graphics; -import java.awt.Transparency; -import java.awt.AWTException; +import java.awt.Graphics2D; import java.awt.Rectangle; +import java.awt.Transparency; +import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; import java.awt.image.DirectColorModel; -import java.awt.image.IndexColorModel; import java.awt.image.ImageConsumer; import java.awt.image.ImageObserver; -import sun.awt.image.ByteComponentRaster; -import sun.awt.image.IntegerComponentRaster; +import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferInt; -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import sun.awt.image.ImageWatched; import java.util.Hashtable; public class ImageRepresentation extends ImageWatched implements ImageConsumer @@ -114,8 +110,8 @@ public synchronized void reconstruct(int flags) { try { startProduction(); missinginfo = flags & ~availinfo; - while ((availinfo & ImageObserver.ERROR) == 0 && - missinginfo != 0) + while ((availinfo & (ImageObserver.ERROR | ImageObserver.FRAMEBITS)) == 0 + && missinginfo != 0) { try { wait(); diff --git a/src/java.desktop/share/classes/sun/swing/SwingAccessor.java b/src/java.desktop/share/classes/sun/swing/SwingAccessor.java index 56cd33ea27b..ddc0fa6b252 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingAccessor.java +++ b/src/java.desktop/share/classes/sun/swing/SwingAccessor.java @@ -280,4 +280,19 @@ public static KeyStrokeAccessor getKeyStrokeAccessor() { public static void setKeyStrokeAccessor(KeyStrokeAccessor accessor) { SwingAccessor.keyStrokeAccessor = accessor; } + + private static ThreadLocal tlObj = new ThreadLocal(); + + public static Boolean getAllowHTMLObject() { + Boolean b = tlObj.get(); + if (b == null) { + return Boolean.TRUE; + } else { + return b; + } + } + + public static void setAllowHTMLObject(Boolean val) { + tlObj.set(val); + } } diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index 9037354540b..e2ed76aa7c6 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,8 +1,8 @@ -## Harfbuzz v4.4.1 +## Harfbuzz v7.2.0 ### Harfbuzz License -https://github.com/harfbuzz/harfbuzz/blob/4.4.1/COPYING +https://github.com/harfbuzz/harfbuzz/blob/7.2.0/COPYING

 
@@ -10,23 +10,24 @@ HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
 For parts of HarfBuzz that are licensed under different licenses see individual
 files names COPYING in subdirectories where applicable.
 
-Copyright © 2010-2022  Google, Inc.
+Copyright © 2010-2023  Google, Inc.
 Copyright © 2018-2020  Ebrahim Byagowi
-Copyright © 2019-2020  Facebook, Inc.
-Copyright © 2012-2015  Mozilla Foundation.
-Copyright © 2011  Codethink Limited
-Copyright © 2008-2010  Nokia Corporation and/or its subsidiary(-ies)
-Copyright © 2009  Keith Stribley
-Copyright © 2009  Martin Hosken and SIL International
-Copyright © 2007  Chris Wilson
-Copyright © 2005-2022 Behdad Esfahbod
-Copyright © 2005  David Turner
 Copyright © 2004-2013  Red Hat, Inc.
-Copyright © 1998-2004  David Turner and Werner Lemberg
-Copyright © 2016  Elie Roux 
+Copyright © 2019  Facebook, Inc.
+Copyright © 2007  Chris Wilson
 Copyright © 2018-2019 Adobe Inc.
+Copyright © 2006-2023 Behdad Esfahbod
+Copyright © 1998-2004  David Turner and Werner Lemberg
+Copyright © 2009  Keith Stribley
 Copyright © 2018  Khaled Hosny
+Copyright © 2016  Elie Roux 
 Copyright © 2016  Igalia S.L.
+Copyright © 2015  Mozilla Foundation.
+Copyright © 1999  David Turner
+Copyright © 2005  Werner Lemberg
+Copyright © 2013-2015  Alexei Podtelezhnikov
+Copyright © 2022 Matthias Clasen
+Copyright © 2011  Codethink Limited
 
 For full copyright notices consult the individual files in the package.
 
@@ -72,3 +73,23 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
+ +### AUTHORS File Information +``` + +Behdad Esfahbod +David Corbett +David Turner +Ebrahim Byagowi +Garret Rieger +Jonathan Kew +Khaled Hosny +Lars Knoll +Martin Hosken +Owen Taylor +Roderick Sheeter +Roozbeh Pournader +Simon Hausmann +Werner Lemberg + +``` diff --git a/src/java.desktop/share/legal/lcms.md b/src/java.desktop/share/legal/lcms.md index 1576edb8db6..da86a9c47ca 100644 --- a/src/java.desktop/share/legal/lcms.md +++ b/src/java.desktop/share/legal/lcms.md @@ -1,8 +1,7 @@ -## Little Color Management System (LCMS) v2.14 +## Little Color Management System (LCMS) v2.15 ### LCMS License
-
 README.1ST file information
 
 LittleCMS core is released under MIT License
@@ -10,7 +9,7 @@ LittleCMS core is released under MIT License
 ---------------------------------
 
 Little CMS
-Copyright (c) 1998-2022 Marti Maria Saguer
+Copyright (c) 1998-2023 Marti Maria Saguer
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
@@ -32,7 +31,6 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 ---------------------------------
-
 The below license applies to the following files:
 liblcms/cmssm.c
 
@@ -44,12 +42,12 @@ SoftSurfer makes no warranty for this code, and cannot be held
 liable for any real or imagined damage resulting from its use.
 Users of this code must verify correctness for their application.
 
-
 
### AUTHORS File Information ``` + Main Author ------------ Marti Maria @@ -90,11 +88,15 @@ Mark Allen Noel Carboni Sergei Trofimovic Philipp Knechtges +Amyspark +Lovell Fuller +Eli Schwartz Special Thanks -------------- Artifex software AlienSkin software +libVIPS Jan Morovic Jos Vernon (WebSupergoo) Harald Schneider (Maxon) @@ -103,5 +105,4 @@ Dimitrios Anastassakis Lemke Software Tim Zaman - ``` diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index 4f69da5383a..f11cfe580ce 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.38 +## libpng v1.6.39 ### libpng License
@@ -188,9 +188,10 @@ Authors, for copyright and licensing purposes.
  * Arm Holdings
    - Richard Townsend
  * Google Inc.
+   - Dan Field
+   - Leon Scroggins III
    - Matt Sarett
    - Mike Klein
-   - Dan Field
    - Sami Boukortt
 
 The build projects, the build scripts, the test scripts, and other
diff --git a/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgrPre.c b/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgrPre.c
index c95d6a985a1..af109a7d880 100644
--- a/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgrPre.c
+++ b/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgrPre.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -69,6 +69,8 @@ DECLARE_SRCOVER_MASKBLIT(IntArgb, FourByteAbgrPre);
 DECLARE_ALPHA_MASKBLIT(IntArgb, FourByteAbgrPre);
 DECLARE_SRCOVER_MASKBLIT(IntArgbPre, FourByteAbgrPre);
 DECLARE_ALPHA_MASKBLIT(IntArgbPre, FourByteAbgrPre);
+DECLARE_SRCOVER_MASKBLIT(FourByteAbgrPre, IntArgbPre);
+DECLARE_ALPHA_MASKBLIT(FourByteAbgrPre, IntArgbPre);
 DECLARE_ALPHA_MASKBLIT(IntRgb, FourByteAbgrPre);
 DECLARE_SOLID_DRAWGLYPHLISTAA(FourByteAbgrPre);
 DECLARE_SOLID_DRAWGLYPHLISTLCD(FourByteAbgrPre);
@@ -103,6 +105,8 @@ NativePrimitive FourByteAbgrPrePrimitives[] = {
     REGISTER_ALPHA_MASKBLIT(IntArgb, FourByteAbgrPre),
     REGISTER_SRCOVER_MASKBLIT(IntArgbPre, FourByteAbgrPre),
     REGISTER_ALPHA_MASKBLIT(IntArgbPre, FourByteAbgrPre),
+    REGISTER_SRCOVER_MASKBLIT(FourByteAbgrPre, IntArgbPre),
+    REGISTER_ALPHA_MASKBLIT(FourByteAbgrPre, IntArgbPre),
     REGISTER_ALPHA_MASKBLIT(IntRgb, FourByteAbgrPre),
     REGISTER_SOLID_DRAWGLYPHLISTAA(FourByteAbgrPre),
     REGISTER_SOLID_DRAWGLYPHLISTLCD(FourByteAbgrPre),
@@ -177,6 +181,10 @@ DEFINE_SRCOVER_MASKBLIT(IntArgbPre, FourByteAbgrPre, 4ByteArgb)
 
 DEFINE_ALPHA_MASKBLIT(IntArgbPre, FourByteAbgrPre, 4ByteArgb)
 
+DEFINE_SRCOVER_MASKBLIT(FourByteAbgrPre, IntArgbPre, 4ByteArgb)
+
+DEFINE_ALPHA_MASKBLIT(FourByteAbgrPre, IntArgbPre, 4ByteArgb)
+
 DEFINE_ALPHA_MASKBLIT(IntRgb, FourByteAbgrPre, 4ByteArgb)
 
 DEFINE_SOLID_DRAWGLYPHLISTAA(FourByteAbgrPre, 4ByteArgb)
diff --git a/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c b/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c
new file mode 100644
index 00000000000..0edf379b437
--- /dev/null
+++ b/src/java.desktop/share/native/libfreetype/src/base/ftcolor.c
@@ -0,0 +1,156 @@
+/****************************************************************************
+ *
+ * ftcolor.c
+ *
+ *   FreeType's glyph color management (body).
+ *
+ * Copyright (C) 2018-2022 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#include 
+#include 
+#include 
+#include 
+
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+
+  static
+  const FT_Palette_Data  null_palette_data = { 0, NULL, NULL, 0, NULL };
+
+
+  /* documentation is in ftcolor.h */
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Palette_Data_Get( FT_Face           face,
+                       FT_Palette_Data  *apalette_data )
+  {
+    if ( !face )
+      return FT_THROW( Invalid_Face_Handle );
+    if ( !apalette_data)
+      return FT_THROW( Invalid_Argument );
+
+    if ( FT_IS_SFNT( face ) )
+      *apalette_data = ( (TT_Face)face )->palette_data;
+    else
+      *apalette_data = null_palette_data;
+
+    return FT_Err_Ok;
+  }
+
+
+  /* documentation is in ftcolor.h */
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Palette_Select( FT_Face     face,
+                     FT_UShort   palette_index,
+                     FT_Color*  *apalette )
+  {
+    FT_Error  error;
+
+    TT_Face       ttface;
+    SFNT_Service  sfnt;
+
+
+    if ( !face )
+      return FT_THROW( Invalid_Face_Handle );
+
+    if ( !FT_IS_SFNT( face ) )
+    {
+      if ( apalette )
+        *apalette = NULL;
+
+      return FT_Err_Ok;
+    }
+
+    ttface = (TT_Face)face;
+    sfnt   = (SFNT_Service)ttface->sfnt;
+
+    error = sfnt->set_palette( ttface, palette_index );
+    if ( error )
+      return error;
+
+    ttface->palette_index = palette_index;
+
+    if ( apalette )
+      *apalette = ttface->palette;
+
+    return FT_Err_Ok;
+  }
+
+
+  /* documentation is in ftcolor.h */
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Palette_Set_Foreground_Color( FT_Face   face,
+                                   FT_Color  foreground_color )
+  {
+    TT_Face  ttface;
+
+
+    if ( !face )
+      return FT_THROW( Invalid_Face_Handle );
+
+    if ( !FT_IS_SFNT( face ) )
+      return FT_Err_Ok;
+
+    ttface = (TT_Face)face;
+
+    ttface->foreground_color      = foreground_color;
+    ttface->have_foreground_color = 1;
+
+    return FT_Err_Ok;
+  }
+
+#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Palette_Data_Get( FT_Face           face,
+                       FT_Palette_Data  *apalette_data )
+  {
+    FT_UNUSED( face );
+    FT_UNUSED( apalette_data );
+
+
+    return FT_THROW( Unimplemented_Feature );
+  }
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Palette_Select( FT_Face     face,
+                     FT_UShort   palette_index,
+                     FT_Color*  *apalette )
+  {
+    FT_UNUSED( face );
+    FT_UNUSED( palette_index );
+    FT_UNUSED( apalette );
+
+
+    return FT_THROW( Unimplemented_Feature );
+  }
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Palette_Set_Foreground_Color( FT_Face   face,
+                                   FT_Color  foreground_color )
+  {
+    FT_UNUSED( face );
+    FT_UNUSED( foreground_color );
+
+
+    return FT_THROW( Unimplemented_Feature );
+  }
+
+#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
+
+
+/* END */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh
similarity index 93%
rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh
rename to src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh
index 70d78c74d3b..11aeda5297f 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh
@@ -24,10 +24,11 @@
  * Google Author(s): Seigo Nonaka, Calder Kitagawa
  */
 
-#ifndef HB_OT_COLOR_CBDT_TABLE_HH
-#define HB_OT_COLOR_CBDT_TABLE_HH
+#ifndef OT_COLOR_CBDT_CBDT_HH
+#define OT_COLOR_CBDT_CBDT_HH
 
-#include "hb-open-type.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-paint.hh"
 
 /*
  * CBLC -- Color Bitmap Location
@@ -67,7 +68,7 @@ _copy_data_to_cbdt (hb_vector_t *cbdt_prime,
 {
   unsigned int new_len = cbdt_prime->length + length;
   if (unlikely (!cbdt_prime->alloc (new_len))) return false;
-  memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length);
+  hb_memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length);
   cbdt_prime->length = new_len;
   return true;
 }
@@ -80,12 +81,15 @@ struct SmallGlyphMetrics
     return_trace (c->check_struct (this));
   }
 
-  void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const
+  void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scale) const
   {
-    extents->x_bearing = font->em_scale_x (bearingX);
-    extents->y_bearing = font->em_scale_y (bearingY);
-    extents->width = font->em_scale_x (width);
-    extents->height = font->em_scale_y (-static_cast(height));
+    extents->x_bearing = bearingX;
+    extents->y_bearing = bearingY;
+    extents->width = width;
+    extents->height = -static_cast (height);
+
+    if (scale)
+      font->scale_glyph_extents (extents);
   }
 
   HBUINT8       height;
@@ -307,7 +311,7 @@ struct IndexSubtable
     }
   }
 
-  bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const
+  bool get_extents (hb_glyph_extents_t *extents HB_UNUSED, bool scale HB_UNUSED) const
   {
     switch (u.header.indexFormat)
     {
@@ -468,13 +472,13 @@ struct IndexSubtableRecord
     if (unlikely (!c->serializer->check_success (records->resize (records->length + 1))))
       return_trace (false);
 
-    (*records)[records->length - 1].firstGlyphIndex = 1;
-    (*records)[records->length - 1].lastGlyphIndex = 0;
+    records->tail ().firstGlyphIndex = 1;
+    records->tail ().lastGlyphIndex = 0;
     bitmap_size_context->size += IndexSubtableRecord::min_size;
 
     c->serializer->push ();
 
-    if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start)))
+    if (unlikely (!add_new_subtable (c, bitmap_size_context, &(records->tail ()), lookup, base, start)))
     {
       c->serializer->pop_discard ();
       c->serializer->revert (snap);
@@ -504,8 +508,8 @@ struct IndexSubtableRecord
     return num_missing;
   }
 
-  bool get_extents (hb_glyph_extents_t *extents, const void *base) const
-  { return (base+offsetToSubtable).get_extents (extents); }
+  bool get_extents (hb_glyph_extents_t *extents, const void *base, bool scale) const
+  { return (base+offsetToSubtable).get_extents (extents, scale); }
 
   bool get_image_data (unsigned int  gid,
                        const void   *base,
@@ -833,7 +837,7 @@ struct CBDT
     }
 
     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, bool scale = true) const
     {
       const void *base;
       const BitmapSizeTable &strike = this->cblc->choose_strike (font);
@@ -841,7 +845,7 @@ struct CBDT
       if (!subtable_record || !strike.ppemX || !strike.ppemY)
         return false;
 
-      if (subtable_record->get_extents (extents, base))
+      if (subtable_record->get_extents (extents, base, scale))
         return true;
 
       unsigned int image_offset = 0, image_length = 0, image_format = 0;
@@ -858,26 +862,29 @@ struct CBDT
         if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
           return false;
         auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset);
-        glyphFormat17.glyphMetrics.get_extents (font, extents);
+        glyphFormat17.glyphMetrics.get_extents (font, extents, scale);
         break;
       }
       case 18: {
         if (unlikely (image_length < GlyphBitmapDataFormat18::min_size))
           return false;
         auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset);
-        glyphFormat18.glyphMetrics.get_extents (font, extents);
+        glyphFormat18.glyphMetrics.get_extents (font, extents, scale);
         break;
       }
       default: return false; /* TODO: Support other image formats. */
       }
 
       /* Convert to font units. */
-      float x_scale = upem / (float) strike.ppemX;
-      float y_scale = upem / (float) strike.ppemY;
-      extents->x_bearing = roundf (extents->x_bearing * x_scale);
-      extents->y_bearing = roundf (extents->y_bearing * y_scale);
-      extents->width = roundf (extents->width * x_scale);
-      extents->height = roundf (extents->height * y_scale);
+      if (scale)
+      {
+        float x_scale = upem / (float) strike.ppemX;
+        float y_scale = upem / (float) strike.ppemY;
+        extents->x_bearing = roundf (extents->x_bearing * x_scale);
+        extents->y_bearing = roundf (extents->y_bearing * y_scale);
+        extents->width = roundf (extents->width * x_scale);
+        extents->height = roundf (extents->height * y_scale);
+      }
 
       return true;
     }
@@ -934,6 +941,32 @@ struct CBDT
 
     bool has_data () const { return cbdt.get_length (); }
 
+    bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+    {
+      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)))
+        return false;
+
+      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+        return false;
+
+      bool ret = funcs->image (data,
+                               blob,
+                               pixel_extents.width, -pixel_extents.height,
+                               HB_PAINT_IMAGE_FORMAT_PNG,
+                               font->slant_xy,
+                               &extents);
+
+      hb_blob_destroy (blob);
+      return ret;
+    }
+
     private:
     hb_blob_ptr_t cblc;
     hb_blob_ptr_t cbdt;
@@ -994,4 +1027,4 @@ struct CBDT_accelerator_t : CBDT::accelerator_t {
 
 } /* namespace OT */
 
-#endif /* HB_OT_COLOR_CBDT_TABLE_HH */
+#endif /* OT_COLOR_CBDT_CBDT_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh
similarity index 56%
rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh
rename to src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh
index 6b9e734615b..e7c34a83fd6 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh
@@ -25,12 +25,14 @@
  * Google Author(s): Calder Kitagawa
  */
 
-#ifndef HB_OT_COLOR_COLR_TABLE_HH
-#define HB_OT_COLOR_COLR_TABLE_HH
+#ifndef OT_COLOR_COLR_COLR_HH
+#define OT_COLOR_COLR_COLR_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-layout-common.hh"
-#include "hb-ot-var-common.hh"
+#include "../../../hb.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-ot-var-common.hh"
+#include "../../../hb-paint.hh"
+#include "../../../hb-paint-extents.hh"
 
 /*
  * COLR -- Color
@@ -38,17 +40,81 @@
  */
 #define HB_OT_TAG_COLR HB_TAG('C','O','L','R')
 
-#ifndef HB_COLRV1_MAX_NESTING_LEVEL
-#define HB_COLRV1_MAX_NESTING_LEVEL     100
-#endif
-
-#ifndef COLRV1_ENABLE_SUBSETTING
-#define COLRV1_ENABLE_SUBSETTING 1
-#endif
+namespace OT {
+struct hb_paint_context_t;
+}
 
 namespace OT {
 
 struct COLR;
+
+struct Paint;
+
+struct hb_paint_context_t :
+       hb_dispatch_context_t
+{
+  template 
+  return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); }
+  static return_t default_return_value () { return hb_empty_t (); }
+
+  const COLR* get_colr_table () const
+  { return reinterpret_cast (base); }
+
+public:
+  const void *base;
+  hb_paint_funcs_t *funcs;
+  void *data;
+  hb_font_t *font;
+  unsigned int palette_index;
+  hb_color_t foreground;
+  VarStoreInstancer &instancer;
+  int depth_left = HB_MAX_NESTING_LEVEL;
+  int edge_count = HB_COLRV1_MAX_EDGE_COUNT;
+
+  hb_paint_context_t (const void *base_,
+                      hb_paint_funcs_t *funcs_,
+                      void *data_,
+                      hb_font_t *font_,
+                      unsigned int palette_,
+                      hb_color_t foreground_,
+                      VarStoreInstancer &instancer_) :
+    base (base_),
+    funcs (funcs_),
+    data (data_),
+    font (font_),
+    palette_index (palette_),
+    foreground (foreground_),
+    instancer (instancer_)
+  { }
+
+  hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground)
+  {
+    hb_color_t color = foreground;
+
+    *is_foreground = true;
+
+    if (color_index != 0xffff)
+    {
+      if (!funcs->custom_palette_color (data, color_index, &color))
+      {
+        unsigned int clen = 1;
+        hb_face_t *face = hb_font_get_face (font);
+
+        hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
+      }
+
+      *is_foreground = false;
+    }
+
+    return HB_COLOR (hb_color_get_blue (color),
+                     hb_color_get_green (color),
+                     hb_color_get_red (color),
+                     hb_color_get_alpha (color) * alpha);
+  }
+
+  inline void recurse (const Paint &paint);
+};
+
 struct hb_colrv1_closure_context_t :
        hb_dispatch_context_t
 {
@@ -102,7 +168,7 @@ struct hb_colrv1_closure_context_t :
                                hb_set_t *glyphs_,
                                hb_set_t *layer_indices_,
                                hb_set_t *palette_indices_,
-                               unsigned nesting_level_left_ = HB_COLRV1_MAX_NESTING_LEVEL) :
+                               unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
                           base (base_),
                           glyphs (glyphs_),
                           layer_indices (layer_indices_),
@@ -145,7 +211,7 @@ struct BaseGlyphRecord
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   public:
@@ -164,6 +230,8 @@ struct BaseGlyphRecord
 template 
 struct Variable
 {
+  static constexpr bool is_variable = true;
+
   Variable* copy (hb_serialize_context_t *c) const
   {
     TRACE_SERIALIZE (this);
@@ -173,10 +241,15 @@ struct Variable
   void closurev1 (hb_colrv1_closure_context_t* c) const
   { value.closurev1 (c); }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
-    if (!value.subset (c)) return_trace (false);
+    if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
+    if (c->plan->all_axes_pinned)
+      return_trace (true);
+
+    //TODO: update varIdxBase for partial-instancing
     return_trace (c->serializer->embed (varIdxBase));
   }
 
@@ -186,8 +259,26 @@ struct Variable
     return_trace (c->check_struct (this) && value.sanitize (c));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    value.paint_glyph (c, varIdxBase);
+  }
+
+  void get_color_stop (hb_paint_context_t *c,
+                       hb_color_stop_t *stop,
+                       const VarStoreInstancer &instancer) const
+  {
+    value.get_color_stop (c, stop, varIdxBase, instancer);
+  }
+
+  hb_paint_extend_t get_extend () const
+  {
+    return value.get_extend ();
+  }
+
   protected:
   T      value;
+  public:
   VarIdx varIdxBase;
   public:
   DEFINE_SIZE_STATIC (4 + T::static_size);
@@ -196,6 +287,10 @@ struct Variable
 template 
 struct NoVariable
 {
+  static constexpr bool is_variable = false;
+
+  static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION;
+
   NoVariable* copy (hb_serialize_context_t *c) const
   {
     TRACE_SERIALIZE (this);
@@ -205,10 +300,11 @@ struct NoVariable
   void closurev1 (hb_colrv1_closure_context_t* c) const
   { value.closurev1 (c); }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
-    return_trace (value.subset (c));
+    return_trace (value.subset (c, instancer, varIdxBase));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -217,6 +313,23 @@ struct NoVariable
     return_trace (c->check_struct (this) && value.sanitize (c));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    value.paint_glyph (c, varIdxBase);
+  }
+
+  void get_color_stop (hb_paint_context_t *c,
+                       hb_color_stop_t *stop,
+                       const VarStoreInstancer &instancer) const
+  {
+    value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
+  }
+
+  hb_paint_extend_t get_extend () const
+  {
+    return value.get_extend ();
+  }
+
   T      value;
   public:
   DEFINE_SIZE_STATIC (T::static_size);
@@ -229,12 +342,21 @@ struct ColorStop
   void closurev1 (hb_colrv1_closure_context_t* c) const
   { c->add_palette_index (paletteIndex); }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (*this);
     if (unlikely (!out)) return_trace (false);
-    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex),
+
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->stopOffset.set_float (stopOffset.to_float(instancer (varIdxBase, 0)));
+      out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 1)));
+    }
+
+    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
   }
 
@@ -244,6 +366,17 @@ struct ColorStop
     return_trace (c->check_struct (this));
   }
 
+  void get_color_stop (hb_paint_context_t *c,
+                       hb_color_stop_t *out,
+                       uint32_t varIdx,
+                       const VarStoreInstancer &instancer) const
+  {
+    out->offset = stopOffset.to_float(instancer (varIdx, 0));
+    out->color = c->get_color (paletteIndex,
+                               alpha.to_float (instancer (varIdx, 1)),
+                               &out->is_foreground);
+  }
+
   F2DOT14       stopOffset;
   HBUINT16      paletteIndex;
   F2DOT14       alpha;
@@ -271,7 +404,8 @@ struct ColorLine
       stop.closurev1 (c);
   }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (this);
@@ -283,7 +417,7 @@ struct ColorLine
 
     for (const auto& stop : stops.iter ())
     {
-      if (!stop.subset (c)) return_trace (false);
+      if (!stop.subset (c, instancer)) return_trace (false);
     }
     return_trace (true);
   }
@@ -295,6 +429,52 @@ struct ColorLine
                   stops.sanitize (c));
   }
 
+  /* get up to count stops from start */
+  unsigned int
+  get_color_stops (hb_paint_context_t *c,
+                   unsigned int start,
+                   unsigned int *count,
+                   hb_color_stop_t *color_stops,
+                   const VarStoreInstancer &instancer) const
+  {
+    unsigned int len = stops.len;
+
+    if (count && color_stops)
+    {
+      unsigned int i;
+      for (i = 0; i < *count && start + i < len; i++)
+        stops[start + i].get_color_stop (c, &color_stops[i], instancer);
+      *count = i;
+    }
+
+    return len;
+  }
+
+  HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line,
+                                                          void *color_line_data,
+                                                          unsigned int start,
+                                                          unsigned int *count,
+                                                          hb_color_stop_t *color_stops,
+                                                          void *user_data)
+  {
+    const ColorLine *thiz = (const ColorLine *) color_line_data;
+    hb_paint_context_t *c = (hb_paint_context_t *) user_data;
+    return thiz->get_color_stops (c, start, count, color_stops, c->instancer);
+  }
+
+  hb_paint_extend_t get_extend () const
+  {
+    return (hb_paint_extend_t) (unsigned int) extend;
+  }
+
+  HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line,
+                                                          void *color_line_data,
+                                                          void *user_data)
+  {
+    const ColorLine *thiz = (const ColorLine *) color_line_data;
+    return thiz->get_extend ();
+  }
+
   Extend        extend;
   Array16Of>     stops;
   public:
@@ -358,26 +538,57 @@ struct Affine2x3
     return_trace (c->check_struct (this));
   }
 
-  HBFixed xx;
-  HBFixed yx;
-  HBFixed xy;
-  HBFixed yy;
-  HBFixed dx;
-  HBFixed dy;
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->xx.set_float (xx.to_float(instancer (varIdxBase, 0)));
+      out->yx.set_float (yx.to_float(instancer (varIdxBase, 1)));
+      out->xy.set_float (xy.to_float(instancer (varIdxBase, 2)));
+      out->yy.set_float (yy.to_float(instancer (varIdxBase, 3)));
+      out->dx.set_float (dx.to_float(instancer (varIdxBase, 4)));
+      out->dy.set_float (dy.to_float(instancer (varIdxBase, 5)));
+    }
+    return_trace (true);
+  }
+
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    c->funcs->push_transform (c->data,
+                              xx.to_float (c->instancer (varIdxBase, 0)),
+                              yx.to_float (c->instancer (varIdxBase, 1)),
+                              xy.to_float (c->instancer (varIdxBase, 2)),
+                              yy.to_float (c->instancer (varIdxBase, 3)),
+                              dx.to_float (c->instancer (varIdxBase, 4)),
+                              dy.to_float (c->instancer (varIdxBase, 5)));
+  }
+
+  F16DOT16 xx;
+  F16DOT16 yx;
+  F16DOT16 xy;
+  F16DOT16 yy;
+  F16DOT16 dx;
+  F16DOT16 dy;
   public:
-  DEFINE_SIZE_STATIC (6 * HBFixed::static_size);
+  DEFINE_SIZE_STATIC (6 * F16DOT16::static_size);
 };
 
 struct PaintColrLayers
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer HB_UNUSED) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
-    return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers->get (firstLayerIndex),
+    return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex),
                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
 
     return_trace (true);
@@ -389,6 +600,8 @@ struct PaintColrLayers
     return_trace (c->check_struct (this));
   }
 
+  inline void paint_glyph (hb_paint_context_t *c) const;
+
   HBUINT8       format; /* format = 1 */
   HBUINT8       numLayers;
   HBUINT32      firstLayerIndex;  /* index into COLRv1::layerList */
@@ -401,12 +614,21 @@ struct PaintSolid
   void closurev1 (hb_colrv1_closure_context_t* c) const
   { c->add_palette_index (paletteIndex); }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (*this);
     if (unlikely (!out)) return_trace (false);
-    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex),
+
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+      out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 0)));
+
+    if (format == 3 && c->plan->all_axes_pinned)
+        out->format = 2;
+
+    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
   }
 
@@ -416,6 +638,17 @@ struct PaintSolid
     return_trace (c->check_struct (this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_bool_t is_foreground;
+    hb_color_t color;
+
+    color = c->get_color (paletteIndex,
+                          alpha.to_float (c->instancer (varIdxBase, 0)),
+                          &is_foreground);
+    c->funcs->color (c->data, is_foreground, color);
+  }
+
   HBUINT8       format; /* format = 2(noVar) or 3(Var)*/
   HBUINT16      paletteIndex;
   F2DOT14       alpha;
@@ -429,13 +662,28 @@ struct PaintLinearGradient
   void closurev1 (hb_colrv1_closure_context_t* c) const
   { (this+colorLine).closurev1 (c); }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->colorLine.serialize_subset (c, colorLine, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
+      out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
+      out->x1 = x1 + (int) roundf (instancer (varIdxBase, 2));
+      out->y1 = y1 + (int) roundf (instancer (varIdxBase, 3));
+      out->x2 = x2 + (int) roundf (instancer (varIdxBase, 4));
+      out->y2 = y2 + (int) roundf (instancer (varIdxBase, 5));
+    }
+
+    if (format == 5 && c->plan->all_axes_pinned)
+        out->format = 4;
+
+    return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -444,6 +692,23 @@ struct PaintLinearGradient
     return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_color_line_t cl = {
+      (void *) &(this+colorLine),
+      (this+colorLine).static_get_color_stops, c,
+      (this+colorLine).static_get_extend, nullptr
+    };
+
+    c->funcs->linear_gradient (c->data, &cl,
+                               x0 + c->instancer (varIdxBase, 0),
+                               y0 + c->instancer (varIdxBase, 1),
+                               x1 + c->instancer (varIdxBase, 2),
+                               y1 + c->instancer (varIdxBase, 3),
+                               x2 + c->instancer (varIdxBase, 4),
+                               y2 + c->instancer (varIdxBase, 5));
+  }
+
   HBUINT8                       format; /* format = 4(noVar) or 5 (Var) */
   Offset24To>    colorLine; /* Offset (from beginning of PaintLinearGradient
                                             * table) to ColorLine subtable. */
@@ -463,13 +728,28 @@ struct PaintRadialGradient
   void closurev1 (hb_colrv1_closure_context_t* c) const
   { (this+colorLine).closurev1 (c); }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->colorLine.serialize_subset (c, colorLine, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
+      out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
+      out->radius0 = radius0 + (unsigned) roundf (instancer (varIdxBase, 2));
+      out->x1 = x1 + (int) roundf (instancer (varIdxBase, 3));
+      out->y1 = y1 + (int) roundf (instancer (varIdxBase, 4));
+      out->radius1 = radius1 + (unsigned) roundf (instancer (varIdxBase, 5));
+    }
+
+    if (format == 7 && c->plan->all_axes_pinned)
+        out->format = 6;
+
+    return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -478,6 +758,23 @@ struct PaintRadialGradient
     return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_color_line_t cl = {
+      (void *) &(this+colorLine),
+      (this+colorLine).static_get_color_stops, c,
+      (this+colorLine).static_get_extend, nullptr
+    };
+
+    c->funcs->radial_gradient (c->data, &cl,
+                               x0 + c->instancer (varIdxBase, 0),
+                               y0 + c->instancer (varIdxBase, 1),
+                               radius0 + c->instancer (varIdxBase, 2),
+                               x1 + c->instancer (varIdxBase, 3),
+                               y1 + c->instancer (varIdxBase, 4),
+                               radius1 + c->instancer (varIdxBase, 5));
+  }
+
   HBUINT8                       format; /* format = 6(noVar) or 7 (Var) */
   Offset24To>    colorLine; /* Offset (from beginning of PaintRadialGradient
                                             * table) to ColorLine subtable. */
@@ -497,13 +794,26 @@ struct PaintSweepGradient
   void closurev1 (hb_colrv1_closure_context_t* c) const
   { (this+colorLine).closurev1 (c); }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->colorLine.serialize_subset (c, colorLine, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 0));
+      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 1));
+      out->startAngle.set_float (startAngle.to_float (instancer (varIdxBase, 2)));
+      out->endAngle.set_float (endAngle.to_float (instancer (varIdxBase, 3)));
+    }
+
+    if (format == 9 && c->plan->all_axes_pinned)
+        out->format = 8;
+
+    return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -512,6 +822,21 @@ struct PaintSweepGradient
     return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_color_line_t cl = {
+      (void *) &(this+colorLine),
+      (this+colorLine).static_get_color_stops, c,
+      (this+colorLine).static_get_extend, nullptr
+    };
+
+    c->funcs->sweep_gradient (c->data, &cl,
+                              centerX + c->instancer (varIdxBase, 0),
+                              centerY + c->instancer (varIdxBase, 1),
+                              (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * HB_PI,
+                              (endAngle.to_float   (c->instancer (varIdxBase, 3)) + 1) * HB_PI);
+  }
+
   HBUINT8                       format; /* format = 8(noVar) or 9 (Var) */
   Offset24To>    colorLine; /* Offset (from beginning of PaintSweepGradient
                                             * table) to ColorLine subtable. */
@@ -523,13 +848,13 @@ struct PaintSweepGradient
   DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size);
 };
 
-struct Paint;
 // Paint a non-COLR glyph, filled as indicated by paint.
 struct PaintGlyph
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
@@ -539,7 +864,7 @@ struct PaintGlyph
                                        HB_SERIALIZE_ERROR_INT_OVERFLOW))
       return_trace (false);
 
-    return_trace (out->paint.serialize_subset (c, paint, this));
+    return_trace (out->paint.serialize_subset (c, paint, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -548,6 +873,17 @@ struct PaintGlyph
     return_trace (c->check_struct (this) && paint.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    c->funcs->push_inverse_root_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->recurse (this+paint);
+    c->funcs->pop_transform (c->data);
+    c->funcs->pop_clip (c->data);
+    c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 10 */
   Offset24To     paint;  /* Offset (from beginning of PaintGlyph table) to Paint subtable. */
   HBUINT16              gid;
@@ -559,7 +895,8 @@ struct PaintColrGlyph
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer HB_UNUSED) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
@@ -575,6 +912,8 @@ struct PaintColrGlyph
     return_trace (c->check_struct (this));
   }
 
+  inline void paint_glyph (hb_paint_context_t *c) const;
+
   HBUINT8       format; /* format = 11 */
   HBUINT16      gid;
   public:
@@ -586,13 +925,16 @@ struct PaintTransform
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
-    if (!out->transform.serialize_copy (c->serializer, transform, this)) return_trace (false);
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (!out->transform.serialize_subset (c, transform, this, instancer)) return_trace (false);
+    if (format == 13 && c->plan->all_axes_pinned)
+      out->format = 12;
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -603,6 +945,13 @@ struct PaintTransform
                   transform.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    (this+transform).paint_glyph (c);
+    c->recurse (this+src);
+    c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8                       format; /* format = 12(noVar) or 13 (Var) */
   Offset24To             src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */
   Offset24To>    transform;
@@ -614,13 +963,24 @@ struct PaintTranslate
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->dx = dx + (int) roundf (instancer (varIdxBase, 0));
+      out->dy = dy + (int) roundf (instancer (varIdxBase, 1));
+    }
+
+    if (format == 15 && c->plan->all_axes_pinned)
+        out->format = 14;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -629,6 +989,16 @@ struct PaintTranslate
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float ddx = dx + c->instancer (varIdxBase, 0);
+    float ddy = dy + c->instancer (varIdxBase, 1);
+
+    bool p1 = c->funcs->push_translate (c->data, ddx, ddy);
+    c->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 14(noVar) or 15 (Var) */
   Offset24To     src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */
   FWORD         dx;
@@ -641,13 +1011,24 @@ struct PaintScale
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
+      out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
+    }
+
+    if (format == 17 && c->plan->all_axes_pinned)
+        out->format = 16;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -656,6 +1037,16 @@ struct PaintScale
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
+    float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
+
+    bool p1 = c->funcs->push_scale (c->data, sx, sy);
+    c->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 16 (noVar) or 17(Var) */
   Offset24To     src; /* Offset (from beginning of PaintScale table) to Paint subtable. */
   F2DOT14               scaleX;
@@ -668,13 +1059,26 @@ struct PaintScaleAroundCenter
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
+      out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
+      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
+      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
+    }
+
+    if (format == 19 && c->plan->all_axes_pinned)
+        out->format = 18;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -683,6 +1087,22 @@ struct PaintScaleAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
+    float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
+    float tCenterX = centerX + c->instancer (varIdxBase, 2);
+    float tCenterY = centerY + c->instancer (varIdxBase, 3);
+
+    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
+    bool p2 = c->funcs->push_scale (c->data, sx, sy);
+    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->recurse (this+src);
+    if (p3) c->funcs->pop_transform (c->data);
+    if (p2) c->funcs->pop_transform (c->data);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 18 (noVar) or 19(Var) */
   Offset24To     src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */
   F2DOT14       scaleX;
@@ -697,13 +1117,21 @@ struct PaintScaleUniform
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+      out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
+
+    if (format == 21 && c->plan->all_axes_pinned)
+        out->format = 20;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -712,6 +1140,15 @@ struct PaintScaleUniform
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float s = scale.to_float (c->instancer (varIdxBase, 0));
+
+    bool p1 = c->funcs->push_scale (c->data, s, s);
+    c->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 20 (noVar) or 21(Var) */
   Offset24To     src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */
   F2DOT14               scale;
@@ -723,13 +1160,25 @@ struct PaintScaleUniformAroundCenter
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
+      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
+      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
+    }
+
+    if (format == 23 && c->plan->all_axes_pinned)
+        out->format = 22;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -738,6 +1187,21 @@ struct PaintScaleUniformAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float s = scale.to_float (c->instancer (varIdxBase, 0));
+    float tCenterX = centerX + c->instancer (varIdxBase, 1);
+    float tCenterY = centerY + c->instancer (varIdxBase, 2);
+
+    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
+    bool p2 = c->funcs->push_scale (c->data, s, s);
+    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->recurse (this+src);
+    if (p3) c->funcs->pop_transform (c->data);
+    if (p2) c->funcs->pop_transform (c->data);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 22 (noVar) or 23(Var) */
   Offset24To     src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */
   F2DOT14       scale;
@@ -751,13 +1215,21 @@ struct PaintRotate
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+      out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
+
+    if (format == 25 && c->plan->all_axes_pinned)
+      out->format = 24;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -766,6 +1238,15 @@ struct PaintRotate
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float a = angle.to_float (c->instancer (varIdxBase, 0));
+
+    bool p1 = c->funcs->push_rotate (c->data, a);
+    c->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 24 (noVar) or 25(Var) */
   Offset24To     src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */
   F2DOT14               angle;
@@ -777,13 +1258,25 @@ struct PaintRotateAroundCenter
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
+      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
+      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
+    }
+
+    if (format ==27 && c->plan->all_axes_pinned)
+        out->format = 26;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -792,6 +1285,21 @@ struct PaintRotateAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float a = angle.to_float (c->instancer (varIdxBase, 0));
+    float tCenterX = centerX + c->instancer (varIdxBase, 1);
+    float tCenterY = centerY + c->instancer (varIdxBase, 2);
+
+    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
+    bool p2 = c->funcs->push_rotate (c->data, a);
+    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->recurse (this+src);
+    if (p3) c->funcs->pop_transform (c->data);
+    if (p2) c->funcs->pop_transform (c->data);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 26 (noVar) or 27(Var) */
   Offset24To     src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */
   F2DOT14       angle;
@@ -805,13 +1313,24 @@ struct PaintSkew
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
+      out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
+    }
+
+    if (format == 29 && c->plan->all_axes_pinned)
+        out->format = 28;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -820,6 +1339,16 @@ struct PaintSkew
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
+    float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
+
+    bool p1 = c->funcs->push_skew (c->data, sx, sy);
+    c->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 28(noVar) or 29 (Var) */
   Offset24To     src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */
   F2DOT14               xSkewAngle;
@@ -832,13 +1361,26 @@ struct PaintSkewAroundCenter
 {
   HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    return_trace (out->src.serialize_subset (c, src, this));
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
+      out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
+      out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
+      out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
+    }
+
+    if (format == 31 && c->plan->all_axes_pinned)
+        out->format = 30;
+
+    return_trace (out->src.serialize_subset (c, src, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -847,6 +1389,22 @@ struct PaintSkewAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
+    float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
+    float tCenterX = centerX + c->instancer (varIdxBase, 2);
+    float tCenterY = centerY + c->instancer (varIdxBase, 3);
+
+    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
+    bool p2 = c->funcs->push_skew (c->data, sx, sy);
+    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->recurse (this+src);
+    if (p3) c->funcs->pop_transform (c->data);
+    if (p2) c->funcs->pop_transform (c->data);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8               format; /* format = 30(noVar) or 31 (Var) */
   Offset24To     src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */
   F2DOT14       xSkewAngle;
@@ -861,14 +1419,15 @@ struct PaintComposite
 {
   void closurev1 (hb_colrv1_closure_context_t* c) const;
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
-    if (!out->src.serialize_subset (c, src, this)) return_trace (false);
-    return_trace (out->backdrop.serialize_subset (c, backdrop, this));
+    if (!out->src.serialize_subset (c, src, this, instancer)) return_trace (false);
+    return_trace (out->backdrop.serialize_subset (c, backdrop, this, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -879,6 +1438,14 @@ struct PaintComposite
                   backdrop.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    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);
+  }
+
   HBUINT8               format; /* format = 32 */
   Offset24To     src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */
   CompositeMode         mode;   /* If mode is unrecognized use COMPOSITE_CLEAR */
@@ -887,6 +1454,11 @@ struct PaintComposite
   DEFINE_SIZE_STATIC (8);
 };
 
+struct ClipBoxData
+{
+  int xMin, yMin, xMax, yMax;
+};
+
 struct ClipBoxFormat1
 {
   bool sanitize (hb_sanitize_context_t *c) const
@@ -895,6 +1467,36 @@ struct ClipBoxFormat1
     return_trace (c->check_struct (this));
   }
 
+  void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const
+  {
+    clip_box.xMin = xMin;
+    clip_box.yMin = yMin;
+    clip_box.xMax = xMax;
+    clip_box.yMax = yMax;
+  }
+
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer,
+               uint32_t varIdxBase) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
+    {
+      out->xMin = xMin + (int) roundf (instancer (varIdxBase, 0));
+      out->yMin = yMin + (int) roundf (instancer (varIdxBase, 1));
+      out->xMax = xMax + (int) roundf (instancer (varIdxBase, 2));
+      out->yMax = yMax + (int) roundf (instancer (varIdxBase, 3));
+    }
+
+    if (format == 2 && c->plan->all_axes_pinned)
+        out->format = 1;
+
+    return_trace (true);
+  }
+
   public:
   HBUINT8       format; /* format = 1(noVar) or 2(Var)*/
   FWORD         xMin;
@@ -905,25 +1507,39 @@ struct ClipBoxFormat1
   DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size);
 };
 
-struct ClipBoxFormat2 : Variable {};
+struct ClipBoxFormat2 : Variable
+{
+  void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const
+  {
+    value.get_clip_box(clip_box, instancer);
+    if (instancer)
+    {
+      clip_box.xMin += _hb_roundf (instancer (varIdxBase, 0));
+      clip_box.yMin += _hb_roundf (instancer (varIdxBase, 1));
+      clip_box.xMax += _hb_roundf (instancer (varIdxBase, 2));
+      clip_box.yMax += _hb_roundf (instancer (varIdxBase, 3));
+    }
+  }
+};
 
 struct ClipBox
 {
-  ClipBox* copy (hb_serialize_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
-    TRACE_SERIALIZE (this);
+    TRACE_SUBSET (this);
     switch (u.format) {
-    case 1: return_trace (reinterpret_cast (c->embed (u.format1)));
-    case 2: return_trace (reinterpret_cast (c->embed (u.format2)));
-    default:return_trace (nullptr);
+    case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION));
+    case 2: return_trace (u.format2.subset (c, instancer));
+    default:return_trace (c->default_return_value ());
     }
   }
 
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
@@ -931,6 +1547,28 @@ struct ClipBox
     }
   }
 
+  bool get_extents (hb_glyph_extents_t *extents,
+                    const VarStoreInstancer &instancer) const
+  {
+    ClipBoxData clip_box;
+    switch (u.format) {
+    case 1:
+      u.format1.get_clip_box (clip_box, instancer);
+      break;
+    case 2:
+      u.format2.get_clip_box (clip_box, instancer);
+      break;
+    default:
+      return false;
+    }
+
+    extents->x_bearing = clip_box.xMin;
+    extents->y_bearing = clip_box.yMax;
+    extents->width = clip_box.xMax - clip_box.xMin;
+    extents->height = clip_box.yMin - clip_box.yMax;
+    return true;
+  }
+
   protected:
   union {
   HBUINT8               format;         /* Format identifier */
@@ -941,13 +1579,18 @@ struct ClipBox
 
 struct ClipRecord
 {
-  ClipRecord* copy (hb_serialize_context_t *c, const void *base) const
+  int cmp (hb_codepoint_t g) const
+  { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; }
+
+  bool subset (hb_subset_context_t *c,
+               const void *base,
+               const VarStoreInstancer &instancer) const
   {
-    TRACE_SERIALIZE (this);
-    auto *out = c->embed (this);
-    if (unlikely (!out)) return_trace (nullptr);
-    if (!out->clipBox.serialize_copy (c, clipBox, base)) return_trace (nullptr);
-    return_trace (out);
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    return_trace (out->clipBox.serialize_subset (c, clipBox, base, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -956,6 +1599,13 @@ struct ClipRecord
     return_trace (c->check_struct (this) && clipBox.sanitize (c, base));
   }
 
+  bool get_extents (hb_glyph_extents_t *extents,
+                    const void *base,
+                    const VarStoreInstancer &instancer) const
+  {
+    return (base+clipBox).get_extents (extents, instancer);
+  }
+
   public:
   HBUINT16              startGlyphID;  // first gid clip applies to
   HBUINT16              endGlyphID;    // last gid clip applies to, inclusive
@@ -963,10 +1613,12 @@ struct ClipRecord
   public:
   DEFINE_SIZE_STATIC (7);
 };
+DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);
 
 struct ClipList
 {
-  unsigned serialize_clip_records (hb_serialize_context_t *c,
+  unsigned serialize_clip_records (hb_subset_context_t *c,
+                                   const VarStoreInstancer &instancer,
                                    const hb_set_t& gids,
                                    const hb_map_t& gid_offset_map) const
   {
@@ -998,7 +1650,7 @@ struct ClipList
       record.endGlyphID = prev_gid;
       record.clipBox = prev_offset;
 
-      if (!c->copy (record, this)) return_trace (0);
+      if (!record.subset (c, this, instancer)) return_trace (0);
       count++;
 
       start_gid = _;
@@ -1012,20 +1664,21 @@ struct ClipList
       record.startGlyphID = start_gid;
       record.endGlyphID = prev_gid;
       record.clipBox = prev_offset;
-      if (!c->copy (record, this)) return_trace (0);
+      if (!record.subset (c, this, instancer)) return_trace (0);
       count++;
     }
     return_trace (count);
   }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (*this);
     if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
     if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
 
-    const hb_set_t& glyphset = *c->plan->_glyphset_colred;
+    const hb_set_t& glyphset = c->plan->_glyphset_colred;
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     hb_map_t new_gid_offset_map;
@@ -1043,7 +1696,7 @@ struct ClipList
       }
     }
 
-    unsigned count = serialize_clip_records (c->serializer, new_gids, new_gid_offset_map);
+    unsigned count = serialize_clip_records (c, instancer, new_gids, new_gid_offset_map);
     if (!count) return_trace (false);
     return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW));
   }
@@ -1051,11 +1704,26 @@ struct ClipList
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    // TODO Make a formatted struct!
     return_trace (c->check_struct (this) && clips.sanitize (c, this));
   }
 
+  bool
+  get_extents (hb_codepoint_t gid,
+               hb_glyph_extents_t *extents,
+               const VarStoreInstancer &instancer) const
+  {
+    auto *rec = clips.as_array ().bsearch (gid);
+    if (rec)
+    {
+      rec->get_extents (extents, this, instancer);
+      return true;
+    }
+    return false;
+  }
+
   HBUINT8                       format;  // Set to 1.
-  Array32Of         clips;  // Clip records, sorted by startGlyphID
+  SortedArray32Of   clips;  // Clip records, sorted by startGlyphID
   public:
   DEFINE_SIZE_ARRAY_SIZED (5, clips);
 };
@@ -1068,7 +1736,7 @@ struct Paint
   {
     TRACE_SANITIZE (this);
 
-    if (unlikely (!c->check_start_recursion (HB_COLRV1_MAX_NESTING_LEVEL)))
+    if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL)))
       return_trace (c->no_dispatch_return_value ());
 
     return_trace (c->end_recursion (this->dispatch (c, std::forward (ds)...)));
@@ -1077,8 +1745,8 @@ struct Paint
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...));
     case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...));
@@ -1120,38 +1788,40 @@ struct Paint
   union {
   HBUINT8                                       format;
   PaintColrLayers                               paintformat1;
-  PaintSolid                                    paintformat2;
+  NoVariable                        paintformat2;
   Variable                          paintformat3;
-  PaintLinearGradient               paintformat4;
+  NoVariable>   paintformat4;
   Variable>       paintformat5;
-  PaintRadialGradient               paintformat6;
+  NoVariable>   paintformat6;
   Variable>       paintformat7;
-  PaintSweepGradient                paintformat8;
+  NoVariable>    paintformat8;
   Variable>        paintformat9;
   PaintGlyph                                    paintformat10;
   PaintColrGlyph                                paintformat11;
   PaintTransform                    paintformat12;
   PaintTransform                      paintformat13;
-  PaintTranslate                                paintformat14;
+  NoVariable                    paintformat14;
   Variable                      paintformat15;
-  PaintScale                                    paintformat16;
+  NoVariable                        paintformat16;
   Variable                          paintformat17;
-  PaintScaleAroundCenter                        paintformat18;
+  NoVariable            paintformat18;
   Variable              paintformat19;
-  PaintScaleUniform                             paintformat20;
+  NoVariable                 paintformat20;
   Variable                   paintformat21;
-  PaintScaleUniformAroundCenter                 paintformat22;
+  NoVariable     paintformat22;
   Variable       paintformat23;
-  PaintRotate                                   paintformat24;
+  NoVariable                       paintformat24;
   Variable                         paintformat25;
-  PaintRotateAroundCenter                       paintformat26;
+  NoVariable           paintformat26;
   Variable             paintformat27;
-  PaintSkew                                     paintformat28;
+  NoVariable                         paintformat28;
   Variable                           paintformat29;
-  PaintSkewAroundCenter                         paintformat30;
+  NoVariable             paintformat30;
   Variable               paintformat31;
   PaintComposite                                paintformat32;
   } u;
+  public:
+  DEFINE_SIZE_MIN (2);
 };
 
 struct BaseGlyphPaintRecord
@@ -1160,7 +1830,8 @@ struct BaseGlyphPaintRecord
   { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
 
   bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
-                  const void* src_base, hb_subset_context_t *c) const
+                  const void* src_base, hb_subset_context_t *c,
+                  const VarStoreInstancer &instancer) const
   {
     TRACE_SERIALIZE (this);
     auto *out = s->embed (this);
@@ -1169,7 +1840,7 @@ struct BaseGlyphPaintRecord
                           HB_SERIALIZE_ERROR_INT_OVERFLOW))
       return_trace (false);
 
-    return_trace (out->paint.serialize_subset (c, paint, src_base));
+    return_trace (out->paint.serialize_subset (c, paint, src_base, instancer));
   }
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -1188,19 +1859,20 @@ struct BaseGlyphPaintRecord
 
 struct BaseGlyphList : SortedArray32Of
 {
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (this);
     if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
-    const hb_set_t* glyphset = c->plan->_glyphset_colred;
+    const hb_set_t* glyphset = &c->plan->_glyphset_colred;
 
     for (const auto& _ : as_array ())
     {
       unsigned gid = _.glyphId;
       if (!glyphset->has (gid)) continue;
 
-      if (_.serialize (c->serializer, c->plan->glyph_map, this, c)) out->len++;
+      if (_.serialize (c->serializer, c->plan->glyph_map, this, c, instancer)) out->len++;
       else return_trace (false);
     }
 
@@ -1219,7 +1891,8 @@ struct LayerList : Array32OfOffset32To
   const Paint& get_paint (unsigned i) const
   { return this+(*this)[i]; }
 
-  bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c,
+               const VarStoreInstancer &instancer) const
   {
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (this);
@@ -1230,7 +1903,7 @@ struct LayerList : Array32OfOffset32To
 
     {
       auto *o = out->serialize_append (c->serializer);
-      if (unlikely (!o) || !o->serialize_subset (c, _.second, this))
+      if (unlikely (!o) || !o->serialize_subset (c, _.second, this, instancer))
         return_trace (false);
     }
     return_trace (true);
@@ -1247,7 +1920,14 @@ struct COLR
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
 
-  bool has_data () const { return numBaseGlyphs; }
+  bool has_v0_data () const { return numBaseGlyphs; }
+  bool has_v1_data () const
+  {
+    if (version == 1)
+      return (this+baseGlyphList).len > 0;
+
+    return false;
+  }
 
   unsigned int get_glyph_layers (hb_codepoint_t       glyph,
                                  unsigned int         start_offset,
@@ -1356,7 +2036,7 @@ struct COLR
                   (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) &&
                   (this+layersZ).sanitize (c, numLayers) &&
                   (version == 0 ||
-                   (COLRV1_ENABLE_SUBSETTING && version == 1 &&
+                   (version == 1 &&
                     baseGlyphList.sanitize (c, this) &&
                     layerList.sanitize (c, this) &&
                     clipList.sanitize (c, this) &&
@@ -1425,9 +2105,8 @@ struct COLR
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-
     const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map;
-    const hb_set_t& glyphset = *c->plan->_glyphset_colred;
+    const hb_set_t& glyphset = c->plan->_glyphset_colred;
 
     auto base_it =
     + hb_range (c->plan->num_output_glyphs ())
@@ -1476,7 +2155,7 @@ struct COLR
                                   if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid)))
                                     return hb_pair_t> (false, out_layers);
                                   out_layers[i].glyphId = new_gid;
-                                  out_layers[i].colorIdx = c->plan->colr_palettes->get (layers[i].colorIdx);
+                                  out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx);
                                 }
 
                                 return hb_pair_t> (true, out_layers);
@@ -1496,7 +2175,12 @@ struct COLR
 
     auto snap = c->serializer->snapshot ();
     if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false);
-    if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this))
+
+    VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr,
+                                 varIdxMap ? &(this+varIdxMap) : nullptr,
+                                 c->plan->normalized_coords.as_array ());
+
+    if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
     {
       if (c->serializer->in_error ()) return_trace (false);
       //no more COLRv1 glyphs: downgrade to version 0
@@ -1506,13 +2190,181 @@ struct COLR
 
     if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
 
-    colr_prime->layerList.serialize_subset (c, layerList, this);
-    colr_prime->clipList.serialize_subset (c, clipList, this);
+    colr_prime->layerList.serialize_subset (c, layerList, this, instancer);
+    colr_prime->clipList.serialize_subset (c, clipList, this, instancer);
+    if (!varStore || c->plan->all_axes_pinned)
+      return_trace (true);
+
     colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this);
-    //TODO: subset varStore once it's implemented in fonttools
+    colr_prime->varStore.serialize_copy (c->serializer, varStore, this);
     return_trace (true);
   }
 
+  const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const
+  {
+    const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
+    const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph);
+    if (record)
+    {
+      const Paint &paint = &baseglyph_paintrecords+record->paint;
+      return &paint;
+    }
+    else
+      return nullptr;
+  }
+
+#ifndef HB_NO_PAINT
+  bool
+  get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+  {
+    if (version != 1)
+      return false;
+
+    VarStoreInstancer instancer (&(this+varStore),
+                                 &(this+varIdxMap),
+                                 hb_array (font->coords, font->num_coords));
+
+    if (get_clip (glyph, extents, instancer))
+    {
+      font->scale_glyph_extents (extents);
+      return true;
+    }
+
+    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));
+
+    hb_extents_t e = extents_data.get_extents ();
+    if (e.is_void ())
+    {
+      extents->x_bearing = 0;
+      extents->y_bearing = 0;
+      extents->width = 0;
+      extents->height = 0;
+    }
+    else
+    {
+      extents->x_bearing = e.xmin;
+      extents->y_bearing = e.ymax;
+      extents->width = e.xmax - e.xmin;
+      extents->height = e.ymin - e.ymax;
+    }
+
+    return ret;
+  }
+#endif
+
+  bool
+  has_paint_for_glyph (hb_codepoint_t glyph) const
+  {
+    if (version == 1)
+    {
+      const Paint *paint = get_base_glyph_paint (glyph);
+
+      return paint != nullptr;
+    }
+
+    return false;
+  }
+
+  bool get_clip (hb_codepoint_t glyph,
+                 hb_glyph_extents_t *extents,
+                 const VarStoreInstancer instancer) const
+  {
+    return (this+clipList).get_extents (glyph,
+                                        extents,
+                                        instancer);
+  }
+
+#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
+  {
+    VarStoreInstancer instancer (&(this+varStore),
+                                 &(this+varIdxMap),
+                                 hb_array (font->coords, font->num_coords));
+    hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
+
+    if (version == 1)
+    {
+      const Paint *paint = get_base_glyph_paint (glyph);
+      if (paint)
+      {
+        // COLRv1 glyph
+
+        VarStoreInstancer instancer (&(this+varStore),
+                                     &(this+varIdxMap),
+                                     hb_array (font->coords, font->num_coords));
+
+        bool is_bounded = true;
+        if (clip)
+        {
+          hb_glyph_extents_t extents;
+          if (get_clip (glyph, &extents, instancer))
+          {
+            font->scale_glyph_extents (&extents);
+            c.funcs->push_clip_rectangle (c.data,
+                                          extents.x_bearing,
+                                          extents.y_bearing + extents.height,
+                                          extents.x_bearing + extents.width,
+                                          extents.y_bearing);
+          }
+          else
+          {
+            auto *extents_funcs = hb_paint_extents_get_funcs ();
+            hb_paint_extents_context_t extents_data;
+
+            paint_glyph (font, glyph,
+                         extents_funcs, &extents_data,
+                         palette_index, foreground,
+                         false);
+
+            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);
+          }
+        }
+
+        c.funcs->push_root_transform (c.data, font);
+
+        if (is_bounded)
+          c.recurse (*paint);
+
+        c.funcs->pop_transform (c.data);
+
+        if (clip)
+          c.funcs->pop_clip (c.data);
+
+        return true;
+      }
+    }
+
+    const BaseGlyphRecord *record = get_base_glyph_record (glyph);
+    if (record && ((hb_codepoint_t) record->glyphId == glyph))
+    {
+      // COLRv0 glyph
+      for (const auto &r : (this+layersZ).as_array (numLayers)
+                           .sub_array (record->firstLayerIdx, record->numLayers))
+      {
+        hb_bool_t is_foreground;
+        hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground);
+        c.funcs->push_clip_glyph (c.data, r.glyphId, c.font);
+        c.funcs->color (c.data, is_foreground, color);
+        c.funcs->pop_clip (c.data);
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+#endif
+
   protected:
   HBUINT16      version;        /* Table version number (starts at 0). */
   HBUINT16      numBaseGlyphs;  /* Number of Base Glyph Records. */
@@ -1535,7 +2387,50 @@ struct COLR_accelerator_t : COLR::accelerator_t {
   COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {}
 };
 
-} /* namespace OT */
+void
+hb_paint_context_t::recurse (const Paint &paint)
+{
+  if (unlikely (depth_left <= 0 || edge_count <= 0)) return;
+  depth_left--;
+  edge_count--;
+  paint.dispatch (this);
+  depth_left++;
+}
+
+void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
+{
+  const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
+  for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
+  {
+    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);
+  }
+}
+
+void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
+{
+  const COLR *colr_table = c->get_colr_table ();
+  const Paint *paint = colr_table->get_base_glyph_paint (gid);
 
+  hb_glyph_extents_t extents = {0};
+  bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer);
+
+  if (has_clip_box)
+    c->funcs->push_clip_rectangle (c->data,
+                                   extents.x_bearing,
+                                   extents.y_bearing + extents.height,
+                                   extents.x_bearing + extents.width,
+                                   extents.y_bearing);
+
+  if (paint)
+    c->recurse (*paint);
+
+  if (has_clip_box)
+    c->funcs->pop_clip (c->data);
+}
+
+} /* namespace OT */
 
-#endif /* HB_OT_COLOR_COLR_TABLE_HH */
+#endif /* OT_COLOR_COLR_COLR_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colrv1-closure.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh
similarity index 94%
rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-colrv1-closure.hh
rename to src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh
index fbaf2ec26b6..705863d4ade 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colrv1-closure.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh
@@ -24,12 +24,11 @@
  *
  */
 
-#ifndef HB_OT_COLR_COLRV1_CLOSURE_HH
-#define HB_OT_COLR_COLRV1_CLOSURE_HH
+#ifndef OT_COLOR_COLR_COLRV1_CLOSURE_HH
+#define OT_COLOR_COLR_COLRV1_CLOSURE_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-layout-common.hh"
-#include "hb-ot-color-colr-table.hh"
+#include "../../../hb-open-type.hh"
+#include "COLR.hh"
 
 /*
  * COLR -- Color
@@ -105,4 +104,4 @@ HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) cons
 } /* namespace OT */
 
 
-#endif /* HB_OT_COLR_COLRV1_CLOSURE_HH */
+#endif /* OT_COLOR_COLR_COLRV1_CLOSURE_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh
similarity index 89%
rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh
rename to src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh
index 5558d518190..ed8f5957e96 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh
@@ -25,12 +25,12 @@
  * Google Author(s): Sascha Brawer
  */
 
-#ifndef HB_OT_COLOR_CPAL_TABLE_HH
-#define HB_OT_COLOR_CPAL_TABLE_HH
+#ifndef OT_COLOR_CPAL_CPAL_HH
+#define OT_COLOR_CPAL_CPAL_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-color.h"
-#include "hb-ot-name.h"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-ot-color.h"
+#include "../../../hb-ot-name.h"
 
 
 /*
@@ -73,6 +73,30 @@ struct CPALV1Tail
   }
 
   public:
+  void collect_name_ids (const void *base,
+                         unsigned palette_count,
+                         unsigned color_count,
+                         const hb_map_t *color_index_map,
+                         hb_set_t *nameids_to_retain /* OUT */) const
+  {
+    if (paletteLabelsZ)
+    {
+      + (base+paletteLabelsZ).as_array (palette_count)
+      | hb_sink (nameids_to_retain)
+      ;
+    }
+
+    if (colorLabelsZ)
+    {
+      const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count);
+      for (unsigned i = 0; i < color_count; i++)
+      {
+        if (!color_index_map->has (i)) continue;
+        nameids_to_retain->add (colorLabels[i]);
+      }
+    }
+  }
+
   bool serialize (hb_serialize_context_t *c,
                   unsigned palette_count,
                   unsigned color_count,
@@ -95,13 +119,10 @@ struct CPALV1Tail
     if (colorLabelsZ)
     {
       c->push ();
-      for (const auto _ : colorLabels)
+      for (unsigned i = 0; i < color_count; i++)
       {
-        const hb_codepoint_t *v;
-        if (!color_index_map->has (_, &v)) continue;
-        NameID new_color_idx;
-        new_color_idx = *v;
-        if (!c->copy (new_color_idx))
+        if (!color_index_map->has (i)) continue;
+        if (!c->copy (colorLabels[i]))
         {
           c->pop_discard ();
           return_trace (false);
@@ -189,6 +210,13 @@ struct CPAL
     return numColors;
   }
 
+  void collect_name_ids (const hb_map_t *color_index_map,
+                         hb_set_t *nameids_to_retain /* OUT */) const
+  {
+    if (version == 1)
+      v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain);
+  }
+
   private:
   const CPALV1Tail& v1 () const
   {
@@ -239,7 +267,7 @@ struct CPAL
     TRACE_SUBSET (this);
     if (!numPalettes) return_trace (false);
 
-    const hb_map_t *color_index_map = c->plan->colr_palettes;
+    const hb_map_t *color_index_map = &c->plan->colr_palettes;
     if (color_index_map->is_empty ()) return_trace (false);
 
     hb_set_t retained_color_indices;
@@ -319,4 +347,4 @@ struct CPAL
 } /* namespace OT */
 
 
-#endif /* HB_OT_COLOR_CPAL_TABLE_HH */
+#endif /* OT_COLOR_CPAL_CPAL_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh
similarity index 88%
rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh
rename to src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh
index a46b2264770..55d770e9283 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh
@@ -25,11 +25,11 @@
  * Google Author(s): Calder Kitagawa
  */
 
-#ifndef HB_OT_COLOR_SBIX_TABLE_HH
-#define HB_OT_COLOR_SBIX_TABLE_HH
+#ifndef OT_COLOR_SBIX_SBIX_HH
+#define OT_COLOR_SBIX_SBIX_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-layout-common.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-paint.hh"
 
 /*
  * sbix -- Standard Bitmap Graphics
@@ -213,10 +213,11 @@ struct sbix
 
     bool get_extents (hb_font_t          *font,
                       hb_codepoint_t      glyph,
-                      hb_glyph_extents_t *extents) const
+                      hb_glyph_extents_t *extents,
+                      bool                scale = true) const
     {
       /* We only support PNG right now, and following function checks type. */
-      return get_png_extents (font, glyph, extents);
+      return get_png_extents (font, glyph, extents, scale);
     }
 
     hb_blob_t *reference_png (hb_font_t      *font,
@@ -231,6 +232,37 @@ struct sbix
                                                   num_glyphs, available_ppem);
     }
 
+    bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+    {
+      if (!has_data ())
+        return false;
+
+      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))
+        return false;
+
+      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+        return false;
+
+      bool ret = funcs->image (data,
+                               blob,
+                               pixel_extents.width, -pixel_extents.height,
+                               HB_PAINT_IMAGE_FORMAT_PNG,
+                               font->slant_xy,
+                               &extents);
+
+      hb_blob_destroy (blob);
+      return ret;
+    }
+
     private:
 
     const SBIXStrike &choose_strike (hb_font_t *font) const
@@ -285,7 +317,8 @@ struct sbix
 
     bool get_png_extents (hb_font_t          *font,
                           hb_codepoint_t      glyph,
-                          hb_glyph_extents_t *extents) const
+                          hb_glyph_extents_t *extents,
+                          bool                scale = true) const
     {
       /* Following code is safe to call even without data.
        * But faster to short-circuit. */
@@ -310,22 +343,18 @@ struct sbix
       extents->height    = -1 * png.IHDR.height;
 
       /* Convert to font units. */
-      if (strike_ppem)
+      if (strike_ppem && scale)
       {
         float scale = font->face->get_upem () / (float) strike_ppem;
-        extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale);
-        extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale);
-        extents->width = font->em_scalef_x (extents->width * scale);
-        extents->height = font->em_scalef_y (extents->height * scale);
-      }
-      else
-      {
-        extents->x_bearing = font->em_scale_x (extents->x_bearing);
-        extents->y_bearing = font->em_scale_y (extents->y_bearing);
-        extents->width = font->em_scale_x (extents->width);
-        extents->height = font->em_scale_y (extents->height);
+        extents->x_bearing = roundf (extents->x_bearing * scale);
+        extents->y_bearing = roundf (extents->y_bearing * scale);
+        extents->width = roundf (extents->width * scale);
+        extents->height = roundf (extents->height * scale);
       }
 
+      if (scale)
+        font->scale_glyph_extents (extents);
+
       hb_blob_destroy (blob);
 
       return strike_ppem;
@@ -420,4 +449,4 @@ struct sbix_accelerator_t : sbix::accelerator_t {
 
 } /* namespace OT */
 
-#endif /* HB_OT_COLOR_SBIX_TABLE_HH */
+#endif /* OT_COLOR_SBIX_SBIX_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh
similarity index 83%
rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh
rename to src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh
index 8d4dee92878..c8ff6ab743e 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh
@@ -22,10 +22,12 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_OT_COLOR_SVG_TABLE_HH
-#define HB_OT_COLOR_SVG_TABLE_HH
+#ifndef OT_COLOR_SVG_SVG_HH
+#define OT_COLOR_SVG_SVG_HH
 
-#include "hb-open-type.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-blob.hh"
+#include "../../../hb-paint.hh"
 
 /*
  * SVG -- SVG (Scalable Vector Graphics)
@@ -91,8 +93,31 @@ struct SVG
 
     bool has_data () const { return table->has_data (); }
 
+    bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+    {
+      if (!has_data ())
+        return false;
+
+      hb_blob_t *blob = reference_blob_for_glyph (glyph);
+
+      if (blob == hb_blob_get_empty ())
+        return false;
+
+      funcs->image (data,
+                    blob,
+                    0, 0,
+                    HB_PAINT_IMAGE_FORMAT_SVG,
+                    font->slant_xy,
+                    nullptr);
+
+      hb_blob_destroy (blob);
+      return true;
+    }
+
     private:
     hb_blob_ptr_t table;
+    public:
+    DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t));
   };
 
   const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const
@@ -123,4 +148,4 @@ struct SVG_accelerator_t : SVG::accelerator_t {
 } /* namespace OT */
 
 
-#endif /* HB_OT_COLOR_SVG_TABLE_HH */
+#endif /* OT_COLOR_SVG_SVG_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh
new file mode 100644
index 00000000000..016a9402e3c
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh
@@ -0,0 +1,337 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,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, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_COMMON_COVERAGE_HH
+#define OT_LAYOUT_COMMON_COVERAGE_HH
+
+#include "../types.hh"
+#include "CoverageFormat1.hh"
+#include "CoverageFormat2.hh"
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+template
+static inline void Coverage_serialize (hb_serialize_context_t *c,
+                                       Iterator it);
+
+struct Coverage
+{
+
+  protected:
+  union {
+  HBUINT16                      format;         /* Format identifier */
+  CoverageFormat1_3 format1;
+  CoverageFormat2_4 format2;
+#ifndef HB_NO_BEYOND_64K
+  CoverageFormat1_3format3;
+  CoverageFormat2_4format4;
+#endif
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format)
+    {
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+#ifndef HB_NO_BEYOND_64K
+    case 3: return_trace (u.format3.sanitize (c));
+    case 4: return_trace (u.format4.sanitize (c));
+#endif
+    default:return_trace (true);
+    }
+  }
+
+  /* Has interface. */
+  unsigned operator [] (hb_codepoint_t k) const { return get (k); }
+  bool has (hb_codepoint_t k) const { return (*this)[k] != NOT_COVERED; }
+  /* Predicate. */
+  bool operator () (hb_codepoint_t k) const { return has (k); }
+
+  unsigned int get (hb_codepoint_t k) const { return get_coverage (k); }
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage (glyph_id);
+    case 2: return u.format2.get_coverage (glyph_id);
+#ifndef HB_NO_BEYOND_64K
+    case 3: return u.format3.get_coverage (glyph_id);
+    case 4: return u.format4.get_coverage (glyph_id);
+#endif
+    default:return NOT_COVERED;
+    }
+  }
+
+  unsigned get_population () const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_population ();
+    case 2: return u.format2.get_population ();
+#ifndef HB_NO_BEYOND_64K
+    case 3: return u.format3.get_population ();
+    case 4: return u.format4.get_population ();
+#endif
+    default:return NOT_COVERED;
+    }
+  }
+
+  template 
+  bool serialize (hb_serialize_context_t *c, Iterator glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+
+    unsigned count = 0;
+    unsigned num_ranges = 0;
+    hb_codepoint_t last = (hb_codepoint_t) -2;
+    for (auto g: glyphs)
+    {
+      if (last + 1 != g)
+        num_ranges++;
+      last = g;
+      count++;
+    }
+    u.format = count <= num_ranges * 3 ? 1 : 2;
+
+#ifndef HB_NO_BEYOND_64K
+    if (count && last > 0xFFFFu)
+      u.format += 2;
+#endif
+
+    switch (u.format)
+    {
+    case 1: return_trace (u.format1.serialize (c, glyphs));
+    case 2: return_trace (u.format2.serialize (c, glyphs));
+#ifndef HB_NO_BEYOND_64K
+    case 3: return_trace (u.format3.serialize (c, glyphs));
+    case 4: return_trace (u.format4.serialize (c, glyphs));
+#endif
+    default:return_trace (false);
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto it =
+    + iter ()
+    | hb_take (c->plan->source->get_num_glyphs ())
+    | hb_filter (c->plan->glyph_map_gsub)
+    | hb_map_retains_sorting (c->plan->glyph_map_gsub)
+    ;
+
+    // Cache the iterator result as it will be iterated multiple times
+    // by the serialize code below.
+    hb_sorted_vector_t glyphs (it);
+    Coverage_serialize (c->serializer, glyphs.iter ());
+    return_trace (bool (glyphs));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.intersects (glyphs);
+    case 2: return u.format2.intersects (glyphs);
+#ifndef HB_NO_BEYOND_64K
+    case 3: return u.format3.intersects (glyphs);
+    case 4: return u.format4.intersects (glyphs);
+#endif
+    default:return false;
+    }
+  }
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.intersects_coverage (glyphs, index);
+    case 2: return u.format2.intersects_coverage (glyphs, index);
+#ifndef HB_NO_BEYOND_64K
+    case 3: return u.format3.intersects_coverage (glyphs, index);
+    case 4: return u.format4.intersects_coverage (glyphs, index);
+#endif
+    default:return false;
+    }
+  }
+
+  /* Might return false if array looks unsorted.
+   * Used for faster rejection of corrupt data. */
+  template 
+  bool collect_coverage (set_t *glyphs) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.collect_coverage (glyphs);
+    case 2: return u.format2.collect_coverage (glyphs);
+#ifndef HB_NO_BEYOND_64K
+    case 3: return u.format3.collect_coverage (glyphs);
+    case 4: return u.format4.collect_coverage (glyphs);
+#endif
+    default:return false;
+    }
+  }
+
+  template 
+  void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.intersect_set (glyphs, intersect_glyphs);
+    case 2: return u.format2.intersect_set (glyphs, intersect_glyphs);
+#ifndef HB_NO_BEYOND_64K
+    case 3: return u.format3.intersect_set (glyphs, intersect_glyphs);
+    case 4: return u.format4.intersect_set (glyphs, intersect_glyphs);
+#endif
+    default:return ;
+    }
+  }
+
+  struct iter_t : hb_iter_with_fallback_t
+  {
+    static constexpr bool is_sorted_iterator = true;
+    iter_t (const Coverage &c_ = Null (Coverage))
+    {
+      hb_memset (this, 0, sizeof (*this));
+      format = c_.u.format;
+      switch (format)
+      {
+      case 1: u.format1.init (c_.u.format1); return;
+      case 2: u.format2.init (c_.u.format2); return;
+#ifndef HB_NO_BEYOND_64K
+      case 3: u.format3.init (c_.u.format3); return;
+      case 4: u.format4.init (c_.u.format4); return;
+#endif
+      default:                               return;
+      }
+    }
+    bool __more__ () const
+    {
+      switch (format)
+      {
+      case 1: return u.format1.__more__ ();
+      case 2: return u.format2.__more__ ();
+#ifndef HB_NO_BEYOND_64K
+      case 3: return u.format3.__more__ ();
+      case 4: return u.format4.__more__ ();
+#endif
+      default:return false;
+      }
+    }
+    void __next__ ()
+    {
+      switch (format)
+      {
+      case 1: u.format1.__next__ (); break;
+      case 2: u.format2.__next__ (); break;
+#ifndef HB_NO_BEYOND_64K
+      case 3: u.format3.__next__ (); break;
+      case 4: u.format4.__next__ (); break;
+#endif
+      default:                   break;
+      }
+    }
+    typedef hb_codepoint_t __item_t__;
+    __item_t__ __item__ () const { return get_glyph (); }
+
+    hb_codepoint_t get_glyph () const
+    {
+      switch (format)
+      {
+      case 1: return u.format1.get_glyph ();
+      case 2: return u.format2.get_glyph ();
+#ifndef HB_NO_BEYOND_64K
+      case 3: return u.format3.get_glyph ();
+      case 4: return u.format4.get_glyph ();
+#endif
+      default:return 0;
+      }
+    }
+    bool operator != (const iter_t& o) const
+    {
+      if (unlikely (format != o.format)) return true;
+      switch (format)
+      {
+      case 1: return u.format1 != o.u.format1;
+      case 2: return u.format2 != o.u.format2;
+#ifndef HB_NO_BEYOND_64K
+      case 3: return u.format3 != o.u.format3;
+      case 4: return u.format4 != o.u.format4;
+#endif
+      default:return false;
+      }
+    }
+    iter_t __end__ () const
+    {
+      iter_t it = {};
+      it.format = format;
+      switch (format)
+      {
+      case 1: it.u.format1 = u.format1.__end__ (); break;
+      case 2: it.u.format2 = u.format2.__end__ (); break;
+#ifndef HB_NO_BEYOND_64K
+      case 3: it.u.format3 = u.format3.__end__ (); break;
+      case 4: it.u.format4 = u.format4.__end__ (); break;
+#endif
+      default: break;
+      }
+      return it;
+    }
+
+    private:
+    unsigned int format;
+    union {
+#ifndef HB_NO_BEYOND_64K
+    CoverageFormat2_4::iter_t      format4; /* Put this one first since it's larger; helps shut up compiler. */
+    CoverageFormat1_3::iter_t      format3;
+#endif
+    CoverageFormat2_4::iter_t       format2; /* Put this one first since it's larger; helps shut up compiler. */
+    CoverageFormat1_3::iter_t       format1;
+    } u;
+  };
+  iter_t iter () const { return iter_t (*this); }
+};
+
+template
+static inline void
+Coverage_serialize (hb_serialize_context_t *c,
+                    Iterator it)
+{ c->start_embed ()->serialize (c, it); }
+
+}
+}
+}
+
+#endif  // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH
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
new file mode 100644
index 00000000000..1fa92df3620
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh
@@ -0,0 +1,133 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,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, Garret Rieger
+ */
+
+
+#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
+#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+#define NOT_COVERED             ((unsigned int) -1)
+
+template 
+struct CoverageFormat1_3
+{
+  friend struct Coverage;
+
+  protected:
+  HBUINT16      coverageFormat; /* Format identifier--format = 1 */
+  SortedArray16Of
+                glyphArray;     /* Array of GlyphIDs--in numerical order */
+  public:
+  DEFINE_SIZE_ARRAY (4, glyphArray);
+
+  private:
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (glyphArray.sanitize (c));
+  }
+
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    unsigned int i;
+    glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED);
+    return i;
+  }
+
+  unsigned get_population () const
+  {
+    return glyphArray.len;
+  }
+
+  template 
+  bool serialize (hb_serialize_context_t *c, Iterator glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (glyphArray.serialize (c, glyphs));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2)
+    {
+      for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+        if (get_coverage (g) != NOT_COVERED)
+          return true;
+      return false;
+    }
+
+    for (const auto& g : glyphArray.as_array ())
+      if (glyphs->has (g))
+        return true;
+    return false;
+  }
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  { return glyphs->has (glyphArray[index]); }
+
+  template 
+  void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const
+  {
+    unsigned count = glyphArray.len;
+    for (unsigned i = 0; i < count; i++)
+      if (glyphs.has (glyphArray[i]))
+        intersect_glyphs << glyphArray[i];
+  }
+
+  template 
+  bool collect_coverage (set_t *glyphs) const
+  { return glyphs->add_sorted_array (glyphArray.as_array ()); }
+
+  public:
+  /* Older compilers need this to be public. */
+  struct iter_t
+  {
+    void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; }
+    bool __more__ () const { return i < c->glyphArray.len; }
+    void __next__ () { i++; }
+    hb_codepoint_t get_glyph () const { return c->glyphArray[i]; }
+    bool operator != (const iter_t& o) const
+    { return i != o.i; }
+    iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; }
+
+    private:
+    const struct CoverageFormat1_3 *c;
+    unsigned int i;
+  };
+  private:
+};
+
+}
+}
+}
+
+#endif  // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
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
new file mode 100644
index 00000000000..2650d198f4c
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh
@@ -0,0 +1,232 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,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, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
+#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
+
+#include "RangeRecord.hh"
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+template 
+struct CoverageFormat2_4
+{
+  friend struct Coverage;
+
+  protected:
+  HBUINT16      coverageFormat; /* Format identifier--format = 2 */
+  SortedArray16Of>
+                rangeRecord;    /* Array of glyph ranges--ordered by
+                                 * Start GlyphID. rangeCount entries
+                                 * long */
+  public:
+  DEFINE_SIZE_ARRAY (4, rangeRecord);
+
+  private:
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (rangeRecord.sanitize (c));
+  }
+
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    const RangeRecord &range = rangeRecord.bsearch (glyph_id);
+    return likely (range.first <= range.last)
+         ? (unsigned int) range.value + (glyph_id - range.first)
+         : NOT_COVERED;
+  }
+
+  unsigned get_population () const
+  {
+    typename Types::large_int ret = 0;
+    for (const auto &r : rangeRecord)
+      ret += r.get_population ();
+    return ret > UINT_MAX ? UINT_MAX : (unsigned) ret;
+  }
+
+  template 
+  bool serialize (hb_serialize_context_t *c, Iterator glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+
+    unsigned num_ranges = 0;
+    hb_codepoint_t last = (hb_codepoint_t) -2;
+    for (auto g: glyphs)
+    {
+      if (last + 1 != g)
+        num_ranges++;
+      last = g;
+    }
+
+    if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false);
+    if (!num_ranges) return_trace (true);
+
+    unsigned count = 0;
+    unsigned range = (unsigned) -1;
+    last = (hb_codepoint_t) -2;
+    for (auto g: glyphs)
+    {
+      if (last + 1 != g)
+      {
+        range++;
+        rangeRecord[range].first = g;
+        rangeRecord[range].value = count;
+      }
+      rangeRecord[range].last = g;
+      last = g;
+      count++;
+    }
+
+    return_trace (true);
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
+    {
+      for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+        if (get_coverage (g) != NOT_COVERED)
+          return true;
+      return false;
+    }
+
+    return hb_any (+ hb_iter (rangeRecord)
+                   | hb_map ([glyphs] (const RangeRecord &range) { return range.intersects (*glyphs); }));
+  }
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  {
+    auto *range = rangeRecord.as_array ().bsearch (index);
+    if (range)
+      return range->intersects (*glyphs);
+    return false;
+  }
+
+  template 
+  void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const
+  {
+    /* Break out of loop for overlapping, broken, tables,
+     * to avoid fuzzer timouts. */
+    hb_codepoint_t last = 0;
+    for (const auto& range : rangeRecord)
+    {
+      if (unlikely (range.first < last))
+        break;
+      last = range.last;
+      for (hb_codepoint_t g = range.first - 1;
+           glyphs.next (&g) && g <= last;)
+        intersect_glyphs << g;
+    }
+  }
+
+  template 
+  bool collect_coverage (set_t *glyphs) const
+  {
+    for (const auto& range: rangeRecord)
+      if (unlikely (!range.collect_coverage (glyphs)))
+        return false;
+    return true;
+  }
+
+  public:
+  /* Older compilers need this to be public. */
+  struct iter_t
+  {
+    void init (const CoverageFormat2_4 &c_)
+    {
+      c = &c_;
+      coverage = 0;
+      i = 0;
+      j = c->rangeRecord.len ? c->rangeRecord[0].first : 0;
+      if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last))
+      {
+        /* Broken table. Skip. */
+        i = c->rangeRecord.len;
+        j = 0;
+      }
+    }
+    bool __more__ () const { return i < c->rangeRecord.len; }
+    void __next__ ()
+    {
+      if (j >= c->rangeRecord[i].last)
+      {
+        i++;
+        if (__more__ ())
+        {
+          unsigned int old = coverage;
+          j = c->rangeRecord[i].first;
+          coverage = c->rangeRecord[i].value;
+          if (unlikely (coverage != old + 1))
+          {
+            /* Broken table. Skip. Important to avoid DoS.
+             * Also, our callers depend on coverage being
+             * consecutive and monotonically increasing,
+             * ie. iota(). */
+           i = c->rangeRecord.len;
+           j = 0;
+           return;
+          }
+        }
+        else
+          j = 0;
+        return;
+      }
+      coverage++;
+      j++;
+    }
+    hb_codepoint_t get_glyph () const { return j; }
+    bool operator != (const iter_t& o) const
+    { return i != o.i || j != o.j; }
+    iter_t __end__ () const
+    {
+      iter_t it;
+      it.init (*c);
+      it.i = c->rangeRecord.len;
+      it.j = 0;
+      return it;
+    }
+
+    private:
+    const struct CoverageFormat2_4 *c;
+    unsigned int i, coverage;
+    hb_codepoint_t j;
+  };
+  private:
+};
+
+}
+}
+}
+
+#endif  // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/RangeRecord.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/RangeRecord.hh
new file mode 100644
index 00000000000..a62629fad34
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/RangeRecord.hh
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,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, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
+#define OT_LAYOUT_COMMON_RANGERECORD_HH
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+template 
+struct RangeRecord
+{
+  typename Types::HBGlyphID     first;          /* First GlyphID in the range */
+  typename Types::HBGlyphID     last;           /* Last GlyphID in the range */
+  HBUINT16                      value;          /* Value */
+
+  DEFINE_SIZE_STATIC (2 + 2 * Types::size);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  int cmp (hb_codepoint_t g) const
+  { return g < first ? -1 : g <= last ? 0 : +1; }
+
+  unsigned get_population () const
+  {
+    if (unlikely (last < first)) return 0;
+    return (last - first + 1);
+  }
+
+  bool intersects (const hb_set_t &glyphs) const
+  { return glyphs.intersects (first, last); }
+
+  template 
+  bool collect_coverage (set_t *glyphs) const
+  { return glyphs->add_range (first, last); }
+};
+
+}
+}
+}
+
+// TODO(garretrieger): This was previously implemented using
+//    DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9);
+//    but that only works when there is only a single namespace level.
+//    The macro should probably be fixed so it can work in this situation.
+extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9];
+template 
+struct Null> {
+  static OT::Layout::Common::RangeRecord const & get_null () {
+    return *reinterpret_cast *> (_hb_Null_OT_RangeRecord);
+  }
+};
+
+
+#endif  // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
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
new file mode 100644
index 00000000000..0cf5753b0e5
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh
@@ -0,0 +1,918 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,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
+ */
+
+#ifndef OT_LAYOUT_GDEF_GDEF_HH
+#define OT_LAYOUT_GDEF_GDEF_HH
+
+#include "../../../hb-ot-layout-common.hh"
+
+#include "../../../hb-font.hh"
+
+
+namespace OT {
+
+
+/*
+ * Attachment List Table
+ */
+
+/* Array of contour point indices--in increasing numerical order */
+struct AttachPoint : Array16Of
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    return_trace (out->serialize (c->serializer, + iter ()));
+  }
+};
+
+struct AttachList
+{
+  unsigned int get_attach_points (hb_codepoint_t glyph_id,
+                                  unsigned int start_offset,
+                                  unsigned int *point_count /* IN/OUT */,
+                                  unsigned int *point_array /* OUT */) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    if (index == NOT_COVERED)
+    {
+      if (point_count)
+        *point_count = 0;
+      return 0;
+    }
+
+    const AttachPoint &points = this+attachPoint[index];
+
+    if (point_count)
+    {
+      + points.as_array ().sub_array (start_offset, point_count)
+      | hb_sink (hb_array (point_array, *point_count))
+      ;
+    }
+
+    return points.len;
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    hb_sorted_vector_t new_coverage;
+    + hb_zip (this+coverage, attachPoint)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (subset_offset_array (c, out->attachPoint, this), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+    return_trace (bool (new_coverage));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
+  }
+
+  protected:
+  Offset16To
+                coverage;               /* Offset to Coverage table -- from
+                                         * beginning of AttachList table */
+  Array16OfOffset16To
+                attachPoint;            /* Array of AttachPoint tables
+                                         * in Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (4, attachPoint);
+};
+
+/*
+ * Ligature Caret Table
+ */
+
+struct CaretValueFormat1
+{
+  friend struct CaretValue;
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (false);
+    return_trace (true);
+  }
+
+  private:
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const
+  {
+    return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  HBUINT16      caretValueFormat;       /* Format identifier--format = 1 */
+  FWORD         coordinate;             /* X or Y value, in design units */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct CaretValueFormat2
+{
+  friend struct CaretValue;
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (false);
+    return_trace (true);
+  }
+
+  private:
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const
+  {
+    hb_position_t x, y;
+    font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y);
+    return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  HBUINT16      caretValueFormat;       /* Format identifier--format = 2 */
+  HBUINT16      caretValuePoint;        /* Contour point index on glyph */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct CaretValueFormat3
+{
+  friend struct CaretValue;
+
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction,
+                                 const VariationStore &var_store) const
+  {
+    return HB_DIRECTION_IS_HORIZONTAL (direction) ?
+           font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) :
+           font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    if (!c->serializer->embed (caretValueFormat)) return_trace (false);
+    if (!c->serializer->embed (coordinate)) return_trace (false);
+
+    unsigned varidx = (this+deviceTable).get_variation_index ();
+    if (c->plan->layout_variation_idx_delta_map.has (varidx))
+    {
+      int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (varidx));
+      if (delta != 0)
+      {
+        if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+          return_trace (false);
+      }
+    }
+
+    if (c->plan->all_axes_pinned)
+      return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
+
+    if (!c->serializer->embed (deviceTable))
+      return_trace (false);
+
+    return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out),
+                                                   hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map));
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  { (this+deviceTable).collect_variation_indices (c); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16      caretValueFormat;       /* Format identifier--format = 3 */
+  FWORD         coordinate;             /* X or Y value, in design units */
+  Offset16To
+                deviceTable;            /* Offset to Device table for X or Y
+                                         * value--from beginning of CaretValue
+                                         * table */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct CaretValue
+{
+  hb_position_t get_caret_value (hb_font_t *font,
+                                 hb_direction_t direction,
+                                 hb_codepoint_t glyph_id,
+                                 const VariationStore &var_store) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_caret_value (font, direction);
+    case 2: return u.format2.get_caret_value (font, direction, glyph_id);
+    case 3: return u.format3.get_caret_value (font, direction, var_store);
+    default:return 0;
+    }
+  }
+
+  template 
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
+    TRACE_DISPATCH (this, u.format);
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+    case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+    case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    switch (u.format) {
+    case 1:
+    case 2:
+      return;
+    case 3:
+      u.format3.collect_variation_indices (c);
+      return;
+    default: return;
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 3: return_trace (u.format3.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  CaretValueFormat1     format1;
+  CaretValueFormat2     format2;
+  CaretValueFormat3     format3;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+struct LigGlyph
+{
+  unsigned get_lig_carets (hb_font_t            *font,
+                           hb_direction_t        direction,
+                           hb_codepoint_t        glyph_id,
+                           const VariationStore &var_store,
+                           unsigned              start_offset,
+                           unsigned             *caret_count /* IN/OUT */,
+                           hb_position_t        *caret_array /* OUT */) const
+  {
+    if (caret_count)
+    {
+      + carets.as_array ().sub_array (start_offset, caret_count)
+      | hb_map (hb_add (this))
+      | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); })
+      | hb_sink (hb_array (caret_array, *caret_count))
+      ;
+    }
+
+    return carets.len;
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    + hb_iter (carets)
+    | hb_apply (subset_offset_array (c, out->carets, this))
+    ;
+
+    return_trace (bool (out->carets));
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    for (const Offset16To& offset : carets.iter ())
+      (this+offset).collect_variation_indices (c);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (carets.sanitize (c, this));
+  }
+
+  protected:
+  Array16OfOffset16To
+                carets;                 /* Offset array of CaretValue tables
+                                         * --from beginning of LigGlyph table
+                                         * --in increasing coordinate order */
+  public:
+  DEFINE_SIZE_ARRAY (2, carets);
+};
+
+struct LigCaretList
+{
+  unsigned int get_lig_carets (hb_font_t *font,
+                               hb_direction_t direction,
+                               hb_codepoint_t glyph_id,
+                               const VariationStore &var_store,
+                               unsigned int start_offset,
+                               unsigned int *caret_count /* IN/OUT */,
+                               hb_position_t *caret_array /* OUT */) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    if (index == NOT_COVERED)
+    {
+      if (caret_count)
+        *caret_count = 0;
+      return 0;
+    }
+    const LigGlyph &lig_glyph = this+ligGlyph[index];
+    return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    hb_sorted_vector_t new_coverage;
+    + hb_zip (this+coverage, ligGlyph)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (subset_offset_array (c, out->ligGlyph, this), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+    return_trace (bool (new_coverage));
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    + hb_zip (this+coverage, ligGlyph)
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    | hb_map (hb_add (this))
+    | hb_apply ([c] (const LigGlyph& _) { _.collect_variation_indices (c); })
+    ;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
+  }
+
+  protected:
+  Offset16To
+                coverage;               /* Offset to Coverage table--from
+                                         * beginning of LigCaretList table */
+  Array16OfOffset16To
+                ligGlyph;               /* Array of LigGlyph tables
+                                         * in Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (4, ligGlyph);
+};
+
+
+struct MarkGlyphSetsFormat1
+{
+  bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    bool ret = true;
+    for (const Offset32To& offset : coverage.iter ())
+    {
+      auto *o = out->coverage.serialize_append (c->serializer);
+      if (unlikely (!o))
+      {
+        ret = false;
+        break;
+      }
+
+      //not using o->serialize_subset (c, offset, this, out) here because
+      //OTS doesn't allow null offset.
+      //See issue: https://github.com/khaledhosny/ots/issues/172
+      c->serializer->push ();
+      c->dispatch (this+offset);
+      c->serializer->add_link (*o, c->serializer->pop_pack ());
+    }
+
+    return_trace (ret && out->coverage.len);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (coverage.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16      format;                 /* Format identifier--format = 1 */
+  Array16Of>
+                coverage;               /* Array of long offsets to mark set
+                                         * coverage tables */
+  public:
+  DEFINE_SIZE_ARRAY (4, coverage);
+};
+
+struct MarkGlyphSets
+{
+  bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.covers (set_index, glyph_id);
+    default:return false;
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    switch (u.format) {
+    case 1: return_trace (u.format1.subset (c));
+    default:return_trace (false);
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 1: return_trace (u.format1.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  HBUINT16              format;         /* Format identifier */
+  MarkGlyphSetsFormat1  format1;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+
+/*
+ * GDEF -- Glyph Definition
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gdef
+ */
+
+
+template 
+struct GDEFVersion1_2
+{
+  friend struct GDEF;
+
+  protected:
+  FixedVersion<>version;                /* Version of the GDEF table--currently
+                                         * 0x00010003u */
+  typename Types::template OffsetTo
+                glyphClassDef;          /* Offset to class definition table
+                                         * for glyph type--from beginning of
+                                         * GDEF header (may be Null) */
+  typename Types::template OffsetTo
+                attachList;             /* Offset to list of glyphs with
+                                         * attachment points--from beginning
+                                         * of GDEF header (may be Null) */
+  typename Types::template OffsetTo
+                ligCaretList;           /* Offset to list of positioning points
+                                         * for ligature carets--from beginning
+                                         * of GDEF header (may be Null) */
+  typename Types::template OffsetTo
+                markAttachClassDef;     /* Offset to class definition table for
+                                         * mark attachment type--from beginning
+                                         * of GDEF header (may be Null) */
+  typename Types::template OffsetTo
+                markGlyphSetsDef;       /* Offset to the table of mark set
+                                         * definitions--from beginning of GDEF
+                                         * header (may be NULL).  Introduced
+                                         * in version 0x00010002. */
+  Offset32To
+                varStore;               /* Offset to the table of Item Variation
+                                         * Store--from beginning of GDEF
+                                         * header (may be NULL).  Introduced
+                                         * in version 0x00010003. */
+  public:
+  DEFINE_SIZE_MIN (4 + 4 * Types::size);
+
+  unsigned int get_size () const
+  {
+    return min_size +
+           (version.to_int () >= 0x00010002u ? markGlyphSetsDef.static_size : 0) +
+           (version.to_int () >= 0x00010003u ? varStore.static_size : 0);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+                  glyphClassDef.sanitize (c, this) &&
+                  attachList.sanitize (c, this) &&
+                  ligCaretList.sanitize (c, this) &&
+                  markAttachClassDef.sanitize (c, this) &&
+                  (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
+                  (version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true);
+    bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
+    bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
+    bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
+
+    bool subset_markglyphsetsdef = false;
+    if (version.to_int () >= 0x00010002u)
+    {
+      subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
+    }
+
+    bool subset_varstore = false;
+    if (version.to_int () >= 0x00010003u)
+    {
+      if (c->plan->all_axes_pinned)
+        out->varStore = 0;
+      else
+        subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ());
+    }
+
+    if (subset_varstore)
+    {
+      out->version.minor = 3;
+    } else if (subset_markglyphsetsdef) {
+      out->version.minor = 2;
+    } else  {
+      out->version.minor = 0;
+    }
+
+    return_trace (subset_glyphclassdef || subset_attachlist ||
+                  subset_ligcaretlist || subset_markattachclassdef ||
+                  (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) ||
+                  (out->version.to_int () >= 0x00010003u && subset_varstore));
+  }
+};
+
+struct GDEF
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_GDEF;
+
+  enum GlyphClasses {
+    UnclassifiedGlyph   = 0,
+    BaseGlyph           = 1,
+    LigatureGlyph       = 2,
+    MarkGlyph           = 3,
+    ComponentGlyph      = 4
+  };
+
+  unsigned int get_size () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.get_size ();
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.get_size ();
+#endif
+    default: return u.version.static_size;
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!u.version.sanitize (c))) return_trace (false);
+    switch (u.version.major) {
+    case 1: return_trace (u.version1.sanitize (c));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (u.version2.sanitize (c));
+#endif
+    default: return_trace (true);
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.subset (c);
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.subset (c);
+#endif
+    default: return false;
+    }
+  }
+
+  bool has_glyph_classes () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.glyphClassDef != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.glyphClassDef != 0;
+#endif
+    default: return false;
+    }
+  }
+  const ClassDef &get_glyph_class_def () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.glyphClassDef;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.glyphClassDef;
+#endif
+    default: return Null(ClassDef);
+    }
+  }
+  bool has_attach_list () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.attachList != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.attachList != 0;
+#endif
+    default: return false;
+    }
+  }
+  const AttachList &get_attach_list () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.attachList;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.attachList;
+#endif
+    default: return Null(AttachList);
+    }
+  }
+  bool has_lig_carets () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.ligCaretList != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.ligCaretList != 0;
+#endif
+    default: return false;
+    }
+  }
+  const LigCaretList &get_lig_caret_list () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.ligCaretList;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.ligCaretList;
+#endif
+    default: return Null(LigCaretList);
+    }
+  }
+  bool has_mark_attachment_types () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.markAttachClassDef != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.markAttachClassDef != 0;
+#endif
+    default: return false;
+    }
+  }
+  const ClassDef &get_mark_attach_class_def () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.markAttachClassDef;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.markAttachClassDef;
+#endif
+    default: return Null(ClassDef);
+    }
+  }
+  bool has_mark_glyph_sets () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010002u && u.version1.markGlyphSetsDef != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.markGlyphSetsDef != 0;
+#endif
+    default: return false;
+    }
+  }
+  const MarkGlyphSets &get_mark_glyph_sets () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010002u ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets);
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.markGlyphSetsDef;
+#endif
+    default: return Null(MarkGlyphSets);
+    }
+  }
+  bool has_var_store () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010003u && u.version1.varStore != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.varStore != 0;
+#endif
+    default: return false;
+    }
+  }
+  const VariationStore &get_var_store () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore);
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.varStore;
+#endif
+    default: return Null(VariationStore);
+    }
+  }
+
+
+  bool has_data () const { return u.version.to_int (); }
+  unsigned int get_glyph_class (hb_codepoint_t glyph) const
+  { return get_glyph_class_def ().get_class (glyph); }
+  void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const
+  { get_glyph_class_def ().collect_class (glyphs, klass); }
+
+  unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const
+  { return get_mark_attach_class_def ().get_class (glyph); }
+
+  unsigned int get_attach_points (hb_codepoint_t glyph_id,
+                                  unsigned int start_offset,
+                                  unsigned int *point_count /* IN/OUT */,
+                                  unsigned int *point_array /* OUT */) const
+  { return get_attach_list ().get_attach_points (glyph_id, start_offset, point_count, point_array); }
+
+  unsigned int get_lig_carets (hb_font_t *font,
+                               hb_direction_t direction,
+                               hb_codepoint_t glyph_id,
+                               unsigned int start_offset,
+                               unsigned int *caret_count /* IN/OUT */,
+                               hb_position_t *caret_array /* OUT */) const
+  { return get_lig_caret_list ().get_lig_carets (font,
+                                                 direction, glyph_id, get_var_store(),
+                                                 start_offset, caret_count, caret_array); }
+
+  bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  { return get_mark_glyph_sets ().covers (set_index, glyph_id); }
+
+  /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
+   * glyph class and other bits, and high 8-bit the mark attachment type (if any).
+   * Not to be confused with lookup_props which is very similar. */
+  unsigned int get_glyph_props (hb_codepoint_t glyph) const
+  {
+    unsigned int klass = get_glyph_class (glyph);
+
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs), "");
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures), "");
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks), "");
+
+    switch (klass) {
+    default:                    return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
+    case BaseGlyph:             return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH;
+    case LigatureGlyph:         return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
+    case MarkGlyph:
+          klass = get_mark_attachment_type (glyph);
+          return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8);
+    }
+  }
+
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+
+  struct accelerator_t
+  {
+    accelerator_t (hb_face_t *face)
+    {
+      table = hb_sanitize_context_t ().reference_table (face);
+      if (unlikely (table->is_blocklisted (table.get_blob (), face)))
+      {
+        hb_blob_destroy (table.get_blob ());
+        table = hb_blob_get_empty ();
+      }
+    }
+    ~accelerator_t () { table.destroy (); }
+
+    hb_blob_ptr_t table;
+  };
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  { get_lig_caret_list ().collect_variation_indices (c); }
+
+  void remap_layout_variation_indices (const hb_set_t *layout_variation_indices,
+                                       hb_hashmap_t> *layout_variation_idx_delta_map /* OUT */) const
+  {
+    if (!has_var_store ()) return;
+    if (layout_variation_indices->is_empty ()) return;
+
+    unsigned new_major = 0, new_minor = 0;
+    unsigned last_major = (layout_variation_indices->get_min ()) >> 16;
+    for (unsigned idx : layout_variation_indices->iter ())
+    {
+      uint16_t major = idx >> 16;
+      if (major >= get_var_store ().get_sub_table_count ()) break;
+      if (major != last_major)
+      {
+        new_minor = 0;
+        ++new_major;
+      }
+
+      unsigned new_idx = (new_major << 16) + new_minor;
+      if (!layout_variation_idx_delta_map->has (idx))
+        continue;
+      int delta = hb_second (layout_variation_idx_delta_map->get (idx));
+
+      layout_variation_idx_delta_map->set (idx, hb_pair_t (new_idx, delta));
+      ++new_minor;
+      last_major = major;
+    }
+  }
+
+  protected:
+  union {
+  FixedVersion<>                version;        /* Version identifier */
+  GDEFVersion1_2    version1;
+#ifndef HB_NO_BEYOND_64K
+  GDEFVersion1_2   version2;
+#endif
+  } u;
+  public:
+  DEFINE_SIZE_MIN (4);
+};
+
+struct GDEF_accelerator_t : GDEF::accelerator_t {
+  GDEF_accelerator_t (hb_face_t *face) : GDEF::accelerator_t (face) {}
+};
+
+} /* namespace OT */
+
+
+#endif /* OT_LAYOUT_GDEF_GDEF_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh
index bfe6b36afd3..49e76e77509 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh
@@ -58,8 +58,7 @@ struct Anchor
         return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer))));
       }
       return_trace (bool (reinterpret_cast (u.format2.copy (c->serializer))));
-    case 3: return_trace (bool (reinterpret_cast (u.format3.copy (c->serializer,
-                                                                            c->plan->layout_variation_idx_map))));
+    case 3: return_trace (u.format3.subset (c));
     default:return_trace (false);
     }
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh
index d77b4699be9..e7e3c5c6d1e 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh
@@ -41,24 +41,54 @@ struct AnchorFormat3
       *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
   }
 
-  AnchorFormat3* copy (hb_serialize_context_t *c,
-                       const hb_map_t *layout_variation_idx_map) const
+  bool subset (hb_subset_context_t *c) const
   {
-    TRACE_SERIALIZE (this);
-    if (!layout_variation_idx_map) return_trace (nullptr);
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    if (unlikely (!c->serializer->embed (format))) return_trace (false);
+    if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false);
+    if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false);
 
-    auto *out = c->embed (this);
-    if (unlikely (!out)) return_trace (nullptr);
+    unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
+    if (c->plan->layout_variation_idx_delta_map.has (x_varidx))
+    {
+      int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (x_varidx));
+      if (delta != 0)
+      {
+        if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta,
+                                          HB_SERIALIZE_ERROR_INT_OVERFLOW))
+          return_trace (false);
+      }
+    }
 
-    out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
-    out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
+    unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
+    if (c->plan->layout_variation_idx_delta_map.has (y_varidx))
+    {
+      int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (y_varidx));
+      if (delta != 0)
+      {
+        if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta,
+                                          HB_SERIALIZE_ERROR_INT_OVERFLOW))
+          return_trace (false);
+      }
+    }
+
+    if (c->plan->all_axes_pinned)
+      return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
+
+    if (!c->serializer->embed (xDeviceTable)) return_trace (false);
+    if (!c->serializer->embed (yDeviceTable)) return_trace (false);
+
+    out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map);
+    out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map);
     return_trace (out);
   }
 
   void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
   {
-    (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices);
-    (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices);
+    (this+xDeviceTable).collect_variation_indices (c);
+    (this+yDeviceTable).collect_variation_indices (c);
   }
 };
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh
index e16c06729d2..408197454f1 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh
@@ -22,7 +22,8 @@ template
 static void SinglePos_serialize (hb_serialize_context_t *c,
                                  const SrcLookup *src,
                                  Iterator it,
-                                 const hb_map_t *layout_variation_idx_map);
+                                 const hb_hashmap_t> *layout_variation_idx_delta_map,
+                                 bool all_axes_pinned);
 
 
 }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh
index c105cfb0916..0105a9b8542 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh
@@ -19,8 +19,8 @@ struct CursivePos
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
     default:return_trace (c->default_return_value ());
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh
index e212fab976b..13a435d00bf 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh
@@ -140,7 +140,14 @@ struct CursivePosFormat1
     unsigned int i = skippy_iter.idx;
     unsigned int j = buffer->idx;
 
-    buffer->unsafe_to_break (i, j);
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "cursive attaching glyph at %u to glyph at %u",
+                          i, j);
+    }
+
+    buffer->unsafe_to_break (i, j + 1);
     float entry_x, entry_y, exit_x, exit_y;
     (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
     (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
@@ -223,7 +230,20 @@ struct CursivePosFormat1
      * https://github.com/harfbuzz/harfbuzz/issues/2469
      */
     if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain()))
+    {
       pos[parent].attach_chain() = 0;
+      if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
+        pos[parent].y_offset = 0;
+      else
+        pos[parent].x_offset = 0;
+    }
+
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "cursive attached glyph at %u to glyph at %u",
+                          i, j);
+    }
 
     buffer->idx++;
     return_trace (true);
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh
similarity index 84%
rename from src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS.hh
rename to src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh
index 224d6b746bd..9493ec987e8 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh
@@ -1,12 +1,15 @@
-#ifndef OT_LAYOUT_GPOS_HH
-#define OT_LAYOUT_GPOS_HH
+#ifndef OT_LAYOUT_GPOS_GPOS_HH
+#define OT_LAYOUT_GPOS_GPOS_HH
 
-#include "../../hb-ot-layout-common.hh"
-#include "../../hb-ot-layout-gsubgpos.hh"
-#include "GPOS/Common.hh"
-#include "GPOS/PosLookup.hh"
+#include "../../../hb-ot-layout-common.hh"
+#include "../../../hb-ot-layout-gsubgpos.hh"
+#include "Common.hh"
+#include "PosLookup.hh"
 
 namespace OT {
+
+using Layout::GPOS_impl::PosLookup;
+
 namespace Layout {
 
 static void
@@ -25,10 +28,10 @@ struct GPOS : GSUBGPOS
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS;
 
-  using Lookup = GPOS_impl::PosLookup;
+  using Lookup = PosLookup;
 
-  const GPOS_impl::PosLookup& get_lookup (unsigned int i) const
-  { return static_cast (GSUBGPOS::get_lookup (i)); }
+  const PosLookup& get_lookup (unsigned int i) const
+  { return static_cast (GSUBGPOS::get_lookup (i)); }
 
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
@@ -36,12 +39,15 @@ struct GPOS : GSUBGPOS
 
   bool subset (hb_subset_context_t *c) const
   {
-    hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features);
-    return GSUBGPOS::subset (&l);
+    hb_subset_layout_context_t l (c, tableTag);
+    return GSUBGPOS::subset (&l);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
-  { return GSUBGPOS::sanitize (c); }
+  {
+    TRACE_SANITIZE (this);
+    return_trace (GSUBGPOS::sanitize (c));
+  }
 
   HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
                                    hb_face_t *face) const;
@@ -51,7 +57,7 @@ struct GPOS : GSUBGPOS
     for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++)
     {
       if (!c->gpos_lookups->has (i)) continue;
-      const GPOS_impl::PosLookup &l = get_lookup (i);
+      const PosLookup &l = get_lookup (i);
       l.dispatch (c);
     }
   }
@@ -59,7 +65,7 @@ struct GPOS : GSUBGPOS
   void closure_lookups (hb_face_t      *face,
                         const hb_set_t *glyphs,
                         hb_set_t       *lookup_indexes /* IN/OUT */) const
-  { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); }
+  { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); }
 
   typedef GSUBGPOS::accelerator_t accelerator_t;
 };
@@ -162,4 +168,4 @@ struct GPOS_accelerator_t : Layout::GPOS::accelerator_t {
 
 }
 
-#endif  /* OT_LAYOUT_GPOS_HH */
+#endif  /* OT_LAYOUT_GPOS_GPOS_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh
new file mode 100644
index 00000000000..a2d807cc320
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh
@@ -0,0 +1,56 @@
+#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH
+#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+
+typedef AnchorMatrix LigatureAttach;    /* component-major--
+                                         * in order of writing direction--,
+                                         * mark-minor--
+                                         * ordered by class--zero-based. */
+
+/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
+struct LigatureArray : List16OfOffset16To
+{
+  template 
+  bool subset (hb_subset_context_t *c,
+               Iterator             coverage,
+               unsigned             class_count,
+               const hb_map_t      *klass_mapping) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+
+    auto *out = c->serializer->start_embed (this);
+    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
+
+    for (const auto _ : + hb_zip (coverage, *this)
+                  | hb_filter (glyphset, hb_first))
+    {
+      auto *matrix = out->serialize_append (c->serializer);
+      if (unlikely (!matrix)) return_trace (false);
+
+      const LigatureAttach& src = (this + _.second);
+      auto indexes =
+          + hb_range (src.rows * class_count)
+          | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
+          ;
+      matrix->serialize_subset (c,
+                                _.second,
+                                this,
+                                src.rows,
+                                indexes);
+    }
+    return_trace (this->len);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh
index f8cddd19918..e80c05cd270 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh
@@ -39,6 +39,13 @@ struct MarkArray : Array16Of        /* Array of MarkRecords--in Cove
     mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "attaching mark glyph at %u to glyph at %u",
+                          c->buffer->idx, glyph_pos);
+    }
+
     hb_glyph_position_t &o = buffer->cur_pos();
     o.x_offset = roundf (base_x - mark_x);
     o.y_offset = roundf (base_y - mark_y);
@@ -46,6 +53,13 @@ struct MarkArray : Array16Of        /* Array of MarkRecords--in Cove
     o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "attached mark glyph at %u to glyph at %u",
+                          c->buffer->idx, glyph_pos);
+    }
+
     buffer->idx++;
     return_trace (true);
   }
@@ -83,10 +97,11 @@ struct MarkArray : Array16Of        /* Array of MarkRecords--in Cove
   }
 };
 
-static void Markclass_closure_and_remap_indexes (const Coverage  &mark_coverage,
-                                                 const MarkArray &mark_array,
-                                                 const hb_set_t  &glyphset,
-                                                 hb_map_t*        klass_mapping /* INOUT */)
+HB_INTERNAL inline
+void Markclass_closure_and_remap_indexes (const Coverage  &mark_coverage,
+                                          const MarkArray &mark_array,
+                                          const hb_set_t  &glyphset,
+                                          hb_map_t*        klass_mapping /* INOUT */)
 {
   hb_set_t orig_classes;
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh
index e99e13ff84f..b1d1118a86b 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh
@@ -11,18 +11,24 @@ struct MarkBasePos
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  MarkBasePosFormat1    format1;
+  HBUINT16                              format;         /* Format identifier */
+  MarkBasePosFormat1_2      format1;
+#ifndef HB_NO_BEYOND_64K
+  MarkBasePosFormat1_2     format2;
+#endif
   } u;
 
   public:
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh
index a10b806fe5f..bf129a4a0e9 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh
@@ -12,26 +12,27 @@ typedef AnchorMatrix BaseArray;         /* base-major--
                                          * mark-minor--
                                          * ordered by class--zero-based. */
 
-struct MarkBasePosFormat1
+template 
+struct MarkBasePosFormat1_2
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 markCoverage;           /* Offset to MarkCoverage table--from
                                          * beginning of MarkBasePos subtable */
-  Offset16To
+  typename Types::template OffsetTo
                 baseCoverage;           /* Offset to BaseCoverage table--from
                                          * beginning of MarkBasePos subtable */
   HBUINT16      classCount;             /* Number of classes defined for marks */
-  Offset16To
+  typename Types::template OffsetTo
                 markArray;              /* Offset to MarkArray table--from
                                          * beginning of MarkBasePos subtable */
-  Offset16To
+  typename Types::template OffsetTo
                 baseArray;              /* Offset to BaseArray table--from
                                          * beginning of MarkBasePos subtable */
 
   public:
-  DEFINE_SIZE_STATIC (12);
+  DEFINE_SIZE_STATIC (4 + 4 * Types::size);
 
     bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -89,6 +90,25 @@ struct MarkBasePosFormat1
 
   const Coverage &get_coverage () const { return this+markCoverage; }
 
+  static inline bool accept (hb_buffer_t *buffer, unsigned idx)
+  {
+    /* We only want to attach to the first of a MultipleSubst sequence.
+     * https://github.com/harfbuzz/harfbuzz/issues/740
+     * Reject others...
+     * ...but stop if we find a mark in the MultipleSubst sequence:
+     * https://github.com/harfbuzz/harfbuzz/issues/1020 */
+    return !_hb_glyph_info_multiplied (&buffer->info[idx]) ||
+           0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) ||
+           (idx == 0 ||
+            _hb_glyph_info_is_mark (&buffer->info[idx - 1]) ||
+            !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) ||
+            _hb_glyph_info_get_lig_id (&buffer->info[idx]) !=
+            _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) ||
+            _hb_glyph_info_get_lig_comp (&buffer->info[idx]) !=
+            _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1
+            );
+  }
+
   bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
@@ -96,47 +116,54 @@ struct MarkBasePosFormat1
     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
-    /* Now we search backwards for a non-mark glyph */
+    /* Now we search backwards for a non-mark glyph.
+     * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */
+
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
-    do {
-      unsigned unsafe_from;
-      if (!skippy_iter.prev (&unsafe_from))
+
+    if (c->last_base_until > buffer->idx)
+    {
+      c->last_base_until = 0;
+      c->last_base = -1;
+    }
+    unsigned j;
+    for (j = buffer->idx; j > c->last_base_until; j--)
+    {
+      auto match = skippy_iter.match (buffer->info[j - 1]);
+      if (match == skippy_iter.MATCH)
       {
-        buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
-        return_trace (false);
+        // https://github.com/harfbuzz/harfbuzz/issues/4124
+        if (!accept (buffer, j - 1) &&
+            NOT_COVERED == (this+baseCoverage).get_coverage  (buffer->info[j - 1].codepoint))
+          match = skippy_iter.SKIP;
       }
-
-      /* We only want to attach to the first of a MultipleSubst sequence.
-       * https://github.com/harfbuzz/harfbuzz/issues/740
-       * Reject others...
-       * ...but stop if we find a mark in the MultipleSubst sequence:
-       * https://github.com/harfbuzz/harfbuzz/issues/1020 */
-      if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
-          0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
-          (skippy_iter.idx == 0 ||
-           _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
-           _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
-           _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
-           _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
-           _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
-           ))
+      if (match == skippy_iter.MATCH)
+      {
+        c->last_base = (signed) j - 1;
         break;
-      skippy_iter.reject ();
-    } while (true);
+      }
+    }
+    c->last_base_until = buffer->idx;
+    if (c->last_base == -1)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
+      return_trace (false);
+    }
+
+    unsigned idx = (unsigned) c->last_base;
 
     /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
-    //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+    //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); }
 
-    unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
+    unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[idx].codepoint);
     if (base_index == NOT_COVERED)
     {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
       return_trace (false);
     }
 
-    return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
+    return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx));
   }
 
   bool subset (hb_subset_context_t *c) const
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh
index 7e74aa73e0a..b10102880c0 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh
@@ -11,18 +11,24 @@ struct MarkLigPos
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  MarkLigPosFormat1     format1;
+  HBUINT16                              format;         /* Format identifier */
+  MarkLigPosFormat1_2       format1;
+#ifndef HB_NO_BEYOND_64K
+  MarkLigPosFormat1_2      format2;
+#endif
   } u;
 
   public:
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh
index 4382aa6c6cb..30bba4b0d8c 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh
@@ -1,72 +1,34 @@
 #ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
 #define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
 
+#include "LigatureArray.hh"
+
 namespace OT {
 namespace Layout {
 namespace GPOS_impl {
 
-typedef AnchorMatrix LigatureAttach;    /* component-major--
-                                         * in order of writing direction--,
-                                         * mark-minor--
-                                         * ordered by class--zero-based. */
 
-/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
-struct LigatureArray : List16OfOffset16To
-{
-  template 
-  bool subset (hb_subset_context_t *c,
-               Iterator             coverage,
-               unsigned             class_count,
-               const hb_map_t      *klass_mapping) const
-  {
-    TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-
-    auto *out = c->serializer->start_embed (this);
-    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
-
-    for (const auto _ : + hb_zip (coverage, *this)
-                  | hb_filter (glyphset, hb_first))
-    {
-      auto *matrix = out->serialize_append (c->serializer);
-      if (unlikely (!matrix)) return_trace (false);
-
-      const LigatureAttach& src = (this + _.second);
-      auto indexes =
-          + hb_range (src.rows * class_count)
-          | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
-          ;
-      matrix->serialize_subset (c,
-                                _.second,
-                                this,
-                                src.rows,
-                                indexes);
-    }
-    return_trace (this->len);
-  }
-};
-
-struct MarkLigPosFormat1
+template 
+struct MarkLigPosFormat1_2
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 markCoverage;           /* Offset to Mark Coverage table--from
                                          * beginning of MarkLigPos subtable */
-  Offset16To
+  typename Types::template OffsetTo
                 ligatureCoverage;       /* Offset to Ligature Coverage
                                          * table--from beginning of MarkLigPos
                                          * subtable */
   HBUINT16      classCount;             /* Number of defined mark classes */
-  Offset16To
+  typename Types::template OffsetTo
                 markArray;              /* Offset to MarkArray table--from
                                          * beginning of MarkLigPos subtable */
-  Offset16To
+  typename Types::template OffsetTo
                 ligatureArray;          /* Offset to LigatureArray table--from
                                          * beginning of MarkLigPos subtable */
   public:
-  DEFINE_SIZE_STATIC (12);
+  DEFINE_SIZE_STATIC (4 + 4 * Types::size);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -138,24 +100,41 @@ struct MarkLigPosFormat1
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
     /* Now we search backwards for a non-mark glyph */
+
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
-    unsigned unsafe_from;
-    if (!skippy_iter.prev (&unsafe_from))
+
+    if (c->last_base_until > buffer->idx)
+    {
+      c->last_base_until = 0;
+      c->last_base = -1;
+    }
+    unsigned j;
+    for (j = buffer->idx; j > c->last_base_until; j--)
     {
-      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+      auto match = skippy_iter.match (buffer->info[j - 1]);
+      if (match == skippy_iter.MATCH)
+      {
+        c->last_base = (signed) j - 1;
+        break;
+      }
+    }
+    c->last_base_until = buffer->idx;
+    if (c->last_base == -1)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
       return_trace (false);
     }
 
+    unsigned idx = (unsigned) c->last_base;
+
     /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
-    //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+    //if (!_hb_glyph_info_is_ligature (&buffer->info[idx])) { return_trace (false); }
 
-    unsigned int j = skippy_iter.idx;
-    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[j].codepoint);
+    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[idx].codepoint);
     if (lig_index == NOT_COVERED)
     {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
       return_trace (false);
     }
 
@@ -166,7 +145,7 @@ struct MarkLigPosFormat1
     unsigned int comp_count = lig_attach.rows;
     if (unlikely (!comp_count))
     {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
       return_trace (false);
     }
 
@@ -175,7 +154,7 @@ struct MarkLigPosFormat1
      * can directly use the component index.  If not, we attach the mark
      * glyph to the last component of the ligature. */
     unsigned int comp_index;
-    unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]);
+    unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[idx]);
     unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
     unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
     if (lig_id && lig_id == mark_id && mark_comp > 0)
@@ -183,7 +162,7 @@ struct MarkLigPosFormat1
     else
       comp_index = comp_count - 1;
 
-    return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
+    return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, idx));
   }
 
   bool subset (hb_subset_context_t *c) const
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh
index c0eee6d54cf..e0d9eca0280 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh
@@ -11,18 +11,24 @@ struct MarkMarkPos
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  MarkMarkPosFormat1    format1;
+  HBUINT16                              format;         /* Format identifier */
+  MarkMarkPosFormat1_2      format1;
+#ifndef HB_NO_BEYOND_64K
+  MarkMarkPosFormat1_2     format2;
+#endif
   } u;
 
   public:
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh
index c48a74f7738..fbcebb80441 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh
@@ -12,27 +12,28 @@ typedef AnchorMatrix Mark2Array;        /* mark2-major--
                                          * mark1-minor--
                                          * ordered by class--zero-based. */
 
-struct MarkMarkPosFormat1
+template 
+struct MarkMarkPosFormat1_2
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 mark1Coverage;          /* Offset to Combining Mark1 Coverage
                                          * table--from beginning of MarkMarkPos
                                          * subtable */
-  Offset16To
+  typename Types::template OffsetTo
                 mark2Coverage;          /* Offset to Combining Mark2 Coverage
                                          * table--from beginning of MarkMarkPos
                                          * subtable */
   HBUINT16      classCount;             /* Number of defined mark classes */
-  Offset16To
+  typename Types::template OffsetTo
                 mark1Array;             /* Offset to Mark1Array table--from
                                          * beginning of MarkMarkPos subtable */
-  Offset16To
+  typename Types::template OffsetTo
                 mark2Array;             /* Offset to Mark2Array table--from
                                          * beginning of MarkMarkPos subtable */
   public:
-  DEFINE_SIZE_STATIC (12);
+  DEFINE_SIZE_STATIC (4 + 4 * Types::size);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -100,7 +101,7 @@ struct MarkMarkPosFormat1
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
-    skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
+    skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags);
     unsigned unsafe_from;
     if (!skippy_iter.prev (&unsafe_from))
     {
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh
index 7a514453aee..a7d489d2a51 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh
@@ -9,7 +9,7 @@ struct MarkRecord
 {
   friend struct MarkArray;
 
-  protected:
+  public:
   HBUINT16      klass;                  /* Class defined for this mark */
   Offset16To
                 markAnchor;             /* Offset to Anchor table--from
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh
index 8479178d384..e3794ea9ed5 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh
@@ -12,20 +12,28 @@ struct PairPos
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  PairPosFormat1        format1;
-  PairPosFormat2        format2;
+  HBUINT16                      format;         /* Format identifier */
+  PairPosFormat1_3  format1;
+  PairPosFormat2_4  format2;
+#ifndef HB_NO_BEYOND_64K
+  PairPosFormat1_3 format3;
+  PairPosFormat2_4 format4;
+#endif
   } u;
 
   public:
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...));
+    case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
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 35a2db2d45e..468eccfd501 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
@@ -1,248 +1,22 @@
 #ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
 #define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
 
+#include "PairSet.hh"
+
 namespace OT {
 namespace Layout {
 namespace GPOS_impl {
 
-struct PairValueRecord
-{
-  friend struct PairSet;
-
-  int cmp (hb_codepoint_t k) const
-  { return secondGlyph.cmp (k); }
-
-  struct context_t
-  {
-    const void          *base;
-    const ValueFormat   *valueFormats;
-    const ValueFormat   *newFormats;
-    unsigned            len1; /* valueFormats[0].get_len() */
-    const hb_map_t      *glyph_map;
-    const hb_map_t      *layout_variation_idx_map;
-  };
-
-  bool subset (hb_subset_context_t *c,
-               context_t *closure) const
-  {
-    TRACE_SERIALIZE (this);
-    auto *s = c->serializer;
-    auto *out = s->start_embed (*this);
-    if (unlikely (!s->extend_min (out))) return_trace (false);
-
-    out->secondGlyph = (*closure->glyph_map)[secondGlyph];
-
-    closure->valueFormats[0].copy_values (s,
-                                          closure->newFormats[0],
-                                          closure->base, &values[0],
-                                          closure->layout_variation_idx_map);
-    closure->valueFormats[1].copy_values (s,
-                                          closure->newFormats[1],
-                                          closure->base,
-                                          &values[closure->len1],
-                                          closure->layout_variation_idx_map);
-
-    return_trace (true);
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-                                  const ValueFormat *valueFormats,
-                                  const void *base) const
-  {
-    unsigned record1_len = valueFormats[0].get_len ();
-    unsigned record2_len = valueFormats[1].get_len ();
-    const hb_array_t values_array = values.as_array (record1_len + record2_len);
-
-    if (valueFormats[0].has_device ())
-      valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
-
-    if (valueFormats[1].has_device ())
-      valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
-  }
-
-  bool intersects (const hb_set_t& glyphset) const
-  {
-    return glyphset.has(secondGlyph);
-  }
-
-  const Value* get_values_1 () const
-  {
-    return &values[0];
-  }
-
-  const Value* get_values_2 (ValueFormat format1) const
-  {
-    return &values[format1.get_len ()];
-  }
-
-  protected:
-  HBGlyphID16   secondGlyph;            /* GlyphID of second glyph in the
-                                         * pair--first glyph is listed in the
-                                         * Coverage table */
-  ValueRecord   values;                 /* Positioning data for the first glyph
-                                         * followed by for second glyph */
-  public:
-  DEFINE_SIZE_ARRAY (2, values);
-};
 
-struct PairSet
+template 
+struct PairPosFormat1_3
 {
-  friend struct PairPosFormat1;
-
-  bool intersects (const hb_set_t *glyphs,
-                   const ValueFormat *valueFormats) const
-  {
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    unsigned int count = len;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (glyphs->has (record->secondGlyph))
-        return true;
-      record = &StructAtOffset (record, record_size);
-    }
-    return false;
-  }
-
-  void collect_glyphs (hb_collect_glyphs_context_t *c,
-                       const ValueFormat *valueFormats) const
-  {
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    c->input->add_array (&record->secondGlyph, len, record_size);
-  }
-
-  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
-                                  const ValueFormat *valueFormats) const
-  {
-    unsigned len1 = valueFormats[0].get_len ();
-    unsigned len2 = valueFormats[1].get_len ();
-    unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    unsigned count = len;
-    for (unsigned i = 0; i < count; i++)
-    {
-      if (c->glyph_set->has (record->secondGlyph))
-      { record->collect_variation_indices (c, valueFormats, this); }
-
-      record = &StructAtOffset (record, record_size);
-    }
-  }
-
-  bool apply (hb_ot_apply_context_t *c,
-              const ValueFormat *valueFormats,
-              unsigned int pos) const
-  {
-    TRACE_APPLY (this);
-    hb_buffer_t *buffer = c->buffer;
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
-
-    const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
-                                                &firstPairValueRecord,
-                                                len,
-                                                record_size);
-    if (record)
-    {
-      bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
-      bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
-      if (applied_first || applied_second)
-        buffer->unsafe_to_break (buffer->idx, pos + 1);
-      if (len2)
-        pos++;
-      buffer->idx = pos;
-      return_trace (true);
-    }
-    buffer->unsafe_to_concat (buffer->idx, pos + 1);
-    return_trace (false);
-  }
-
-  bool subset (hb_subset_context_t *c,
-               const ValueFormat valueFormats[2],
-               const ValueFormat newFormats[2]) const
-  {
-    TRACE_SUBSET (this);
-    auto snap = c->serializer->snapshot ();
-
-    auto *out = c->serializer->start_embed (*this);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-    out->len = 0;
-
-    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
-    const hb_map_t &glyph_map = *c->plan->glyph_map;
-
-    unsigned len1 = valueFormats[0].get_len ();
-    unsigned len2 = valueFormats[1].get_len ();
-    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
-
-    PairValueRecord::context_t context =
-    {
-      this,
-      valueFormats,
-      newFormats,
-      len1,
-      &glyph_map,
-      c->plan->layout_variation_idx_map
-    };
-
-    const PairValueRecord *record = &firstPairValueRecord;
-    unsigned count = len, num = 0;
-    for (unsigned i = 0; i < count; i++)
-    {
-      if (glyphset.has (record->secondGlyph)
-         && record->subset (c, &context)) num++;
-      record = &StructAtOffset (record, record_size);
-    }
-
-    out->len = num;
-    if (!num) c->serializer->revert (snap);
-    return_trace (num);
-  }
-
-  struct sanitize_closure_t
-  {
-    const ValueFormat *valueFormats;
-    unsigned int len1; /* valueFormats[0].get_len() */
-    unsigned int stride; /* 1 + len1 + len2 */
-  };
-
-  bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
-  {
-    TRACE_SANITIZE (this);
-    if (!(c->check_struct (this)
-       && c->check_range (&firstPairValueRecord,
-                          len,
-                          HBUINT16::static_size,
-                          closure->stride))) return_trace (false);
-
-    unsigned int count = len;
-    const PairValueRecord *record = &firstPairValueRecord;
-    return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
-                  closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
-  }
+  using PairSet = GPOS_impl::PairSet;
+  using PairValueRecord = GPOS_impl::PairValueRecord;
 
-  protected:
-  HBUINT16              len;    /* Number of PairValueRecords */
-  PairValueRecord       firstPairValueRecord;
-                                /* Array of PairValueRecords--ordered
-                                 * by GlyphID of the second glyph */
-  public:
-  DEFINE_SIZE_MIN (2);
-};
-
-struct PairPosFormat1
-{
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 coverage;               /* Offset to Coverage table--from
                                          * beginning of subtable */
   ValueFormat   valueFormat[2];         /* [0] Defines the types of data in
@@ -251,11 +25,11 @@ struct PairPosFormat1
                                         /* [1] Defines the types of data in
                                          * ValueRecord2--for the second glyph
                                          * in the pair--may be zero (0) */
-  Array16OfOffset16To
+  Array16Of>
                 pairSet;                /* Array of PairSet tables
                                          * ordered by Coverage Index */
   public:
-  DEFINE_SIZE_ARRAY (10, pairSet);
+  DEFINE_SIZE_ARRAY (8 + Types::size, pairSet);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -265,24 +39,36 @@ struct PairPosFormat1
 
     unsigned int len1 = valueFormat[0].get_len ();
     unsigned int len2 = valueFormat[1].get_len ();
-    PairSet::sanitize_closure_t closure =
+    typename PairSet::sanitize_closure_t closure =
     {
       valueFormat,
       len1,
-      1 + len1 + len2
+      PairSet::get_size (len1, len2)
     };
 
     return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
   }
 
-
   bool intersects (const hb_set_t *glyphs) const
   {
+    auto &cov = this+coverage;
+
+    if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4)
+    {
+      for (hb_codepoint_t g : glyphs->iter())
+      {
+        unsigned i = cov.get_coverage (g);
+        if ((this+pairSet[i]).intersects (glyphs, valueFormat))
+          return true;
+      }
+      return false;
+    }
+
     return
-    + hb_zip (this+coverage, pairSet)
+    + hb_zip (cov, pairSet)
     | hb_filter (*glyphs, hb_first)
     | hb_map (hb_second)
-    | hb_map ([glyphs, this] (const Offset16To &_)
+    | hb_map ([glyphs, this] (const typename Types::template OffsetTo &_)
               { return (this+_).intersects (glyphs, valueFormat); })
     | hb_any
     ;
@@ -354,11 +140,17 @@ struct PairPosFormat1
       out->valueFormat[1] = newFormats.second;
     }
 
+    if (c->plan->all_axes_pinned)
+    {
+      out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags ();
+      out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags ();
+    }
+
     hb_sorted_vector_t new_coverage;
 
     + hb_zip (this+coverage, pairSet)
     | hb_filter (glyphset, hb_first)
-    | hb_filter ([this, c, out] (const Offset16To& _)
+    | hb_filter ([this, c, out] (const typename Types::template OffsetTo& _)
                  {
                    auto snap = c->serializer->snapshot ();
                    auto *o = out->pairSet.serialize_append (c->serializer);
@@ -385,19 +177,21 @@ struct PairPosFormat1
 
   hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset) const
   {
-    unsigned len1 = valueFormat[0].get_len ();
-    unsigned len2 = valueFormat[1].get_len ();
-    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
+    unsigned record_size = PairSet::get_size (valueFormat);
 
     unsigned format1 = 0;
     unsigned format2 = 0;
-    for (const Offset16To& _ :
-             + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second))
+    for (const auto & _ :
+          + hb_zip (this+coverage, pairSet)
+          | hb_filter (glyphset, hb_first)
+          | hb_map (hb_second)
+        )
     {
       const PairSet& set = (this + _);
       const PairValueRecord *record = &set.firstPairValueRecord;
 
-      for (unsigned i = 0; i < set.len; i++)
+      unsigned count = set.len;
+      for (unsigned i = 0; i < count; i++)
       {
         if (record->intersects (glyphset))
         {
@@ -406,6 +200,9 @@ struct PairPosFormat1
         }
         record = &StructAtOffset (record, record_size);
       }
+
+      if (format1 == valueFormat[0] && format2 == valueFormat[1])
+        break;
     }
 
     return hb_pair (format1, format2);
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh
index 3f5f9959c46..17486dddaf7 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh
@@ -7,11 +7,12 @@ namespace OT {
 namespace Layout {
 namespace GPOS_impl {
 
-struct PairPosFormat2
+template 
+struct PairPosFormat2_4
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 2 */
-  Offset16To
+  typename Types::template OffsetTo
                 coverage;               /* Offset to Coverage table--from
                                          * beginning of subtable */
   ValueFormat   valueFormat1;           /* ValueRecord definition--for the
@@ -20,11 +21,11 @@ struct PairPosFormat2
   ValueFormat   valueFormat2;           /* ValueRecord definition--for the
                                          * second glyph of the pair--may be
                                          * zero (0) */
-  Offset16To
+  typename Types::template OffsetTo
                 classDef1;              /* Offset to ClassDef table--from
                                          * beginning of PairPos subtable--for
                                          * the first glyph of the pair */
-  Offset16To
+  typename Types::template OffsetTo
                 classDef2;              /* Offset to ClassDef table--from
                                          * beginning of PairPos subtable--for
                                          * the second glyph of the pair */
@@ -36,7 +37,7 @@ struct PairPosFormat2
                                          * class1-major, class2-minor,
                                          * Each entry has value1 and value2 */
   public:
-  DEFINE_SIZE_ARRAY (16, values);
+  DEFINE_SIZE_ARRAY (10 + 3 * Types::size, values);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -48,7 +49,7 @@ struct PairPosFormat2
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
-    unsigned int stride = len1 + len2;
+    unsigned int stride = HBUINT16::static_size * (len1 + len2);
     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
     return_trace (c->check_range ((const void *) values,
@@ -216,10 +217,31 @@ struct PairPosFormat2
     }
     bail:
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "try kerning glyphs at %u,%u",
+                          c->buffer->idx, skippy_iter.idx);
+    }
 
     applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos());
     applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
 
+    if (applied_first || applied_second)
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->message (c->font,
+                            "kerned glyphs at %u,%u",
+                            c->buffer->idx, skippy_iter.idx);
+      }
+
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "tried kerning glyphs at %u,%u",
+                          c->buffer->idx, skippy_iter.idx);
+    }
+
     success:
     if (applied_first || applied_second)
       buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
@@ -227,10 +249,15 @@ struct PairPosFormat2
     boring:
       buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
 
+    if (len2)
+    {
+      skippy_iter.idx++;
+      // https://github.com/harfbuzz/harfbuzz/issues/3824
+      // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116
+      buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
+    }
 
     buffer->idx = skippy_iter.idx;
-    if (len2)
-      buffer->idx++;
 
     return_trace (true);
   }
@@ -260,13 +287,19 @@ struct PairPosFormat2
     out->valueFormat1 = newFormats.first;
     out->valueFormat2 = newFormats.second;
 
+    if (c->plan->all_axes_pinned)
+    {
+      out->valueFormat1 = out->valueFormat1.drop_device_table_flags ();
+      out->valueFormat2 = out->valueFormat2.drop_device_table_flags ();
+    }
+
     for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
     {
       for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
       {
         unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
-        valueFormat1.copy_values (c->serializer, newFormats.first, this, &values[idx], c->plan->layout_variation_idx_map);
-        valueFormat2.copy_values (c->serializer, newFormats.second, this, &values[idx + len1], c->plan->layout_variation_idx_map);
+        valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map);
+        valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map);
       }
     }
 
@@ -289,6 +322,7 @@ struct PairPosFormat2
   {
     unsigned len1 = valueFormat1.get_len ();
     unsigned len2 = valueFormat2.get_len ();
+    unsigned record_size = len1 + len2;
 
     unsigned format1 = 0;
     unsigned format2 = 0;
@@ -297,10 +331,13 @@ struct PairPosFormat2
     {
       for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
       {
-        unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
+        unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size;
         format1 = format1 | valueFormat1.get_effective_format (&values[idx]);
         format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]);
       }
+
+      if (format1 == valueFormat1 && format2 == valueFormat2)
+        break;
     }
 
     return hb_pair (format1, format2);
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh
new file mode 100644
index 00000000000..adeb08e910b
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh
@@ -0,0 +1,207 @@
+#ifndef OT_LAYOUT_GPOS_PAIRSET_HH
+#define OT_LAYOUT_GPOS_PAIRSET_HH
+
+#include "PairValueRecord.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+
+template 
+struct PairSet
+{
+  template 
+  friend struct PairPosFormat1_3;
+
+  using PairValueRecord = GPOS_impl::PairValueRecord;
+
+  protected:
+  HBUINT16              len;    /* Number of PairValueRecords */
+  PairValueRecord       firstPairValueRecord;
+                                /* Array of PairValueRecords--ordered
+                                 * by GlyphID of the second glyph */
+  public:
+  DEFINE_SIZE_MIN (2);
+
+  static unsigned get_size (unsigned len1, unsigned len2)
+  {
+    return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2);
+  }
+  static unsigned get_size (const ValueFormat valueFormats[2])
+  {
+    unsigned len1 = valueFormats[0].get_len ();
+    unsigned len2 = valueFormats[1].get_len ();
+    return get_size (len1, len2);
+  }
+
+  struct sanitize_closure_t
+  {
+    const ValueFormat *valueFormats;
+    unsigned int len1; /* valueFormats[0].get_len() */
+    unsigned int stride; /* bytes */
+  };
+
+  bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
+  {
+    TRACE_SANITIZE (this);
+    if (!(c->check_struct (this)
+       && c->check_range (&firstPairValueRecord,
+                          len,
+                          closure->stride))) return_trace (false);
+
+    unsigned int count = len;
+    const PairValueRecord *record = &firstPairValueRecord;
+    return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
+                  closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
+  }
+
+  bool intersects (const hb_set_t *glyphs,
+                   const ValueFormat *valueFormats) const
+  {
+    unsigned record_size = get_size (valueFormats);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (glyphs->has (record->secondGlyph))
+        return true;
+      record = &StructAtOffset (record, record_size);
+    }
+    return false;
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c,
+                       const ValueFormat *valueFormats) const
+  {
+    unsigned record_size = get_size (valueFormats);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    c->input->add_array (&record->secondGlyph, len, record_size);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const ValueFormat *valueFormats) const
+  {
+    unsigned record_size = get_size (valueFormats);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned count = len;
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (c->glyph_set->has (record->secondGlyph))
+      { record->collect_variation_indices (c, valueFormats, this); }
+
+      record = &StructAtOffset (record, record_size);
+    }
+  }
+
+  bool apply (hb_ot_apply_context_t *c,
+              const ValueFormat *valueFormats,
+              unsigned int pos) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned record_size = get_size (len1, len2);
+
+    const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
+                                                &firstPairValueRecord,
+                                                len,
+                                                record_size);
+    if (record)
+    {
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->message (c->font,
+                            "try kerning glyphs at %u,%u",
+                            c->buffer->idx, pos);
+      }
+
+      bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
+      bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
+
+      if (applied_first || applied_second)
+        if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+        {
+          c->buffer->message (c->font,
+                              "kerned glyphs at %u,%u",
+                              c->buffer->idx, pos);
+        }
+
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->message (c->font,
+                            "tried kerning glyphs at %u,%u",
+                            c->buffer->idx, pos);
+      }
+
+      if (applied_first || applied_second)
+        buffer->unsafe_to_break (buffer->idx, pos + 1);
+
+      if (len2)
+      {
+        pos++;
+      // https://github.com/harfbuzz/harfbuzz/issues/3824
+      // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116
+      buffer->unsafe_to_break (buffer->idx, pos + 1);
+      }
+
+      buffer->idx = pos;
+      return_trace (true);
+    }
+    buffer->unsafe_to_concat (buffer->idx, pos + 1);
+    return_trace (false);
+  }
+
+  bool subset (hb_subset_context_t *c,
+               const ValueFormat valueFormats[2],
+               const ValueFormat newFormats[2]) const
+  {
+    TRACE_SUBSET (this);
+    auto snap = c->serializer->snapshot ();
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->len = 0;
+
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    unsigned len1 = valueFormats[0].get_len ();
+    unsigned len2 = valueFormats[1].get_len ();
+    unsigned record_size = get_size (len1, len2);
+
+    typename PairValueRecord::context_t context =
+    {
+      this,
+      valueFormats,
+      newFormats,
+      len1,
+      &glyph_map,
+      &c->plan->layout_variation_idx_delta_map
+    };
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned count = len, num = 0;
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (glyphset.has (record->secondGlyph)
+         && record->subset (c, &context)) num++;
+      record = &StructAtOffset (record, record_size);
+    }
+
+    out->len = num;
+    if (!num) c->serializer->revert (snap);
+    return_trace (num);
+  }
+};
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_PAIRSET_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh
new file mode 100644
index 00000000000..d4f549a480d
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh
@@ -0,0 +1,99 @@
+#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
+#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
+
+#include "ValueFormat.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+
+template 
+struct PairValueRecord
+{
+  template 
+  friend struct PairSet;
+
+  protected:
+  typename Types::HBGlyphID
+                secondGlyph;            /* GlyphID of second glyph in the
+                                         * pair--first glyph is listed in the
+                                         * Coverage table */
+  ValueRecord   values;                 /* Positioning data for the first glyph
+                                         * followed by for second glyph */
+  public:
+  DEFINE_SIZE_ARRAY (Types::size, values);
+
+  int cmp (hb_codepoint_t k) const
+  { return secondGlyph.cmp (k); }
+
+  struct context_t
+  {
+    const void          *base;
+    const ValueFormat   *valueFormats;
+    const ValueFormat   *newFormats;
+    unsigned            len1; /* valueFormats[0].get_len() */
+    const hb_map_t      *glyph_map;
+    const hb_hashmap_t> *layout_variation_idx_delta_map;
+  };
+
+  bool subset (hb_subset_context_t *c,
+               context_t *closure) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *s = c->serializer;
+    auto *out = s->start_embed (*this);
+    if (unlikely (!s->extend_min (out))) return_trace (false);
+
+    out->secondGlyph = (*closure->glyph_map)[secondGlyph];
+
+    closure->valueFormats[0].copy_values (s,
+                                          closure->newFormats[0],
+                                          closure->base, &values[0],
+                                          closure->layout_variation_idx_delta_map);
+    closure->valueFormats[1].copy_values (s,
+                                          closure->newFormats[1],
+                                          closure->base,
+                                          &values[closure->len1],
+                                          closure->layout_variation_idx_delta_map);
+
+    return_trace (true);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const ValueFormat *valueFormats,
+                                  const void *base) const
+  {
+    unsigned record1_len = valueFormats[0].get_len ();
+    unsigned record2_len = valueFormats[1].get_len ();
+    const hb_array_t values_array = values.as_array (record1_len + record2_len);
+
+    if (valueFormats[0].has_device ())
+      valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
+
+    if (valueFormats[1].has_device ())
+      valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
+  }
+
+  bool intersects (const hb_set_t& glyphset) const
+  {
+    return glyphset.has(secondGlyph);
+  }
+
+  const Value* get_values_1 () const
+  {
+    return &values[0];
+  }
+
+  const Value* get_values_2 (ValueFormat format1) const
+  {
+    return &values[format1.get_len ()];
+  }
+};
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh
index 57e146befd6..3af6c499659 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh
@@ -38,17 +38,18 @@ struct SinglePos
   void serialize (hb_serialize_context_t *c,
                   const SrcLookup* src,
                   Iterator glyph_val_iter_pairs,
-                  const hb_map_t *layout_variation_idx_map)
+                  const hb_hashmap_t> *layout_variation_idx_delta_map,
+                  bool all_axes_pinned)
   {
     if (unlikely (!c->extend_min (u.format))) return;
     unsigned format = 2;
     ValueFormat new_format = src->get_value_format ();
 
+    if (all_axes_pinned)
+      new_format = new_format.drop_device_table_flags ();
+
     if (glyph_val_iter_pairs)
-    {
       format = get_format (glyph_val_iter_pairs);
-      new_format = src->get_value_format ().get_effective_format (+ glyph_val_iter_pairs | hb_map (hb_second));
-    }
 
     u.format = format;
     switch (u.format) {
@@ -56,13 +57,13 @@ struct SinglePos
                                  src,
                                  glyph_val_iter_pairs,
                                  new_format,
-                                 layout_variation_idx_map);
+                                 layout_variation_idx_delta_map);
       return;
     case 2: u.format2.serialize (c,
                                  src,
                                  glyph_val_iter_pairs,
                                  new_format,
-                                 layout_variation_idx_map);
+                                 layout_variation_idx_delta_map);
       return;
     default:return;
     }
@@ -71,8 +72,8 @@ struct SinglePos
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
@@ -87,8 +88,9 @@ static void
 SinglePos_serialize (hb_serialize_context_t *c,
                      const SrcLookup *src,
                      Iterator it,
-                     const hb_map_t *layout_variation_idx_map)
-{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_map); }
+                     const hb_hashmap_t> *layout_variation_idx_delta_map,
+                     bool all_axes_pinned)
+{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); }
 
 
 }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh
index 8b7840ed0eb..47391c77028 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh
@@ -28,7 +28,15 @@ struct SinglePosFormat1
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
                   coverage.sanitize (c, this) &&
+                  /* The coverage  table may use a range to represent a set
+                   * of glyphs, which means a small number of bytes can
+                   * generate a large glyph set. Manually modify the
+                   * sanitizer max ops to take this into account.
+                   *
+                   * Note: This check *must* be right after coverage sanitize. */
+                  c->check_ops ((this + coverage).get_population () >> 1) &&
                   valueFormat.sanitize_value (c, this, values));
+
   }
 
   bool intersects (const hb_set_t *glyphs) const
@@ -39,12 +47,10 @@ struct SinglePosFormat1
   {
     if (!valueFormat.has_device ()) return;
 
-    auto it =
-    + hb_iter (this+coverage)
-    | hb_filter (c->glyph_set)
-    ;
+    hb_set_t intersection;
+    (this+coverage).intersect_set (*c->glyph_set, intersection);
+    if (!intersection) return;
 
-    if (!it) return;
     valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ()));
   }
 
@@ -62,12 +68,44 @@ struct SinglePosFormat1
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "positioning glyph at %u",
+                          c->buffer->idx);
+    }
+
     valueFormat.apply_value (c, this, values, buffer->cur_pos());
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "positioned glyph at %u",
+                          c->buffer->idx);
+    }
+
     buffer->idx++;
     return_trace (true);
   }
 
+  bool
+  position_single (hb_font_t           *font,
+                   hb_direction_t       direction,
+                   hb_codepoint_t       gid,
+                   hb_glyph_position_t &pos) const
+  {
+    unsigned int index = (this+coverage).get_coverage  (gid);
+    if (likely (index == NOT_COVERED)) return false;
+
+    /* This is ugly... */
+    hb_buffer_t buffer;
+    buffer.props.direction = direction;
+    OT::hb_ot_apply_context_t c (1, font, &buffer);
+
+    valueFormat.apply_value (&c, this, values, pos);
+    return true;
+  }
+
   template
@@ -75,7 +113,7 @@ struct SinglePosFormat1
                   const SrcLookup *src,
                   Iterator it,
                   ValueFormat newFormat,
-                  const hb_map_t *layout_variation_idx_map)
+                  const hb_hashmap_t> *layout_variation_idx_delta_map)
   {
     if (unlikely (!c->extend_min (this))) return;
     if (unlikely (!c->check_assign (valueFormat,
@@ -84,7 +122,7 @@ struct SinglePosFormat1
 
     for (const hb_array_t& _ : + it | hb_map (hb_second))
     {
-      src->get_value_format ().copy_values (c, newFormat, src,  &_, layout_variation_idx_map);
+      src->get_value_format ().copy_values (c, newFormat, src,  &_, layout_variation_idx_delta_map);
       // Only serialize the first entry in the iterator, the rest are assumed to
       // be the same.
       break;
@@ -104,15 +142,17 @@ struct SinglePosFormat1
     const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
+    hb_set_t intersection;
+    (this+coverage).intersect_set (glyphset, intersection);
+
     auto it =
-    + hb_iter (this+coverage)
-    | hb_filter (glyphset)
+    + hb_iter (intersection)
     | hb_map_retains_sorting (glyph_map)
     | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
     ;
 
     bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
+    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
     return_trace (ret);
   }
 };
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh
index 0d038b44220..6546eb16703 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh
@@ -68,16 +68,52 @@ struct SinglePosFormat2
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    if (likely (index >= valueCount)) return_trace (false);
+    if (unlikely (index >= valueCount)) return_trace (false);
+
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "positioning glyph at %u",
+                          c->buffer->idx);
+    }
 
     valueFormat.apply_value (c, this,
                              &values[index * valueFormat.get_len ()],
                              buffer->cur_pos());
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "positioned glyph at %u",
+                          c->buffer->idx);
+    }
+
     buffer->idx++;
     return_trace (true);
   }
 
+  bool
+  position_single (hb_font_t           *font,
+                   hb_direction_t       direction,
+                   hb_codepoint_t       gid,
+                   hb_glyph_position_t &pos) const
+  {
+    unsigned int index = (this+coverage).get_coverage  (gid);
+    if (likely (index == NOT_COVERED)) return false;
+    if (unlikely (index >= valueCount)) return false;
+
+    /* This is ugly... */
+    hb_buffer_t buffer;
+    buffer.props.direction = direction;
+    OT::hb_ot_apply_context_t c (1, font, &buffer);
+
+    valueFormat.apply_value (&c, this,
+                             &values[index * valueFormat.get_len ()],
+                             pos);
+    return true;
+  }
+
+
   template
@@ -85,7 +121,7 @@ struct SinglePosFormat2
                   const SrcLookup *src,
                   Iterator it,
                   ValueFormat newFormat,
-                  const hb_map_t *layout_variation_idx_map)
+                  const hb_hashmap_t> *layout_variation_idx_delta_map)
   {
     auto out = c->extend_min (this);
     if (unlikely (!out)) return;
@@ -95,7 +131,7 @@ struct SinglePosFormat2
     + it
     | hb_map (hb_second)
     | hb_apply ([&] (hb_array_t _)
-    { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); })
+    { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); })
     ;
 
     auto glyphs =
@@ -127,7 +163,7 @@ struct SinglePosFormat2
     ;
 
     bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
+    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
     return_trace (ret);
   }
 };
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh
index b29f287bcee..1aa451abcc8 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh
@@ -59,6 +59,24 @@ struct ValueFormat : HBUINT16
   unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
   unsigned int get_size () const { return get_len () * Value::static_size; }
 
+  hb_vector_t get_device_table_indices () const {
+    unsigned i = 0;
+    hb_vector_t result;
+    unsigned format = *this;
+
+    if (format & xPlacement) i++;
+    if (format & yPlacement) i++;
+    if (format & xAdvance)   i++;
+    if (format & yAdvance)   i++;
+
+    if (format & xPlaDevice) result.push (i++);
+    if (format & yPlaDevice) result.push (i++);
+    if (format & xAdvDevice) result.push (i++);
+    if (format & yAdvDevice) result.push (i++);
+
+    return result;
+  }
+
   bool apply_value (hb_ot_apply_context_t *c,
                     const void            *base,
                     const Value           *values,
@@ -145,30 +163,50 @@ struct ValueFormat : HBUINT16
                     unsigned int new_format,
                     const void *base,
                     const Value *values,
-                    const hb_map_t *layout_variation_idx_map) const
+                    const hb_hashmap_t> *layout_variation_idx_delta_map) const
   {
     unsigned int format = *this;
     if (!format) return;
 
-    if (format & xPlacement) copy_value (c, new_format, xPlacement, *values++);
-    if (format & yPlacement) copy_value (c, new_format, yPlacement, *values++);
-    if (format & xAdvance)   copy_value (c, new_format, xAdvance, *values++);
-    if (format & yAdvance)   copy_value (c, new_format, yAdvance, *values++);
+    HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr;
+    if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++);
+    if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++);
+    if (format & xAdvance)   x_adv = copy_value (c, new_format, xAdvance, *values++);
+    if (format & yAdvance)   y_adv = copy_value (c, new_format, yAdvance, *values++);
 
-    if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
-    if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
-    if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
-    if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
+    if (format & xPlaDevice)
+    {
+      add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
+      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice);
+    }
+
+    if (format & yPlaDevice)
+    {
+      add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map);
+      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice);
+    }
+
+    if (format & xAdvDevice)
+    {
+      add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map);
+      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice);
+    }
+
+    if (format & yAdvDevice)
+    {
+      add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map);
+      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice);
+    }
   }
 
-  void copy_value (hb_serialize_context_t *c,
-                   unsigned int new_format,
-                   Flags flag,
-                   Value value) const
+  HBINT16* copy_value (hb_serialize_context_t *c,
+                       unsigned int new_format,
+                       Flags flag,
+                       Value value) const
   {
     // Filter by new format.
-    if (!(new_format & flag)) return;
-    c->copy (value);
+    if (!(new_format & flag)) return nullptr;
+    return reinterpret_cast (c->copy (value));
   }
 
   void collect_variation_indices (hb_collect_variation_indices_context_t *c,
@@ -183,31 +221,40 @@ struct ValueFormat : HBUINT16
     if (format & yAdvance) i++;
     if (format & xPlaDevice)
     {
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      (base + get_device (&(values[i]))).collect_variation_indices (c);
       i++;
     }
 
     if (format & ValueFormat::yPlaDevice)
     {
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      (base + get_device (&(values[i]))).collect_variation_indices (c);
       i++;
     }
 
     if (format & ValueFormat::xAdvDevice)
     {
 
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      (base + get_device (&(values[i]))).collect_variation_indices (c);
       i++;
     }
 
     if (format & ValueFormat::yAdvDevice)
     {
 
-      (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+      (base + get_device (&(values[i]))).collect_variation_indices (c);
       i++;
     }
   }
 
+  unsigned drop_device_table_flags () const
+  {
+    unsigned format = *this;
+    for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1)
+      format = format & ~flag;
+
+    return format;
+  }
+
   private:
   bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
   {
@@ -236,9 +283,27 @@ struct ValueFormat : HBUINT16
     return *static_cast *> (value);
   }
 
+  void add_delta_to_value (HBINT16 *value,
+                           const void *base,
+                           const Value *src_value,
+                           const hb_hashmap_t> *layout_variation_idx_delta_map) const
+  {
+    if (!value) return;
+    unsigned varidx = (base + get_device (src_value)).get_variation_index ();
+    hb_pair_t *varidx_delta;
+    if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return;
+
+    *value += hb_second (*varidx_delta);
+  }
+
   bool copy_device (hb_serialize_context_t *c, const void *base,
-                    const Value *src_value, const hb_map_t *layout_variation_idx_map) const
+                    const Value *src_value,
+                    const hb_hashmap_t> *layout_variation_idx_delta_map,
+                    unsigned int new_format, Flags flag) const
   {
+    // Filter by new format.
+    if (!(new_format & flag)) return true;
+
     Value       *dst_value = c->copy (*src_value);
 
     if (!dst_value) return false;
@@ -246,7 +311,7 @@ struct ValueFormat : HBUINT16
 
     *dst_value = 0;
     c->push ();
-    if ((base + get_device (src_value)).copy (c, layout_variation_idx_map))
+    if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map))
     {
       c->add_link (*dst_value, c->pop_pack ());
       return true;
@@ -306,7 +371,7 @@ struct ValueFormat : HBUINT16
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
         return_trace (false);
-      values += stride;
+      values = &StructAtOffset (values, stride);
     }
 
     return_trace (true);
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh
index 484f3474686..b5d506f36f9 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh
@@ -5,12 +5,13 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
+template 
 struct AlternateSet
 {
   protected:
-  Array16Of
+  Array16Of
                 alternates;             /* Array of alternate GlyphIDs--in
                                          * arbitrary order */
   public:
@@ -56,8 +57,23 @@ struct AlternateSet
 
     if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->sync_so_far ();
+      c->buffer->message (c->font,
+                          "replacing glyph at %u (alternate substitution)",
+                          c->buffer->idx);
+    }
+
     c->replace_glyph (alternates[alt_index - 1]);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "replaced glyph at %u (alternate substitution)",
+                          c->buffer->idx - 1u);
+    }
+
     return_trace (true);
   }
 
@@ -68,7 +84,7 @@ struct AlternateSet
   {
     if (alternates.len && alternate_count)
     {
-      + alternates.sub_array (start_offset, alternate_count)
+      + alternates.as_array ().sub_array (start_offset, alternate_count)
       | hb_sink (hb_array (alternate_glyphs, *alternate_count))
       ;
     }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh
index e5d999261fc..8951f5a7a17 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh
@@ -6,28 +6,36 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct AlternateSubst
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  AlternateSubstFormat1 format1;
+  HBUINT16                              format;         /* Format identifier */
+  AlternateSubstFormat1_2   format1;
+#ifndef HB_NO_BEYOND_64K
+  AlternateSubstFormat1_2  format2;
+#endif
   } u;
   public:
 
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
 
+  /* TODO This function is unused and not updated to 24bit GIDs. Should be done by using
+   * iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */
   bool serialize (hb_serialize_context_t *c,
                   hb_sorted_array_t glyphs,
                   hb_array_t alternate_len_list,
@@ -42,6 +50,9 @@ struct AlternateSubst
     default:return_trace (false);
     }
   }
+
+  /* TODO subset() should choose format. */
+
 };
 
 }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh
index af1cd7bedba..adec65d5864 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh
@@ -6,20 +6,21 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
-struct AlternateSubstFormat1
+template 
+struct AlternateSubstFormat1_2
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 coverage;               /* Offset to Coverage table--from
                                          * beginning of Substitution table */
-  Array16OfOffset16To
+  Array16Of>>
                 alternateSet;           /* Array of AlternateSet tables
                                          * ordered by Coverage Index */
   public:
-  DEFINE_SIZE_ARRAY (6, alternateSet);
+  DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -39,9 +40,8 @@ struct AlternateSubstFormat1
     | hb_filter (c->parent_active_glyphs (), hb_first)
     | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const AlternateSet &_) { _.closure (c); })
+    | hb_apply ([c] (const AlternateSet &_) { _.closure (c); })
     ;
-
   }
 
   void closure_lookups (hb_closure_lookups_context_t *c) const {}
@@ -52,7 +52,7 @@ struct AlternateSubstFormat1
     + hb_zip (this+coverage, alternateSet)
     | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); })
+    | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); })
     ;
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh
index bbb88b222f8..08fd779f730 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh
@@ -7,7 +7,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct ChainContextSubst : ChainContext {};
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh
index f4c78a9f020..968bba0481a 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh
@@ -6,7 +6,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 typedef hb_pair_t hb_codepoint_pair_t;
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh
index 2af54e8ff47..9f8cb46b5e2 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh
@@ -7,7 +7,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct ContextSubst : Context {};
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh
index 40a3ff439f1..831a7dfa2d1 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh
@@ -7,7 +7,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct ExtensionSubst : Extension
 {
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh
index 3f0c4b2ad9a..900cf603e45 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh
@@ -1,16 +1,15 @@
 #ifndef OT_LAYOUT_GSUB_GSUB_HH
 #define OT_LAYOUT_GSUB_GSUB_HH
 
-// TODO(garretrieger): move to new layout.
 #include "../../../hb-ot-layout-gsubgpos.hh"
 #include "Common.hh"
 #include "SubstLookup.hh"
 
-using OT::Layout::GSUB::SubstLookup;
-
 namespace OT {
+
+using Layout::GSUB_impl::SubstLookup;
+
 namespace Layout {
-namespace GSUB {
 
 /*
  * GSUB -- Glyph Substitution
@@ -28,12 +27,15 @@ struct GSUB : GSUBGPOS
 
   bool subset (hb_subset_context_t *c) const
   {
-    hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_langsys, c->plan->gsub_features);
+    hb_subset_layout_context_t l (c, tableTag);
     return GSUBGPOS::subset (&l);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
-  { return GSUBGPOS::sanitize (c); }
+  {
+    TRACE_SANITIZE (this);
+    return_trace (GSUBGPOS::sanitize (c));
+  }
 
   HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
                                    hb_face_t *face) const;
@@ -47,11 +49,10 @@ struct GSUB : GSUBGPOS
 };
 
 
-}
 }
 
-struct GSUB_accelerator_t : Layout::GSUB::GSUB::accelerator_t {
-  GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::GSUB::accelerator_t (face) {}
+struct GSUB_accelerator_t : Layout::GSUB::accelerator_t {
+  GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {}
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh
index 0448d925d12..308da587d1e 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh
@@ -5,18 +5,20 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
+template 
 struct Ligature
 {
   protected:
-  HBGlyphID16   ligGlyph;               /* GlyphID of ligature to substitute */
-  HeadlessArrayOf
+  typename Types::HBGlyphID
+                ligGlyph;               /* GlyphID of ligature to substitute */
+  HeadlessArrayOf
                 component;              /* Array of component GlyphIDs--start
                                          * with the second  component--ordered
                                          * in writing direction */
   public:
-  DEFINE_SIZE_ARRAY (4, component);
+  DEFINE_SIZE_ARRAY (Types::size + 2, component);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -27,6 +29,9 @@ struct Ligature
   bool intersects (const hb_set_t *glyphs) const
   { return hb_all (component, glyphs); }
 
+  bool intersects_lig_glyph (const hb_set_t *glyphs) const
+  { return glyphs->has(ligGlyph); }
+
   void closure (hb_closure_context_t *c) const
   {
     if (!intersects (c->glyphs)) return;
@@ -62,7 +67,24 @@ struct Ligature
      * as a "ligated" substitution. */
     if (unlikely (count == 1))
     {
+
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->sync_so_far ();
+        c->buffer->message (c->font,
+                            "replacing glyph at %u (ligature substitution)",
+                            c->buffer->idx);
+      }
+
       c->replace_glyph (ligGlyph);
+
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->message (c->font,
+                            "replaced glyph at %u (ligature substitution)",
+                            c->buffer->idx - 1u);
+      }
+
       return_trace (true);
     }
 
@@ -83,6 +105,31 @@ struct Ligature
       return_trace (false);
     }
 
+    unsigned pos = 0;
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      unsigned delta = c->buffer->sync_so_far ();
+
+      pos = c->buffer->idx;
+
+      char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
+      char *p = buf;
+
+      match_end += delta;
+      for (unsigned i = 0; i < count; i++)
+      {
+        match_positions[i] += delta;
+        if (i)
+          *p++ = ',';
+        snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]);
+        p += strlen(p);
+      }
+
+      c->buffer->message (c->font,
+                          "ligating glyphs at %s",
+                          buf);
+    }
+
     ligate_input (c,
                   count,
                   match_positions,
@@ -90,6 +137,14 @@ struct Ligature
                   ligGlyph,
                   total_component_count);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->sync_so_far ();
+      c->buffer->message (c->font,
+                          "ligated glyph at %u",
+                          pos);
+    }
+
     return_trace (true);
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh
index 185b324b35b..2b232622802 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh
@@ -6,12 +6,13 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
+template 
 struct LigatureSet
 {
   protected:
-  Array16OfOffset16To
+  Array16OfOffset16To>
                 ligature;               /* Array LigatureSet tables
                                          * ordered by preference */
   public:
@@ -28,7 +29,19 @@ struct LigatureSet
     return
     + hb_iter (ligature)
     | hb_map (hb_add (this))
-    | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
+    | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
+    | hb_any
+    ;
+  }
+
+  bool intersects_lig_glyph (const hb_set_t *glyphs) const
+  {
+    return
+    + hb_iter (ligature)
+    | hb_map (hb_add (this))
+    | hb_map ([glyphs] (const Ligature &_) {
+      return _.intersects_lig_glyph (glyphs) && _.intersects (glyphs);
+    })
     | hb_any
     ;
   }
@@ -37,7 +50,7 @@ struct LigatureSet
   {
     + hb_iter (ligature)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const Ligature &_) { _.closure (c); })
+    | hb_apply ([c] (const Ligature &_) { _.closure (c); })
     ;
   }
 
@@ -45,7 +58,7 @@ struct LigatureSet
   {
     + hb_iter (ligature)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
+    | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
     ;
   }
 
@@ -54,7 +67,7 @@ struct LigatureSet
     return
     + hb_iter (ligature)
     | hb_map (hb_add (this))
-    | hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
+    | hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
     | hb_any
     ;
   }
@@ -65,7 +78,7 @@ struct LigatureSet
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
     {
-      const Ligature &lig = this+ligature[i];
+      const auto &lig = this+ligature[i];
       if (lig.apply (c)) return_trace (true);
     }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh
index a029bf5e9f4..cffa910295f 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh
@@ -6,28 +6,37 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct LigatureSubst
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  LigatureSubstFormat1  format1;
+  HBUINT16                              format;         /* Format identifier */
+  LigatureSubstFormat1_2    format1;
+#ifndef HB_NO_BEYOND_64K
+  LigatureSubstFormat1_2   format2;
+#endif
   } u;
 
   public:
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
 
+  /* TODO This function is only used by small GIDs, and not updated to 24bit GIDs. Should
+   * be done by using iterators. While at it perhaps using iterator of arrays of hb_codepoint_t
+   * instead. */
   bool serialize (hb_serialize_context_t *c,
                   hb_sorted_array_t first_glyphs,
                   hb_array_t ligature_per_first_glyph_count_list,
@@ -49,6 +58,9 @@ struct LigatureSubst
     default:return_trace (false);
     }
   }
+
+  /* TODO subset() should choose format. */
+
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh
index 19dfe984694..5c7df97d13a 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh
@@ -6,20 +6,21 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
-struct LigatureSubstFormat1
+template 
+struct LigatureSubstFormat1_2
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 coverage;               /* Offset to Coverage table--from
                                          * beginning of Substitution table */
-  Array16OfOffset16To
+  Array16Of>>
                 ligatureSet;            /* Array LigatureSet tables
                                          * ordered by Coverage Index */
   public:
-  DEFINE_SIZE_ARRAY (6, ligatureSet);
+  DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -33,7 +34,7 @@ struct LigatureSubstFormat1
     + hb_zip (this+coverage, ligatureSet)
     | hb_filter (*glyphs, hb_first)
     | hb_map (hb_second)
-    | hb_map ([this, glyphs] (const Offset16To &_)
+    | hb_map ([this, glyphs] (const typename Types::template OffsetTo> &_)
               { return (this+_).intersects (glyphs); })
     | hb_any
     ;
@@ -48,7 +49,7 @@ struct LigatureSubstFormat1
     | hb_filter (c->parent_active_glyphs (), hb_first)
     | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
+    | hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
     ;
 
   }
@@ -62,7 +63,7 @@ struct LigatureSubstFormat1
     + hb_zip (this+coverage, ligatureSet)
     | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
+    | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
     ;
   }
 
@@ -73,7 +74,7 @@ struct LigatureSubstFormat1
     unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
     if (likely (index == NOT_COVERED)) return false;
 
-    const LigatureSet &lig_set = this+ligatureSet[index];
+    const auto &lig_set = this+ligatureSet[index];
     return lig_set.would_apply (c);
   }
 
@@ -84,7 +85,7 @@ struct LigatureSubstFormat1
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    const LigatureSet &lig_set = this+ligatureSet[index];
+    const auto &lig_set = this+ligatureSet[index];
     return_trace (lig_set.apply (c));
   }
 
@@ -128,8 +129,8 @@ struct LigatureSubstFormat1
     hb_set_t new_coverage;
     + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
     | hb_filter (glyphset, hb_first)
-    | hb_filter ([&] (const LigatureSet& _) {
-      return _.intersects (&glyphset);
+    | hb_filter ([&] (const LigatureSet& _) {
+      return _.intersects_lig_glyph (&glyphset);
     }, hb_second)
     | hb_map (hb_first)
     | hb_sink (new_coverage);
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh
index b2891755048..cf3d754e3cc 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh
@@ -6,14 +6,17 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct MultipleSubst
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  MultipleSubstFormat1  format1;
+  HBUINT16                              format;         /* Format identifier */
+  MultipleSubstFormat1_2    format1;
+#ifndef HB_NO_BEYOND_64K
+  MultipleSubstFormat1_2   format2;
+#endif
   } u;
 
   public:
@@ -21,28 +24,34 @@ struct MultipleSubst
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
 
+  template
   bool serialize (hb_serialize_context_t *c,
-                  hb_sorted_array_t glyphs,
-                  hb_array_t substitute_len_list,
-                  hb_array_t substitute_glyphs_list)
+                  Iterator it)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format = format;
     switch (u.format) {
-    case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
+    case 1: return_trace (u.format1.serialize (c, it));
     default:return_trace (false);
     }
   }
+
+  /* TODO subset() should choose format. */
+
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh
index 54c6dc84783..4a9972c29cc 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh
@@ -6,20 +6,21 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
-struct MultipleSubstFormat1
+template 
+struct MultipleSubstFormat1_2
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 coverage;               /* Offset to Coverage table--from
                                          * beginning of Substitution table */
-  Array16OfOffset16To
+  Array16Of>>
                 sequence;               /* Array of Sequence tables
                                          * ordered by Coverage Index */
   public:
-  DEFINE_SIZE_ARRAY (6, sequence);
+  DEFINE_SIZE_ARRAY (4 + Types::size, sequence);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -39,7 +40,7 @@ struct MultipleSubstFormat1
     | hb_filter (c->parent_active_glyphs (), hb_first)
     | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const Sequence &_) { _.closure (c); })
+    | hb_apply ([c] (const Sequence &_) { _.closure (c); })
     ;
   }
 
@@ -51,7 +52,7 @@ struct MultipleSubstFormat1
     + hb_zip (this+coverage, sequence)
     | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); })
+    | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); })
     ;
   }
 
@@ -70,22 +71,31 @@ struct MultipleSubstFormat1
     return_trace ((this+sequence[index]).apply (c));
   }
 
+  template
   bool serialize (hb_serialize_context_t *c,
-                  hb_sorted_array_t glyphs,
-                  hb_array_t substitute_len_list,
-                  hb_array_t substitute_glyphs_list)
+                  Iterator it)
   {
     TRACE_SERIALIZE (this);
+    auto sequences =
+      + it
+      | hb_map (hb_second)
+      ;
+    auto glyphs =
+      + it
+      | hb_map_retains_sorting (hb_first)
+      ;
     if (unlikely (!c->extend_min (this))) return_trace (false);
-    if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
-    for (unsigned int i = 0; i < glyphs.length; i++)
+
+    if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false);
+
+    for (auto& pair : hb_zip (sequences, sequence))
     {
-      unsigned int substitute_len = substitute_len_list[i];
-      if (unlikely (!sequence[i]
-                        .serialize_serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
+      if (unlikely (!pair.second
+                    .serialize_serialize (c, pair.first)))
         return_trace (false);
-      substitute_glyphs_list += substitute_len;
     }
+
     return_trace (coverage.serialize_serialize (c, glyphs));
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh
index 435d80fd310..5ad463fea79 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh
@@ -6,7 +6,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct ReverseChainSingleSubst
 {
@@ -20,8 +20,8 @@ struct ReverseChainSingleSubst
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
     default:return_trace (c->default_return_value ());
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
index 7a79a9df25e..73f222746e5 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
@@ -5,7 +5,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct ReverseChainSingleSubstFormat1
 {
@@ -33,10 +33,10 @@ struct ReverseChainSingleSubstFormat1
     TRACE_SANITIZE (this);
     if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
       return_trace (false);
-    const Array16OfOffset16To &lookahead = StructAfter> (backtrack);
+    const auto &lookahead = StructAfter (backtrack);
     if (!lookahead.sanitize (c, this))
       return_trace (false);
-    const Array16Of &substitute = StructAfter> (lookahead);
+    const auto &substitute = StructAfter (lookahead);
     return_trace (substitute.sanitize (c));
   }
 
@@ -45,7 +45,7 @@ struct ReverseChainSingleSubstFormat1
     if (!(this+coverage).intersects (glyphs))
       return false;
 
-    const Array16OfOffset16To &lookahead = StructAfter> (backtrack);
+    const auto &lookahead = StructAfter (backtrack);
 
     unsigned int count;
 
@@ -69,8 +69,8 @@ struct ReverseChainSingleSubstFormat1
   {
     if (!intersects (c->glyphs)) return;
 
-    const Array16OfOffset16To &lookahead = StructAfter> (backtrack);
-    const Array16Of &substitute = StructAfter> (lookahead);
+    const auto &lookahead = StructAfter (backtrack);
+    const auto &substitute = StructAfter (lookahead);
 
     + hb_zip (this+coverage, substitute)
     | hb_filter (c->parent_active_glyphs (), hb_first)
@@ -91,12 +91,12 @@ struct ReverseChainSingleSubstFormat1
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
 
-    const Array16OfOffset16To &lookahead = StructAfter> (backtrack);
+    const auto &lookahead = StructAfter (backtrack);
     count = lookahead.len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
 
-    const Array16Of &substitute = StructAfter> (lookahead);
+    const auto &substitute = StructAfter (lookahead);
     count = substitute.len;
     c->output->add_array (substitute.arrayZ, substitute.len);
   }
@@ -115,8 +115,8 @@ struct ReverseChainSingleSubstFormat1
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    const Array16OfOffset16To &lookahead = StructAfter> (backtrack);
-    const Array16Of &substitute = StructAfter> (lookahead);
+    const auto &lookahead = StructAfter (backtrack);
+    const auto &substitute = StructAfter (lookahead);
 
     if (unlikely (index >= substitute.len)) return_trace (false);
 
@@ -131,7 +131,23 @@ struct ReverseChainSingleSubstFormat1
                          c->buffer->idx + 1, &end_index))
     {
       c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
+
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->message (c->font,
+                            "replacing glyph at %u (reverse chaining substitution)",
+                            c->buffer->idx);
+      }
+
       c->replace_glyph_inplace (substitute[index]);
+
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->message (c->font,
+                            "replaced glyph at %u (reverse chaining substitution)",
+                            c->buffer->idx);
+      }
+
       /* Note: We DON'T decrease buffer->idx.  The main loop does it
        * for us.  This is useful for preventing surprises if someone
        * calls us through a Context lookup. */
@@ -206,8 +222,8 @@ struct ReverseChainSingleSubstFormat1
     const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
-    const Array16OfOffset16To &lookahead = StructAfter> (backtrack);
-    const Array16Of &substitute = StructAfter> (lookahead);
+    const auto &lookahead = StructAfter (backtrack);
+    const auto &substitute = StructAfter (lookahead);
 
     auto it =
     + hb_zip (this+coverage, substitute)
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh
index ebd451e6ba3..62d68160c7b 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh
@@ -5,12 +5,13 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
+template 
 struct Sequence
 {
   protected:
-  Array16Of
+  Array16Of
                 substitute;             /* String of GlyphIDs to substitute */
   public:
   DEFINE_SIZE_ARRAY (2, substitute);
@@ -39,17 +40,58 @@ struct Sequence
      * as a "multiplied" substitution. */
     if (unlikely (count == 1))
     {
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->sync_so_far ();
+        c->buffer->message (c->font,
+                            "replacing glyph at %u (multiple substitution)",
+                            c->buffer->idx);
+      }
+
       c->replace_glyph (substitute.arrayZ[0]);
+
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->message (c->font,
+                            "replaced glyph at %u (multiple subtitution)",
+                            c->buffer->idx - 1u);
+      }
+
       return_trace (true);
     }
     /* Spec disallows this, but Uniscribe allows it.
      * https://github.com/harfbuzz/harfbuzz/issues/253 */
     else if (unlikely (count == 0))
     {
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->sync_so_far ();
+        c->buffer->message (c->font,
+                            "deleting glyph at %u (multiple substitution)",
+                            c->buffer->idx);
+      }
+
       c->buffer->delete_glyph ();
+
+      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+      {
+        c->buffer->sync_so_far ();
+        c->buffer->message (c->font,
+                            "deleted glyph at %u (multiple substitution)",
+                            c->buffer->idx);
+      }
+
       return_trace (true);
     }
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->sync_so_far ();
+      c->buffer->message (c->font,
+                          "multiplying glyph at %u",
+                          c->buffer->idx);
+    }
+
     unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
                          HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
     unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur());
@@ -64,6 +106,26 @@ struct Sequence
     }
     c->buffer->skip_glyph ();
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->sync_so_far ();
+
+      char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
+      char *p = buf;
+
+      for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++)
+      {
+        if (buf < p)
+          *p++ = ',';
+        snprintf (p, sizeof(buf) - (p - buf), "%u", i);
+        p += strlen(p);
+      }
+
+      c->buffer->message (c->font,
+                          "multiplied glyphs at %s",
+                          buf);
+    }
+
     return_trace (true);
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh
index 786428fe453..304d1928e23 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh
@@ -7,15 +7,19 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct SingleSubst
 {
   protected:
   union {
-  HBUINT16              format;         /* Format identifier */
-  SingleSubstFormat1    format1;
-  SingleSubstFormat2    format2;
+  HBUINT16                              format;         /* Format identifier */
+  SingleSubstFormat1_3      format1;
+  SingleSubstFormat2_4      format2;
+#ifndef HB_NO_BEYOND_64K
+  SingleSubstFormat1_3     format3;
+  SingleSubstFormat2_4     format4;
+#endif
   } u;
 
   public:
@@ -23,11 +27,15 @@ struct SingleSubst
   template 
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...));
+#ifndef HB_NO_BEYOND_64K
+    case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...));
+    case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...));
+#endif
     default:return_trace (c->default_return_value ());
     }
   }
@@ -45,11 +53,24 @@ struct SingleSubst
     if (glyphs)
     {
       format = 1;
+      hb_codepoint_t mask = 0xFFFFu;
+
+#ifndef HB_NO_BEYOND_64K
+       if (+ glyphs
+           | hb_map_retains_sorting (hb_first)
+           | hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; }))
+       {
+         format += 2;
+         mask = 0xFFFFFFu;
+       }
+#endif
+
       auto get_delta = [=] (hb_codepoint_pair_t _)
-                       { return (unsigned) (_.second - _.first) & 0xFFFF; };
+                       { return (unsigned) (_.second - _.first) & mask; };
       delta = get_delta (*glyphs);
-      if (!hb_all (++(+glyphs), delta, get_delta)) format = 2;
+      if (!hb_all (++(+glyphs), delta, get_delta)) format += 1;
     }
+
     u.format = format;
     switch (u.format) {
     case 1: return_trace (u.format1.serialize (c,
@@ -57,6 +78,13 @@ struct SingleSubst
                                                | hb_map_retains_sorting (hb_first),
                                                delta));
     case 2: return_trace (u.format2.serialize (c, glyphs));
+#ifndef HB_NO_BEYOND_64K
+    case 3: return_trace (u.format3.serialize (c,
+                                               + glyphs
+                                               | hb_map_retains_sorting (hb_first),
+                                               delta));
+    case 4: return_trace (u.format4.serialize (c, glyphs));
+#endif
     default:return_trace (false);
     }
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh
index 3c6b2954cec..268487c5ae7 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh
@@ -5,27 +5,40 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
-struct SingleSubstFormat1
+template 
+struct SingleSubstFormat1_3
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 1 */
-  Offset16To
+  typename Types::template OffsetTo
                 coverage;               /* Offset to Coverage table--from
                                          * beginning of Substitution table */
-  HBUINT16      deltaGlyphID;           /* Add to original GlyphID to get
+  typename Types::HBUINT
+                deltaGlyphID;           /* Add to original GlyphID to get
                                          * substitute GlyphID, modulo 0x10000 */
 
   public:
-  DEFINE_SIZE_STATIC (6);
+  DEFINE_SIZE_STATIC (2 + 2 * Types::size);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
+    return_trace (c->check_struct (this) &&
+                  coverage.sanitize (c, this) &&
+                  /* The coverage  table may use a range to represent a set
+                   * of glyphs, which means a small number of bytes can
+                   * generate a large glyph set. Manually modify the
+                   * sanitizer max ops to take this into account.
+                   *
+                   * Note: This check *must* be right after coverage sanitize. */
+                  c->check_ops ((this + coverage).get_population () >> 1));
   }
 
+  hb_codepoint_t get_mask () const
+  { return (1 << (8 * Types::size)) - 1; }
+
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
@@ -34,14 +47,33 @@ struct SingleSubstFormat1
 
   void closure (hb_closure_context_t *c) const
   {
-    unsigned d = deltaGlyphID;
-
-    + hb_iter (this+coverage)
-    | hb_filter (c->parent_active_glyphs ())
-    | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
+    hb_codepoint_t d = deltaGlyphID;
+    hb_codepoint_t mask = get_mask ();
+
+    /* Help fuzzer avoid this function as much. */
+    unsigned pop = (this+coverage).get_population ();
+    if (pop >= mask)
+      return;
+
+    hb_set_t intersection;
+    (this+coverage).intersect_set (c->parent_active_glyphs (), intersection);
+
+    /* In degenerate fuzzer-found fonts, but not real fonts,
+     * this table can keep adding new glyphs in each round of closure.
+     * Refuse to close-over, if it maps glyph range to overlapping range. */
+    hb_codepoint_t min_before = intersection.get_min ();
+    hb_codepoint_t max_before = intersection.get_max ();
+    hb_codepoint_t min_after = (min_before + d) & mask;
+    hb_codepoint_t max_after = (max_before + d) & mask;
+    if (intersection.get_population () == max_before - min_before + 1 &&
+        ((min_before <= min_after && min_after <= max_before) ||
+         (min_before <= max_after && max_after <= max_before)))
+      return;
+
+    + hb_iter (intersection)
+    | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
     | hb_sink (c->output)
     ;
-
   }
 
   void closure_lookups (hb_closure_lookups_context_t *c) const {}
@@ -49,9 +81,11 @@ struct SingleSubstFormat1
   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
-    unsigned d = deltaGlyphID;
+    hb_codepoint_t d = deltaGlyphID;
+    hb_codepoint_t mask = get_mask ();
+
     + hb_iter (this+coverage)
-    | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
+    | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
     | hb_sink (c->output)
     ;
   }
@@ -61,6 +95,34 @@ struct SingleSubstFormat1
   bool would_apply (hb_would_apply_context_t *c) const
   { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
 
+  unsigned
+  get_glyph_alternates (hb_codepoint_t  glyph_id,
+                        unsigned        start_offset,
+                        unsigned       *alternate_count  /* IN/OUT.  May be NULL. */,
+                        hb_codepoint_t *alternate_glyphs /* OUT.     May be NULL. */) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    if (likely (index == NOT_COVERED))
+    {
+      if (alternate_count)
+        *alternate_count = 0;
+      return 0;
+    }
+
+    if (alternate_count && *alternate_count)
+    {
+      hb_codepoint_t d = deltaGlyphID;
+      hb_codepoint_t mask = get_mask ();
+
+      glyph_id = (glyph_id + d) & mask;
+
+      *alternate_glyphs = glyph_id;
+      *alternate_count = 1;
+    }
+
+    return 1;
+  }
+
   bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
@@ -68,11 +130,28 @@ struct SingleSubstFormat1
     unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    /* According to the Adobe Annotated OpenType Suite, result is always
-     * limited to 16bit. */
-    glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
+    hb_codepoint_t d = deltaGlyphID;
+    hb_codepoint_t mask = get_mask ();
+
+    glyph_id = (glyph_id + d) & mask;
+
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->sync_so_far ();
+      c->buffer->message (c->font,
+                          "replacing glyph at %u (single substitution)",
+                          c->buffer->idx);
+    }
+
     c->replace_glyph (glyph_id);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "replaced glyph at %u (single substitution)",
+                          c->buffer->idx - 1u);
+    }
+
     return_trace (true);
   }
 
@@ -95,14 +174,17 @@ struct SingleSubstFormat1
     const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
-    hb_codepoint_t delta = deltaGlyphID;
+    hb_codepoint_t d = deltaGlyphID;
+    hb_codepoint_t mask = get_mask ();
+
+    hb_set_t intersection;
+    (this+coverage).intersect_set (glyphset, intersection);
 
     auto it =
-    + hb_iter (this+coverage)
-    | hb_filter (glyphset)
-    | hb_map_retains_sorting ([&] (hb_codepoint_t g) {
+    + hb_iter (intersection)
+    | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) {
                                 return hb_codepoint_pair_t (g,
-                                                            (g + delta) & 0xFFFF); })
+                                                            (g + d) & mask); })
     | hb_filter (glyphset, hb_second)
     | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
                               { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh
index df75bb52bbc..51890011671 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh
@@ -5,21 +5,22 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
-struct SingleSubstFormat2
+template 
+struct SingleSubstFormat2_4
 {
   protected:
   HBUINT16      format;                 /* Format identifier--format = 2 */
-  Offset16To
+  typename Types::template OffsetTo
                 coverage;               /* Offset to Coverage table--from
                                          * beginning of Substitution table */
-  Array16Of
+  Array16Of
                 substitute;             /* Array of substitute
                                          * GlyphIDs--ordered by Coverage Index */
 
   public:
-  DEFINE_SIZE_ARRAY (6, substitute);
+  DEFINE_SIZE_ARRAY (4 + Types::size, substitute);
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -35,12 +36,27 @@ struct SingleSubstFormat2
 
   void closure (hb_closure_context_t *c) const
   {
-    + hb_zip (this+coverage, substitute)
-    | hb_filter (c->parent_active_glyphs (), hb_first)
+    auto &cov = this+coverage;
+    auto &glyph_set = c->parent_active_glyphs ();
+
+    if (substitute.len > glyph_set.get_population () * 4)
+    {
+      for (auto g : glyph_set)
+      {
+        unsigned i = cov.get_coverage (g);
+        if (i == NOT_COVERED || i >= substitute.len)
+          continue;
+        c->output->add (substitute.arrayZ[i]);
+      }
+
+      return;
+    }
+
+    + hb_zip (cov, substitute)
+    | hb_filter (glyph_set, hb_first)
     | hb_map (hb_second)
     | hb_sink (c->output)
     ;
-
   }
 
   void closure_lookups (hb_closure_lookups_context_t *c) const {}
@@ -59,6 +75,31 @@ struct SingleSubstFormat2
   bool would_apply (hb_would_apply_context_t *c) const
   { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
 
+  unsigned
+  get_glyph_alternates (hb_codepoint_t  glyph_id,
+                        unsigned        start_offset,
+                        unsigned       *alternate_count  /* IN/OUT.  May be NULL. */,
+                        hb_codepoint_t *alternate_glyphs /* OUT.     May be NULL. */) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    if (likely (index == NOT_COVERED))
+    {
+      if (alternate_count)
+        *alternate_count = 0;
+      return 0;
+    }
+
+    if (alternate_count && *alternate_count)
+    {
+      glyph_id = substitute[index];
+
+      *alternate_glyphs = glyph_id;
+      *alternate_count = 1;
+    }
+
+    return 1;
+  }
+
   bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
@@ -67,8 +108,23 @@ struct SingleSubstFormat2
 
     if (unlikely (index >= substitute.len)) return_trace (false);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->sync_so_far ();
+      c->buffer->message (c->font,
+                          "replacing glyph at %u (single substitution)",
+                          c->buffer->idx);
+    }
+
     c->replace_glyph (substitute[index]);
 
+    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+    {
+      c->buffer->message (c->font,
+                          "replaced glyph at %u (single substitution)",
+                          c->buffer->idx - 1u);
+    }
+
     return_trace (true);
   }
 
@@ -103,7 +159,7 @@ struct SingleSubstFormat2
     + hb_zip (this+coverage, substitute)
     | hb_filter (glyphset, hb_first)
     | hb_filter (glyphset, hb_second)
-    | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t
+    | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t
                               { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
     ;
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh
index 8fb3b550976..5796cc3be57 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh
@@ -6,7 +6,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct SubstLookup : Lookup
 {
@@ -25,7 +25,7 @@ struct SubstLookup : Lookup
   {
     unsigned int type = get_type ();
     if (unlikely (type == SubTable::Extension))
-      return reinterpret_cast (get_subtable (0)).is_reverse ();
+      return get_subtable (0).u.extension.is_reverse ();
     return lookup_type_is_reverse (type);
   }
 
@@ -98,10 +98,15 @@ struct SubstLookup : Lookup
       return dispatch (c);
   }
 
+  template
   bool serialize_single (hb_serialize_context_t *c,
                          uint32_t lookup_props,
-                         hb_sorted_array_t glyphs,
-                         hb_array_t substitutes)
+                         Glyphs glyphs,
+                         Substitutes substitutes)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
@@ -114,19 +119,16 @@ struct SubstLookup : Lookup
     return_trace (false);
   }
 
-  bool serialize_multiple (hb_serialize_context_t *c,
-                           uint32_t lookup_props,
-                           hb_sorted_array_t glyphs,
-                           hb_array_t substitute_len_list,
-                           hb_array_t substitute_glyphs_list)
+  template
+  bool serialize (hb_serialize_context_t *c,
+                  uint32_t lookup_props,
+                  Iterator it)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
     if (c->push ()->u.multiple.
-        serialize (c,
-                   glyphs,
-                   substitute_len_list,
-                   substitute_glyphs_list))
+        serialize (c, it))
     {
       c->add_link (get_subtables ()[0], c->pop_pack ());
       return_trace (true);
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh
index 53e963e2a29..a525fba0399 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh
@@ -13,7 +13,7 @@
 
 namespace OT {
 namespace Layout {
-namespace GSUB {
+namespace GSUB_impl {
 
 struct SubstLookupSubTable
 {
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh
new file mode 100644
index 00000000000..6a43403e94b
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,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, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_TYPES_HH
+#define OT_LAYOUT_TYPES_HH
+
+namespace OT {
+namespace Layout {
+
+struct SmallTypes {
+  static constexpr unsigned size = 2;
+  using large_int = uint32_t;
+  using HBUINT = HBUINT16;
+  using HBGlyphID = HBGlyphID16;
+  using Offset = Offset16;
+  template 
+  using OffsetTo = OT::Offset16To;
+  template 
+  using ArrayOf = OT::Array16Of;
+  template 
+  using SortedArrayOf = OT::SortedArray16Of;
+};
+
+struct MediumTypes {
+  static constexpr unsigned size = 3;
+  using large_int = uint64_t;
+  using HBUINT = HBUINT24;
+  using HBGlyphID = HBGlyphID24;
+  using Offset = Offset24;
+  template 
+  using OffsetTo = OT::Offset24To;
+  template 
+  using ArrayOf = OT::Array24Of;
+  template 
+  using SortedArrayOf = OT::SortedArray24Of;
+};
+
+}
+}
+
+#endif  /* OT_LAYOUT_TYPES_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh
index 7ebf64d0d5c..94cb61abc07 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh
@@ -3,6 +3,7 @@
 
 
 #include "../../hb-open-type.hh"
+#include "composite-iter.hh"
 
 
 namespace OT {
@@ -25,13 +26,20 @@ struct CompositeGlyphRecord
     USE_MY_METRICS              = 0x0200,
     OVERLAP_COMPOUND            = 0x0400,
     SCALED_COMPONENT_OFFSET     = 0x0800,
-    UNSCALED_COMPONENT_OFFSET   = 0x1000
+    UNSCALED_COMPONENT_OFFSET   = 0x1000,
+#ifndef HB_NO_BEYOND_64K
+    GID_IS_24BIT                = 0x2000
+#endif
   };
 
   public:
   unsigned int get_size () const
   {
     unsigned int size = min_size;
+    /* glyphIndex is 24bit instead of 16bit */
+#ifndef HB_NO_BEYOND_64K
+    if (flags & GID_IS_24BIT) size += HBGlyphID24::static_size - HBGlyphID16::static_size;
+#endif
     /* arg1 and 2 are int16 */
     if (flags & ARG_1_AND_2_ARE_WORDS) size += 4;
     /* arg1 and 2 are int8 */
@@ -60,7 +68,13 @@ struct CompositeGlyphRecord
   bool is_anchored ()       const { return !(flags & ARGS_ARE_XY_VALUES); }
   void get_anchor_points (unsigned int &point1, unsigned int &point2) const
   {
-    const HBUINT8 *p = &StructAfter (glyphIndex);
+    const auto *p = &StructAfter (flags);
+#ifndef HB_NO_BEYOND_64K
+    if (flags & GID_IS_24BIT)
+      p += HBGlyphID24::static_size;
+    else
+#endif
+      p += HBGlyphID16::static_size;
     if (flags & ARG_1_AND_2_ARE_WORDS)
     {
       point1 = ((const HBUINT16 *) p)[0];
@@ -73,36 +87,110 @@ struct CompositeGlyphRecord
     }
   }
 
-  void transform_points (contour_point_vector_t &points) const
+  void transform_points (contour_point_vector_t &points,
+                         const float (&matrix)[4],
+                         const contour_point_t &trans) const
+  {
+    if (scaled_offsets ())
+    {
+      points.translate (trans);
+      points.transform (matrix);
+    }
+    else
+    {
+      points.transform (matrix);
+      points.translate (trans);
+    }
+  }
+
+  bool get_points (contour_point_vector_t &points) const
   {
     float matrix[4];
     contour_point_t trans;
-    if (get_transformation (matrix, trans))
+    get_transformation (matrix, trans);
+    if (unlikely (!points.resize (points.length + 1))) return false;
+    points[points.length - 1] = trans;
+    return true;
+  }
+
+  unsigned compile_with_point (const contour_point_t &point,
+                               char *out) const
+  {
+    const HBINT8 *p = &StructAfter (flags);
+#ifndef HB_NO_BEYOND_64K
+    if (flags & GID_IS_24BIT)
+      p += HBGlyphID24::static_size;
+    else
+#endif
+      p += HBGlyphID16::static_size;
+
+    unsigned len = get_size ();
+    unsigned len_before_val = (const char *)p - (const char *)this;
+    if (flags & ARG_1_AND_2_ARE_WORDS)
+    {
+      // no overflow, copy value
+      hb_memcpy (out, this, len);
+
+      HBINT16 *o = reinterpret_cast (out + len_before_val);
+      o[0] = roundf (point.x);
+      o[1] = roundf (point.y);
+    }
+    else
     {
-      if (scaled_offsets ())
+      int new_x = roundf (point.x);
+      int new_y = roundf (point.y);
+      if (new_x <= 127 && new_x >= -128 &&
+          new_y <= 127 && new_y >= -128)
       {
-        points.translate (trans);
-        points.transform (matrix);
+        hb_memcpy (out, this, len);
+        HBINT8 *o = reinterpret_cast (out + len_before_val);
+        o[0] = new_x;
+        o[1] = new_y;
       }
       else
       {
-        points.transform (matrix);
-        points.translate (trans);
+        // new point value has an int8 overflow
+        hb_memcpy (out, this, len_before_val);
+
+        //update flags
+        CompositeGlyphRecord *o = reinterpret_cast (out);
+        o->flags = flags | ARG_1_AND_2_ARE_WORDS;
+        out += len_before_val;
+
+        HBINT16 new_value;
+        new_value = new_x;
+        hb_memcpy (out, &new_value, HBINT16::static_size);
+        out += HBINT16::static_size;
+
+        new_value = new_y;
+        hb_memcpy (out, &new_value, HBINT16::static_size);
+        out += HBINT16::static_size;
+
+        hb_memcpy (out, p+2, len - len_before_val - 2);
+        len += 2;
       }
     }
+    return len;
   }
 
   protected:
   bool scaled_offsets () const
   { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; }
 
+  public:
   bool get_transformation (float (&matrix)[4], contour_point_t &trans) const
   {
     matrix[0] = matrix[3] = 1.f;
     matrix[1] = matrix[2] = 0.f;
 
+    const auto *p = &StructAfter (flags);
+#ifndef HB_NO_BEYOND_64K
+    if (flags & GID_IS_24BIT)
+      p += HBGlyphID24::static_size;
+    else
+#endif
+      p += HBGlyphID16::static_size;
     int tx, ty;
-    const HBINT8 *p = &StructAfter (glyphIndex);
     if (flags & ARG_1_AND_2_ARE_WORDS)
     {
       tx = *(const HBINT16 *) p;
@@ -144,63 +232,56 @@ struct CompositeGlyphRecord
     return tx || ty;
   }
 
-  public:
-  HBUINT16      flags;
-  HBGlyphID16   glyphIndex;
-  public:
-  DEFINE_SIZE_MIN (4);
-};
-
-struct composite_iter_t : hb_iter_with_fallback_t
-{
-  typedef const CompositeGlyphRecord *__item_t__;
-  composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) :
-      glyph (glyph_), current (nullptr), current_size (0)
+  hb_codepoint_t get_gid () const
   {
-    set_current (current_);
+#ifndef HB_NO_BEYOND_64K
+    if (flags & GID_IS_24BIT)
+      return StructAfter (flags);
+    else
+#endif
+      return StructAfter (flags);
   }
-
-  composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {}
-
-  item_t __item__ () const { return *current; }
-  bool __more__ () const { return current; }
-  void __next__ ()
+  void set_gid (hb_codepoint_t gid)
   {
-    if (!current->has_more ()) { current = nullptr; return; }
-
-    set_current (&StructAtOffset (current, current_size));
+#ifndef HB_NO_BEYOND_64K
+    if (flags & GID_IS_24BIT)
+      StructAfter (flags) = gid;
+    else
+#endif
+      /* TODO assert? */
+      StructAfter (flags) = gid;
   }
-  composite_iter_t __end__ () const { return composite_iter_t (); }
-  bool operator != (const composite_iter_t& o) const
-  { return current != o.current; }
 
-
-  void set_current (__item_t__ current_)
+#ifndef HB_NO_BEYOND_64K
+  void lower_gid_24_to_16 ()
   {
-    if (!glyph.check_range (current_, CompositeGlyphRecord::min_size))
-    {
-      current = nullptr;
-      current_size = 0;
-      return;
-    }
-    unsigned size = current_->get_size ();
-    if (!glyph.check_range (current_, size))
-    {
-      current = nullptr;
-      current_size = 0;
+    hb_codepoint_t gid = get_gid ();
+    if (!(flags & GID_IS_24BIT) || gid > 0xFFFFu)
       return;
-    }
 
-    current = current_;
-    current_size = size;
+    /* Lower the flag and move the rest of the struct down. */
+
+    unsigned size = get_size ();
+    char *end = (char *) this + size;
+    char *p = &StructAfter (flags);
+    p += HBGlyphID24::static_size;
+
+    flags = flags & ~GID_IS_24BIT;
+    set_gid (gid);
+
+    memmove (p - HBGlyphID24::static_size + HBGlyphID16::static_size, p, end - p);
   }
+#endif
 
-  private:
-  hb_bytes_t glyph;
-  __item_t__ current;
-  unsigned current_size;
+  protected:
+  HBUINT16      flags;
+  HBUINT24      pad;
+  public:
+  DEFINE_SIZE_MIN (4);
 };
 
+using composite_iter_t = composite_iter_tmpl;
+
 struct CompositeGlyph
 {
   const GlyphHeader &header;
@@ -248,6 +329,66 @@ struct CompositeGlyph
       return;
     glyph_chain.set_overlaps_flag ();
   }
+
+  bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes,
+                                  const contour_point_vector_t &points_with_deltas,
+                                  hb_bytes_t &dest_bytes /* OUT */)
+  {
+    if (source_bytes.length <= GlyphHeader::static_size ||
+        header.numberOfContours != -1)
+    {
+      dest_bytes = hb_bytes_t ();
+      return true;
+    }
+
+    unsigned source_len = source_bytes.length - GlyphHeader::static_size;
+
+    /* try to allocate more memories than source glyph bytes
+     * in case that there might be an overflow for int8 value
+     * and we would need to use int16 instead */
+    char *o = (char *) hb_calloc (source_len * 2, sizeof (char));
+    if (unlikely (!o)) return false;
+
+    const CompositeGlyphRecord *c = reinterpret_cast (source_bytes.arrayZ + GlyphHeader::static_size);
+    auto it = composite_iter_t (hb_bytes_t ((const char *)c, source_len), c);
+
+    char *p = o;
+    unsigned i = 0, source_comp_len = 0;
+    for (const auto &component : it)
+    {
+      /* last 4 points in points_with_deltas are phantom points and should not be included */
+      if (i >= points_with_deltas.length - 4) {
+        free (o);
+        return false;
+      }
+
+      unsigned comp_len = component.get_size ();
+      if (component.is_anchored ())
+      {
+        hb_memcpy (p, &component, comp_len);
+        p += comp_len;
+      }
+      else
+      {
+        unsigned new_len = component.compile_with_point (points_with_deltas[i], p);
+        p += new_len;
+      }
+      i++;
+      source_comp_len += comp_len;
+    }
+
+    //copy instructions if any
+    if (source_len > source_comp_len)
+    {
+      unsigned instr_len = source_len - source_comp_len;
+      hb_memcpy (p, (const char *)c + source_comp_len, instr_len);
+      p += instr_len;
+    }
+
+    unsigned len = p - o;
+    dest_bytes = hb_bytes_t (o, len);
+    return true;
+  }
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh
index 3d2095ac2df..1ebaaa3f831 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh
@@ -7,6 +7,8 @@
 #include "GlyphHeader.hh"
 #include "SimpleGlyph.hh"
 #include "CompositeGlyph.hh"
+#include "VarCompositeGlyph.hh"
+#include "coord-setter.hh"
 
 
 namespace OT {
@@ -27,7 +29,14 @@ enum phantom_point_index_t
 
 struct Glyph
 {
-  enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE };
+  enum glyph_type_t {
+    EMPTY,
+    SIMPLE,
+    COMPOSITE,
+#ifndef HB_NO_VAR_COMPOSITES
+    VAR_COMPOSITE,
+#endif
+  };
 
   public:
   composite_iter_t get_composite_iterator () const
@@ -35,12 +44,25 @@ struct Glyph
     if (type != COMPOSITE) return composite_iter_t ();
     return CompositeGlyph (*header, bytes).iter ();
   }
+  var_composite_iter_t get_var_composite_iterator () const
+  {
+#ifndef HB_NO_VAR_COMPOSITES
+    if (type != VAR_COMPOSITE) return var_composite_iter_t ();
+    return VarCompositeGlyph (*header, bytes).iter ();
+#else
+    return var_composite_iter_t ();
+#endif
+  }
 
   const hb_bytes_t trim_padding () const
   {
     switch (type) {
+#ifndef HB_NO_VAR_COMPOSITES
+    case VAR_COMPOSITE: return VarCompositeGlyph (*header, bytes).trim_padding ();
+#endif
     case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
     case SIMPLE:    return SimpleGlyph (*header, bytes).trim_padding ();
+    case EMPTY:     return bytes;
     default:        return bytes;
     }
   }
@@ -48,92 +70,318 @@ struct Glyph
   void drop_hints ()
   {
     switch (type) {
+#ifndef HB_NO_VAR_COMPOSITES
+    case VAR_COMPOSITE: return; // No hinting
+#endif
     case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
     case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints (); return;
-    default:        return;
+    case EMPTY:     return;
     }
   }
 
   void set_overlaps_flag ()
   {
     switch (type) {
+#ifndef HB_NO_VAR_COMPOSITES
+    case VAR_COMPOSITE: return; // No overlaps flag
+#endif
     case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
     case SIMPLE:    SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
-    default:        return;
+    case EMPTY:     return;
     }
   }
 
   void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
   {
     switch (type) {
+#ifndef HB_NO_VAR_COMPOSITES
+    case VAR_COMPOSITE: return; // No hinting
+#endif
     case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
     case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
-    default:        return;
+    case EMPTY:     return;
+    }
+  }
+
+  void update_mtx (const hb_subset_plan_t *plan,
+                   int xMin, int xMax,
+                   int yMin, int yMax,
+                   const contour_point_vector_t &all_points) const
+  {
+    hb_codepoint_t new_gid = 0;
+    if (!plan->new_gid_for_old_gid (gid, &new_gid))
+      return;
+
+    if (type != EMPTY)
+    {
+      plan->bounds_width_map.set (new_gid, xMax - xMin);
+      plan->bounds_height_map.set (new_gid, yMax - yMin);
+    }
+
+    unsigned len = all_points.length;
+    float leftSideX = all_points[len - 4].x;
+    float rightSideX = all_points[len - 3].x;
+    float topSideY = all_points[len - 2].y;
+    float bottomSideY = all_points[len - 1].y;
+
+    signed hori_aw = roundf (rightSideX - leftSideX);
+    if (hori_aw < 0) hori_aw = 0;
+    int lsb = roundf (xMin - leftSideX);
+    plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
+    //flag value should be computed using non-empty glyphs
+    if (type != EMPTY && lsb != xMin)
+      plan->head_maxp_info.allXMinIsLsb = false;
+
+    signed vert_aw = roundf (topSideY - bottomSideY);
+    if (vert_aw < 0) vert_aw = 0;
+    int tsb = roundf (topSideY - yMax);
+    plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
+  }
+
+  bool compile_header_bytes (const hb_subset_plan_t *plan,
+                             const contour_point_vector_t &all_points,
+                             hb_bytes_t &dest_bytes /* OUT */) const
+  {
+    GlyphHeader *glyph_header = nullptr;
+    if (!plan->pinned_at_default && type != EMPTY && all_points.length >= 4)
+    {
+      glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
+      if (unlikely (!glyph_header)) return false;
+    }
+
+    float xMin = 0, xMax = 0;
+    float yMin = 0, yMax = 0;
+    if (all_points.length > 4)
+    {
+      xMin = xMax = all_points[0].x;
+      yMin = yMax = all_points[0].y;
+    }
+
+    for (unsigned i = 1; i < all_points.length - 4; i++)
+    {
+      float x = all_points[i].x;
+      float y = all_points[i].y;
+      xMin = hb_min (xMin, x);
+      xMax = hb_max (xMax, x);
+      yMin = hb_min (yMin, y);
+      yMax = hb_max (yMax, y);
+    }
+
+    update_mtx (plan, roundf (xMin), roundf (xMax), roundf (yMin), roundf (yMax), all_points);
+
+    int rounded_xMin = roundf (xMin);
+    int rounded_xMax = roundf (xMax);
+    int rounded_yMin = roundf (yMin);
+    int rounded_yMax = roundf (yMax);
+
+    if (type != EMPTY)
+    {
+      plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, rounded_xMin);
+      plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, rounded_yMin);
+      plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, rounded_xMax);
+      plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, rounded_yMax);
+    }
+
+    /* when pinned at default, no need to compile glyph header
+     * and for empty glyphs: all_points only include phantom points.
+     * just update metrics and then return */
+    if (!glyph_header)
+      return true;
+
+    glyph_header->numberOfContours = header->numberOfContours;
+
+    glyph_header->xMin = rounded_xMin;
+    glyph_header->yMin = rounded_yMin;
+    glyph_header->xMax = rounded_xMax;
+    glyph_header->yMax = rounded_yMax;
+
+    dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size);
+    return true;
+  }
+
+  bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
+                                  hb_font_t *font,
+                                  const glyf_accelerator_t &glyf,
+                                  hb_bytes_t &dest_start,  /* IN/OUT */
+                                  hb_bytes_t &dest_end /* OUT */)
+  {
+    contour_point_vector_t all_points, points_with_deltas;
+    unsigned composite_contours = 0;
+    head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info;
+    unsigned *composite_contours_p = &composite_contours;
+
+    // don't compute head/maxp values when glyph has no contours(type is EMPTY)
+    // also ignore .notdef glyph when --notdef-outline is not enabled
+    if (type == EMPTY ||
+        (gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)))
+    {
+      head_maxp_info_p = nullptr;
+      composite_contours_p = nullptr;
+    }
+
+    if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
+      return false;
+
+    // .notdef, set type to empty so we only update metrics and don't compile bytes for
+    // it
+    if (gid == 0 &&
+        !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
+    {
+      type = EMPTY;
+      dest_start = hb_bytes_t ();
+      dest_end = hb_bytes_t ();
+    }
+
+    //dont compile bytes when pinned at default, just recalculate bounds
+    if (!plan->pinned_at_default)
+    {
+      switch (type)
+      {
+#ifndef HB_NO_VAR_COMPOSITES
+      case VAR_COMPOSITE:
+        // TODO
+        dest_end = hb_bytes_t ();
+        break;
+#endif
+
+      case COMPOSITE:
+        if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
+                                                                        points_with_deltas,
+                                                                        dest_end))
+          return false;
+        break;
+      case SIMPLE:
+        if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points,
+                                                                     plan->flags & HB_SUBSET_FLAGS_NO_HINTING,
+                                                                     dest_end))
+          return false;
+        break;
+      case EMPTY:
+        /* set empty bytes for empty glyph
+         * do not use source glyph's pointers */
+        dest_start = hb_bytes_t ();
+        dest_end = hb_bytes_t ();
+        break;
+      }
     }
+
+    if (!compile_header_bytes (plan, all_points, dest_start))
+    {
+      dest_end.fini ();
+      return false;
+    }
+    return true;
   }
 
+
   /* Note: Recursively calls itself.
    * all_points includes phantom points
    */
   template 
   bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
                    contour_point_vector_t &all_points /* OUT */,
+                   contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
+                   head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
+                   unsigned *composite_contours = nullptr, /* OUT */
+                   bool shift_points_hori = true,
+                   bool use_my_metrics = true,
                    bool phantom_only = false,
-                   unsigned int depth = 0) const
+                   hb_array_t coords = hb_array_t (),
+                   unsigned int depth = 0,
+                   unsigned *edge_count = nullptr) const
   {
     if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
+    unsigned stack_edge_count = 0;
+    if (!edge_count) edge_count = &stack_edge_count;
+    if (unlikely (*edge_count > HB_GLYF_MAX_EDGE_COUNT)) return false;
+    (*edge_count)++;
+
+    if (head_maxp_info)
+    {
+      head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
+    }
+
+    if (!coords)
+      coords = hb_array (font->coords, font->num_coords);
+
     contour_point_vector_t stack_points;
     bool inplace = type == SIMPLE && all_points.length == 0;
     /* Load into all_points if it's empty, as an optimization. */
     contour_point_vector_t &points = inplace ? all_points : stack_points;
 
     switch (type) {
+    case SIMPLE:
+      if (depth == 0 && head_maxp_info)
+        head_maxp_info->maxContours = hb_max (head_maxp_info->maxContours, (unsigned) header->numberOfContours);
+      if (depth > 0 && composite_contours)
+        *composite_contours += (unsigned) header->numberOfContours;
+      if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
+        return false;
+      break;
     case COMPOSITE:
     {
-      /* pseudo component points for each component in composite glyph */
-      unsigned num_points = hb_len (CompositeGlyph (*header, bytes).iter ());
-      if (unlikely (!points.resize (num_points))) return false;
+      for (auto &item : get_composite_iterator ())
+        if (unlikely (!item.get_points (points))) return false;
       break;
     }
-    case SIMPLE:
-      if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
-        return false;
+#ifndef HB_NO_VAR_COMPOSITES
+    case VAR_COMPOSITE:
+    {
+      for (auto &item : get_var_composite_iterator ())
+        if (unlikely (!item.get_points (points))) return false;
+    }
+#endif
+    case EMPTY:
       break;
     }
 
     /* Init phantom points */
     if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
-    hb_array_t phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
+    hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
     {
-      int h_delta = (int) header->xMin -
-                    glyf_accelerator.hmtx->get_side_bearing (gid);
+      int lsb = 0;
+      int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ?
+                    (int) header->xMin - lsb : 0;
+      HB_UNUSED int tsb = 0;
       int v_orig  = (int) header->yMax +
 #ifndef HB_NO_VERTICAL
-                    glyf_accelerator.vmtx->get_side_bearing (gid)
+                    ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb)
 #else
                     0
 #endif
                     ;
-      unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid);
+      unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid);
       unsigned v_adv =
 #ifndef HB_NO_VERTICAL
-                       glyf_accelerator.vmtx->get_advance (gid)
+                       glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
 #else
                        - font->face->get_upem ()
 #endif
                        ;
       phantoms[PHANTOM_LEFT].x = h_delta;
-      phantoms[PHANTOM_RIGHT].x = h_adv + h_delta;
+      phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
       phantoms[PHANTOM_TOP].y = v_orig;
       phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
     }
 
 #ifndef HB_NO_VAR
-    glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ());
+    glyf_accelerator.gvar->apply_deltas_to_points (gid,
+                                                   coords,
+                                                   points.as_array ());
 #endif
 
+    // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
+    // with child glyphs' points
+    if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
+    {
+      if (unlikely (!points_with_deltas->resize (points.length))) return false;
+      points_with_deltas->copy_vector (points);
+    }
+
     switch (type) {
     case SIMPLE:
+      if (depth == 0 && head_maxp_info)
+        head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, points.length - 4);
       if (!inplace)
         all_points.extend (points.as_array ());
       break;
@@ -144,21 +392,32 @@ struct Glyph
       for (auto &item : get_composite_iterator ())
       {
         comp_points.reset ();
-        if (unlikely (!glyf_accelerator.glyph_for_gid (item.glyphIndex)
-                                       .get_points (font, glyf_accelerator, comp_points,
-                                                    phantom_only, depth + 1)))
+        if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
+                                       .get_points (font,
+                                                    glyf_accelerator,
+                                                    comp_points,
+                                                    points_with_deltas,
+                                                    head_maxp_info,
+                                                    composite_contours,
+                                                    shift_points_hori,
+                                                    use_my_metrics,
+                                                    phantom_only,
+                                                    coords,
+                                                    depth + 1,
+                                                    edge_count)))
           return false;
 
         /* Copy phantom points from component if USE_MY_METRICS flag set */
-        if (item.is_use_my_metrics ())
+        if (use_my_metrics && item.is_use_my_metrics ())
           for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
             phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
 
-        /* Apply component transformation & translation */
-        item.transform_points (comp_points);
+        float matrix[4];
+        contour_point_t default_trans;
+        item.get_transformation (matrix, default_trans);
 
-        /* Apply translation from gvar */
-        comp_points.translate (points[comp_index]);
+        /* Apply component transformation & translation (with deltas applied) */
+        item.transform_points (comp_points, matrix, points[comp_index]);
 
         if (item.is_anchored ())
         {
@@ -174,18 +433,81 @@ struct Glyph
           }
         }
 
-        all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT));
+        all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT));
+
+        if (all_points.length > HB_GLYF_MAX_POINTS)
+          return false;
 
         comp_index++;
       }
 
+      if (head_maxp_info && depth == 0)
+      {
+        if (composite_contours)
+          head_maxp_info->maxCompositeContours = hb_max (head_maxp_info->maxCompositeContours, *composite_contours);
+        head_maxp_info->maxCompositePoints = hb_max (head_maxp_info->maxCompositePoints, all_points.length);
+        head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
+      }
+      all_points.extend (phantoms);
+    } break;
+#ifndef HB_NO_VAR_COMPOSITES
+    case VAR_COMPOSITE:
+    {
+      contour_point_vector_t comp_points;
+      hb_array_t points_left = points.as_array ();
+      for (auto &item : get_var_composite_iterator ())
+      {
+        unsigned item_num_points = item.get_num_points ();
+        hb_array_t record_points = points_left.sub_array (0, item_num_points);
+
+        comp_points.reset ();
+
+        auto component_coords = coords;
+        if (item.is_reset_unspecified_axes ())
+          component_coords = hb_array ();
+
+        coord_setter_t coord_setter (component_coords);
+        item.set_variations (coord_setter, record_points);
+
+        if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
+                                       .get_points (font,
+                                                    glyf_accelerator,
+                                                    comp_points,
+                                                    points_with_deltas,
+                                                    head_maxp_info,
+                                                    nullptr,
+                                                    shift_points_hori,
+                                                    use_my_metrics,
+                                                    phantom_only,
+                                                    coord_setter.get_coords (),
+                                                    depth + 1,
+                                                    edge_count)))
+          return false;
+
+        /* Apply component transformation */
+        item.transform_points (record_points, comp_points);
+
+        /* Copy phantom points from component if USE_MY_METRICS flag set */
+        if (use_my_metrics && item.is_use_my_metrics ())
+          for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
+            phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
+
+        all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT));
+
+        if (all_points.length > HB_GLYF_MAX_POINTS)
+          return false;
+
+        points_left += item_num_points;
+      }
       all_points.extend (phantoms);
     } break;
-    default:
+#endif
+    case EMPTY:
       all_points.extend (phantoms);
+      break;
     }
 
-    if (depth == 0) /* Apply at top level */
+    if (depth == 0 && shift_points_hori) /* Apply at top level */
     {
       /* Undocumented rasterizer behavior:
        * Shift points horizontally by the updated left side bearing
@@ -198,23 +520,34 @@ struct Glyph
     return !all_points.in_error ();
   }
 
-  bool get_extents (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator,
-                    hb_glyph_extents_t *extents) const
+  bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator,
+                                       hb_glyph_extents_t *extents) const
   {
     if (type == EMPTY) return true; /* Empty glyph; zero extents. */
-    return header->get_extents (font, glyf_accelerator, gid, extents);
+    return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents);
   }
 
   hb_bytes_t get_bytes () const { return bytes; }
-
-  Glyph (hb_bytes_t bytes_ = hb_bytes_t (),
-         hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_),
-                                                      header (bytes.as ()),
-                                                      gid (gid_)
+  glyph_type_t get_type () const { return type; }
+  const GlyphHeader *get_header () const { return header; }
+
+  Glyph () : bytes (),
+             header (bytes.as ()),
+             gid (-1),
+             type(EMPTY)
+  {}
+
+  Glyph (hb_bytes_t bytes_,
+         hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_),
+                                                header (bytes.as ()),
+                                                gid (gid_)
   {
     int num_contours = header->numberOfContours;
     if (unlikely (num_contours == 0)) type = EMPTY;
     else if (num_contours > 0) type = SIMPLE;
+#ifndef HB_NO_VAR_COMPOSITES
+    else if (num_contours == -2) type = VAR_COMPOSITE;
+#endif
     else type = COMPOSITE; /* negative numbers */
   }
 
@@ -222,7 +555,7 @@ struct Glyph
   hb_bytes_t bytes;
   const GlyphHeader *header;
   hb_codepoint_t gid;
-  unsigned type;
+  glyph_type_t type;
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh
index a2a83fe198a..72c99f88cdd 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh
@@ -14,15 +14,19 @@ struct GlyphHeader
   bool has_data () const { return numberOfContours; }
 
   template 
-  bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator,
-                    hb_codepoint_t gid, hb_glyph_extents_t *extents) const
+  bool get_extents_without_var_scaled (hb_font_t *font, const accelerator_t &glyf_accelerator,
+                                       hb_codepoint_t gid, hb_glyph_extents_t *extents) const
   {
     /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */
     /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
-    extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid));
-    extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax));
-    extents->width     = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax));
-    extents->height    = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax));
+    int lsb = hb_min (xMin, xMax);
+    (void) glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
+    extents->x_bearing = lsb;
+    extents->y_bearing = hb_max (yMin, yMax);
+    extents->width     = hb_max (xMin, xMax) - hb_min (xMin, xMax);
+    extents->height    = hb_min (yMin, yMax) - hb_max (yMin, yMax);
+
+    font->scale_glyph_extents (extents);
 
     return true;
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh
index 4b74967928b..bed9fc81d81 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh
@@ -20,7 +20,7 @@ struct SimpleGlyph
     FLAG_X_SAME         = 0x10,
     FLAG_Y_SAME         = 0x20,
     FLAG_OVERLAP_SIMPLE = 0x40,
-    FLAG_RESERVED2      = 0x80
+    FLAG_CUBIC          = 0x80
   };
 
   const GlyphHeader &header;
@@ -34,6 +34,11 @@ struct SimpleGlyph
   unsigned int length (unsigned int instruction_len) const
   { return instruction_len_offset () + 2 + instruction_len; }
 
+  bool has_instructions_length () const
+  {
+    return instruction_len_offset () + 2 <= bytes.length;
+  }
+
   unsigned int instructions_length () const
   {
     unsigned int instruction_length_offset = instruction_len_offset ();
@@ -94,6 +99,7 @@ struct SimpleGlyph
   /* zero instruction length */
   void drop_hints ()
   {
+    if (!has_instructions_length ()) return;
     GlyphHeader &glyph_header = const_cast (header);
     (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0;
   }
@@ -132,8 +138,8 @@ struct SimpleGlyph
         if (unlikely (p + 1 > end)) return false;
         unsigned int repeat_count = *p++;
         unsigned stop = hb_min (i + repeat_count, count);
-        for (; i < stop;)
-          points_.arrayZ[i++].flag = flag;
+        for (; i < stop; i++)
+          points_.arrayZ[i].flag = flag;
       }
     }
     return true;
@@ -184,7 +190,7 @@ struct SimpleGlyph
     if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false;
     unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
 
-    points_.alloc (num_points + 4); // Allocate for phantom points, to avoid a possible copy
+    points_.alloc (num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
     if (!points_.resize (num_points)) return false;
     if (phantom_only) return true;
 
@@ -206,6 +212,129 @@ struct SimpleGlyph
         && read_points (p, points_, end, &contour_point_t::y,
                         FLAG_Y_SHORT, FLAG_Y_SAME);
   }
+
+  static void encode_coord (int value,
+                            uint8_t &flag,
+                            const simple_glyph_flag_t short_flag,
+                            const simple_glyph_flag_t same_flag,
+                            hb_vector_t &coords /* OUT */)
+  {
+    if (value == 0)
+    {
+      flag |= same_flag;
+    }
+    else if (value >= -255 && value <= 255)
+    {
+      flag |= short_flag;
+      if (value > 0) flag |= same_flag;
+      else value = -value;
+
+      coords.arrayZ[coords.length++] = (uint8_t) value;
+    }
+    else
+    {
+      int16_t val = value;
+      coords.arrayZ[coords.length++] = val >> 8;
+      coords.arrayZ[coords.length++] = val & 0xff;
+    }
+  }
+
+  static void encode_flag (uint8_t &flag,
+                           uint8_t &repeat,
+                           uint8_t lastflag,
+                           hb_vector_t &flags /* OUT */)
+  {
+    if (flag == lastflag && repeat != 255)
+    {
+      repeat++;
+      if (repeat == 1)
+      {
+        /* We know there's room. */
+        flags.arrayZ[flags.length++] = flag;
+      }
+      else
+      {
+        unsigned len = flags.length;
+        flags.arrayZ[len-2] = flag | FLAG_REPEAT;
+        flags.arrayZ[len-1] = repeat;
+      }
+    }
+    else
+    {
+      repeat = 0;
+      flags.push (flag);
+    }
+  }
+
+  bool compile_bytes_with_deltas (const contour_point_vector_t &all_points,
+                                  bool no_hinting,
+                                  hb_bytes_t &dest_bytes /* OUT */)
+  {
+    if (header.numberOfContours == 0 || all_points.length <= 4)
+    {
+      dest_bytes = hb_bytes_t ();
+      return true;
+    }
+    unsigned num_points = all_points.length - 4;
+
+    hb_vector_t flags, x_coords, y_coords;
+    if (unlikely (!flags.alloc (num_points, true))) return false;
+    if (unlikely (!x_coords.alloc (2*num_points, true))) return false;
+    if (unlikely (!y_coords.alloc (2*num_points, true))) return false;
+
+    uint8_t lastflag = 255, repeat = 0;
+    int prev_x = 0, prev_y = 0;
+
+    for (unsigned i = 0; i < num_points; i++)
+    {
+      uint8_t flag = all_points.arrayZ[i].flag;
+      flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE;
+
+      int cur_x = roundf (all_points.arrayZ[i].x);
+      int cur_y = roundf (all_points.arrayZ[i].y);
+      encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords);
+      encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords);
+      encode_flag (flag, repeat, lastflag, flags);
+
+      prev_x = cur_x;
+      prev_y = cur_y;
+      lastflag = flag;
+    }
+
+    unsigned len_before_instrs = 2 * header.numberOfContours + 2;
+    unsigned len_instrs = instructions_length ();
+    unsigned total_len = len_before_instrs + flags.length + x_coords.length + y_coords.length;
+
+    if (!no_hinting)
+      total_len += len_instrs;
+
+    char *p = (char *) hb_malloc (total_len);
+    if (unlikely (!p)) return false;
+
+    const char *src = bytes.arrayZ + GlyphHeader::static_size;
+    char *cur = p;
+    hb_memcpy (p, src, len_before_instrs);
+
+    cur += len_before_instrs;
+    src += len_before_instrs;
+
+    if (!no_hinting)
+    {
+      hb_memcpy (cur, src, len_instrs);
+      cur += len_instrs;
+    }
+
+    hb_memcpy (cur, flags.arrayZ, flags.length);
+    cur += flags.length;
+
+    hb_memcpy (cur, x_coords.arrayZ, x_coords.length);
+    cur += x_coords.length;
+
+    hb_memcpy (cur, y_coords.arrayZ, y_coords.length);
+
+    dest_bytes = hb_bytes_t (p, total_len);
+    return true;
+  }
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh
index 8106a80338e..d6ce5be07bb 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh
@@ -6,27 +6,35 @@
 
 
 namespace OT {
+
+struct glyf_accelerator_t;
+
 namespace glyf_impl {
 
 
 struct SubsetGlyph
 {
-  hb_codepoint_t new_gid;
   hb_codepoint_t old_gid;
   Glyph source_glyph;
   hb_bytes_t dest_start;  /* region of source_glyph to copy first */
   hb_bytes_t dest_end;    /* region of source_glyph to copy second */
+  bool allocated;
 
   bool serialize (hb_serialize_context_t *c,
                   bool use_short_loca,
-                  const hb_subset_plan_t *plan) const
+                  const hb_subset_plan_t *plan)
   {
     TRACE_SERIALIZE (this);
 
     hb_bytes_t dest_glyph = dest_start.copy (c);
-    dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length);
+    hb_bytes_t end_copy = dest_end.copy (c);
+    if (!end_copy.arrayZ || !dest_glyph.arrayZ) {
+      return false;
+    }
+
+    dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + end_copy.length);
     unsigned int pad_length = use_short_loca ? padding () : 0;
-    DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
+    DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
 
     HBUINT8 pad;
     pad = 0;
@@ -38,13 +46,68 @@ struct SubsetGlyph
 
     if (unlikely (!dest_glyph.length)) return_trace (true);
 
-    /* update components gids */
+    /* update components gids. */
     for (auto &_ : Glyph (dest_glyph).get_composite_iterator ())
     {
       hb_codepoint_t new_gid;
-      if (plan->new_gid_for_old_gid (_.glyphIndex, &new_gid))
-        const_cast (_).glyphIndex = new_gid;
+      if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
+        const_cast (_).set_gid (new_gid);
     }
+#ifndef HB_NO_VAR_COMPOSITES
+    for (auto &_ : Glyph (dest_glyph).get_var_composite_iterator ())
+    {
+      hb_codepoint_t new_gid;
+      if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
+        const_cast (_).set_gid (new_gid);
+    }
+#endif
+
+#ifndef HB_NO_BEYOND_64K
+    auto it = Glyph (dest_glyph).get_composite_iterator ();
+    if (it)
+    {
+      /* lower GID24 to GID16 in components if possible.
+       *
+       * TODO: VarComposite. Not as critical, since VarComposite supports
+       * gid24 from the first version. */
+      char *p = it ? (char *) &*it : nullptr;
+      char *q = p;
+      const char *end = dest_glyph.arrayZ + dest_glyph.length;
+      while (it)
+      {
+        auto &rec = const_cast (*it);
+        ++it;
+
+        q += rec.get_size ();
+
+        rec.lower_gid_24_to_16 ();
+
+        unsigned size = rec.get_size ();
+
+        memmove (p, &rec, size);
+
+        p += size;
+      }
+      memmove (p, q, end - q);
+      p += end - q;
+
+      /* We want to shorten the glyph, but we can't do that without
+       * updating the length in the loca table, which is already
+       * written out :-(.  So we just fill the rest of the glyph with
+       * harmless instructions, since that's what they will be
+       * interpreted as.
+       *
+       * Should move the lowering to _populate_subset_glyphs() to
+       * fix this issue. */
+
+      hb_memset (p, 0x7A /* TrueType instruction ROFF; harmless */, end - p);
+      p += end - p;
+      dest_glyph = hb_bytes_t (dest_glyph.arrayZ, p - (char *) dest_glyph.arrayZ);
+
+      // TODO: Padding; & trim serialized bytes.
+      // TODO: Update length in loca. Ugh.
+    }
+#endif
 
     if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
       Glyph (dest_glyph).drop_hints ();
@@ -55,6 +118,23 @@ struct SubsetGlyph
     return_trace (true);
   }
 
+  bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
+                                  hb_font_t *font,
+                                  const glyf_accelerator_t &glyf)
+  {
+    allocated = source_glyph.compile_bytes_with_deltas (plan, font, glyf, dest_start, dest_end);
+    return allocated;
+  }
+
+  void free_compiled_bytes ()
+  {
+    if (likely (allocated)) {
+      allocated = false;
+      dest_start.fini ();
+      dest_end.fini ();
+    }
+  }
+
   void drop_hints_bytes ()
   { source_glyph.drop_hints_bytes (dest_start, dest_end); }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh
new file mode 100644
index 00000000000..f2dcb065e26
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh
@@ -0,0 +1,371 @@
+#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH
+#define OT_GLYF_VARCOMPOSITEGLYPH_HH
+
+
+#include "../../hb-open-type.hh"
+#include "coord-setter.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+struct VarCompositeGlyphRecord
+{
+  protected:
+  enum var_composite_glyph_flag_t
+  {
+    USE_MY_METRICS              = 0x0001,
+    AXIS_INDICES_ARE_SHORT      = 0x0002,
+    UNIFORM_SCALE               = 0x0004,
+    HAVE_TRANSLATE_X            = 0x0008,
+    HAVE_TRANSLATE_Y            = 0x0010,
+    HAVE_ROTATION               = 0x0020,
+    HAVE_SCALE_X                = 0x0040,
+    HAVE_SCALE_Y                = 0x0080,
+    HAVE_SKEW_X                 = 0x0100,
+    HAVE_SKEW_Y                 = 0x0200,
+    HAVE_TCENTER_X              = 0x0400,
+    HAVE_TCENTER_Y              = 0x0800,
+    GID_IS_24BIT                = 0x1000,
+    AXES_HAVE_VARIATION         = 0x2000,
+    RESET_UNSPECIFIED_AXES      = 0x4000,
+  };
+
+  public:
+
+  unsigned int get_size () const
+  {
+    unsigned int size = min_size;
+
+    unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 4 : 3;
+    size += numAxes * axis_width;
+
+    // gid
+    size += 2;
+    if (flags & GID_IS_24BIT)           size += 1;
+
+    if (flags & HAVE_TRANSLATE_X)       size += 2;
+    if (flags & HAVE_TRANSLATE_Y)       size += 2;
+    if (flags & HAVE_ROTATION)          size += 2;
+    if (flags & HAVE_SCALE_X)           size += 2;
+    if (flags & HAVE_SCALE_Y)           size += 2;
+    if (flags & HAVE_SKEW_X)            size += 2;
+    if (flags & HAVE_SKEW_Y)            size += 2;
+    if (flags & HAVE_TCENTER_X)         size += 2;
+    if (flags & HAVE_TCENTER_Y)         size += 2;
+
+    return size;
+  }
+
+  bool has_more () const { return true; }
+
+  bool is_use_my_metrics () const { return flags & USE_MY_METRICS; }
+  bool is_reset_unspecified_axes () const { return flags & RESET_UNSPECIFIED_AXES; }
+
+  hb_codepoint_t get_gid () const
+  {
+    if (flags & GID_IS_24BIT)
+      return StructAfter (numAxes);
+    else
+      return StructAfter (numAxes);
+  }
+
+  void set_gid (hb_codepoint_t gid)
+  {
+    if (flags & GID_IS_24BIT)
+      StructAfter (numAxes) = gid;
+    else
+      StructAfter (numAxes) = gid;
+  }
+
+  unsigned get_numAxes () const
+  {
+    return numAxes;
+  }
+
+  unsigned get_num_points () const
+  {
+    unsigned num = 0;
+    if (flags & AXES_HAVE_VARIATION)                    num += numAxes;
+    if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))  num++;
+    if (flags & HAVE_ROTATION)                          num++;
+    if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y))          num++;
+    if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y))            num++;
+    if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y))      num++;
+    return num;
+  }
+
+  void transform_points (hb_array_t record_points,
+                         contour_point_vector_t &points) const
+  {
+    float matrix[4];
+    contour_point_t trans;
+
+    get_transformation_from_points (record_points, matrix, trans);
+
+    points.transform (matrix);
+    points.translate (trans);
+  }
+
+  static inline void transform (float (&matrix)[4], contour_point_t &trans,
+                                float (other)[6])
+  {
+    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268
+    float xx1 = other[0];
+    float xy1 = other[1];
+    float yx1 = other[2];
+    float yy1 = other[3];
+    float dx1 = other[4];
+    float dy1 = other[5];
+    float xx2 = matrix[0];
+    float xy2 = matrix[1];
+    float yx2 = matrix[2];
+    float yy2 = matrix[3];
+    float dx2 = trans.x;
+    float dy2 = trans.y;
+
+    matrix[0] = xx1*xx2 + xy1*yx2;
+    matrix[1] = xx1*xy2 + xy1*yy2;
+    matrix[2] = yx1*xx2 + yy1*yx2;
+    matrix[3] = yx1*xy2 + yy1*yy2;
+    trans.x = xx2*dx1 + yx2*dy1 + dx2;
+    trans.y = xy2*dx1 + yy2*dy1 + dy2;
+  }
+
+  static void translate (float (&matrix)[4], contour_point_t &trans,
+                         float translateX, float translateY)
+  {
+    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L213
+    float other[6] = {1.f, 0.f, 0.f, 1.f, translateX, translateY};
+    transform (matrix, trans, other);
+  }
+
+  static void scale (float (&matrix)[4], contour_point_t &trans,
+                     float scaleX, float scaleY)
+  {
+    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L224
+    float other[6] = {scaleX, 0.f, 0.f, scaleY, 0.f, 0.f};
+    transform (matrix, trans, other);
+  }
+
+  static void rotate (float (&matrix)[4], contour_point_t &trans,
+                      float rotation)
+  {
+    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
+    rotation = rotation * HB_PI;
+    float c = cosf (rotation);
+    float s = sinf (rotation);
+    float other[6] = {c, s, -s, c, 0.f, 0.f};
+    transform (matrix, trans, other);
+  }
+
+  static void skew (float (&matrix)[4], contour_point_t &trans,
+                    float skewX, float skewY)
+  {
+    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
+    skewX = skewX * HB_PI;
+    skewY = skewY * HB_PI;
+    float other[6] = {1.f, tanf (skewY), tanf (skewX), 1.f, 0.f, 0.f};
+    transform (matrix, trans, other);
+  }
+
+  bool get_points (contour_point_vector_t &points) const
+  {
+    float translateX = 0.f;
+    float translateY = 0.f;
+    float rotation = 0.f;
+    float scaleX = 1.f * (1 << 10);
+    float scaleY = 1.f * (1 << 10);
+    float skewX = 0.f;
+    float skewY = 0.f;
+    float tCenterX = 0.f;
+    float tCenterY = 0.f;
+
+    unsigned num_points = get_num_points ();
+
+    if (unlikely (!points.resize (points.length + num_points))) return false;
+
+    unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
+    unsigned axes_size = numAxes * axis_width;
+
+    const F2DOT14 *q = (const F2DOT14 *) (axes_size +
+                                          (flags & GID_IS_24BIT ? 3 : 2) +
+                                          &StructAfter (numAxes));
+
+    hb_array_t rec_points = points.as_array ().sub_array (points.length - num_points);
+
+    unsigned count = numAxes;
+    if (flags & AXES_HAVE_VARIATION)
+    {
+      for (unsigned i = 0; i < count; i++)
+        rec_points[i].x = q++->to_int ();
+      rec_points += count;
+    }
+    else
+      q += count;
+
+    const HBUINT16 *p = (const HBUINT16 *) q;
+
+    if (flags & HAVE_TRANSLATE_X)       translateX = * (const FWORD *) p++;
+    if (flags & HAVE_TRANSLATE_Y)       translateY = * (const FWORD *) p++;
+    if (flags & HAVE_ROTATION)          rotation = ((const F4DOT12 *) p++)->to_int ();
+    if (flags & HAVE_SCALE_X)           scaleX = ((const F6DOT10 *) p++)->to_int ();
+    if (flags & HAVE_SCALE_Y)           scaleY = ((const F6DOT10 *) p++)->to_int ();
+    if (flags & HAVE_SKEW_X)            skewX = ((const F4DOT12 *) p++)->to_int ();
+    if (flags & HAVE_SKEW_Y)            skewY = ((const F4DOT12 *) p++)->to_int ();
+    if (flags & HAVE_TCENTER_X)         tCenterX = * (const FWORD *) p++;
+    if (flags & HAVE_TCENTER_Y)         tCenterY = * (const FWORD *) p++;
+
+    if ((flags & UNIFORM_SCALE) && !(flags & HAVE_SCALE_Y))
+      scaleY = scaleX;
+
+    if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
+    {
+      rec_points[0].x = translateX;
+      rec_points[0].y = translateY;
+      rec_points++;
+    }
+    if (flags & HAVE_ROTATION)
+    {
+      rec_points[0].x = rotation;
+      rec_points++;
+    }
+    if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y))
+    {
+      rec_points[0].x = scaleX;
+      rec_points[0].y = scaleY;
+      rec_points++;
+    }
+    if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y))
+    {
+      rec_points[0].x = skewX;
+      rec_points[0].y = skewY;
+      rec_points++;
+    }
+    if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
+    {
+      rec_points[0].x = tCenterX;
+      rec_points[0].y = tCenterY;
+      rec_points++;
+    }
+    assert (!rec_points);
+
+    return true;
+  }
+
+  void get_transformation_from_points (hb_array_t rec_points,
+                                       float (&matrix)[4], contour_point_t &trans) const
+  {
+    if (flags & AXES_HAVE_VARIATION)
+      rec_points += numAxes;
+
+    matrix[0] = matrix[3] = 1.f;
+    matrix[1] = matrix[2] = 0.f;
+    trans.init (0.f, 0.f);
+
+    float translateX = 0.f;
+    float translateY = 0.f;
+    float rotation = 0.f;
+    float scaleX = 1.f;
+    float scaleY = 1.f;
+    float skewX = 0.f;
+    float skewY = 0.f;
+    float tCenterX = 0.f;
+    float tCenterY = 0.f;
+
+    if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
+    {
+      translateX = rec_points[0].x;
+      translateY = rec_points[0].y;
+      rec_points++;
+    }
+    if (flags & HAVE_ROTATION)
+    {
+      rotation = rec_points[0].x / (1 << 12);
+      rec_points++;
+    }
+    if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y))
+    {
+      scaleX = rec_points[0].x / (1 << 10);
+      scaleY = rec_points[0].y / (1 << 10);
+      rec_points++;
+    }
+    if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y))
+    {
+      skewX = rec_points[0].x / (1 << 12);
+      skewY = rec_points[0].y / (1 << 12);
+      rec_points++;
+    }
+    if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
+    {
+      tCenterX = rec_points[0].x;
+      tCenterY = rec_points[0].y;
+      rec_points++;
+    }
+    assert (!rec_points);
+
+    translate (matrix, trans, translateX + tCenterX, translateY + tCenterY);
+    rotate (matrix, trans, rotation);
+    scale (matrix, trans, scaleX, scaleY);
+    skew (matrix, trans, -skewX, skewY);
+    translate (matrix, trans, -tCenterX, -tCenterY);
+  }
+
+  void set_variations (coord_setter_t &setter,
+                       hb_array_t rec_points) const
+  {
+    bool have_variations = flags & AXES_HAVE_VARIATION;
+    unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
+
+    const HBUINT8  *p = (const HBUINT8 *)  (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2));
+    const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2));
+
+    const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + numAxes) : (HBUINT8 *) (q + numAxes)));
+
+    unsigned count = numAxes;
+    for (unsigned i = 0; i < count; i++)
+    {
+      unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++;
+
+      signed v = have_variations ? rec_points[i].x : a++->to_int ();
+
+      v = hb_clamp (v, -(1<<14), (1<<14));
+      setter[axis_index] = v;
+    }
+  }
+
+  protected:
+  HBUINT16      flags;
+  HBUINT8       numAxes;
+  public:
+  DEFINE_SIZE_MIN (3);
+};
+
+using var_composite_iter_t = composite_iter_tmpl;
+
+struct VarCompositeGlyph
+{
+  const GlyphHeader &header;
+  hb_bytes_t bytes;
+  VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
+    header (header_), bytes (bytes_) {}
+
+  var_composite_iter_t iter () const
+  { return var_composite_iter_t (bytes, &StructAfter (header)); }
+
+  const hb_bytes_t trim_padding () const
+  {
+    unsigned length = GlyphHeader::static_size;
+    for (auto &comp : iter ())
+      length += comp.get_size ();
+    return bytes.sub_array (0, length);
+  }
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+
+#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/composite-iter.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/composite-iter.hh
new file mode 100644
index 00000000000..21e6b4ee962
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/composite-iter.hh
@@ -0,0 +1,68 @@
+#ifndef OT_GLYF_COMPOSITE_ITER_HH
+#define OT_GLYF_COMPOSITE_ITER_HH
+
+
+#include "../../hb.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+template 
+struct composite_iter_tmpl : hb_iter_with_fallback_t,
+                                                     const CompositeGlyphRecord &>
+{
+  typedef const CompositeGlyphRecord *__item_t__;
+  composite_iter_tmpl (hb_bytes_t glyph_, __item_t__ current_) :
+      glyph (glyph_), current (nullptr), current_size (0)
+  {
+    set_current (current_);
+  }
+
+  composite_iter_tmpl () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {}
+
+  const CompositeGlyphRecord & __item__ () const { return *current; }
+  bool __more__ () const { return current; }
+  void __next__ ()
+  {
+    if (!current->has_more ()) { current = nullptr; return; }
+
+    set_current (&StructAtOffset (current, current_size));
+  }
+  composite_iter_tmpl __end__ () const { return composite_iter_tmpl (); }
+  bool operator != (const composite_iter_tmpl& o) const
+  { return current != o.current; }
+
+
+  void set_current (__item_t__ current_)
+  {
+    if (!glyph.check_range (current_, CompositeGlyphRecord::min_size))
+    {
+      current = nullptr;
+      current_size = 0;
+      return;
+    }
+    unsigned size = current_->get_size ();
+    if (!glyph.check_range (current_, size))
+    {
+      current = nullptr;
+      current_size = 0;
+      return;
+    }
+
+    current = current_;
+    current_size = size;
+  }
+
+  private:
+  hb_bytes_t glyph;
+  __item_t__ current;
+  unsigned current_size;
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+#endif /* OT_GLYF_COMPOSITE_ITER_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh
new file mode 100644
index 00000000000..df64ed5af7d
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh
@@ -0,0 +1,34 @@
+#ifndef OT_GLYF_COORD_SETTER_HH
+#define OT_GLYF_COORD_SETTER_HH
+
+
+#include "../../hb.hh"
+
+
+namespace OT {
+namespace glyf_impl {
+
+
+struct coord_setter_t
+{
+  coord_setter_t (hb_array_t coords) :
+    coords (coords) {}
+
+  int& operator [] (unsigned idx)
+  {
+    if (coords.length < idx + 1)
+      coords.resize (idx + 1);
+    return coords[idx];
+  }
+
+  hb_array_t get_coords ()
+  { return coords.as_array (); }
+
+  hb_vector_t coords;
+};
+
+
+} /* namespace glyf_impl */
+} /* namespace OT */
+
+#endif /* OT_GLYF_COORD_SETTER_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh
index 1fad84ce831..18e2d92d0f4 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh
@@ -16,7 +16,7 @@ template
 static void
-_write_loca (IteratorIn it, bool short_offsets, IteratorOut dest)
+_write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest)
 {
   unsigned right_shift = short_offsets ? 1 : 0;
   unsigned int offset = 0;
@@ -25,7 +25,7 @@ _write_loca (IteratorIn it, bool short_offsets, IteratorOut dest)
   | hb_map ([=, &offset] (unsigned int padded_size)
             {
               offset += padded_size;
-              DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset);
+              DEBUG_MSG (SUBSET, nullptr, "loca entry offset %u", offset);
               return offset >> right_shift;
             })
   | hb_sink (dest)
@@ -44,6 +44,20 @@ _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
 
   head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
   head_prime->indexToLocFormat = use_short_loca ? 0 : 1;
+  if (plan->normalized_coords)
+  {
+    head_prime->xMin = plan->head_maxp_info.xMin;
+    head_prime->xMax = plan->head_maxp_info.xMax;
+    head_prime->yMin = plan->head_maxp_info.yMin;
+    head_prime->yMax = plan->head_maxp_info.yMax;
+
+    unsigned orig_flag = head_prime->flags;
+    if (plan->head_maxp_info.allXMinIsLsb)
+      orig_flag |= 1 << 1;
+    else
+      orig_flag &= ~(1 << 1);
+    head_prime->flags = orig_flag;
+  }
   bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
 
   hb_blob_destroy (head_prime_blob);
@@ -61,7 +75,7 @@ _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_s
 
   if (unlikely (!loca_prime_data)) return false;
 
-  DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d",
+  DEBUG_MSG (SUBSET, nullptr, "loca entry_size %u num_offsets %u size %u",
              entry_size, num_offsets, entry_size * num_offsets);
 
   if (use_short_loca)
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 caaf2d1f2eb..d2a93a56d85 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh
@@ -7,6 +7,7 @@
 #include "../../hb-ot-hmtx-table.hh"
 #include "../../hb-ot-var-gvar-table.hh"
 #include "../../hb-draw.hh"
+#include "../../hb-paint.hh"
 
 #include "glyf-helpers.hh"
 #include "Glyph.hh"
@@ -24,13 +25,18 @@ namespace OT {
  */
 #define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
 
-
 struct glyf
 {
   friend struct glyf_accelerator_t;
 
   static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
 
+  static bool has_valid_glyf_format(const hb_face_t* face)
+  {
+    const OT::head &head = *face->table.head;
+    return head.indexToLocFormat <= 1 && head.glyphDataFormat <= 1;
+  }
+
   bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
   {
     TRACE_SANITIZE (this);
@@ -46,8 +52,11 @@ struct glyf
                   const hb_subset_plan_t *plan)
   {
     TRACE_SERIALIZE (this);
+
     unsigned init_len = c->length ();
-    for (const auto &_ : it) _.serialize (c, use_short_loca, plan);
+    for (auto &_ : it)
+      if (unlikely (!_.serialize (c, use_short_loca, plan)))
+        return false;
 
     /* As a special case when all glyph in the font are empty, add a zero byte
      * to the table, so that OTS doesn’t reject it, and to make the table work
@@ -69,39 +78,83 @@ struct glyf
   {
     TRACE_SUBSET (this);
 
+    if (!has_valid_glyf_format (c->plan->source)) {
+      // glyf format is unknown don't attempt to subset it.
+      DEBUG_MSG (SUBSET, nullptr,
+                 "unkown glyf format, dropping from subset.");
+      return_trace (false);
+    }
+
     glyf *glyf_prime = c->serializer->start_embed  ();
     if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
 
+    hb_font_t *font = nullptr;
+    if (c->plan->normalized_coords)
+    {
+      font = _create_font_for_instancing (c->plan);
+      if (unlikely (!font)) return false;
+    }
+
+    hb_vector_t padded_offsets;
+    unsigned num_glyphs = c->plan->num_output_glyphs ();
+    if (unlikely (!padded_offsets.resize (num_glyphs)))
+    {
+      hb_font_destroy (font);
+      return false;
+    }
+
     hb_vector_t glyphs;
-    _populate_subset_glyphs (c->plan, &glyphs);
+    if (!_populate_subset_glyphs (c->plan, font, glyphs))
+    {
+      hb_font_destroy (font);
+      return false;
+    }
 
-    auto padded_offsets =
-    + hb_iter (glyphs)
-    | hb_map (&glyf_impl::SubsetGlyph::padded_size)
-    ;
+    if (font)
+      hb_font_destroy (font);
 
-    unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0);
-    bool use_short_loca = max_offset < 0x1FFFF;
+    unsigned max_offset = 0;
+    for (unsigned i = 0; i < num_glyphs; i++)
+    {
+      padded_offsets[i] = glyphs[i].padded_size ();
+      max_offset += padded_offsets[i];
+    }
 
+    bool use_short_loca = false;
+    if (likely (!c->plan->force_long_loca))
+      use_short_loca = max_offset < 0x1FFFF;
 
-    glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
     if (!use_short_loca) {
-      padded_offsets =
-          + hb_iter (glyphs)
-          | hb_map (&glyf_impl::SubsetGlyph::length)
-          ;
+      for (unsigned i = 0; i < num_glyphs; i++)
+        padded_offsets[i] = glyphs[i].length ();
     }
 
+    bool result = glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan);
+    if (c->plan->normalized_coords && !c->plan->pinned_at_default)
+      _free_compiled_subset_glyphs (glyphs);
+
+    if (!result) return false;
 
     if (unlikely (c->serializer->in_error ())) return_trace (false);
+
     return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan,
-                                                                               padded_offsets,
+                                                                               padded_offsets.iter (),
                                                                                use_short_loca)));
   }
 
-  void
+  bool
   _populate_subset_glyphs (const hb_subset_plan_t   *plan,
-                           hb_vector_t *glyphs /* OUT */) const;
+                           hb_font_t                *font,
+                           hb_vector_t &glyphs /* OUT */) const;
+
+  hb_font_t *
+  _create_font_for_instancing (const hb_subset_plan_t *plan) const;
+
+  void _free_compiled_subset_glyphs (hb_vector_t &glyphs) const
+  {
+    for (unsigned i = 0; i < glyphs.length; i++)
+      glyphs[i].free_compiled_bytes ();
+  }
 
   protected:
   UnsizedArrayOf
@@ -128,7 +181,7 @@ struct glyf_accelerator_t
     vmtx = nullptr;
 #endif
     const OT::head &head = *face->table.head;
-    if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0)
+    if (!glyf::has_valid_glyf_format (face))
       /* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
       return;
     short_offset = 0 == head.indexToLocFormat;
@@ -166,7 +219,7 @@ struct glyf_accelerator_t
     contour_point_vector_t all_points;
 
     bool phantom_only = !consumer.is_consuming_contour_points ();
-    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only)))
+    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only)))
       return false;
 
     if (consumer.is_consuming_contour_points ())
@@ -188,12 +241,15 @@ struct glyf_accelerator_t
     return true;
   }
 
+  public:
+
 #ifndef HB_NO_VAR
   struct points_aggregator_t
   {
     hb_font_t *font;
     hb_glyph_extents_t *extents;
     contour_point_t *phantoms;
+    bool scaled;
 
     struct contour_bounds_t
     {
@@ -209,7 +265,7 @@ struct glyf_accelerator_t
 
       bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
 
-      void get_extents (hb_font_t *font, hb_glyph_extents_t *extents)
+      void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled)
       {
         if (unlikely (empty ()))
         {
@@ -219,49 +275,54 @@ struct glyf_accelerator_t
           extents->y_bearing = 0;
           return;
         }
-        extents->x_bearing = font->em_scalef_x (min_x);
-        extents->width = font->em_scalef_x (max_x) - extents->x_bearing;
-        extents->y_bearing = font->em_scalef_y (max_y);
-        extents->height = font->em_scalef_y (min_y) - extents->y_bearing;
+        {
+          extents->x_bearing = roundf (min_x);
+          extents->width = roundf (max_x - extents->x_bearing);
+          extents->y_bearing = roundf (max_y);
+          extents->height = roundf (min_y - extents->y_bearing);
+
+          if (scaled)
+            font->scale_glyph_extents (extents);
+        }
       }
 
       protected:
       float min_x, min_y, max_x, max_y;
     } bounds;
 
-    points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_)
+    points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_)
     {
       font = font_;
       extents = extents_;
       phantoms = phantoms_;
+      scaled = scaled_;
       if (extents) bounds = contour_bounds_t ();
     }
 
     void consume_point (const contour_point_t &point) { bounds.add (point); }
-    void points_end () { bounds.get_extents (font, extents); }
+    void points_end () { bounds.get_extents (font, extents, scaled); }
 
     bool is_consuming_contour_points () { return extents; }
     contour_point_t *get_phantoms_sink () { return phantoms; }
   };
 
-  public:
   unsigned
-  get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
+  get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
   {
     if (unlikely (gid >= num_glyphs)) return 0;
 
     bool success = false;
 
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
-    if (likely (font->num_coords == gvar->get_axis_count ()))
-      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms));
+    if (font->num_coords)
+      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false));
 
     if (unlikely (!success))
       return
 #ifndef HB_NO_VERTICAL
-        is_vertical ? vmtx->get_advance (gid) :
+        is_vertical ? vmtx->get_advance_without_var_unscaled (gid) :
 #endif
-        hmtx->get_advance (gid);
+        hmtx->get_advance_without_var_unscaled (gid);
 
     float result = is_vertical
                  ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
@@ -269,26 +330,32 @@ struct glyf_accelerator_t
     return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
   }
 
-  int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
+  bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const
   {
-    if (unlikely (gid >= num_glyphs)) return 0;
+    if (unlikely (gid >= num_glyphs)) return false;
 
     hb_glyph_extents_t extents;
 
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
-    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms))))
-      return
-#ifndef HB_NO_VERTICAL
-        is_vertical ? vmtx->get_side_bearing (gid) :
-#endif
-        hmtx->get_side_bearing (gid);
+    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false))))
+      return false;
 
-    return is_vertical
-         ? ceilf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing
-         : floorf (phantoms[glyf_impl::PHANTOM_LEFT].x);
+    *lsb = is_vertical
+         ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing
+         : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x);
+    return true;
   }
 #endif
 
+  bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const
+  {
+    if (unlikely (gid >= num_glyphs)) return false;
+    if (is_vertical) return false; // TODO Humm, what to do here?
+
+    *lsb = glyph_for_gid (gid).get_header ()->xMin;
+    return true;
+  }
+
   public:
   bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
   {
@@ -296,9 +363,18 @@ struct glyf_accelerator_t
 
 #ifndef HB_NO_VAR
     if (font->num_coords)
-      return get_points (font, gid, points_aggregator_t (font, extents, nullptr));
+      return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true));
 #endif
-    return glyph_for_gid (gid).get_extents (font, *this, extents);
+    return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
+  }
+
+  bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
+  {
+    funcs->push_clip_glyph (data, gid, font);
+    funcs->color (data, true, foreground);
+    funcs->pop_clip (data);
+
+    return true;
   }
 
   const glyf_impl::Glyph
@@ -349,37 +425,77 @@ struct glyf_accelerator_t
 };
 
 
-inline void
+inline bool
 glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
-                               hb_vector_t *glyphs /* OUT */) const
+                               hb_font_t *font,
+                               hb_vector_t& glyphs /* OUT */) const
 {
   OT::glyf_accelerator_t glyf (plan->source);
+  unsigned num_glyphs = plan->num_output_glyphs ();
+  if (!glyphs.resize (num_glyphs)) return false;
 
-  + hb_range (plan->num_output_glyphs ())
-  | hb_map ([&] (hb_codepoint_t new_gid)
-        {
-          glyf_impl::SubsetGlyph subset_glyph = {0};
-          subset_glyph.new_gid = new_gid;
-
-          /* should never fail: all old gids should be mapped */
-          if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid))
-            return subset_glyph;
-
-          if (new_gid == 0 &&
-              !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
-            subset_glyph.source_glyph = glyf_impl::Glyph ();
-          else
-            subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
-          if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
-            subset_glyph.drop_hints_bytes ();
-          else
-            subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
-          return subset_glyph;
-        })
-  | hb_sink (glyphs)
-  ;
+  for (auto p : plan->glyph_map->iter ())
+  {
+    unsigned new_gid = p.second;
+    glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid];
+    subset_glyph.old_gid = p.first;
+
+    if (unlikely (new_gid == 0 &&
+                  !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
+                  !plan->normalized_coords)
+      subset_glyph.source_glyph = glyf_impl::Glyph ();
+    else
+    {
+      /* If plan has an accelerator, the preprocessing step already trimmed glyphs.
+       * Don't trim them again! */
+      subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator);
+    }
+
+    if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
+      subset_glyph.drop_hints_bytes ();
+    else
+      subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
+
+    if (font)
+    {
+      if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf)))
+      {
+        // when pinned at default, only bounds are updated, thus no need to free
+        if (!plan->pinned_at_default)
+          _free_compiled_subset_glyphs (glyphs);
+        return false;
+      }
+    }
+  }
+  return true;
 }
 
+inline hb_font_t *
+glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
+{
+  hb_font_t *font = hb_font_create (plan->source);
+  if (unlikely (font == hb_font_get_empty ())) return nullptr;
+
+  hb_vector_t vars;
+  if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true)))
+  {
+    hb_font_destroy (font);
+    return nullptr;
+  }
+
+  for (auto _ : plan->user_axes_location)
+  {
+    hb_variation_t var;
+    var.tag = _.first;
+    var.value = _.second;
+    vars.push (var);
+  }
+
+#ifndef HB_NO_VAR
+  hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
+#endif
+  return font;
+}
 
 
 } /* namespace OT */
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh
index 853c7ed1eaa..6a476204f10 100644
--- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh
+++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh
@@ -26,22 +26,29 @@ struct path_builder_t
 
     optional_point_t lerp (optional_point_t p, float t)
     { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
-  } first_oncurve, first_offcurve, last_offcurve;
+  } first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2;
 
   path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
   {
     font = font_;
     draw_session = &draw_session_;
-    first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+    first_oncurve = first_offcurve = first_offcurve2 = last_offcurve = last_offcurve2 = optional_point_t ();
   }
 
   /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
      See also:
      * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
-     * https://stackoverflow.com/a/20772557 */
+     * https://stackoverflow.com/a/20772557
+     *
+     * Cubic support added. */
   void consume_point (const contour_point_t &point)
   {
     bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
+#ifdef HB_NO_CUBIC_GLYF
+    bool is_cubic = false;
+#else
+    bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
+#endif
     optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
     if (!first_oncurve)
     {
@@ -52,7 +59,12 @@ struct path_builder_t
       }
       else
       {
-        if (first_offcurve)
+        if (is_cubic && !first_offcurve2)
+        {
+          first_offcurve2 = first_offcurve;
+          first_offcurve = p;
+        }
+        else if (first_offcurve)
         {
           optional_point_t mid = first_offcurve.lerp (p, .5f);
           first_oncurve = mid;
@@ -69,16 +81,41 @@ struct path_builder_t
       {
         if (is_on_curve)
         {
-          draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-                                     p.x, p.y);
+          if (last_offcurve2)
+          {
+            draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+                                    last_offcurve.x, last_offcurve.y,
+                                    p.x, p.y);
+            last_offcurve2 = optional_point_t ();
+          }
+          else
+            draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+                                       p.x, p.y);
           last_offcurve = optional_point_t ();
         }
         else
         {
-          optional_point_t mid = last_offcurve.lerp (p, .5f);
-          draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-                                     mid.x, mid.y);
-          last_offcurve = p;
+          if (is_cubic && !last_offcurve2)
+          {
+            last_offcurve2 = last_offcurve;
+            last_offcurve = p;
+          }
+          else
+          {
+            optional_point_t mid = last_offcurve.lerp (p, .5f);
+
+            if (is_cubic)
+            {
+              draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+                                      last_offcurve.x, last_offcurve.y,
+                                      mid.x, mid.y);
+              last_offcurve2 = optional_point_t ();
+            }
+            else
+              draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+                                         mid.x, mid.y);
+            last_offcurve = p;
+          }
         }
       }
       else
@@ -94,19 +131,40 @@ struct path_builder_t
     {
       if (first_offcurve && last_offcurve)
       {
-        optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
-        draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-                                   mid.x, mid.y);
+        optional_point_t mid = last_offcurve.lerp (first_offcurve2 ?
+                                                   first_offcurve2 :
+                                                   first_offcurve, .5f);
+        if (last_offcurve2)
+          draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+                                  last_offcurve.x, last_offcurve.y,
+                                  mid.x, mid.y);
+        else
+          draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+                                     mid.x, mid.y);
         last_offcurve = optional_point_t ();
-        /* now check the rest */
       }
+      /* now check the rest */
 
       if (first_offcurve && first_oncurve)
-        draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
-                                   first_oncurve.x, first_oncurve.y);
+      {
+        if (first_offcurve2)
+          draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
+                                  first_offcurve.x, first_offcurve.y,
+                                  first_oncurve.x, first_oncurve.y);
+        else
+          draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
+                                     first_oncurve.x, first_oncurve.y);
+      }
       else if (last_offcurve && first_oncurve)
-        draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-                                   first_oncurve.x, first_oncurve.y);
+      {
+        if (last_offcurve2)
+          draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+                                  last_offcurve.x, last_offcurve.y,
+                                  first_oncurve.x, first_oncurve.y);
+        else
+          draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+                                     first_oncurve.x, first_oncurve.y);
+      }
       else if (first_oncurve)
         draw_session->line_to (first_oncurve.x, first_oncurve.y);
       else if (first_offcurve)
@@ -117,7 +175,7 @@ struct path_builder_t
       }
 
       /* Getting ready for the next contour */
-      first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+      first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
       draw_session->close_path ();
     }
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh b/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh
new file mode 100644
index 00000000000..15ff7a8bdb7
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh
@@ -0,0 +1,589 @@
+/*
+ * 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.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef OT_NAME_NAME_HH
+#define OT_NAME_NAME_HH
+
+#include "../../hb-open-type.hh"
+#include "../../hb-ot-name-language.hh"
+#include "../../hb-aat-layout.hh"
+#include "../../hb-utf.hh"
+
+
+namespace OT {
+
+template 
+inline unsigned int
+hb_ot_name_convert_utf (hb_bytes_t                       bytes,
+                        unsigned int                    *text_size /* IN/OUT */,
+                        typename out_utf_t::codepoint_t *text /* OUT */)
+{
+  unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t);
+  const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ;
+  const typename in_utf_t::codepoint_t *src_end = src + src_len;
+
+  typename out_utf_t::codepoint_t *dst = text;
+
+  hb_codepoint_t unicode;
+  const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
+
+  if (text_size && *text_size)
+  {
+    (*text_size)--; /* Save room for NUL-termination. */
+    const typename out_utf_t::codepoint_t *dst_end = text + *text_size;
+
+    while (src < src_end && dst < dst_end)
+    {
+      const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement);
+      typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode);
+      if (dst_next == dst)
+        break; /* Out-of-room. */
+
+      dst = dst_next;
+      src = src_next;
+    }
+
+    *text_size = dst - text;
+    *dst = 0; /* NUL-terminate. */
+  }
+
+  /* Accumulate length of rest. */
+  unsigned int dst_len = dst - text;
+  while (src < src_end)
+  {
+    src = in_utf_t::next (src, src_end, &unicode, replacement);
+    dst_len += out_utf_t::encode_len (unicode);
+  }
+  return dst_len;
+}
+
+#define entry_score var.u16[0]
+#define entry_index var.u16[1]
+
+
+/*
+ * name -- Naming
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/name
+ */
+#define HB_OT_TAG_name HB_TAG('n','a','m','e')
+
+#define UNSUPPORTED     42
+
+struct NameRecord
+{
+  hb_language_t language (hb_face_t *face) const
+  {
+#ifndef HB_NO_OT_NAME_LANGUAGE
+    unsigned int p = platformID;
+    unsigned int l = languageID;
+
+    if (p == 3)
+      return _hb_ot_name_language_for_ms_code (l);
+
+    if (p == 1)
+      return _hb_ot_name_language_for_mac_code (l);
+
+#ifndef HB_NO_OT_NAME_LANGUAGE_AAT
+    if (p == 0)
+      return face->table.ltag->get_language (l);
+#endif
+
+#endif
+    return HB_LANGUAGE_INVALID;
+  }
+
+  uint16_t score () const
+  {
+    /* Same order as in cmap::find_best_subtable(). */
+    unsigned int p = platformID;
+    unsigned int e = encodingID;
+
+    /* 32-bit. */
+    if (p == 3 && e == 10) return 0;
+    if (p == 0 && e ==  6) return 1;
+    if (p == 0 && e ==  4) return 2;
+
+    /* 16-bit. */
+    if (p == 3 && e ==  1) return 3;
+    if (p == 0 && e ==  3) return 4;
+    if (p == 0 && e ==  2) return 5;
+    if (p == 0 && e ==  1) return 6;
+    if (p == 0 && e ==  0) return 7;
+
+    /* Symbol. */
+    if (p == 3 && e ==  0) return 8;
+
+    /* We treat all Mac Latin names as ASCII only. */
+    if (p == 1 && e ==  0) return 10; /* 10 is magic number :| */
+
+    return UNSUPPORTED;
+  }
+
+  NameRecord* copy (hb_serialize_context_t *c, const void *base
+#ifdef HB_EXPERIMENTAL_API
+                    , const hb_hashmap_t *name_table_overrides
+#endif
+                    ) const
+  {
+    TRACE_SERIALIZE (this);
+    HB_UNUSED auto snap = c->snapshot ();
+    auto *out = c->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+#ifdef HB_EXPERIMENTAL_API
+    hb_ot_name_record_ids_t record_ids (platformID, encodingID, languageID, nameID);
+    hb_bytes_t* name_bytes;
+
+    if (name_table_overrides->has (record_ids, &name_bytes)) {
+      hb_bytes_t encoded_bytes = *name_bytes;
+      char *name_str_utf16_be = nullptr;
+
+      if (platformID != 1)
+      {
+        unsigned text_size = hb_ot_name_convert_utf (*name_bytes, nullptr, nullptr);
+
+        text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf()
+        unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
+        name_str_utf16_be = (char *) hb_calloc (byte_len, 1);
+        if (!name_str_utf16_be)
+        {
+          c->revert (snap);
+          return_trace (nullptr);
+        }
+        hb_ot_name_convert_utf (*name_bytes, &text_size,
+                                                          (hb_utf16_be_t::codepoint_t *) name_str_utf16_be);
+
+        unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
+        if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
+          c->revert (snap);
+          hb_free (name_str_utf16_be);
+          return_trace (nullptr);
+        }
+
+        encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len);
+      }
+      else
+      {
+        // mac platform, copy the UTF-8 string(all ascii characters) as is
+        if (!c->check_assign (out->length, encoded_bytes.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
+          c->revert (snap);
+          return_trace (nullptr);
+        }
+      }
+
+      out->offset = 0;
+      c->push ();
+      encoded_bytes.copy (c);
+      c->add_link (out->offset, c->pop_pack (), hb_serialize_context_t::Tail, 0);
+      hb_free (name_str_utf16_be);
+    }
+    else
+#endif
+    {
+      out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length);
+    }
+    return_trace (out);
+  }
+
+  bool isUnicode () const
+  {
+    unsigned int p = platformID;
+    unsigned int e = encodingID;
+
+    return (p == 0 ||
+            (p == 3 && (e == 0 || e == 1 || e == 10)));
+  }
+
+  static int cmp (const void *pa, const void *pb)
+  {
+    const NameRecord *a = (const NameRecord *)pa;
+    const NameRecord *b = (const NameRecord *)pb;
+
+    if (a->platformID != b->platformID)
+      return a->platformID - b->platformID;
+
+    if (a->encodingID != b->encodingID)
+      return a->encodingID - b->encodingID;
+
+    if (a->languageID != b->languageID)
+      return a->languageID - b->languageID;
+
+    if (a->nameID != b->nameID)
+      return a->nameID - b->nameID;
+
+    if (a->length != b->length)
+      return a->length - b->length;
+
+    return 0;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && offset.sanitize (c, base, length));
+  }
+
+  HBUINT16      platformID;     /* Platform ID. */
+  HBUINT16      encodingID;     /* Platform-specific encoding ID. */
+  HBUINT16      languageID;     /* Language ID. */
+  HBUINT16      nameID;         /* Name ID. */
+  HBUINT16      length;         /* String length (in bytes). */
+  NNOffset16To>
+                offset;         /* String offset from start of storage area (in bytes). */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+static int
+_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact)
+{
+  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
+  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
+
+  /* Compare by name_id, then language. */
+
+  if (a->name_id != b->name_id)
+    return a->name_id - b->name_id;
+
+  if (a->language == b->language) return 0;
+  if (!a->language) return -1;
+  if (!b->language) return +1;
+
+  const char *astr = hb_language_to_string (a->language);
+  const char *bstr = hb_language_to_string (b->language);
+
+  signed c = strcmp (astr, bstr);
+
+  // 'a' is the user request, and 'b' is string in the font.
+  // If eg. user asks for "en-us" and font has "en", approve.
+  if (!exact && c &&
+      hb_language_matches (b->language, a->language))
+    return 0;
+
+  return c;
+}
+
+static int
+_hb_ot_name_entry_cmp (const void *pa, const void *pb)
+{
+  /* Compare by name_id, then language, then score, then index. */
+
+  int v = _hb_ot_name_entry_cmp_key (pa, pb, true);
+  if (v)
+    return v;
+
+  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
+  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
+
+  if (a->entry_score != b->entry_score)
+    return a->entry_score - b->entry_score;
+
+  if (a->entry_index != b->entry_index)
+    return a->entry_index - b->entry_index;
+
+  return 0;
+}
+
+struct name
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_name;
+
+  unsigned int get_size () const
+  { return min_size + count * nameRecordZ.item_size; }
+
+  template 
+  bool serialize (hb_serialize_context_t *c,
+                  Iterator it,
+                  const void *src_string_pool
+#ifdef HB_EXPERIMENTAL_API
+                  , const hb_vector_t& insert_name_records
+                  , const hb_hashmap_t *name_table_overrides
+#endif
+                  )
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely (!c->extend_min ((*this))))  return_trace (false);
+
+    unsigned total_count = it.len ()
+#ifdef HB_EXPERIMENTAL_API
+        + insert_name_records.length
+#endif
+        ;
+    this->format = 0;
+    if (!c->check_assign (this->count, total_count, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+      return false;
+
+    NameRecord *name_records = (NameRecord *) hb_calloc (total_count, NameRecord::static_size);
+    if (unlikely (!name_records)) return_trace (false);
+
+    hb_array_t records (name_records, total_count);
+
+    for (const NameRecord& record : it)
+    {
+      hb_memcpy (name_records, &record, NameRecord::static_size);
+      name_records++;
+    }
+
+#ifdef HB_EXPERIMENTAL_API
+    for (unsigned i = 0; i < insert_name_records.length; i++)
+    {
+      const hb_ot_name_record_ids_t& ids = insert_name_records[i];
+      NameRecord record;
+      record.platformID = ids.platform_id;
+      record.encodingID = ids.encoding_id;
+      record.languageID = ids.language_id;
+      record.nameID = ids.name_id;
+      record.length = 0; // handled in NameRecord copy()
+      record.offset = 0;
+      memcpy (name_records, &record, NameRecord::static_size);
+      name_records++;
+    }
+#endif
+
+    records.qsort ();
+
+    c->copy_all (records,
+                 src_string_pool
+#ifdef HB_EXPERIMENTAL_API
+                 , name_table_overrides
+#endif
+                 );
+    hb_free (records.arrayZ);
+
+
+    if (unlikely (c->ran_out_of_room ())) return_trace (false);
+
+    this->stringOffset = c->length ();
+
+    return_trace (true);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+
+    name *name_prime = c->serializer->start_embed ();
+    if (unlikely (!name_prime)) return_trace (false);
+
+#ifdef HB_EXPERIMENTAL_API
+    const hb_hashmap_t *name_table_overrides =
+        &c->plan->name_table_overrides;
+#endif
+
+    auto it =
+    + nameRecordZ.as_array (count)
+    | hb_filter (c->plan->name_ids, &NameRecord::nameID)
+    | hb_filter (c->plan->name_languages, &NameRecord::languageID)
+    | hb_filter ([&] (const NameRecord& namerecord) {
+      return
+          (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY)
+          || namerecord.isUnicode ();
+    })
+#ifdef HB_EXPERIMENTAL_API
+    | hb_filter ([&] (const NameRecord& namerecord) {
+      if (name_table_overrides->is_empty ())
+        return true;
+      hb_ot_name_record_ids_t rec_ids (namerecord.platformID,
+                                       namerecord.encodingID,
+                                       namerecord.languageID,
+                                       namerecord.nameID);
+
+      hb_bytes_t *p;
+      if (name_table_overrides->has (rec_ids, &p) &&
+          (*p).length == 0)
+        return false;
+      return true;
+    })
+#endif
+    ;
+
+#ifdef HB_EXPERIMENTAL_API
+    hb_hashmap_t retained_name_record_ids;
+    for (const NameRecord& rec : it)
+    {
+      hb_ot_name_record_ids_t rec_ids (rec.platformID,
+                                       rec.encodingID,
+                                       rec.languageID,
+                                       rec.nameID);
+      retained_name_record_ids.set (rec_ids, 1);
+    }
+
+    hb_vector_t insert_name_records;
+    if (!name_table_overrides->is_empty ())
+    {
+      if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true)))
+        return_trace (false);
+      for (const auto& record_ids : name_table_overrides->keys ())
+      {
+        if (name_table_overrides->get (record_ids).length == 0)
+          continue;
+        if (retained_name_record_ids.has (record_ids))
+          continue;
+        insert_name_records.push (record_ids);
+      }
+    }
+#endif
+
+    return (name_prime->serialize (c->serializer, it,
+                                   std::addressof (this + stringOffset)
+#ifdef HB_EXPERIMENTAL_API
+                                   , insert_name_records
+                                   , name_table_overrides
+#endif
+                                   ));
+  }
+
+  bool sanitize_records (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    const void *string_pool = (this+stringOffset).arrayZ;
+    return_trace (nameRecordZ.sanitize (c, count, string_pool));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  likely (format == 0 || format == 1) &&
+                  c->check_array (nameRecordZ.arrayZ, count) &&
+                  c->check_range (this, stringOffset) &&
+                  sanitize_records (c));
+  }
+
+  struct accelerator_t
+  {
+    accelerator_t (hb_face_t *face)
+    {
+      this->table = hb_sanitize_context_t ().reference_table (face);
+      assert (this->table.get_length () >= this->table->stringOffset);
+      this->pool = (const char *) (const void *) (this->table+this->table->stringOffset);
+      this->pool_len = this->table.get_length () - this->table->stringOffset;
+      const hb_array_t all_names (this->table->nameRecordZ.arrayZ,
+                                                    this->table->count);
+
+      this->names.alloc (all_names.length, true);
+
+      for (unsigned int i = 0; i < all_names.length; i++)
+      {
+        hb_ot_name_entry_t *entry = this->names.push ();
+
+        entry->name_id = all_names[i].nameID;
+        entry->language = all_names[i].language (face);
+        entry->entry_score =  all_names[i].score ();
+        entry->entry_index = i;
+      }
+
+      this->names.qsort (_hb_ot_name_entry_cmp);
+      /* Walk and pick best only for each name_id,language pair,
+       * while dropping unsupported encodings. */
+      unsigned int j = 0;
+      for (unsigned int i = 0; i < this->names.length; i++)
+      {
+        if (this->names[i].entry_score == UNSUPPORTED ||
+            this->names[i].language == HB_LANGUAGE_INVALID)
+          continue;
+        if (i &&
+            this->names[i - 1].name_id  == this->names[i].name_id &&
+            this->names[i - 1].language == this->names[i].language)
+          continue;
+        this->names[j++] = this->names[i];
+      }
+      this->names.resize (j);
+    }
+    ~accelerator_t ()
+    {
+      this->table.destroy ();
+    }
+
+    int get_index (hb_ot_name_id_t  name_id,
+                   hb_language_t    language,
+                   unsigned int    *width=nullptr) const
+    {
+      const hb_ot_name_entry_t key = {name_id, {0}, language};
+      const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
+                                                    this->names.length,
+                                                    sizeof (hb_ot_name_entry_t),
+                                                    _hb_ot_name_entry_cmp_key,
+                                                    true);
+
+      if (!entry)
+      {
+        entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
+                            this->names.length,
+                            sizeof (hb_ot_name_entry_t),
+                            _hb_ot_name_entry_cmp_key,
+                            false);
+      }
+
+      if (!entry)
+        return -1;
+
+      if (width)
+        *width = entry->entry_score < 10 ? 2 : 1;
+
+      return entry->entry_index;
+    }
+
+    hb_bytes_t get_name (unsigned int idx) const
+    {
+      const hb_array_t all_names (table->nameRecordZ.arrayZ, table->count);
+      const NameRecord &record = all_names[idx];
+      const hb_bytes_t string_pool (pool, pool_len);
+      return string_pool.sub_array (record.offset, record.length);
+    }
+
+    private:
+    const char *pool;
+    unsigned int pool_len;
+    public:
+    hb_blob_ptr_t table;
+    hb_vector_t names;
+  };
+
+  public:
+  /* We only implement format 0 for now. */
+  HBUINT16      format;         /* Format selector (=0/1). */
+  HBUINT16      count;          /* Number of name records. */
+  NNOffset16To>
+                stringOffset;   /* Offset to start of string storage (from start of table). */
+  UnsizedArrayOf
+                nameRecordZ;    /* The name records where count is the number of records. */
+  public:
+  DEFINE_SIZE_ARRAY (6, nameRecordZ);
+};
+
+#undef entry_index
+#undef entry_score
+
+struct name_accelerator_t : name::accelerator_t {
+  name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {}
+};
+
+} /* namespace OT */
+
+
+#endif /* OT_NAME_NAME_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/UPDATING.txt b/src/java.desktop/share/native/libharfbuzz/UPDATING.txt
index 34434ddef04..6b4e7ccc4fe 100644
--- a/src/java.desktop/share/native/libharfbuzz/UPDATING.txt
+++ b/src/java.desktop/share/native/libharfbuzz/UPDATING.txt
@@ -1,53 +1,119 @@
 Tips and tasks when updating harfbuzz sources to a newer version.
 -----------------------------------------------------------------
 
+STEP 1: UPDATING FILES
+----------------------
+Download and unzip the latest version from https://github.com/harfbuzz/harfbuzz/releases
 We only use files from the src directory and even then only the ones we need.
 So just C++ include and source files and only the ones needed for the library,
-and even then just the ones we use. Do NOT just copy everything.
+and even then just the ones we use.
+IMPORTANT! DO NOT just copy everything.
 
-So one way to update is to
-
-- copy over from the updated harfbuzz the exact same files we already have
-- it isn't a flat directory so watch out for that
-- any that are no longer available (copy fails) we remove but these may come
-  back later if they were actually renamed
-- look for files in the destination that were NOT updated - perhaps they
-  are gone in the upstream - or renamed. Remove them if they are really
-  obsolete, or add their replacements/renames.
-- iterate over : build and see what new file is missing that causes a build failure
-- when this is done we have something buildable
-- make sure it builds on all supported platforms.
-- Harfbuzz is not modular so it is not easy,
+- Harfbuzz is not modular, so the update is not a straightforward process.
 - The main thing is we do NOT want any
    * "test" programs (test in the name, have a main are clues)
    * support for (eg) GLib, DirectWrite, Graphite, GDI, ICU, Uniscribe
    * aggregators like harfbuzz.cc - since it includes things from the above
      as well as hb-ft.cc which we specifically exclude in the Makefile
-  * but we do use core text support on macOS.
-  * I really wish that "src" were just library source but I expect the authors
+   * but we do use core text support on macOS.
+   * I really wish that "src" were just library source, but I expect the authors
     have their reasons.
 
-- we do not apply any header file changes so this is not an issue
+So one way to update is to
+
+- copy over from the updated harfbuzz the exact same files we already have
+- it isn't a flat directory so watch out for that.
+
+- For files that are no longer available (for which copy fails), we remove such files,
+  but these may come back later if they were actually renamed.
+
+- look for files in the destination that were NOT updated - perhaps they
+  are removed or renamed in the upstream. Remove them if they are really
+  obsolete, or add their replacements/renames.
+  In IntelliJ IDE:
+    Newly added files are shown in RED
+    Modified in BLUE
+    NOT Updated in WHITE
+  This feature might be helpful to keep track of new, modified and unchanged files.
+
+
+STEP 2: BUILD CHANGES INCREMENTALLY
+-----------------------------------
+- iterate over : build and see what new file is missing that causes a build failure.
+  Sometimes just running a build does not show up any failures due to stale files.
+  Clean followed by build would be helpful in this situation.
+
+- You might run into compiler warnings that are treated as errors or the requirement
+  to set certain compiler flags if the build fails on a specific platform.
+  Check "COMPILER WARNINGS AND SETTING FLAGS" section for more details.
+
+- when this is done we have something buildable, make sure it builds
+  on all supported platforms.
+
+
+STEP 3: COMPILER WARNINGS AND SETTING FLAGS
+-------------------------------------------
+- Update make parameters in Awt2DLibraries.gmk
+  Since we don't use configure we need to manually specify the options
+  we need in the Harfbuzz section of Awt2DLibraries.gmk.
+  As well as adding new options, we may need to clean up obsolete options.
+  Note there may be platform variations in the flags.
+
+- As with other 3rd party libs we do not fix the code to eliminate compiler
+  warnings unless they are critical and clearly avoiding a bug. Even then
+  we'd report it upstream and apply the patch once it is made available.
+  The usual practice is do just disable the warnings.
+
+
+STEP 4: UPDATING .md FILE
+-------------------------
+- we do not apply any header file changes so this is not an issue.
+
 - verify the license text is unchanged (extra steps are needed if it is) and update
-   src/java.desktop/share/legal/harfbuzz.md with the new version
+  src/java.desktop/share/legal/harfbuzz.md with the new version.
+
+
+STEP 5: REPLACE TABS & REMOVE TRAILING SPACES
+---------------------------------------------
 - clean up trailing white space and tabs to follow jcheck rules.
   Use "expand" and "sed" to remove tabs and trailing white space from the
   imported sources.
-- test using all the automated jtreg tests on all platforms
-- do manual verification of Arabic, Hebrew, Thai, Indic against previous releases. 
-  Look for manual related layout jtreg tests and run on Windows,Linux and Mac.
+  To clean up the extra spaces and tabs run the following script at
+  each folder level within libharfbuzz.
+
+  shopt -s nullglob
+  for f in *.c *.h *.cc *.hh;
+      do
+          # replace tabs with spaces
+          expand ${f} > ${f}.tmp;
+          mv ${f}.tmp $f;
+
+          # fix line endings to LF
+          sed -e 's/\r$//g' ${f} > ${f}.tmp;
+          mv ${f}.tmp $f;
+
+          # remove trailing spaces
+          sed -e 's/[ ]* $//g' ${f} > ${f}.tmp;
+          mv ${f}.tmp $f;
+      done
+
+
+STEP 6: TESTING
+---------------
+- test using all the automated jtreg tests on all platforms.
+
+- do MANUAL verification of Arabic, Hebrew, Thai, Indic against previous releases.
+  Look for manual related layout jtreg tests (test/jdk/java/awt/font/TextLayout)
+  and run on Windows,Linux and Mac.
   Use Font2DTest set to TextLayout and check the above languages. Probably
   not going to see layout problems a code point at a time but it needs to
   be checked.
 
-- Update make parameters as needed
-  Since we don't use configure we need to manually specify the options
-  we need in the harfbuzz section of Awt2DLibraries.gmk.
-  As well as adding new options, we may need to clean up obsolete options.
-  Note there may be platform variations in the flags.
+  Different unicode combinations can be checked using Font2DTest.
+  Run Font2DTest, select 'UserText' option for 'Text to use'.
+  Paste unicodes of different languages (Arabic, Hebrew, Thai, Indic)
+  and compare the glyphs with previous versions.
+  It should look the same in both cases.
 
-- As with other 3rd party libs we do not fix the code to eliminate compiler
-  warnings unless they are critical and clearly avoiding a bug. Even then
-  we'd report it upstream. The usual practice is do just disable the warnings
 
-- Update THIS UPDATING.txt file too if it is outdated.
+- FINALLY, Do update THIS UPDATING.txt file too if it is outdated.
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh
new file mode 100644
index 00000000000..c2e24a70678
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh
@@ -0,0 +1,216 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#include "graph.hh"
+#include "../hb-ot-layout-common.hh"
+
+#ifndef GRAPH_CLASSDEF_GRAPH_HH
+#define GRAPH_CLASSDEF_GRAPH_HH
+
+namespace graph {
+
+struct ClassDefFormat1 : public OT::ClassDefFormat1_3
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    constexpr unsigned min_size = OT::ClassDefFormat1_3::min_size;
+    if (vertex_len < min_size) return false;
+    return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size ();
+  }
+};
+
+struct ClassDefFormat2 : public OT::ClassDefFormat2_4
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    constexpr unsigned min_size = OT::ClassDefFormat2_4::min_size;
+    if (vertex_len < min_size) return false;
+    return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
+  }
+};
+
+struct ClassDef : public OT::ClassDef
+{
+  template
+  static bool add_class_def (gsubgpos_graph_context_t& c,
+                             unsigned parent_id,
+                             unsigned link_position,
+                             It glyph_and_class,
+                             unsigned max_size)
+  {
+    unsigned class_def_prime_id = c.graph.new_node (nullptr, nullptr);
+    auto& class_def_prime_vertex = c.graph.vertices_[class_def_prime_id];
+    if (!make_class_def (c, glyph_and_class, class_def_prime_id, max_size))
+      return false;
+
+    auto* class_def_link = c.graph.vertices_[parent_id].obj.real_links.push ();
+    class_def_link->width = SmallTypes::size;
+    class_def_link->objidx = class_def_prime_id;
+    class_def_link->position = link_position;
+    class_def_prime_vertex.parents.push (parent_id);
+
+    return true;
+  }
+
+  template
+  static bool make_class_def (gsubgpos_graph_context_t& c,
+                              It glyph_and_class,
+                              unsigned dest_obj,
+                              unsigned max_size)
+  {
+    char* buffer = (char*) hb_calloc (1, max_size);
+    hb_serialize_context_t serializer (buffer, max_size);
+    OT::ClassDef_serialize (&serializer, glyph_and_class);
+    serializer.end_serialize ();
+    if (serializer.in_error ())
+    {
+      hb_free (buffer);
+      return false;
+    }
+
+    hb_bytes_t class_def_copy = serializer.copy_bytes ();
+    c.add_buffer ((char *) class_def_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
+
+    auto& obj = c.graph.vertices_[dest_obj].obj;
+    obj.head = (char *) class_def_copy.arrayZ;
+    obj.tail = obj.head + class_def_copy.length;
+
+    hb_free (buffer);
+    return true;
+  }
+
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < OT::ClassDef::min_size) return false;
+    switch (u.format)
+    {
+    case 1: return ((ClassDefFormat1*)this)->sanitize (vertex);
+    case 2: return ((ClassDefFormat2*)this)->sanitize (vertex);
+#ifndef HB_NO_BEYOND_64K
+    // Not currently supported
+    case 3:
+    case 4:
+#endif
+    default: return false;
+    }
+  }
+};
+
+
+struct class_def_size_estimator_t
+{
+  template
+  class_def_size_estimator_t (It glyph_and_class)
+      : gids_consecutive (true), num_ranges_per_class (), glyphs_per_class ()
+  {
+    unsigned last_gid = (unsigned) -1;
+    for (auto p : + glyph_and_class)
+    {
+      unsigned gid = p.first;
+      unsigned klass = p.second;
+
+      if (last_gid != (unsigned) -1 && gid != last_gid + 1)
+        gids_consecutive = false;
+      last_gid = gid;
+
+      hb_set_t* glyphs;
+      if (glyphs_per_class.has (klass, &glyphs) && glyphs) {
+        glyphs->add (gid);
+        continue;
+      }
+
+      hb_set_t new_glyphs;
+      new_glyphs.add (gid);
+      glyphs_per_class.set (klass, std::move (new_glyphs));
+    }
+
+    if (in_error ()) return;
+
+    for (unsigned klass : glyphs_per_class.keys ())
+    {
+      if (!klass) continue; // class 0 doesn't get encoded.
+
+      const hb_set_t& glyphs = glyphs_per_class.get (klass);
+      hb_codepoint_t start = HB_SET_VALUE_INVALID;
+      hb_codepoint_t end = HB_SET_VALUE_INVALID;
+
+      unsigned count = 0;
+      while (glyphs.next_range (&start, &end))
+        count++;
+
+      num_ranges_per_class.set (klass, count);
+    }
+  }
+
+  // Incremental increase in the Coverage and ClassDef table size
+  // (worst case) if all glyphs associated with 'klass' were added.
+  unsigned incremental_coverage_size (unsigned klass) const
+  {
+    // Coverage takes 2 bytes per glyph worst case,
+    return 2 * glyphs_per_class.get (klass).get_population ();
+  }
+
+  // Incremental increase in the Coverage and ClassDef table size
+  // (worst case) if all glyphs associated with 'klass' were added.
+  unsigned incremental_class_def_size (unsigned klass) const
+  {
+    // ClassDef takes 6 bytes per range
+    unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass);
+    if (gids_consecutive)
+    {
+      // ClassDef1 takes 2 bytes per glyph, but only can be used
+      // when gids are consecutive.
+      return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size);
+    }
+
+    return class_def_2_size;
+  }
+
+  bool in_error ()
+  {
+    if (num_ranges_per_class.in_error ()) return true;
+    if (glyphs_per_class.in_error ()) return true;
+
+    for (const hb_set_t& s : glyphs_per_class.values ())
+    {
+      if (s.in_error ()) return true;
+    }
+    return false;
+  }
+
+ private:
+  bool gids_consecutive;
+  hb_hashmap_t num_ranges_per_class;
+  hb_hashmap_t glyphs_per_class;
+};
+
+
+}
+
+#endif  // GRAPH_CLASSDEF_GRAPH_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh
new file mode 100644
index 00000000000..49d09363156
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh
@@ -0,0 +1,152 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#include "graph.hh"
+#include "../OT/Layout/Common/Coverage.hh"
+
+#ifndef GRAPH_COVERAGE_GRAPH_HH
+#define GRAPH_COVERAGE_GRAPH_HH
+
+namespace graph {
+
+struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size;
+    if (vertex_len < min_size) return false;
+    return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size ();
+  }
+};
+
+struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size;
+    if (vertex_len < min_size) return false;
+    return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
+  }
+};
+
+struct Coverage : public OT::Layout::Common::Coverage
+{
+  static Coverage* clone_coverage (gsubgpos_graph_context_t& c,
+                                   unsigned coverage_id,
+                                   unsigned new_parent_id,
+                                   unsigned link_position,
+                                   unsigned start, unsigned end)
+
+  {
+    unsigned coverage_size = c.graph.vertices_[coverage_id].table_size ();
+    auto& coverage_v = c.graph.vertices_[coverage_id];
+    Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
+    if (!coverage_table || !coverage_table->sanitize (coverage_v))
+      return nullptr;
+
+    auto new_coverage =
+        + hb_zip (coverage_table->iter (), hb_range ())
+        | hb_filter ([&] (hb_pair_t p) {
+          return p.second >= start && p.second < end;
+        })
+        | hb_map_retains_sorting (hb_first)
+        ;
+
+    return add_coverage (c, new_parent_id, link_position, new_coverage, coverage_size);
+  }
+
+  template
+  static Coverage* add_coverage (gsubgpos_graph_context_t& c,
+                                 unsigned parent_id,
+                                 unsigned link_position,
+                                 It glyphs,
+                                 unsigned max_size)
+  {
+    unsigned coverage_prime_id = c.graph.new_node (nullptr, nullptr);
+    auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id];
+    if (!make_coverage (c, glyphs, coverage_prime_id, max_size))
+      return nullptr;
+
+    auto* coverage_link = c.graph.vertices_[parent_id].obj.real_links.push ();
+    coverage_link->width = SmallTypes::size;
+    coverage_link->objidx = coverage_prime_id;
+    coverage_link->position = link_position;
+    coverage_prime_vertex.parents.push (parent_id);
+
+    return (Coverage*) coverage_prime_vertex.obj.head;
+  }
+
+  template
+  static bool make_coverage (gsubgpos_graph_context_t& c,
+                             It glyphs,
+                             unsigned dest_obj,
+                             unsigned max_size)
+  {
+    char* buffer = (char*) hb_calloc (1, max_size);
+    hb_serialize_context_t serializer (buffer, max_size);
+    OT::Layout::Common::Coverage_serialize (&serializer, glyphs);
+    serializer.end_serialize ();
+    if (serializer.in_error ())
+    {
+      hb_free (buffer);
+      return false;
+    }
+
+    hb_bytes_t coverage_copy = serializer.copy_bytes ();
+    c.add_buffer ((char *) coverage_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
+
+    auto& obj = c.graph.vertices_[dest_obj].obj;
+    obj.head = (char *) coverage_copy.arrayZ;
+    obj.tail = obj.head + coverage_copy.length;
+
+    hb_free (buffer);
+    return true;
+  }
+
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < OT::Layout::Common::Coverage::min_size) return false;
+    switch (u.format)
+    {
+    case 1: return ((CoverageFormat1*)this)->sanitize (vertex);
+    case 2: return ((CoverageFormat2*)this)->sanitize (vertex);
+#ifndef HB_NO_BEYOND_64K
+    // Not currently supported
+    case 3:
+    case 4:
+#endif
+    default: return false;
+    }
+  }
+};
+
+
+}
+
+#endif  // GRAPH_COVERAGE_GRAPH_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh
index 52ca9dd142e..38ca5db0961 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh
@@ -24,6 +24,10 @@
  * Google Author(s): Garret Rieger
  */
 
+#include "../hb-set.hh"
+#include "../hb-priority-queue.hh"
+#include "../hb-serialize.hh"
+
 #ifndef GRAPH_GRAPH_HH
 #define GRAPH_GRAPH_HH
 
@@ -45,6 +49,95 @@ struct graph_t
     unsigned end = 0;
     unsigned priority = 0;
 
+
+    bool link_positions_valid (unsigned num_objects, bool removed_nil)
+    {
+      hb_set_t assigned_bytes;
+      for (const auto& l : obj.real_links)
+      {
+        if (l.objidx >= num_objects
+            || (removed_nil && !l.objidx))
+        {
+          DEBUG_MSG (SUBSET_REPACK, nullptr,
+                     "Invalid graph. Invalid object index.");
+          return false;
+        }
+
+        unsigned start = l.position;
+        unsigned end = start + l.width - 1;
+
+        if (unlikely (l.width < 2 || l.width > 4))
+        {
+          DEBUG_MSG (SUBSET_REPACK, nullptr,
+                     "Invalid graph. Invalid link width.");
+          return false;
+        }
+
+        if (unlikely (end >= table_size ()))
+        {
+          DEBUG_MSG (SUBSET_REPACK, nullptr,
+                     "Invalid graph. Link position is out of bounds.");
+          return false;
+        }
+
+        if (unlikely (assigned_bytes.intersects (start, end)))
+        {
+          DEBUG_MSG (SUBSET_REPACK, nullptr,
+                     "Invalid graph. Found offsets whose positions overlap.");
+          return false;
+        }
+
+        assigned_bytes.add_range (start, end);
+      }
+
+      return !assigned_bytes.in_error ();
+    }
+
+    void normalize ()
+    {
+      obj.real_links.qsort ();
+      for (auto& l : obj.real_links)
+      {
+        for (unsigned i = 0; i < l.width; i++)
+        {
+          obj.head[l.position + i] = 0;
+        }
+      }
+    }
+
+    bool equals (const vertex_t& other,
+                 const graph_t& graph,
+                 const graph_t& other_graph,
+                 unsigned depth) const
+    {
+      if (!(as_bytes () == other.as_bytes ()))
+      {
+        DEBUG_MSG (SUBSET_REPACK, nullptr,
+                   "vertex [%lu] bytes != [%lu] bytes, depth = %u",
+                   (unsigned long) table_size (),
+                   (unsigned long) other.table_size (),
+                   depth);
+
+        auto a = as_bytes ();
+        auto b = other.as_bytes ();
+        while (a || b)
+        {
+          DEBUG_MSG (SUBSET_REPACK, nullptr,
+                     "  0x%x %s 0x%x", (unsigned) *a, (*a == *b) ? "==" : "!=", (unsigned) *b);
+          a++;
+          b++;
+        }
+        return false;
+      }
+
+      return links_equal (obj.real_links, other.obj.real_links, graph, other_graph, depth);
+    }
+
+    hb_bytes_t as_bytes () const
+    {
+      return hb_bytes_t (obj.head, table_size ());
+    }
+
     friend void swap (vertex_t& a, vertex_t& b)
     {
       hb_swap (a.obj, b.obj);
@@ -56,6 +149,18 @@ struct graph_t
       hb_swap (a.priority, b.priority);
     }
 
+    hb_hashmap_t
+    position_to_index_map () const
+    {
+      hb_hashmap_t result;
+
+      for (const auto& l : obj.real_links) {
+        result.set (l.position, l.objidx);
+      }
+
+      return result;
+    }
+
     bool is_shared () const
     {
       return parents.length > 1;
@@ -71,11 +176,27 @@ struct graph_t
       for (unsigned i = 0; i < parents.length; i++)
       {
         if (parents[i] != parent_index) continue;
-        parents.remove (i);
+        parents.remove_unordered (i);
         break;
       }
     }
 
+    void remove_real_link (unsigned child_index, const void* offset)
+    {
+      for (unsigned i = 0; i < obj.real_links.length; i++)
+      {
+        auto& link = obj.real_links.arrayZ[i];
+        if (link.objidx != child_index)
+          continue;
+
+        if ((obj.head + link.position) != offset)
+          continue;
+
+        obj.real_links.remove_unordered (i);
+        return;
+      }
+    }
+
     void remap_parents (const hb_vector_t& id_map)
     {
       for (unsigned i = 0; i < parents.length; i++)
@@ -107,6 +228,10 @@ struct graph_t
       return priority >= 3;
     }
 
+    size_t table_size () const {
+      return obj.tail - obj.head;
+    }
+
     int64_t modified_distance (unsigned order) const
     {
       // TODO(garretrieger): once priority is high enough, should try
@@ -131,6 +256,57 @@ struct graph_t
 
       return -table_size;
     }
+
+   private:
+    bool links_equal (const hb_vector_t& this_links,
+                      const hb_vector_t& other_links,
+                      const graph_t& graph,
+                      const graph_t& other_graph,
+                      unsigned depth) const
+    {
+      auto a = this_links.iter ();
+      auto b = other_links.iter ();
+
+      while (a && b)
+      {
+        const auto& link_a = *a;
+        const auto& link_b = *b;
+
+        if (link_a.width != link_b.width ||
+            link_a.is_signed != link_b.is_signed ||
+            link_a.whence != link_b.whence ||
+            link_a.position != link_b.position ||
+            link_a.bias != link_b.bias)
+          return false;
+
+        if (!graph.vertices_[link_a.objidx].equals (
+                other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1))
+          return false;
+
+        a++;
+        b++;
+      }
+
+      if (bool (a) != bool (b))
+        return false;
+
+      return true;
+    }
+  };
+
+  template 
+  struct vertex_and_table_t
+  {
+    vertex_and_table_t () : index (0), vertex (nullptr), table (nullptr)
+    {}
+
+    unsigned index;
+    vertex_t* vertex;
+    T* table;
+
+    operator bool () {
+       return table && vertex;
+    }
   };
 
   /*
@@ -145,7 +321,8 @@ struct graph_t
       : parents_invalid (true),
         distance_invalid (true),
         positions_invalid (true),
-        successful (true)
+        successful (true),
+        buffers ()
   {
     num_roots_for_space_.push (1);
     bool removed_nil = false;
@@ -153,8 +330,6 @@ struct graph_t
     vertices_scratch_.alloc (objects.length);
     for (unsigned i = 0; i < objects.length; i++)
     {
-      // TODO(grieger): check all links point to valid objects.
-
       // If this graph came from a serialization buffer object 0 is the
       // nil object. We don't need it for our purposes here so drop it.
       if (i == 0 && !objects[i])
@@ -166,6 +341,9 @@ struct graph_t
       vertex_t* v = vertices_.push ();
       if (check_success (!vertices_.in_error ()))
         v->obj = *objects[i];
+
+      check_success (v->link_positions_valid (objects.length, removed_nil));
+
       if (!removed_nil) continue;
       // Fix indices to account for removed nil object.
       for (auto& l : v->obj.all_links_writer ()) {
@@ -177,6 +355,20 @@ struct graph_t
   ~graph_t ()
   {
     vertices_.fini ();
+    for (char* b : buffers)
+      hb_free (b);
+  }
+
+  bool operator== (const graph_t& other) const
+  {
+    return root ().equals (other.root (), *this, other, 0);
+  }
+
+  // Sorts links of all objects in a consistent manner and zeroes all offsets.
+  void normalize ()
+  {
+    for (auto& v : vertices_.writer ())
+      v.normalize ();
   }
 
   bool in_error () const
@@ -199,11 +391,43 @@ struct graph_t
     return vertices_.length - 1;
   }
 
-  const hb_serialize_context_t::object_t& object(unsigned i) const
+  const hb_serialize_context_t::object_t& object (unsigned i) const
   {
     return vertices_[i].obj;
   }
 
+  void add_buffer (char* buffer)
+  {
+    buffers.push (buffer);
+  }
+
+  /*
+   * Adds a 16 bit link from parent_id to child_id
+   */
+  template
+  void add_link (T* offset,
+                 unsigned parent_id,
+                 unsigned child_id)
+  {
+    auto& v = vertices_[parent_id];
+    auto* link = v.obj.real_links.push ();
+    link->width = 2;
+    link->objidx = child_id;
+    link->position = (char*) offset - (char*) v.obj.head;
+    vertices_[child_id].parents.push (parent_id);
+  }
+
+  /*
+   * Generates a new topological sorting of graph ordered by the shortest
+   * distance to each node if positions are marked as invalid.
+   */
+  void sort_shortest_distance_if_needed ()
+  {
+    if (!positions_invalid) return;
+    sort_shortest_distance ();
+  }
+
+
   /*
    * Generates a new topological sorting of graph ordered by the shortest
    * distance to each node.
@@ -239,6 +463,13 @@ struct graph_t
       hb_swap (sorted_graph[new_id], vertices_[next_id]);
       const vertex_t& next = sorted_graph[new_id];
 
+      if (unlikely (!check_success(new_id >= 0))) {
+        // We are out of ids. Which means we've visited a node more than once.
+        // This graph contains a cycle which is not allowed.
+        DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle.");
+        return;
+      }
+
       id_map[next_id] = new_id--;
 
       for (const auto& link : next.obj.all_links ()) {
@@ -256,44 +487,152 @@ struct graph_t
 
     check_success (!queue.in_error ());
     check_success (!sorted_graph.in_error ());
-    if (!check_success (new_id == -1))
-      print_orphaned_nodes ();
 
     remap_all_obj_indices (id_map, &sorted_graph);
-
     hb_swap (vertices_, sorted_graph);
+
+    if (!check_success (new_id == -1))
+      print_orphaned_nodes ();
   }
 
   /*
-   * Assign unique space numbers to each connected subgraph of 32 bit offset(s).
+   * Finds the set of nodes (placed into roots) that should be assigned unique spaces.
+   * More specifically this looks for the top most 24 bit or 32 bit links in the graph.
+   * Some special casing is done that is specific to the layout of GSUB/GPOS tables.
    */
-  bool assign_32bit_spaces ()
+  void find_space_roots (hb_set_t& visited, hb_set_t& roots)
   {
-    unsigned root_index = root_idx ();
-    hb_set_t visited;
-    hb_set_t roots;
-    for (unsigned i = 0; i <= root_index; i++)
+    int root_index = (int) root_idx ();
+    for (int i = root_index; i >= 0; i--)
     {
+      if (visited.has (i)) continue;
+
       // Only real links can form 32 bit spaces
       for (auto& l : vertices_[i].obj.real_links)
       {
-        if (l.width == 4 && !l.is_signed)
+        if (l.is_signed || l.width < 3)
+          continue;
+
+        if (i == root_index && l.width == 3)
+          // Ignore 24bit links from the root node, this skips past the single 24bit
+          // pointer to the lookup list.
+          continue;
+
+        if (l.width == 3)
         {
-          roots.add (l.objidx);
-          find_subgraph (l.objidx, visited);
+          // A 24bit offset forms a root, unless there is 32bit offsets somewhere
+          // in it's subgraph, then those become the roots instead. This is to make sure
+          // that extension subtables beneath a 24bit lookup become the spaces instead
+          // of the offset to the lookup.
+          hb_set_t sub_roots;
+          find_32bit_roots (l.objidx, sub_roots);
+          if (sub_roots) {
+            for (unsigned sub_root_idx : sub_roots) {
+              roots.add (sub_root_idx);
+              find_subgraph (sub_root_idx, visited);
+            }
+            continue;
+          }
         }
+
+        roots.add (l.objidx);
+        find_subgraph (l.objidx, visited);
       }
     }
+  }
 
-    // Mark everything not in the subgraphs of 32 bit roots as visited.
-    // This prevents 32 bit subgraphs from being connected via nodes not in the 32 bit subgraphs.
+  template 
+  vertex_and_table_t as_table (unsigned parent, const void* offset, Ts... ds)
+  {
+    return as_table_from_index (index_for_offset (parent, offset), std::forward(ds)...);
+  }
+
+  template 
+  vertex_and_table_t as_mutable_table (unsigned parent, const void* offset, Ts... ds)
+  {
+    return as_table_from_index (mutable_index_for_offset (parent, offset), std::forward(ds)...);
+  }
+
+  template 
+  vertex_and_table_t as_table_from_index (unsigned index, Ts... ds)
+  {
+    if (index >= vertices_.length)
+      return vertex_and_table_t ();
+
+    vertex_and_table_t r;
+    r.vertex = &vertices_[index];
+    r.table = (T*) r.vertex->obj.head;
+    r.index = index;
+    if (!r.table)
+      return vertex_and_table_t ();
+
+    if (!r.table->sanitize (*(r.vertex), std::forward(ds)...))
+      return vertex_and_table_t ();
+
+    return r;
+  }
+
+  // Finds the object id of the object pointed to by the offset at 'offset'
+  // within object[node_idx].
+  unsigned index_for_offset (unsigned node_idx, const void* offset) const
+  {
+    const auto& node = object (node_idx);
+    if (offset < node.head || offset >= node.tail) return -1;
+
+    unsigned length = node.real_links.length;
+    for (unsigned i = 0; i < length; i++)
+    {
+      // Use direct access for increased performance, this is a hot method.
+      const auto& link = node.real_links.arrayZ[i];
+      if (offset != node.head + link.position)
+        continue;
+      return link.objidx;
+    }
+
+    return -1;
+  }
+
+  // Finds the object id of the object pointed to by the offset at 'offset'
+  // within object[node_idx]. Ensures that the returned object is safe to mutate.
+  // That is, if the original child object is shared by parents other than node_idx
+  // it will be duplicated and the duplicate will be returned instead.
+  unsigned mutable_index_for_offset (unsigned node_idx, const void* offset)
+  {
+    unsigned child_idx = index_for_offset (node_idx, offset);
+    auto& child = vertices_[child_idx];
+    for (unsigned p : child.parents)
+    {
+      if (p != node_idx) {
+        return duplicate (node_idx, child_idx);
+      }
+    }
+
+    return child_idx;
+  }
+
+
+  /*
+   * Assign unique space numbers to each connected subgraph of 24 bit and/or 32 bit offset(s).
+   * Currently, this is implemented specifically tailored to the structure of a GPOS/GSUB
+   * (including with 24bit offsets) table.
+   */
+  bool assign_spaces ()
+  {
+    update_parents ();
+
+    hb_set_t visited;
+    hb_set_t roots;
+    find_space_roots (visited, roots);
+
+    // Mark everything not in the subgraphs of the roots as visited. This prevents
+    // subgraphs from being connected via nodes not in those subgraphs.
     visited.invert ();
 
     if (!roots) return false;
 
     while (roots)
     {
-      unsigned next = HB_SET_VALUE_INVALID;
+      uint32_t next = HB_SET_VALUE_INVALID;
       if (unlikely (!check_success (!roots.in_error ()))) break;
       if (!roots.next (&next)) break;
 
@@ -361,6 +700,9 @@ struct graph_t
       }
     }
 
+    if (in_error ())
+      return false;
+
     if (!made_changes)
       return false;
 
@@ -374,8 +716,8 @@ struct graph_t
 
     auto new_subgraph =
         + subgraph.keys ()
-        | hb_map([&] (unsigned node_idx) {
-          const unsigned *v;
+        | hb_map([&] (uint32_t node_idx) {
+          const uint32_t *v;
           if (index_map.has (node_idx, &v)) return *v;
           return node_idx;
         })
@@ -385,10 +727,10 @@ struct graph_t
     remap_obj_indices (index_map, parents.iter (), true);
 
     // Update roots set with new indices as needed.
-    unsigned next = HB_SET_VALUE_INVALID;
+    uint32_t next = HB_SET_VALUE_INVALID;
     while (roots.next (&next))
     {
-      const unsigned *v;
+      const uint32_t *v;
       if (index_map.has (next, &v))
       {
         roots.del (next);
@@ -403,7 +745,7 @@ struct graph_t
   {
     for (const auto& link : vertices_[node_idx].obj.all_links ())
     {
-      const unsigned *v;
+      const uint32_t *v;
       if (subgraph.has (link.objidx, &v))
       {
         subgraph.set (link.objidx, *v + 1);
@@ -422,6 +764,68 @@ struct graph_t
       find_subgraph (link.objidx, subgraph);
   }
 
+  size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph, unsigned max_depth = -1)
+  {
+    if (subgraph.has (node_idx)) return 0;
+    subgraph.add (node_idx);
+
+    const auto& o = vertices_[node_idx].obj;
+    size_t size = o.tail - o.head;
+    if (max_depth == 0)
+      return size;
+
+    for (const auto& link : o.all_links ())
+      size += find_subgraph_size (link.objidx, subgraph, max_depth - 1);
+    return size;
+  }
+
+  /*
+   * Finds the topmost children of 32bit offsets in the subgraph starting
+   * at node_idx. Found indices are placed into 'found'.
+   */
+  void find_32bit_roots (unsigned node_idx, hb_set_t& found)
+  {
+    for (const auto& link : vertices_[node_idx].obj.all_links ())
+    {
+      if (!link.is_signed && link.width == 4) {
+        found.add (link.objidx);
+        continue;
+      }
+      find_32bit_roots (link.objidx, found);
+    }
+  }
+
+  /*
+   * Moves the child of old_parent_idx pointed to by old_offset to a new
+   * vertex at the new_offset.
+   */
+  template
+  void move_child (unsigned old_parent_idx,
+                   const O* old_offset,
+                   unsigned new_parent_idx,
+                   const O* new_offset)
+  {
+    distance_invalid = true;
+    positions_invalid = true;
+
+    auto& old_v = vertices_[old_parent_idx];
+    auto& new_v = vertices_[new_parent_idx];
+
+    unsigned child_id = index_for_offset (old_parent_idx,
+                                          old_offset);
+
+    auto* new_link = new_v.obj.real_links.push ();
+    new_link->width = O::static_size;
+    new_link->objidx = child_id;
+    new_link->position = (const char*) new_offset - (const char*) new_v.obj.head;
+
+    auto& child = vertices_[child_id];
+    child.parents.push (new_parent_idx);
+
+    old_v.remove_real_link (child_id, old_offset);
+    child.remove_parent (old_parent_idx);
+  }
+
   /*
    * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign
    * links. index_map is updated with mappings from old id to new id. If a duplication has already
@@ -432,7 +836,11 @@ struct graph_t
     if (index_map.has (node_idx))
       return;
 
-    index_map.set (node_idx, duplicate (node_idx));
+    unsigned clone_idx = duplicate (node_idx);
+    if (!check_success (clone_idx != (unsigned) -1))
+      return;
+
+    index_map.set (node_idx, clone_idx);
     for (const auto& l : object (node_idx).all_links ()) {
       duplicate_subgraph (l.objidx, index_map);
     }
@@ -490,7 +898,20 @@ struct graph_t
    * parent to the clone. The copy is a shallow copy, objects
    * linked from child are not duplicated.
    */
-  bool duplicate (unsigned parent_idx, unsigned child_idx)
+  unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx)
+  {
+    unsigned new_idx = duplicate (parent_idx, child_idx);
+    if (new_idx == (unsigned) -1) return child_idx;
+    return new_idx;
+  }
+
+
+  /*
+   * Creates a copy of child and re-assigns the link from
+   * parent to the clone. The copy is a shallow copy, objects
+   * linked from child are not duplicated.
+   */
+  unsigned duplicate (unsigned parent_idx, unsigned child_idx)
   {
     update_parents ();
 
@@ -504,12 +925,12 @@ struct graph_t
     {
       // Can't duplicate this node, doing so would orphan the original one as all remaining links
       // to child are from parent.
-      DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %d => %d",
+      DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %u => %u",
                  parent_idx, child_idx);
-      return false;
+      return -1;
     }
 
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %d => %d",
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %u => %u",
                parent_idx, child_idx);
 
     unsigned clone_idx = duplicate (child_idx);
@@ -526,7 +947,40 @@ struct graph_t
       reassign_link (l, parent_idx, clone_idx);
     }
 
-    return true;
+    return clone_idx;
+  }
+
+
+  /*
+   * Adds a new node to the graph, not connected to anything.
+   */
+  unsigned new_node (char* head, char* tail)
+  {
+    positions_invalid = true;
+    distance_invalid = true;
+
+    auto* clone = vertices_.push ();
+    if (vertices_.in_error ()) {
+      return -1;
+    }
+
+    clone->obj.head = head;
+    clone->obj.tail = tail;
+    clone->distance = 0;
+    clone->space = 0;
+
+    unsigned clone_idx = vertices_.length - 2;
+
+    // The last object is the root of the graph, so swap back the root to the end.
+    // The root's obj idx does change, however since it's root nothing else refers to it.
+    // all other obj idx's will be unaffected.
+    hb_swap (vertices_[vertices_.length - 2], *clone);
+
+    // Since the root moved, update the parents arrays of all children on the root.
+    for (const auto& l : root ().obj.all_links ())
+      vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ());
+
+    return clone_idx;
   }
 
   /*
@@ -534,7 +988,7 @@ struct graph_t
    */
   bool raise_childrens_priority (unsigned parent_idx)
   {
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Raising priority of all children of %d",
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Raising priority of all children of %u",
                parent_idx);
     // This operation doesn't change ordering until a sort is run, so no need
     // to invalidate positions. It does not change graph structure so no need
@@ -546,6 +1000,72 @@ struct graph_t
     return made_change;
   }
 
+  bool is_fully_connected ()
+  {
+    update_parents();
+
+    if (root().parents)
+      // Root cannot have parents.
+      return false;
+
+    for (unsigned i = 0; i < root_idx (); i++)
+    {
+      if (!vertices_[i].parents)
+        return false;
+    }
+    return true;
+  }
+
+#if 0
+  /*
+   * Saves the current graph to a packed binary format which the repacker fuzzer takes
+   * as a seed.
+   */
+  void save_fuzzer_seed (hb_tag_t tag) const
+  {
+    FILE* f = fopen ("./repacker_fuzzer_seed", "w");
+    fwrite ((void*) &tag, sizeof (tag), 1, f);
+
+    uint16_t num_objects = vertices_.length;
+    fwrite ((void*) &num_objects, sizeof (num_objects), 1, f);
+
+    for (const auto& v : vertices_)
+    {
+      uint16_t blob_size = v.table_size ();
+      fwrite ((void*) &blob_size, sizeof (blob_size), 1, f);
+      fwrite ((const void*) v.obj.head, blob_size, 1, f);
+    }
+
+    uint16_t link_count = 0;
+    for (const auto& v : vertices_)
+      link_count += v.obj.real_links.length;
+
+    fwrite ((void*) &link_count, sizeof (link_count), 1, f);
+
+    typedef struct
+    {
+      uint16_t parent;
+      uint16_t child;
+      uint16_t position;
+      uint8_t width;
+    } link_t;
+
+    for (unsigned i = 0; i < vertices_.length; i++)
+    {
+      for (const auto& l : vertices_[i].obj.real_links)
+      {
+        link_t link {
+          (uint16_t) i, (uint16_t) l.objidx,
+          (uint16_t) l.position, (uint8_t) l.width
+        };
+        fwrite ((void*) &link, sizeof (link), 1, f);
+      }
+    }
+
+    fclose (f);
+  }
+#endif
+
   void print_orphaned_nodes ()
   {
     if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
@@ -554,6 +1074,10 @@ struct graph_t
     parents_invalid = true;
     update_parents();
 
+    if (root().parents) {
+      DEBUG_MSG (SUBSET_REPACK, nullptr, "Root node has incoming edges.");
+    }
+
     for (unsigned i = 0; i < root_idx (); i++)
     {
       const auto& v = vertices_[i];
@@ -622,7 +1146,7 @@ struct graph_t
  private:
 
   /*
-   * Returns the numbers of incoming edges that are 32bits wide.
+   * Returns the numbers of incoming edges that are 24 or 32 bits wide.
    */
   unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const
   {
@@ -636,7 +1160,9 @@ struct graph_t
       // Only real links can be wide
       for (const auto& l : vertices_[p].obj.real_links)
       {
-        if (l.objidx == node_idx && l.width == 4 && !l.is_signed)
+        if (l.objidx == node_idx
+            && (l.width == 3 || l.width == 4)
+            && !l.is_signed)
         {
           count++;
           parents.add (p);
@@ -668,6 +1194,11 @@ struct graph_t
       }
     }
 
+    for (unsigned i = 0; i < vertices_.length; i++)
+      // parents arrays must be accurate or downstream operations like cycle detection
+      // and sorting won't work correctly.
+      check_success (!vertices_[i].parents.in_error ());
+
     parents_invalid = false;
   }
 
@@ -786,7 +1317,7 @@ struct graph_t
     {
       for (auto& link : vertices_[i].obj.all_links_writer ())
       {
-        const unsigned *v;
+        const uint32_t *v;
         if (!id_map.has (link.objidx, &v)) continue;
         if (only_wide && !(link.width == 4 && !link.is_signed)) continue;
 
@@ -853,6 +1384,7 @@ struct graph_t
   bool positions_invalid;
   bool successful;
   hb_vector_t num_roots_for_space_;
+  hb_vector_t buffers;
 };
 
 }
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.cc b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.cc
new file mode 100644
index 00000000000..b2044426d46
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#include "gsubgpos-graph.hh"
+
+namespace graph {
+
+gsubgpos_graph_context_t::gsubgpos_graph_context_t (hb_tag_t table_tag_,
+                                                    graph_t& graph_)
+    : table_tag (table_tag_),
+      graph (graph_),
+      lookup_list_index (0),
+      lookups ()
+{
+  if (table_tag_ != HB_OT_TAG_GPOS
+      &&  table_tag_ != HB_OT_TAG_GSUB)
+    return;
+
+  GSTAR* gstar = graph::GSTAR::graph_to_gstar (graph_);
+  if (gstar) {
+    gstar->find_lookups (graph, lookups);
+    lookup_list_index = gstar->get_lookup_list_index (graph_);
+  }
+}
+
+unsigned gsubgpos_graph_context_t::create_node (unsigned size)
+{
+  char* buffer = (char*) hb_calloc (1, size);
+  if (!buffer)
+    return -1;
+
+  add_buffer (buffer);
+
+  return graph.new_node (buffer, buffer + size);
+}
+
+unsigned gsubgpos_graph_context_t::num_non_ext_subtables ()  {
+  unsigned count = 0;
+  for (auto l : lookups.values ())
+  {
+    if (l->is_extension (table_tag)) continue;
+    count += l->number_of_subtables ();
+  }
+  return count;
+}
+
+}
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh
new file mode 100644
index 00000000000..9fe9662e645
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#include "graph.hh"
+#include "../hb-ot-layout-gsubgpos.hh"
+
+#ifndef GRAPH_GSUBGPOS_CONTEXT_HH
+#define GRAPH_GSUBGPOS_CONTEXT_HH
+
+namespace graph {
+
+struct Lookup;
+
+struct gsubgpos_graph_context_t
+{
+  hb_tag_t table_tag;
+  graph_t& graph;
+  unsigned lookup_list_index;
+  hb_hashmap_t lookups;
+
+
+  HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_,
+                                        graph_t& graph_);
+
+  HB_INTERNAL unsigned create_node (unsigned size);
+
+  void add_buffer (char* buffer)
+  {
+    graph.add_buffer (buffer);
+  }
+
+ private:
+  HB_INTERNAL unsigned num_non_ext_subtables ();
+};
+
+}
+
+#endif  // GRAPH_GSUBGPOS_CONTEXT
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh
new file mode 100644
index 00000000000..c170638409f
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh
@@ -0,0 +1,414 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#include "graph.hh"
+#include "../hb-ot-layout-gsubgpos.hh"
+#include "../OT/Layout/GSUB/ExtensionSubst.hh"
+#include "gsubgpos-context.hh"
+#include "pairpos-graph.hh"
+#include "markbasepos-graph.hh"
+
+#ifndef GRAPH_GSUBGPOS_GRAPH_HH
+#define GRAPH_GSUBGPOS_GRAPH_HH
+
+namespace graph {
+
+struct Lookup;
+
+template
+struct ExtensionFormat1 : public OT::ExtensionFormat1
+{
+  void reset(unsigned type)
+  {
+    this->format = 1;
+    this->extensionLookupType = type;
+    this->extensionOffset = 0;
+  }
+
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    return vertex_len >= OT::ExtensionFormat1::static_size;
+  }
+
+  unsigned get_lookup_type () const
+  {
+    return this->extensionLookupType;
+  }
+
+  unsigned get_subtable_index (graph_t& graph, unsigned this_index) const
+  {
+    return graph.index_for_offset (this_index, &this->extensionOffset);
+  }
+};
+
+struct Lookup : public OT::Lookup
+{
+  unsigned number_of_subtables () const
+  {
+    return subTable.len;
+  }
+
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < OT::Lookup::min_size) return false;
+    return vertex_len >= this->get_size ();
+  }
+
+  bool is_extension (hb_tag_t table_tag) const
+  {
+    return lookupType == extension_type (table_tag);
+  }
+
+  bool make_extension (gsubgpos_graph_context_t& c,
+                       unsigned this_index)
+  {
+    unsigned type = lookupType;
+    unsigned ext_type = extension_type (c.table_tag);
+    if (!ext_type || is_extension (c.table_tag))
+    {
+      // NOOP
+      return true;
+    }
+
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "Promoting lookup type %u (obj %u) to extension.",
+               type,
+               this_index);
+
+    for (unsigned i = 0; i < subTable.len; i++)
+    {
+      unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
+      if (!make_subtable_extension (c,
+                                    this_index,
+                                    subtable_index))
+        return false;
+    }
+
+    lookupType = ext_type;
+    return true;
+  }
+
+  bool split_subtables_if_needed (gsubgpos_graph_context_t& c,
+                                  unsigned this_index)
+  {
+    unsigned type = lookupType;
+    bool is_ext = is_extension (c.table_tag);
+
+    if (c.table_tag != HB_OT_TAG_GPOS)
+      return true;
+
+    if (!is_ext &&
+        type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair &&
+        type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
+      return true;
+
+    hb_vector_t>> all_new_subtables;
+    for (unsigned i = 0; i < subTable.len; i++)
+    {
+      unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
+      unsigned parent_index = this_index;
+      if (is_ext) {
+        unsigned ext_subtable_index = subtable_index;
+        parent_index = ext_subtable_index;
+        ExtensionFormat1* extension =
+            (ExtensionFormat1*)
+            c.graph.object (ext_subtable_index).head;
+        if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index]))
+          continue;
+
+        subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index);
+        type = extension->get_lookup_type ();
+        if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair
+            && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
+          continue;
+      }
+
+      hb_vector_t new_sub_tables;
+      switch (type)
+      {
+      case 2:
+        new_sub_tables = split_subtable (c, parent_index, subtable_index); break;
+      case 4:
+        new_sub_tables = split_subtable (c, parent_index, subtable_index); break;
+      default:
+        break;
+      }
+      if (new_sub_tables.in_error ()) return false;
+      if (!new_sub_tables) continue;
+      hb_pair_t>* entry = all_new_subtables.push ();
+      entry->first = i;
+      entry->second = std::move (new_sub_tables);
+    }
+
+    if (all_new_subtables) {
+      add_sub_tables (c, this_index, type, all_new_subtables);
+    }
+
+    return true;
+  }
+
+  template
+  hb_vector_t split_subtable (gsubgpos_graph_context_t& c,
+                                        unsigned parent_idx,
+                                        unsigned objidx)
+  {
+    T* sub_table = (T*) c.graph.object (objidx).head;
+    if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
+      return hb_vector_t ();
+
+    return sub_table->split_subtables (c, parent_idx, objidx);
+  }
+
+  void add_sub_tables (gsubgpos_graph_context_t& c,
+                       unsigned this_index,
+                       unsigned type,
+                       hb_vector_t>>& subtable_ids)
+  {
+    bool is_ext = is_extension (c.table_tag);
+    auto& v = c.graph.vertices_[this_index];
+    fix_existing_subtable_links (c, this_index, subtable_ids);
+
+    unsigned new_subtable_count = 0;
+    for (const auto& p : subtable_ids)
+      new_subtable_count += p.second.length;
+
+    size_t new_size = v.table_size ()
+                      + new_subtable_count * OT::Offset16::static_size;
+    char* buffer = (char*) hb_calloc (1, new_size);
+    c.add_buffer (buffer);
+    hb_memcpy (buffer, v.obj.head, v.table_size());
+
+    v.obj.head = buffer;
+    v.obj.tail = buffer + new_size;
+
+    Lookup* new_lookup = (Lookup*) buffer;
+
+    unsigned shift = 0;
+    new_lookup->subTable.len = subTable.len + new_subtable_count;
+    for (const auto& p : subtable_ids)
+    {
+      unsigned offset_index = p.first + shift + 1;
+      shift += p.second.length;
+
+      for (unsigned subtable_id : p.second)
+      {
+        if (is_ext)
+        {
+          unsigned ext_id = create_extension_subtable (c, subtable_id, type);
+          c.graph.vertices_[subtable_id].parents.push (ext_id);
+          subtable_id = ext_id;
+        }
+
+        auto* link = v.obj.real_links.push ();
+        link->width = 2;
+        link->objidx = subtable_id;
+        link->position = (char*) &new_lookup->subTable[offset_index++] -
+                         (char*) new_lookup;
+        c.graph.vertices_[subtable_id].parents.push (this_index);
+      }
+    }
+
+    // Repacker sort order depends on link order, which we've messed up so resort it.
+    v.obj.real_links.qsort ();
+
+    // The head location of the lookup has changed, invalidating the lookups map entry
+    // in the context. Update the map.
+    c.lookups.set (this_index, new_lookup);
+  }
+
+  void fix_existing_subtable_links (gsubgpos_graph_context_t& c,
+                                    unsigned this_index,
+                                    hb_vector_t>>& subtable_ids)
+  {
+    auto& v = c.graph.vertices_[this_index];
+    Lookup* lookup = (Lookup*) v.obj.head;
+
+    unsigned shift = 0;
+    for (const auto& p : subtable_ids)
+    {
+      unsigned insert_index = p.first + shift;
+      unsigned pos_offset = p.second.length * OT::Offset16::static_size;
+      unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup;
+      shift += p.second.length;
+
+      for (auto& l : v.obj.all_links_writer ())
+      {
+        if (l.position > insert_offset) l.position += pos_offset;
+      }
+    }
+  }
+
+  unsigned create_extension_subtable (gsubgpos_graph_context_t& c,
+                                      unsigned subtable_index,
+                                      unsigned type)
+  {
+    unsigned extension_size = OT::ExtensionFormat1::static_size;
+
+    unsigned ext_index = c.create_node (extension_size);
+    if (ext_index == (unsigned) -1)
+      return -1;
+
+    auto& ext_vertex = c.graph.vertices_[ext_index];
+    ExtensionFormat1* extension =
+        (ExtensionFormat1*) ext_vertex.obj.head;
+    extension->reset (type);
+
+    // Make extension point at the subtable.
+    auto* l = ext_vertex.obj.real_links.push ();
+
+    l->width = 4;
+    l->objidx = subtable_index;
+    l->position = 4;
+
+    return ext_index;
+  }
+
+  bool make_subtable_extension (gsubgpos_graph_context_t& c,
+                                unsigned lookup_index,
+                                unsigned subtable_index)
+  {
+    unsigned type = lookupType;
+
+    unsigned ext_index = create_extension_subtable(c, subtable_index, type);
+    if (ext_index == (unsigned) -1)
+      return false;
+
+    auto& lookup_vertex = c.graph.vertices_[lookup_index];
+    for (auto& l : lookup_vertex.obj.real_links.writer ())
+    {
+      if (l.objidx == subtable_index)
+        // Change lookup to point at the extension.
+        l.objidx = ext_index;
+    }
+
+    // Make extension point at the subtable.
+    auto& ext_vertex = c.graph.vertices_[ext_index];
+    auto& subtable_vertex = c.graph.vertices_[subtable_index];
+    ext_vertex.parents.push (lookup_index);
+    subtable_vertex.remap_parent (lookup_index, ext_index);
+
+    return true;
+  }
+
+ private:
+  unsigned extension_type (hb_tag_t table_tag) const
+  {
+    switch (table_tag)
+    {
+    case HB_OT_TAG_GPOS: return 9;
+    case HB_OT_TAG_GSUB: return 7;
+    default: return 0;
+    }
+  }
+};
+
+template 
+struct LookupList : public OT::LookupList
+{
+  bool sanitize (const graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < OT::LookupList::min_size) return false;
+    return vertex_len >= OT::LookupList::item_size * this->len;
+  }
+};
+
+struct GSTAR : public OT::GSUBGPOS
+{
+  static GSTAR* graph_to_gstar (graph_t& graph)
+  {
+    const auto& r = graph.root ();
+
+    GSTAR* gstar = (GSTAR*) r.obj.head;
+    if (!gstar || !gstar->sanitize (r))
+      return nullptr;
+
+    return gstar;
+  }
+
+  const void* get_lookup_list_field_offset () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.get_lookup_list_offset ();
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.get_lookup_list_offset ();
+#endif
+    default: return 0;
+    }
+  }
+
+  bool sanitize (const graph_t::vertex_t& vertex)
+  {
+    int64_t len = vertex.obj.tail - vertex.obj.head;
+    if (len < OT::GSUBGPOS::min_size) return false;
+    return len >= get_size ();
+  }
+
+  void find_lookups (graph_t& graph,
+                     hb_hashmap_t& lookups /* OUT */)
+  {
+    switch (u.version.major) {
+      case 1: find_lookups (graph, lookups); break;
+#ifndef HB_NO_BEYOND_64K
+      case 2: find_lookups (graph, lookups); break;
+#endif
+    }
+  }
+
+  unsigned get_lookup_list_index (graph_t& graph)
+  {
+    return graph.index_for_offset (graph.root_idx (),
+                                   get_lookup_list_field_offset());
+  }
+
+  template
+  void find_lookups (graph_t& graph,
+                     hb_hashmap_t& lookups /* OUT */)
+  {
+    unsigned lookup_list_idx = get_lookup_list_index (graph);
+    const LookupList* lookupList =
+        (const LookupList*) graph.object (lookup_list_idx).head;
+    if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx]))
+      return;
+
+    for (unsigned i = 0; i < lookupList->len; i++)
+    {
+      unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
+      Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
+      if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue;
+      lookups.set (lookup_idx, lookup);
+    }
+  }
+};
+
+
+
+
+}
+
+#endif  /* GRAPH_GSUBGPOS_GRAPH_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh
new file mode 100644
index 00000000000..84ef5f71b93
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh
@@ -0,0 +1,510 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#ifndef GRAPH_MARKBASEPOS_GRAPH_HH
+#define GRAPH_MARKBASEPOS_GRAPH_HH
+
+#include "split-helpers.hh"
+#include "coverage-graph.hh"
+#include "../OT/Layout/GPOS/MarkBasePos.hh"
+#include "../OT/Layout/GPOS/PosLookupSubTable.hh"
+
+namespace graph {
+
+struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix
+{
+  bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < AnchorMatrix::min_size) return false;
+
+    return vertex_len >= AnchorMatrix::min_size +
+        OT::Offset16::static_size * class_count * this->rows;
+  }
+
+  bool shrink (gsubgpos_graph_context_t& c,
+               unsigned this_index,
+               unsigned old_class_count,
+               unsigned new_class_count)
+  {
+    if (new_class_count >= old_class_count) return false;
+    auto& o = c.graph.vertices_[this_index].obj;
+    unsigned base_count = rows;
+    o.tail = o.head +
+             AnchorMatrix::min_size +
+             OT::Offset16::static_size * base_count * new_class_count;
+
+    // Reposition links into the new indexing scheme.
+    for (auto& link : o.real_links.writer ())
+    {
+      unsigned index = (link.position - 2) / 2;
+      unsigned base = index / old_class_count;
+      unsigned klass = index % old_class_count;
+      if (klass >= new_class_count)
+        // should have already been removed
+        return false;
+
+      unsigned new_index = base * new_class_count + klass;
+
+      link.position = (char*) &(this->matrixZ[new_index]) - (char*) this;
+    }
+
+    return true;
+  }
+
+  unsigned clone (gsubgpos_graph_context_t& c,
+                  unsigned this_index,
+                  unsigned start,
+                  unsigned end,
+                  unsigned class_count)
+  {
+    unsigned base_count = rows;
+    unsigned new_class_count = end - start;
+    unsigned size = AnchorMatrix::min_size +
+                    OT::Offset16::static_size * new_class_count * rows;
+    unsigned prime_id = c.create_node (size);
+    if (prime_id == (unsigned) -1) return -1;
+    AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head;
+    prime->rows = base_count;
+
+    auto& o = c.graph.vertices_[this_index].obj;
+    int num_links = o.real_links.length;
+    for (int i = 0; i < num_links; i++)
+    {
+      const auto& link = o.real_links[i];
+      unsigned old_index = (link.position - 2) / OT::Offset16::static_size;
+      unsigned klass = old_index % class_count;
+      if (klass < start || klass >= end) continue;
+
+      unsigned base = old_index / class_count;
+      unsigned new_klass = klass - start;
+      unsigned new_index = base * new_class_count + new_klass;
+
+
+      unsigned child_idx = link.objidx;
+      c.graph.add_link (&(prime->matrixZ[new_index]),
+                        prime_id,
+                        child_idx);
+
+      auto& child = c.graph.vertices_[child_idx];
+      child.remove_parent (this_index);
+
+      o.real_links.remove_unordered (i);
+      num_links--;
+      i--;
+    }
+
+    return prime_id;
+  }
+};
+
+struct MarkArray : public OT::Layout::GPOS_impl::MarkArray
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    unsigned min_size = MarkArray::min_size;
+    if (vertex_len < min_size) return false;
+
+    return vertex_len >= get_size ();
+  }
+
+  bool shrink (gsubgpos_graph_context_t& c,
+               const hb_hashmap_t& mark_array_links,
+               unsigned this_index,
+               unsigned new_class_count)
+  {
+    auto& o = c.graph.vertices_[this_index].obj;
+    for (const auto& link : o.real_links)
+      c.graph.vertices_[link.objidx].remove_parent (this_index);
+    o.real_links.reset ();
+
+    unsigned new_index = 0;
+    for (const auto& record : this->iter ())
+    {
+      unsigned klass = record.klass;
+      if (klass >= new_class_count) continue;
+
+      (*this)[new_index].klass = klass;
+      unsigned position = (char*) &record.markAnchor - (char*) this;
+      unsigned* objidx;
+      if (!mark_array_links.has (position, &objidx))
+      {
+        new_index++;
+        continue;
+      }
+
+      c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx);
+      new_index++;
+    }
+
+    this->len = new_index;
+    o.tail = o.head + MarkArray::min_size +
+             OT::Layout::GPOS_impl::MarkRecord::static_size * new_index;
+    return true;
+  }
+
+  unsigned clone (gsubgpos_graph_context_t& c,
+                  unsigned this_index,
+                  const hb_hashmap_t& pos_to_index,
+                  hb_set_t& marks,
+                  unsigned start_class)
+  {
+    unsigned size = MarkArray::min_size +
+                    OT::Layout::GPOS_impl::MarkRecord::static_size *
+                    marks.get_population ();
+    unsigned prime_id = c.create_node (size);
+    if (prime_id == (unsigned) -1) return -1;
+    MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head;
+    prime->len = marks.get_population ();
+
+
+    unsigned i = 0;
+    for (hb_codepoint_t mark : marks)
+    {
+      (*prime)[i].klass = (*this)[mark].klass - start_class;
+      unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this;
+      unsigned* anchor_index;
+      if (pos_to_index.has (offset_pos, &anchor_index))
+        c.graph.move_child (this_index,
+                            &((*this)[mark].markAnchor),
+                            prime_id,
+                            &((*prime)[i].markAnchor));
+
+      i++;
+    }
+
+    return prime_id;
+  }
+};
+
+struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    return vertex_len >= MarkBasePosFormat1::static_size;
+  }
+
+  hb_vector_t split_subtables (gsubgpos_graph_context_t& c,
+                                         unsigned parent_index,
+                                         unsigned this_index)
+  {
+    hb_set_t visited;
+
+    const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage);
+    const unsigned base_size =
+        OT::Layout::GPOS_impl::PairPosFormat1_3::min_size +
+        MarkArray::min_size +
+        AnchorMatrix::min_size +
+        c.graph.vertices_[base_coverage_id].table_size ();
+
+    hb_vector_t class_to_info = get_class_info (c, this_index);
+
+    unsigned class_count = classCount;
+    auto base_array = c.graph.as_table (this_index,
+                                                      &baseArray,
+                                                      class_count);
+    if (!base_array) return hb_vector_t ();
+    unsigned base_count = base_array.table->rows;
+
+    unsigned partial_coverage_size = 4;
+    unsigned accumulated = base_size;
+    hb_vector_t split_points;
+
+    for (unsigned klass = 0; klass < class_count; klass++)
+    {
+      class_info_t& info = class_to_info[klass];
+      partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population ();
+      unsigned accumulated_delta =
+          OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () +
+          OT::Offset16::static_size * base_count;
+
+      for (unsigned objidx : info.child_indices)
+        accumulated_delta += c.graph.find_subgraph_size (objidx, visited);
+
+      accumulated += accumulated_delta;
+      unsigned total = accumulated + partial_coverage_size;
+
+      if (total >= (1 << 16))
+      {
+        split_points.push (klass);
+        accumulated = base_size + accumulated_delta;
+        partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population ();
+        visited.clear (); // node sharing isn't allowed between splits.
+      }
+    }
+
+
+    const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray);
+    split_context_t split_context {
+      c,
+      this,
+      c.graph.duplicate_if_shared (parent_index, this_index),
+      std::move (class_to_info),
+      c.graph.vertices_[mark_array_id].position_to_index_map (),
+    };
+
+    return actuate_subtable_split (split_context, split_points);
+  }
+
+ private:
+
+  struct class_info_t {
+    hb_set_t marks;
+    hb_vector_t child_indices;
+  };
+
+  struct split_context_t {
+    gsubgpos_graph_context_t& c;
+    MarkBasePosFormat1* thiz;
+    unsigned this_index;
+    hb_vector_t class_to_info;
+    hb_hashmap_t mark_array_links;
+
+    hb_set_t marks_for (unsigned start, unsigned end)
+    {
+      hb_set_t marks;
+      for (unsigned klass = start; klass < end; klass++)
+      {
+        + class_to_info[klass].marks.iter ()
+        | hb_sink (marks)
+        ;
+      }
+      return marks;
+    }
+
+    unsigned original_count ()
+    {
+      return thiz->classCount;
+    }
+
+    unsigned clone_range (unsigned start, unsigned end)
+    {
+      return thiz->clone_range (*this, this->this_index, start, end);
+    }
+
+    bool shrink (unsigned count)
+    {
+      return thiz->shrink (*this, this->this_index, count);
+    }
+  };
+
+  hb_vector_t get_class_info (gsubgpos_graph_context_t& c,
+                                            unsigned this_index)
+  {
+    hb_vector_t class_to_info;
+
+    unsigned class_count= classCount;
+    class_to_info.resize (class_count);
+
+    auto mark_array = c.graph.as_table (this_index, &markArray);
+    if (!mark_array) return hb_vector_t ();
+    unsigned mark_count = mark_array.table->len;
+    for (unsigned mark = 0; mark < mark_count; mark++)
+    {
+      unsigned klass = (*mark_array.table)[mark].get_class ();
+      class_to_info[klass].marks.add (mark);
+    }
+
+    for (const auto& link : mark_array.vertex->obj.real_links)
+    {
+      unsigned mark = (link.position - 2) /
+                     OT::Layout::GPOS_impl::MarkRecord::static_size;
+      unsigned klass = (*mark_array.table)[mark].get_class ();
+      class_to_info[klass].child_indices.push (link.objidx);
+    }
+
+    unsigned base_array_id =
+        c.graph.index_for_offset (this_index, &baseArray);
+    auto& base_array_v = c.graph.vertices_[base_array_id];
+
+    for (const auto& link : base_array_v.obj.real_links)
+    {
+      unsigned index = (link.position - 2) / OT::Offset16::static_size;
+      unsigned klass = index % class_count;
+      class_to_info[klass].child_indices.push (link.objidx);
+    }
+
+    return class_to_info;
+  }
+
+  bool shrink (split_context_t& sc,
+               unsigned this_index,
+               unsigned count)
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Shrinking MarkBasePosFormat1 (%u) to [0, %u).",
+               this_index,
+               count);
+
+    unsigned old_count = classCount;
+    if (count >= old_count)
+      return true;
+
+    classCount = count;
+
+    auto mark_coverage = sc.c.graph.as_mutable_table (this_index,
+                                                                &markCoverage);
+    if (!mark_coverage) return false;
+    hb_set_t marks = sc.marks_for (0, count);
+    auto new_coverage =
+        + hb_enumerate (mark_coverage.table->iter ())
+        | hb_filter (marks, hb_first)
+        | hb_map_retains_sorting (hb_second)
+        ;
+    if (!Coverage::make_coverage (sc.c, + new_coverage,
+                                  mark_coverage.index,
+                                  4 + 2 * marks.get_population ()))
+      return false;
+
+
+    auto base_array = sc.c.graph.as_mutable_table (this_index,
+                                                                 &baseArray,
+                                                                 old_count);
+    if (!base_array || !base_array.table->shrink (sc.c,
+                                                  base_array.index,
+                                                  old_count,
+                                                  count))
+      return false;
+
+    auto mark_array = sc.c.graph.as_mutable_table (this_index,
+                                                              &markArray);
+    if (!mark_array || !mark_array.table->shrink (sc.c,
+                                                  sc.mark_array_links,
+                                                  mark_array.index,
+                                                  count))
+      return false;
+
+    return true;
+  }
+
+  // Create a new MarkBasePos that has all of the data for classes from [start, end).
+  unsigned clone_range (split_context_t& sc,
+                        unsigned this_index,
+                        unsigned start, unsigned end) const
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end);
+
+    graph_t& graph = sc.c.graph;
+    unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2::static_size;
+
+    unsigned prime_id = sc.c.create_node (prime_size);
+    if (prime_id == (unsigned) -1) return -1;
+
+    MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head;
+    prime->format = this->format;
+    unsigned new_class_count = end - start;
+    prime->classCount = new_class_count;
+
+    unsigned base_coverage_id =
+        graph.index_for_offset (sc.this_index, &baseCoverage);
+    graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id);
+    graph.duplicate (prime_id, base_coverage_id);
+
+    auto mark_coverage = sc.c.graph.as_table (this_index,
+                                                        &markCoverage);
+    if (!mark_coverage) return false;
+    hb_set_t marks = sc.marks_for (start, end);
+    auto new_coverage =
+        + hb_enumerate (mark_coverage.table->iter ())
+        | hb_filter (marks, hb_first)
+        | hb_map_retains_sorting (hb_second)
+        ;
+    if (!Coverage::add_coverage (sc.c,
+                                 prime_id,
+                                 2,
+                                 + new_coverage,
+                                 marks.get_population () * 2 + 4))
+      return -1;
+
+    auto mark_array =
+        graph.as_table  (sc.this_index, &markArray);
+    if (!mark_array) return -1;
+    unsigned new_mark_array =
+        mark_array.table->clone (sc.c,
+                                 mark_array.index,
+                                 sc.mark_array_links,
+                                 marks,
+                                 start);
+    graph.add_link (&(prime->markArray), prime_id, new_mark_array);
+
+    unsigned class_count = classCount;
+    auto base_array =
+        graph.as_table (sc.this_index, &baseArray, class_count);
+    if (!base_array) return -1;
+    unsigned new_base_array =
+        base_array.table->clone (sc.c,
+                                 base_array.index,
+                                 start, end, this->classCount);
+    graph.add_link (&(prime->baseArray), prime_id, new_base_array);
+
+    return prime_id;
+  }
+};
+
+
+struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos
+{
+  hb_vector_t split_subtables (gsubgpos_graph_context_t& c,
+                                         unsigned parent_index,
+                                         unsigned this_index)
+  {
+    switch (u.format) {
+    case 1:
+      return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
+#ifndef HB_NO_BEYOND_64K
+    case 2: HB_FALLTHROUGH;
+      // Don't split 24bit PairPos's.
+#endif
+    default:
+      return hb_vector_t ();
+    }
+  }
+
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < u.format.get_size ()) return false;
+
+    switch (u.format) {
+    case 1:
+      return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex);
+#ifndef HB_NO_BEYOND_64K
+    case 2: HB_FALLTHROUGH;
+#endif
+    default:
+      // We don't handle format 3 and 4 here.
+      return false;
+    }
+  }
+};
+
+
+}
+
+#endif  // GRAPH_MARKBASEPOS_GRAPH_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh
new file mode 100644
index 00000000000..1c13eb24f94
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh
@@ -0,0 +1,647 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#ifndef GRAPH_PAIRPOS_GRAPH_HH
+#define GRAPH_PAIRPOS_GRAPH_HH
+
+#include "split-helpers.hh"
+#include "coverage-graph.hh"
+#include "classdef-graph.hh"
+#include "../OT/Layout/GPOS/PairPos.hh"
+#include "../OT/Layout/GPOS/PosLookupSubTable.hh"
+
+namespace graph {
+
+struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size;
+    if (vertex_len < min_size) return false;
+
+    return vertex_len >=
+        min_size + pairSet.get_size () - pairSet.len.get_size();
+  }
+
+  hb_vector_t split_subtables (gsubgpos_graph_context_t& c,
+                                         unsigned parent_index,
+                                         unsigned this_index)
+  {
+    hb_set_t visited;
+
+    const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
+    const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size ();
+    const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size;
+
+    unsigned partial_coverage_size = 4;
+    unsigned accumulated = base_size;
+    hb_vector_t split_points;
+    for (unsigned i = 0; i < pairSet.len; i++)
+    {
+      unsigned pair_set_index = pair_set_graph_index (c, this_index, i);
+      unsigned accumulated_delta =
+          c.graph.find_subgraph_size (pair_set_index, visited) +
+          SmallTypes::size; // for PairSet offset.
+      partial_coverage_size += OT::HBUINT16::static_size;
+
+      accumulated += accumulated_delta;
+      unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size);
+
+      if (total >= (1 << 16))
+      {
+        split_points.push (i);
+        accumulated = base_size + accumulated_delta;
+        partial_coverage_size = 6;
+        visited.clear (); // node sharing isn't allowed between splits.
+      }
+    }
+
+    split_context_t split_context {
+      c,
+      this,
+      c.graph.duplicate_if_shared (parent_index, this_index),
+    };
+
+    return actuate_subtable_split (split_context, split_points);
+  }
+
+ private:
+
+  struct split_context_t {
+    gsubgpos_graph_context_t& c;
+    PairPosFormat1* thiz;
+    unsigned this_index;
+
+    unsigned original_count ()
+    {
+      return thiz->pairSet.len;
+    }
+
+    unsigned clone_range (unsigned start, unsigned end)
+    {
+      return thiz->clone_range (this->c, this->this_index, start, end);
+    }
+
+    bool shrink (unsigned count)
+    {
+      return thiz->shrink (this->c, this->this_index, count);
+    }
+  };
+
+  bool shrink (gsubgpos_graph_context_t& c,
+               unsigned this_index,
+               unsigned count)
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Shrinking PairPosFormat1 (%u) to [0, %u).",
+               this_index,
+               count);
+    unsigned old_count = pairSet.len;
+    if (count >= old_count)
+      return true;
+
+    pairSet.len = count;
+    c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size;
+
+    auto coverage = c.graph.as_mutable_table (this_index, &this->coverage);
+    if (!coverage) return false;
+
+    unsigned coverage_size = coverage.vertex->table_size ();
+    auto new_coverage =
+        + hb_zip (coverage.table->iter (), hb_range ())
+        | hb_filter ([&] (hb_pair_t p) {
+          return p.second < count;
+        })
+        | hb_map_retains_sorting (hb_first)
+        ;
+
+    return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size);
+  }
+
+  // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive).
+  // Returns object id of the new object.
+  unsigned clone_range (gsubgpos_graph_context_t& c,
+                        unsigned this_index,
+                        unsigned start, unsigned end) const
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end);
+
+    unsigned num_pair_sets = end - start;
+    unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size
+                          + num_pair_sets * SmallTypes::size;
+
+    unsigned pair_pos_prime_id = c.create_node (prime_size);
+    if (pair_pos_prime_id == (unsigned) -1) return -1;
+
+    PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head;
+    pair_pos_prime->format = this->format;
+    pair_pos_prime->valueFormat[0] = this->valueFormat[0];
+    pair_pos_prime->valueFormat[1] = this->valueFormat[1];
+    pair_pos_prime->pairSet.len = num_pair_sets;
+
+    for (unsigned i = start; i < end; i++)
+    {
+      c.graph.move_child<> (this_index,
+                            &pairSet[i],
+                            pair_pos_prime_id,
+                            &pair_pos_prime->pairSet[i - start]);
+    }
+
+    unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
+    if (!Coverage::clone_coverage (c,
+                                   coverage_id,
+                                   pair_pos_prime_id,
+                                   2,
+                                   start, end))
+      return -1;
+
+    return pair_pos_prime_id;
+  }
+
+
+
+  unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const
+  {
+    return c.graph.index_for_offset (this_index, &pairSet[i]);
+  }
+};
+
+struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    size_t vertex_len = vertex.table_size ();
+    unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size;
+    if (vertex_len < min_size) return false;
+
+    const unsigned class1_count = class1Count;
+    return vertex_len >=
+        min_size + class1_count * get_class1_record_size ();
+  }
+
+  hb_vector_t split_subtables (gsubgpos_graph_context_t& c,
+                                         unsigned parent_index,
+                                         unsigned this_index)
+  {
+    const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size;
+    const unsigned class_def_2_size = size_of (c, this_index, &classDef2);
+    const Coverage* coverage = get_coverage (c, this_index);
+    const ClassDef* class_def_1 = get_class_def_1 (c, this_index);
+    auto gid_and_class =
+        + coverage->iter ()
+        | hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
+          return hb_pair_t (gid, class_def_1->get_class (gid));
+        })
+        ;
+    class_def_size_estimator_t estimator (gid_and_class);
+
+    const unsigned class1_count = class1Count;
+    const unsigned class2_count = class2Count;
+    const unsigned class1_record_size = get_class1_record_size ();
+
+    const unsigned value_1_len = valueFormat1.get_len ();
+    const unsigned value_2_len = valueFormat2.get_len ();
+    const unsigned total_value_len = value_1_len + value_2_len;
+
+    unsigned accumulated = base_size;
+    unsigned coverage_size = 4;
+    unsigned class_def_1_size = 4;
+    unsigned max_coverage_size = coverage_size;
+    unsigned max_class_def_1_size = class_def_1_size;
+
+    hb_vector_t split_points;
+
+    hb_hashmap_t device_tables = get_all_device_tables (c, this_index);
+    hb_vector_t format1_device_table_indices = valueFormat1.get_device_table_indices ();
+    hb_vector_t format2_device_table_indices = valueFormat2.get_device_table_indices ();
+    bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices);
+
+    hb_set_t visited;
+    for (unsigned i = 0; i < class1_count; i++)
+    {
+      unsigned accumulated_delta = class1_record_size;
+      coverage_size += estimator.incremental_coverage_size (i);
+      class_def_1_size += estimator.incremental_class_def_size (i);
+      max_coverage_size = hb_max (max_coverage_size, coverage_size);
+      max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size);
+
+      if (has_device_tables) {
+        for (unsigned j = 0; j < class2_count; j++)
+        {
+          unsigned value1_index = total_value_len * (class2_count * i + j);
+          unsigned value2_index = value1_index + value_1_len;
+          accumulated_delta += size_of_value_record_children (c,
+                                                        device_tables,
+                                                        format1_device_table_indices,
+                                                        value1_index,
+                                                        visited);
+          accumulated_delta += size_of_value_record_children (c,
+                                                        device_tables,
+                                                        format2_device_table_indices,
+                                                        value2_index,
+                                                        visited);
+        }
+      }
+
+      accumulated += accumulated_delta;
+      unsigned total = accumulated
+                       + coverage_size + class_def_1_size + class_def_2_size
+                       // The largest object will pack last and can exceed the size limit.
+                       - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size);
+      if (total >= (1 << 16))
+      {
+        split_points.push (i);
+        // split does not include i, so add the size for i when we reset the size counters.
+        accumulated = base_size + accumulated_delta;
+        coverage_size = 4 + estimator.incremental_coverage_size (i);
+        class_def_1_size = 4 + estimator.incremental_class_def_size (i);
+        visited.clear (); // node sharing isn't allowed between splits.
+      }
+    }
+
+    split_context_t split_context {
+      c,
+      this,
+      c.graph.duplicate_if_shared (parent_index, this_index),
+      class1_record_size,
+      total_value_len,
+      value_1_len,
+      value_2_len,
+      max_coverage_size,
+      max_class_def_1_size,
+      device_tables,
+      format1_device_table_indices,
+      format2_device_table_indices
+    };
+
+    return actuate_subtable_split (split_context, split_points);
+  }
+ private:
+
+  struct split_context_t
+  {
+    gsubgpos_graph_context_t& c;
+    PairPosFormat2* thiz;
+    unsigned this_index;
+    unsigned class1_record_size;
+    unsigned value_record_len;
+    unsigned value1_record_len;
+    unsigned value2_record_len;
+    unsigned max_coverage_size;
+    unsigned max_class_def_size;
+
+    const hb_hashmap_t& device_tables;
+    const hb_vector_t& format1_device_table_indices;
+    const hb_vector_t& format2_device_table_indices;
+
+    unsigned original_count ()
+    {
+      return thiz->class1Count;
+    }
+
+    unsigned clone_range (unsigned start, unsigned end)
+    {
+      return thiz->clone_range (*this, start, end);
+    }
+
+    bool shrink (unsigned count)
+    {
+      return thiz->shrink (*this, count);
+    }
+  };
+
+  size_t get_class1_record_size () const
+  {
+    const size_t class2_count = class2Count;
+    return
+        class2_count * (valueFormat1.get_size () + valueFormat2.get_size ());
+  }
+
+  unsigned clone_range (split_context_t& split_context,
+                        unsigned start, unsigned end) const
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end);
+
+    graph_t& graph = split_context.c.graph;
+
+    unsigned num_records = end - start;
+    unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size
+                          + num_records * split_context.class1_record_size;
+
+    unsigned pair_pos_prime_id = split_context.c.create_node (prime_size);
+    if (pair_pos_prime_id == (unsigned) -1) return -1;
+
+    PairPosFormat2* pair_pos_prime =
+        (PairPosFormat2*) graph.object (pair_pos_prime_id).head;
+    pair_pos_prime->format = this->format;
+    pair_pos_prime->valueFormat1 = this->valueFormat1;
+    pair_pos_prime->valueFormat2 = this->valueFormat2;
+    pair_pos_prime->class1Count = num_records;
+    pair_pos_prime->class2Count = this->class2Count;
+    clone_class1_records (split_context,
+                          pair_pos_prime_id,
+                          start,
+                          end);
+
+    unsigned coverage_id =
+        graph.index_for_offset (split_context.this_index, &coverage);
+    unsigned class_def_1_id =
+        graph.index_for_offset (split_context.this_index, &classDef1);
+    auto& coverage_v = graph.vertices_[coverage_id];
+    auto& class_def_1_v = graph.vertices_[class_def_1_id];
+    Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
+    ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head;
+    if (!coverage_table
+        || !coverage_table->sanitize (coverage_v)
+        || !class_def_1_table
+        || !class_def_1_table->sanitize (class_def_1_v))
+      return -1;
+
+    auto klass_map =
+    + coverage_table->iter ()
+    | hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
+      return hb_pair_t (gid, class_def_1_table->get_class (gid));
+    })
+    | hb_filter ([&] (hb_codepoint_t klass) {
+      return klass >= start && klass < end;
+    }, hb_second)
+    | hb_map_retains_sorting ([&] (hb_pair_t gid_and_class) {
+      // Classes must be from 0...N so subtract start
+      return hb_pair_t (gid_and_class.first, gid_and_class.second - start);
+    })
+    ;
+
+    if (!Coverage::add_coverage (split_context.c,
+                                 pair_pos_prime_id,
+                                 2,
+                                 + klass_map | hb_map_retains_sorting (hb_first),
+                                 split_context.max_coverage_size))
+      return -1;
+
+    // classDef1
+    if (!ClassDef::add_class_def (split_context.c,
+                                  pair_pos_prime_id,
+                                  8,
+                                  + klass_map,
+                                  split_context.max_class_def_size))
+      return -1;
+
+    // classDef2
+    unsigned class_def_2_id =
+        graph.index_for_offset (split_context.this_index, &classDef2);
+    auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push ();
+    class_def_link->width = SmallTypes::size;
+    class_def_link->objidx = class_def_2_id;
+    class_def_link->position = 10;
+    graph.vertices_[class_def_2_id].parents.push (pair_pos_prime_id);
+    graph.duplicate (pair_pos_prime_id, class_def_2_id);
+
+    return pair_pos_prime_id;
+  }
+
+  void clone_class1_records (split_context_t& split_context,
+                             unsigned pair_pos_prime_id,
+                             unsigned start, unsigned end) const
+  {
+    PairPosFormat2* pair_pos_prime =
+        (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head;
+
+    char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size;
+    unsigned num_records = end - start;
+    hb_memcpy (&pair_pos_prime->values[0],
+            start_addr,
+            num_records * split_context.class1_record_size);
+
+    if (!split_context.format1_device_table_indices
+        && !split_context.format2_device_table_indices)
+      // No device tables to move over.
+      return;
+
+    unsigned class2_count = class2Count;
+    for (unsigned i = start; i < end; i++)
+    {
+      for (unsigned j = 0; j < class2_count; j++)
+      {
+        unsigned value1_index = split_context.value_record_len * (class2_count * i + j);
+        unsigned value2_index = value1_index + split_context.value1_record_len;
+
+        unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j);
+        unsigned new_value2_index = new_value1_index + split_context.value1_record_len;
+
+        transfer_device_tables (split_context,
+                                pair_pos_prime_id,
+                                split_context.format1_device_table_indices,
+                                value1_index,
+                                new_value1_index);
+
+        transfer_device_tables (split_context,
+                                pair_pos_prime_id,
+                                split_context.format2_device_table_indices,
+                                value2_index,
+                                new_value2_index);
+      }
+    }
+  }
+
+  void transfer_device_tables (split_context_t& split_context,
+                               unsigned pair_pos_prime_id,
+                               const hb_vector_t& device_table_indices,
+                               unsigned old_value_record_index,
+                               unsigned new_value_record_index) const
+  {
+    PairPosFormat2* pair_pos_prime =
+        (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head;
+
+    for (unsigned i : device_table_indices)
+    {
+      OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i];
+      unsigned record_position = ((char*) record) - ((char*) this);
+      if (!split_context.device_tables.has (record_position)) continue;
+
+      split_context.c.graph.move_child (
+          split_context.this_index,
+          record,
+          pair_pos_prime_id,
+          (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]);
+    }
+  }
+
+  bool shrink (split_context_t& split_context,
+               unsigned count)
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Shrinking PairPosFormat2 (%u) to [0, %u).",
+               split_context.this_index,
+               count);
+    unsigned old_count = class1Count;
+    if (count >= old_count)
+      return true;
+
+    graph_t& graph = split_context.c.graph;
+    class1Count = count;
+    graph.vertices_[split_context.this_index].obj.tail -=
+        (old_count - count) * split_context.class1_record_size;
+
+    auto coverage =
+        graph.as_mutable_table (split_context.this_index, &this->coverage);
+    if (!coverage) return false;
+
+    auto class_def_1 =
+        graph.as_mutable_table (split_context.this_index, &classDef1);
+    if (!class_def_1) return false;
+
+    auto klass_map =
+    + coverage.table->iter ()
+    | hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
+      return hb_pair_t (gid, class_def_1.table->get_class (gid));
+    })
+    | hb_filter ([&] (hb_codepoint_t klass) {
+      return klass < count;
+    }, hb_second)
+    ;
+
+    auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first);
+    if (!Coverage::make_coverage (split_context.c,
+                                  + new_coverage,
+                                  coverage.index,
+                                  // existing ranges my not be kept, worst case size is a format 1
+                                  // coverage table.
+                                  4 + new_coverage.len() * 2))
+      return false;
+
+    return ClassDef::make_class_def (split_context.c,
+                                     + klass_map,
+                                     class_def_1.index,
+                                     class_def_1.vertex->table_size ());
+  }
+
+  hb_hashmap_t
+  get_all_device_tables (gsubgpos_graph_context_t& c,
+                         unsigned this_index) const
+  {
+    const auto& v = c.graph.vertices_[this_index];
+    return v.position_to_index_map ();
+  }
+
+  const Coverage* get_coverage (gsubgpos_graph_context_t& c,
+                          unsigned this_index) const
+  {
+    unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
+    auto& coverage_v = c.graph.vertices_[coverage_id];
+
+    Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
+    if (!coverage_table || !coverage_table->sanitize (coverage_v))
+      return &Null(Coverage);
+    return coverage_table;
+  }
+
+  const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c,
+                                   unsigned this_index) const
+  {
+    unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1);
+    auto& class_def_1_v = c.graph.vertices_[class_def_1_id];
+
+    ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head;
+    if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v))
+      return &Null(ClassDef);
+    return class_def_1_table;
+  }
+
+  unsigned size_of_value_record_children (gsubgpos_graph_context_t& c,
+                                          const hb_hashmap_t& device_tables,
+                                          const hb_vector_t device_table_indices,
+                                          unsigned value_record_index,
+                                          hb_set_t& visited)
+  {
+    unsigned size = 0;
+    for (unsigned i : device_table_indices)
+    {
+      OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i];
+      unsigned record_position = ((char*) record) - ((char*) this);
+      unsigned* obj_idx;
+      if (!device_tables.has (record_position, &obj_idx)) continue;
+      size += c.graph.find_subgraph_size (*obj_idx, visited);
+    }
+    return size;
+  }
+
+  unsigned size_of (gsubgpos_graph_context_t& c,
+                    unsigned this_index,
+                    const void* offset) const
+  {
+    const unsigned id = c.graph.index_for_offset (this_index, offset);
+    return c.graph.vertices_[id].table_size ();
+  }
+};
+
+struct PairPos : public OT::Layout::GPOS_impl::PairPos
+{
+  hb_vector_t split_subtables (gsubgpos_graph_context_t& c,
+                                         unsigned parent_index,
+                                         unsigned this_index)
+  {
+    switch (u.format) {
+    case 1:
+      return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
+    case 2:
+      return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index);
+#ifndef HB_NO_BEYOND_64K
+    case 3: HB_FALLTHROUGH;
+    case 4: HB_FALLTHROUGH;
+      // Don't split 24bit PairPos's.
+#endif
+    default:
+      return hb_vector_t ();
+    }
+  }
+
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < u.format.get_size ()) return false;
+
+    switch (u.format) {
+    case 1:
+      return ((PairPosFormat1*)(&u.format1))->sanitize (vertex);
+    case 2:
+      return ((PairPosFormat2*)(&u.format2))->sanitize (vertex);
+#ifndef HB_NO_BEYOND_64K
+    case 3: HB_FALLTHROUGH;
+    case 4: HB_FALLTHROUGH;
+#endif
+    default:
+      // We don't handle format 3 and 4 here.
+      return false;
+    }
+  }
+};
+
+}
+
+#endif  // GRAPH_PAIRPOS_GRAPH_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh
index ecc6cc5aea2..040fd1de5fd 100644
--- a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh
+++ b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh
@@ -33,6 +33,23 @@ struct overflow_record_t
 {
   unsigned parent;
   unsigned child;
+
+  bool operator != (const overflow_record_t o) const
+  { return !(*this == o); }
+
+  inline bool operator == (const overflow_record_t& o) const
+  {
+    return parent == o.parent &&
+        child == o.child;
+  }
+
+  inline uint32_t hash () const
+  {
+    uint32_t current = 0;
+    current = current * 31 + hb_hash (parent);
+    current = current * 31 + hb_hash (child);
+    return current;
+  }
 };
 
 inline
@@ -94,6 +111,7 @@ will_overflow (graph_t& graph,
   if (overflows) overflows->resize (0);
   graph.update_positions ();
 
+  hb_hashmap_t record_set;
   const auto& vertices = graph.vertices_;
   for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--)
   {
@@ -109,7 +127,10 @@ will_overflow (graph_t& graph,
       overflow_record_t r;
       r.parent = parent_idx;
       r.child = link.objidx;
+      if (record_set.has(&r)) continue; // don't keep duplicate overflows.
+
       overflows->push (r);
+      record_set.set(&r, true);
     }
   }
 
@@ -132,8 +153,8 @@ void print_overflows (graph_t& graph,
     const auto& child = graph.vertices_[o.child];
     DEBUG_MSG (SUBSET_REPACK, nullptr,
                "  overflow from "
-               "%4d (%4d in, %4d out, space %2d) => "
-               "%4d (%4d in, %4d out, space %2d)",
+               "%4u (%4u in, %4u out, space %2u) => "
+               "%4u (%4u in, %4u out, space %2u)",
                o.parent,
                parent.incoming_edges (),
                parent.obj.real_links.length + parent.obj.virtual_links.length,
@@ -144,7 +165,7 @@ void print_overflows (graph_t& graph,
                graph.space_for (o.child));
   }
   if (overflows.length > 10) {
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  ... plus %d more overflows.", overflows.length - 10);
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  ... plus %u more overflows.", overflows.length - 10);
   }
 }
 
@@ -223,7 +244,7 @@ inline hb_blob_t* serialize (const graph_t& graph)
       return nullptr;
     }
 
-    memcpy (start, vertices[i].obj.head, size);
+    hb_memcpy (start, vertices[i].obj.head, size);
 
     // Only real links needs to be serialized.
     for (const auto& link : vertices[i].obj.real_links)
diff --git a/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh
new file mode 100644
index 00000000000..61fd7c2d2ff
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2022  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.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#ifndef GRAPH_SPLIT_HELPERS_HH
+#define GRAPH_SPLIT_HELPERS_HH
+
+namespace graph {
+
+template
+HB_INTERNAL
+hb_vector_t actuate_subtable_split (Context& split_context,
+                                              const hb_vector_t& split_points)
+{
+  hb_vector_t new_objects;
+  if (!split_points)
+    return new_objects;
+
+  for (unsigned i = 0; i < split_points.length; i++)
+  {
+    unsigned start = split_points[i];
+    unsigned end = (i < split_points.length - 1)
+                   ? split_points[i + 1]
+                   : split_context.original_count ();
+    unsigned id = split_context.clone_range (start, end);
+
+    if (id == (unsigned) -1)
+    {
+      new_objects.reset ();
+      new_objects.allocated = -1; // mark error
+      return new_objects;
+    }
+    new_objects.push (id);
+  }
+
+  if (!split_context.shrink (split_points[0]))
+  {
+    new_objects.reset ();
+    new_objects.allocated = -1; // mark error
+  }
+
+  return new_objects;
+}
+
+}
+
+#endif  // GRAPH_SPLIT_HELPERS_HH
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
index ed376b9efc7..24d53e224c7 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
@@ -42,7 +42,7 @@ struct BaselineTableFormat0Part
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
@@ -78,7 +78,7 @@ struct BaselineTableFormat2Part
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
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 b8c9b992fb8..8230cba7c94 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
@@ -28,6 +28,7 @@
 #define HB_AAT_LAYOUT_COMMON_HH
 
 #include "hb-aat-layout.hh"
+#include "hb-aat-map.hh"
 #include "hb-open-type.hh"
 
 namespace OT {
@@ -39,6 +40,43 @@ namespace AAT {
 using namespace OT;
 
 
+struct ankr;
+
+struct hb_aat_apply_context_t :
+       hb_dispatch_context_t
+{
+  const char *get_name () { return "APPLY"; }
+  template 
+  return_t dispatch (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value () { return false; }
+  bool stop_sublookup_iteration (return_t r) const { return r; }
+
+  const hb_ot_shape_plan_t *plan;
+  hb_font_t *font;
+  hb_face_t *face;
+  hb_buffer_t *buffer;
+  hb_sanitize_context_t sanitizer;
+  const ankr *ankr_table;
+  const OT::GDEF *gdef_table;
+  const hb_sorted_vector_t *range_flags = nullptr;
+  hb_mask_t subtable_flags = 0;
+
+  /* Unused. For debug tracing only. */
+  unsigned int lookup_index;
+
+  HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
+                                      hb_font_t *font_,
+                                      hb_buffer_t *buffer_,
+                                      hb_blob_t *blob = const_cast (&Null (hb_blob_t)));
+
+  HB_INTERNAL ~hb_aat_apply_context_t ();
+
+  HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
+
+  void set_lookup_index (unsigned int i) { lookup_index = i; }
+};
+
+
 /*
  * Lookup Table
  */
@@ -415,18 +453,7 @@ struct Lookup
   public:
   DEFINE_SIZE_UNION (2, format);
 };
-/* Lookup 0 has unbounded size (dependant on num_glyphs).  So we need to defined
- * special NULL objects for Lookup<> objects, but since it's template our macros
- * don't work.  So we have to hand-code them here.  UGLY. */
-} /* Close namespace. */
-/* Ugly hand-coded null objects for template Lookup<> :(. */
-extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
-template 
-struct Null> {
-  static AAT::Lookup const & get_null ()
-  { return *reinterpret_cast *> (_hb_Null_AAT_Lookup); }
-};
-namespace AAT {
+DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
 
 enum { DELETED_GLYPH = 0xFFFF };
 
@@ -437,7 +464,8 @@ enum { DELETED_GLYPH = 0xFFFF };
 template 
 struct Entry
 {
-  bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  // This does seem like it's ever called.
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* Note, we don't recurse-sanitize data because we don't access it.
@@ -465,7 +493,8 @@ struct Entry
 template <>
 struct Entry
 {
-  bool sanitize (hb_sanitize_context_t *c, unsigned int count /*XXX Unused?*/) const
+  // This does seem like it's ever called.
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -681,6 +710,13 @@ struct ObsoleteTypes
                                      const void *base,
                                      const T *array)
   {
+    /* https://github.com/harfbuzz/harfbuzz/issues/3483 */
+    /* If offset is less than base, return an offset that would
+     * result in an address half a 32bit address-space away,
+     * to make sure sanitize fails even on 32bit builds. */
+    if (unlikely (offset < unsigned ((const char *) array - (const char *) base)))
+      return INT_MAX / T::static_size;
+
     /* https://github.com/harfbuzz/harfbuzz/issues/2816 */
     return (offset - unsigned ((const char *) array - (const char *) base)) / T::static_size;
   }
@@ -744,16 +780,44 @@ struct StateTableDriver
               num_glyphs (face_->get_num_glyphs ()) {}
 
   template 
-  void drive (context_t *c)
+  void drive (context_t *c, hb_aat_apply_context_t *ac)
   {
     if (!c->in_place)
       buffer->clear_output ();
 
     int state = StateTableT::STATE_START_OF_TEXT;
+    // If there's only one range, we already checked the flag.
+    auto *last_range = ac->range_flags && (ac->range_flags->length > 1) ? &(*ac->range_flags)[0] : nullptr;
     for (buffer->idx = 0; buffer->successful;)
     {
+      /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */
+      if (last_range)
+      {
+        auto *range = last_range;
+        if (buffer->idx < buffer->len)
+        {
+          unsigned cluster = buffer->cur().cluster;
+          while (cluster < range->cluster_first)
+            range--;
+          while (cluster > range->cluster_last)
+            range++;
+
+
+          last_range = range;
+        }
+        if (!(range->flags & ac->subtable_flags))
+        {
+          if (buffer->idx == buffer->len || unlikely (!buffer->successful))
+            break;
+
+          state = StateTableT::STATE_START_OF_TEXT;
+          (void) buffer->next_glyph ();
+          continue;
+        }
+      }
+
       unsigned int klass = buffer->idx < buffer->len ?
-                           machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
+                           machine.get_class (buffer->cur().codepoint, num_glyphs) :
                            (unsigned) StateTableT::CLASS_END_OF_TEXT;
       DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       const EntryT &entry = machine.get_entry (state, klass);
@@ -849,41 +913,6 @@ struct StateTableDriver
 };
 
 
-struct ankr;
-
-struct hb_aat_apply_context_t :
-       hb_dispatch_context_t
-{
-  const char *get_name () { return "APPLY"; }
-  template 
-  return_t dispatch (const T &obj) { return obj.apply (this); }
-  static return_t default_return_value () { return false; }
-  bool stop_sublookup_iteration (return_t r) const { return r; }
-
-  const hb_ot_shape_plan_t *plan;
-  hb_font_t *font;
-  hb_face_t *face;
-  hb_buffer_t *buffer;
-  hb_sanitize_context_t sanitizer;
-  const ankr *ankr_table;
-  const OT::GDEF *gdef_table;
-
-  /* Unused. For debug tracing only. */
-  unsigned int lookup_index;
-
-  HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
-                                      hb_font_t *font_,
-                                      hb_buffer_t *buffer_,
-                                      hb_blob_t *blob = const_cast (&Null (hb_blob_t)));
-
-  HB_INTERNAL ~hb_aat_apply_context_t ();
-
-  HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
-
-  void set_lookup_index (unsigned int i) { lookup_index = i; }
-};
-
-
 } /* namespace AAT */
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
index 527bfe45b35..04260a94616 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
@@ -62,7 +62,7 @@ struct SettingName
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
index 4b1319ac31c..0588310b53b 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
@@ -48,7 +48,7 @@ struct ActionSubrecordHeader
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   HBUINT16      actionClass;    /* The JustClass value associated with this
@@ -65,14 +65,14 @@ struct DecompositionAction
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   ActionSubrecordHeader
                 header;
-  HBFixed       lowerLimit;     /* If the distance factor is less than this value,
+  F16DOT16      lowerLimit;     /* If the distance factor is less than this value,
                                  * then the ligature is decomposed. */
-  HBFixed       upperLimit;     /* If the distance factor is greater than this value,
+  F16DOT16      upperLimit;     /* If the distance factor is greater than this value,
                                  * then the ligature is decomposed. */
   HBUINT16      order;          /* Numerical order in which this ligature will
                                  * be decomposed; you may want infrequent ligatures
@@ -112,13 +112,13 @@ struct ConditionalAddGlyphAction
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
   ActionSubrecordHeader
                 header;
-  HBFixed       substThreshold; /* Distance growth factor (in ems) at which
+  F16DOT16      substThreshold; /* Distance growth factor (in ems) at which
                                  * this glyph is replaced and the growth factor
                                  * recalculated. */
   HBGlyphID16   addGlyph;       /* Glyph to be added as kashida. If this value is
@@ -137,7 +137,7 @@ struct DuctileGlyphAction
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
@@ -146,13 +146,13 @@ struct DuctileGlyphAction
   HBUINT32      variationAxis;  /* The 4-byte tag identifying the ductile axis.
                                  * This would normally be 0x64756374 ('duct'),
                                  * but you may use any axis the font contains. */
-  HBFixed       minimumLimit;   /* The lowest value for the ductility axis that
+  F16DOT16      minimumLimit;   /* The lowest value for the ductility axis that
                                  * still yields an acceptable appearance. Normally
                                  * this will be 1.0. */
-  HBFixed       noStretchValue; /* This is the default value that corresponds to
+  F16DOT16      noStretchValue; /* This is the default value that corresponds to
                                  * no change in appearance. Normally, this will
                                  * be 1.0. */
-  HBFixed       maximumLimit;   /* The highest value for the ductility axis that
+  F16DOT16      maximumLimit;   /* The highest value for the ductility axis that
                                  * still yields an acceptable appearance. */
   public:
   DEFINE_SIZE_STATIC (22);
@@ -163,7 +163,7 @@ struct RepeatedAddGlyphAction
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
@@ -271,14 +271,14 @@ struct JustWidthDeltaEntry
   };
 
   protected:
-  HBFixed       beforeGrowLimit;/* The ratio by which the advance width of the
+  F16DOT16      beforeGrowLimit;/* The ratio by which the advance width of the
                                  * glyph is permitted to grow on the left or top side. */
-  HBFixed       beforeShrinkLimit;
+  F16DOT16      beforeShrinkLimit;
                                 /* The ratio by which the advance width of the
                                  * glyph is permitted to shrink on the left or top side. */
-  HBFixed       afterGrowLimit; /* The ratio by which the advance width of the glyph
+  F16DOT16      afterGrowLimit; /* The ratio by which the advance width of the glyph
                                  * is permitted to shrink on the left or top side. */
-  HBFixed       afterShrinkLimit;
+  F16DOT16      afterShrinkLimit;
                                 /* The ratio by which the advance width of the glyph
                                  * is at most permitted to shrink on the right or
                                  * bottom side. */
@@ -294,7 +294,7 @@ struct WidthDeltaPair
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
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 51cb106bb08..2bc7b03ce89 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
@@ -350,7 +350,7 @@ struct KerxSubTableFormat1
     driver_context_t dc (this, c);
 
     StateTableDriver driver (machine, c->buffer, c->font->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (true);
   }
@@ -594,7 +594,7 @@ struct KerxSubTableFormat4
     driver_context_t dc (this, c);
 
     StateTableDriver driver (machine, c->buffer, c->font->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (true);
   }
@@ -751,7 +751,7 @@ struct KerxSubTableHeader
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   public:
@@ -869,6 +869,8 @@ struct KerxTable
 
   bool apply (AAT::hb_aat_apply_context_t *c) const
   {
+    c->buffer->unsafe_to_concat ();
+
     typedef typename T::SubTable SubTable;
 
     bool ret = false;
@@ -889,7 +891,7 @@ struct KerxTable
       reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
                 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
-      if (!c->buffer->message (c->font, "start subtable %d", c->lookup_index))
+      if (!c->buffer->message (c->font, "start subtable %u", c->lookup_index))
         goto skip;
 
       if (!seenCrossStream &&
@@ -921,7 +923,7 @@ struct KerxTable
       if (reverse)
         c->buffer->reverse ();
 
-      (void) c->buffer->message (c->font, "end subtable %d", c->lookup_index);
+      (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index);
 
     skip:
       st = &StructAfter (*st);
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 889784e7d52..81e126d5eb1 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
@@ -131,14 +131,14 @@ struct RearrangementSubtable
           hb_glyph_info_t *info = buffer->info;
           hb_glyph_info_t buf[4];
 
-          memcpy (buf, info + start, l * sizeof (buf[0]));
-          memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
+          hb_memcpy (buf, info + start, l * sizeof (buf[0]));
+          hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
 
           if (l != r)
             memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0]));
 
-          memcpy (info + start, buf + 2, r * sizeof (buf[0]));
-          memcpy (info + end - l, buf, l * sizeof (buf[0]));
+          hb_memcpy (info + start, buf + 2, r * sizeof (buf[0]));
+          hb_memcpy (info + end - l, buf, l * sizeof (buf[0]));
           if (reverse_l)
           {
             buf[0] = info[end - 1];
@@ -169,7 +169,7 @@ struct RearrangementSubtable
     driver_context_t dc (this);
 
     StateTableDriver driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -325,7 +325,7 @@ struct ContextualSubtable
     driver_context_t dc (this, c);
 
     StateTableDriver driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -525,7 +525,7 @@ struct LigatureSubtable
           if (unlikely (!componentData.sanitize (&c->sanitizer))) break;
           ligature_idx += componentData;
 
-          DEBUG_MSG (APPLY, nullptr, "Action store %u last %u",
+          DEBUG_MSG (APPLY, nullptr, "Action store %d last %d",
                      bool (action & LigActionStore),
                      bool (action & LigActionLast));
           if (action & (LigActionStore | LigActionLast))
@@ -577,7 +577,7 @@ struct LigatureSubtable
     driver_context_t dc (this, c);
 
     StateTableDriver driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -618,8 +618,27 @@ struct NoncontextualSubtable
 
     hb_glyph_info_t *info = c->buffer->info;
     unsigned int count = c->buffer->len;
+    // If there's only one range, we already checked the flag.
+    auto *last_range = c->range_flags && (c->range_flags->length > 1) ? &(*c->range_flags)[0] : nullptr;
     for (unsigned int i = 0; i < count; i++)
     {
+      /* This block copied from StateTableDriver::drive. Keep in sync. */
+      if (last_range)
+      {
+        auto *range = last_range;
+        {
+          unsigned cluster = info[i].cluster;
+          while (cluster < range->cluster_first)
+            range--;
+          while (cluster > range->cluster_last)
+            range++;
+
+          last_range = range;
+        }
+        if (!(range->flags & c->subtable_flags))
+          continue;
+      }
+
       const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       if (replacement)
       {
@@ -820,7 +839,7 @@ struct InsertionSubtable
     driver_context_t dc (this, c);
 
     StateTableDriver driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -968,7 +987,7 @@ struct Chain
         // Check whether this type/setting pair was requested in the map, and if so, apply its flags.
         // (The search here only looks at the type and setting fields of feature_info_t.)
         hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 };
-        if (map->features.bsearch (info))
+        if (map->current_features.bsearch (info))
         {
           flags &= feature.disableFlags;
           flags |= feature.enableFlags;
@@ -980,13 +999,21 @@ struct Chain
           setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS;
           goto retry;
         }
+#ifndef HB_NO_AAT
+        else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting &&
+                 /* TODO: Rudimentary language matching. */
+                 hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language))
+        {
+          flags &= feature.disableFlags;
+          flags |= feature.enableFlags;
+        }
+#endif
       }
     }
     return flags;
   }
 
-  void apply (hb_aat_apply_context_t *c,
-              hb_mask_t flags) const
+  void apply (hb_aat_apply_context_t *c) const
   {
     const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount));
     unsigned int count = subtableCount;
@@ -994,8 +1021,10 @@ struct Chain
     {
       bool reverse;
 
-      if (!(subtable->subFeatureFlags & flags))
+      if (hb_none (hb_iter (c->range_flags) |
+                   hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); })))
         goto skip;
+      c->subtable_flags = subtable->subFeatureFlags;
 
       if (!(subtable->get_coverage() & ChainSubtable::AllDirections) &&
           HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
@@ -1034,7 +1063,7 @@ struct Chain
                 bool (subtable->get_coverage () & ChainSubtable::Backwards) !=
                 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
-      if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index))
+      if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
         goto skip;
 
       if (reverse)
@@ -1045,7 +1074,7 @@ struct Chain
       if (reverse)
         c->buffer->reverse ();
 
-      (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index);
+      (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index);
 
       if (unlikely (!c->buffer->successful)) return;
 
@@ -1111,22 +1140,31 @@ struct mortmorx
   {
     const Chain *chain = &firstChain;
     unsigned int count = chainCount;
+    if (unlikely (!map->chain_flags.resize (count)))
+      return;
     for (unsigned int i = 0; i < count; i++)
     {
-      map->chain_flags.push (chain->compile_flags (mapper));
+      map->chain_flags[i].push (hb_aat_map_t::range_flags_t {chain->compile_flags (mapper),
+                                                             mapper->range_first,
+                                                             mapper->range_last});
       chain = &StructAfter> (*chain);
     }
   }
 
-  void apply (hb_aat_apply_context_t *c) const
+  void apply (hb_aat_apply_context_t *c,
+              const hb_aat_map_t &map) const
   {
     if (unlikely (!c->buffer->successful)) return;
+
+    c->buffer->unsafe_to_concat ();
+
     c->set_lookup_index (0);
     const Chain *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      chain->apply (c, c->plan->aat_map.chain_flags[i]);
+      c->range_flags = &map.chain_flags[i];
+      chain->apply (c);
       if (unlikely (!c->buffer->successful)) return;
       chain = &StructAfter> (*chain);
     }
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
index 3054c76b96b..959382f356f 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
@@ -42,7 +42,7 @@ struct OpticalBounds
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   FWORD         leftSide;
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 d99a53b14c4..cb531283498 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
@@ -62,7 +62,7 @@ struct TrackTableEntry
   }
 
   protected:
-  HBFixed       track;          /* Track value for this record. */
+  F16DOT16      track;          /* Track value for this record. */
   NameID        trackNameID;    /* The 'name' table index for this track.
                                  * (a short word or phrase like "loose"
                                  * or "very tight") */
@@ -82,7 +82,7 @@ struct TrackData
                         const void *base) const
   {
     unsigned int sizes = nSizes;
-    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
+    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
 
     float s0 = size_table[idx].to_float ();
     float s1 = size_table[idx + 1].to_float ();
@@ -120,7 +120,7 @@ struct TrackData
     if (!sizes) return 0.;
     if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
 
-    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
+    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
     unsigned int size_index;
     for (size_index = 0; size_index < sizes - 1; size_index++)
       if (size_table[size_index].to_float () >= ptem)
@@ -141,7 +141,7 @@ struct TrackData
   protected:
   HBUINT16      nTracks;        /* Number of separate tracks included in this table. */
   HBUINT16      nSizes;         /* Number of point sizes included in this table. */
-  NNOffset32To>
+  NNOffset32To>
                 sizeTable;      /* Offset from start of the tracking table to
                                  * Array[nSizes] of size values.. */
   UnsizedArrayOf
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 a9a9ce6b326..b0afbdfbb04 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
@@ -131,6 +131,7 @@ static const hb_aat_feature_mapping_t feature_mappings[] =
   {HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING,          HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS,           (hb_aat_layout_feature_selector_t) 4},
   {HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT,              (hb_aat_layout_feature_selector_t) 7},
   {HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT,             (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('r','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES,               HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON,          HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF},
   {HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA,               HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON,                   HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF},
   {HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION,       HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS,           HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
   {HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE,              HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS,          HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE},
@@ -229,7 +230,7 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
  *
  * Note: does not examine the `GSUB` table.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 2.3.0
  */
@@ -243,15 +244,23 @@ hb_aat_layout_has_substitution (hb_face_t *face)
 void
 hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
                           hb_font_t *font,
-                          hb_buffer_t *buffer)
+                          hb_buffer_t *buffer,
+                          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);
+
   hb_blob_t *morx_blob = font->face->table.morx.get_blob ();
   const AAT::morx& morx = *morx_blob->as ();
   if (morx.has_data ())
   {
     AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob);
     if (!buffer->message (font, "start table morx")) return;
-    morx.apply (&c);
+    morx.apply (&c, map);
     (void) buffer->message (font, "end table morx");
     return;
   }
@@ -262,7 +271,7 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
   {
     AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob);
     if (!buffer->message (font, "start table mort")) return;
-    mort.apply (&c);
+    mort.apply (&c, map);
     (void) buffer->message (font, "end table mort");
     return;
   }
@@ -288,7 +297,7 @@ is_deleted_glyph (const hb_glyph_info_t *info)
 void
 hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
 {
-  hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph);
+  buffer->delete_glyphs_inplace (is_deleted_glyph);
 }
 
 /**
@@ -300,7 +309,7 @@ hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
  *
  * Note: does not examine the `GPOS` table.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 2.3.0
  */
@@ -333,7 +342,7 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
  * Tests whether the specified face includes any tracking information
  * in the `trak` table.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 2.3.0
  */
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 1a95507ccee..36bb0ef07dd 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
@@ -53,7 +53,9 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 HB_INTERNAL void
 hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
                           hb_font_t *font,
-                          hb_buffer_t *buffer);
+                          hb_buffer_t *buffer,
+                          const hb_feature_t *features,
+                          unsigned num_features);
 
 HB_INTERNAL void
 hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer);
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 ad3eff7935f..122f29dc681 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
@@ -36,27 +36,29 @@
 #include "hb-aat-layout-feat-table.hh"
 
 
-void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
+void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature)
 {
   if (!face->table.feat->has_data ()) return;
 
-  if (tag == HB_TAG ('a','a','l','t'))
+  if (feature.tag == HB_TAG ('a','a','l','t'))
   {
     if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
       return;
-    feature_info_t *info = features.push();
-    info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
-    info->setting = (hb_aat_layout_feature_selector_t) value;
-    info->seq = features.length;
-    info->is_exclusive = true;
+    feature_range_t *range = features.push();
+    range->start = feature.start;
+    range->end = feature.end;
+    range->info.type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
+    range->info.setting = (hb_aat_layout_feature_selector_t) feature.value;
+    range->info.seq = features.length;
+    range->info.is_exclusive = true;
     return;
   }
 
-  const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
+  const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag);
   if (!mapping) return;
 
-  const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
-  if (!feature->has_data ())
+  const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType);
+  if (!feature_name->has_data ())
   {
     /* Special case: Chain::compile_flags will fall back to the deprecated version of
      * small-caps if necessary, so we need to check for that possibility.
@@ -64,38 +66,106 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
     if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
         mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
     {
-      feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
-      if (!feature->has_data ()) return;
+      feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
+      if (!feature_name->has_data ()) return;
     }
     else return;
   }
 
-  feature_info_t *info = features.push();
-  info->type = mapping->aatFeatureType;
-  info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
-  info->seq = features.length;
-  info->is_exclusive = feature->is_exclusive ();
+  feature_range_t *range = features.push();
+  range->start = feature.start;
+  range->end = feature.end;
+  range->info.type = mapping->aatFeatureType;
+  range->info.setting = feature.value ? mapping->selectorToEnable : mapping->selectorToDisable;
+  range->info.seq = features.length;
+  range->info.is_exclusive = feature_name->is_exclusive ();
 }
 
 void
 hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 {
-  /* Sort features and merge duplicates */
-  if (features.length)
+  /* Compute active features per range, and compile each. */
+
+  /* Sort features by start/end events. */
+  hb_vector_t feature_events;
+  for (unsigned int i = 0; i < features.length; i++)
+  {
+    auto &feature = features[i];
+
+    if (features[i].start == features[i].end)
+      continue;
+
+    feature_event_t *event;
+
+    event = feature_events.push ();
+    event->index = features[i].start;
+    event->start = true;
+    event->feature = feature.info;
+
+    event = feature_events.push ();
+    event->index = features[i].end;
+    event->start = false;
+    event->feature = feature.info;
+  }
+  feature_events.qsort ();
+  /* Add a strategic final event. */
+  {
+    feature_info_t feature;
+    feature.seq = features.length + 1;
+
+    feature_event_t *event = feature_events.push ();
+    event->index = -1; /* This value does magic. */
+    event->start = false;
+    event->feature = feature;
+  }
+
+  /* Scan events and save features for each range. */
+  hb_sorted_vector_t active_features;
+  unsigned int last_index = 0;
+  for (unsigned int i = 0; i < feature_events.length; i++)
   {
-    features.qsort ();
-    unsigned int j = 0;
-    for (unsigned int i = 1; i < features.length; i++)
-      if (features[i].type != features[j].type ||
-          /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
-           * respectively, so we mask out the low-order bit when checking for "duplicates"
-           * (selectors referring to the same feature setting) here. */
-          (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1))))
-        features[++j] = features[i];
-    features.shrink (j + 1);
+    feature_event_t *event = &feature_events[i];
+
+    if (event->index != last_index)
+    {
+      /* Save a snapshot of active features and the range. */
+
+      /* Sort features and merge duplicates */
+      current_features = active_features;
+      range_first = last_index;
+      range_last = event->index - 1;
+      if (current_features.length)
+      {
+        current_features.qsort ();
+        unsigned int j = 0;
+        for (unsigned int i = 1; i < current_features.length; i++)
+          if (current_features[i].type != current_features[j].type ||
+              /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
+               * respectively, so we mask out the low-order bit when checking for "duplicates"
+               * (selectors referring to the same feature setting) here. */
+              (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
+            current_features[++j] = current_features[i];
+        current_features.shrink (j + 1);
+      }
+
+      hb_aat_layout_compile_map (this, &m);
+
+      last_index = event->index;
+    }
+
+    if (event->start)
+    {
+      active_features.push (event->feature);
+    } else {
+      feature_info_t *feature = active_features.lsearch (event->feature);
+      if (feature)
+        active_features.remove_ordered (feature - active_features.arrayZ);
+    }
   }
 
-  hb_aat_layout_compile_map (this, &m);
+  for (auto &chain_flags : m.chain_flags)
+    // With our above setup this value is one less than desired; adjust it.
+    chain_flags.tail().cluster_last = HB_FEATURE_GLOBAL_END;
 }
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
index ce30daa6084..b21f5bacb9e 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
@@ -35,16 +35,15 @@ struct hb_aat_map_t
   friend struct hb_aat_map_builder_t;
 
   public:
-
-  void init ()
+  struct range_flags_t
   {
-    memset (this, 0, sizeof (*this));
-    chain_flags.init ();
-  }
-  void fini () { chain_flags.fini (); }
+    hb_mask_t flags;
+    unsigned cluster_first;
+    unsigned cluster_last; // end - 1
+  };
 
   public:
-  hb_vector_t chain_flags;
+  hb_vector_t> chain_flags;
 };
 
 struct hb_aat_map_builder_t
@@ -52,10 +51,11 @@ struct hb_aat_map_builder_t
   public:
 
   HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_,
-                                    const hb_segment_properties_t *props_ HB_UNUSED) :
-                                      face (face_) {}
+                                    const hb_segment_properties_t props_) :
+                                      face (face_),
+                                      props (props_) {}
 
-  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1);
+  HB_INTERNAL void add_feature (const hb_feature_t &feature);
 
   HB_INTERNAL void compile (hb_aat_map_t  &m);
 
@@ -77,7 +77,7 @@ struct hb_aat_map_builder_t
             return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
     }
 
-    /* compares type & setting only, not is_exclusive flag or seq number */
+    /* compares type & setting only */
     int cmp (const feature_info_t& f) const
     {
       return (f.type != type) ? (f.type < type ? -1 : 1) :
@@ -85,11 +85,38 @@ struct hb_aat_map_builder_t
     }
   };
 
+  struct feature_range_t
+  {
+    feature_info_t info;
+    unsigned start;
+    unsigned end;
+  };
+
+  private:
+  struct feature_event_t
+  {
+    unsigned int index;
+    bool start;
+    feature_info_t feature;
+
+    HB_INTERNAL static int cmp (const void *pa, const void *pb) {
+      const feature_event_t *a = (const feature_event_t *) pa;
+      const feature_event_t *b = (const feature_event_t *) pb;
+      return a->index < b->index ? -1 : a->index > b->index ? 1 :
+             a->start < b->start ? -1 : a->start > b->start ? 1 :
+             feature_info_t::cmp (&a->feature, &b->feature);
+    }
+  };
+
   public:
   hb_face_t *face;
+  hb_segment_properties_t props;
 
   public:
-  hb_sorted_vector_t features;
+  hb_sorted_vector_t features;
+  hb_sorted_vector_t current_features;
+  unsigned range_first = HB_FEATURE_GLOBAL_START;
+  unsigned range_last = HB_FEATURE_GLOBAL_END;
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
index 162601b380d..e2b970f968f 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
@@ -109,15 +109,17 @@ struct BEInt
   struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
   constexpr operator Type () const
   {
-#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
+#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
     defined(__BYTE_ORDER) && \
-    (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+    (__BYTE_ORDER == __BIG_ENDIAN || \
+     (__BYTE_ORDER == __LITTLE_ENDIAN && \
+      hb_has_builtin(__builtin_bswap16)))
     /* Spoon-feed the compiler a big-endian integer with alignment 1.
      * https://github.com/harfbuzz/harfbuzz/pull/1398 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-    return __builtin_bswap16 (((packed_uint16_t *) this)->v);
+    return __builtin_bswap16 (((packed_uint16_t *) v)->v);
 #else /* __BYTE_ORDER == __BIG_ENDIAN */
-    return ((packed_uint16_t *) this)->v;
+    return ((packed_uint16_t *) v)->v;
 #endif
 #else
     return (v[0] <<  8)
@@ -153,15 +155,17 @@ struct BEInt
 
   struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
   constexpr operator Type () const {
-#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
+#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
     defined(__BYTE_ORDER) && \
-    (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+    (__BYTE_ORDER == __BIG_ENDIAN || \
+     (__BYTE_ORDER == __LITTLE_ENDIAN && \
+      hb_has_builtin(__builtin_bswap32)))
     /* Spoon-feed the compiler a big-endian integer with alignment 1.
      * https://github.com/harfbuzz/harfbuzz/pull/1398 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-    return __builtin_bswap32 (((packed_uint32_t *) this)->v);
+    return __builtin_bswap32 (((packed_uint32_t *) v)->v);
 #else /* __BYTE_ORDER == __BIG_ENDIAN */
-    return ((packed_uint32_t *) this)->v;
+    return ((packed_uint32_t *) v)->v;
 #endif
 #else
     return (v[0] << 24)
@@ -234,17 +238,6 @@ struct
   template  constexpr auto
   impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
 
-  template  constexpr uint32_t
-  impl (const hb::shared_ptr& v, hb_priority<1>) const
-  {
-    return v.get () ? v.get ()->hash () : 0;
-  }
-  template  constexpr uint32_t
-  impl (const hb::unique_ptr& v, hb_priority<1>) const
-  {
-    return v.get () ? v.get ()->hash () : 0;
-  }
-
   template  constexpr auto
   impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash>{} (hb_deref (v)))
 
@@ -493,6 +486,17 @@ struct
 }
 HB_FUNCOBJ (hb_equal);
 
+struct
+{
+  template  void
+  operator () (T& a, T& b) const
+  {
+    using std::swap; // allow ADL
+    swap (a, b);
+  }
+}
+HB_FUNCOBJ (hb_swap);
+
 
 template 
 struct hb_pair_t
@@ -505,7 +509,7 @@ struct hb_pair_t
             hb_enable_if (std::is_default_constructible::value &&
                           std::is_default_constructible::value)>
   hb_pair_t () : first (), second () {}
-  hb_pair_t (T1 a, T2 b) : first (a), second (b) {}
+  hb_pair_t (T1 a, T2 b) : first (std::forward (a)), second (std::forward (b)) {}
 
   template  (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); }
   bool operator <= (const pair_t& o) const { return !(*this > o); }
 
+  static int cmp (const void *pa, const void *pb)
+  {
+    pair_t *a = (pair_t *) pa;
+    pair_t *b = (pair_t *) pb;
+
+    if (a->first < b->first) return -1;
+    if (a->first > b->first) return +1;
+    if (a->second < b->second) return -1;
+    if (a->second > b->second) return +1;
+    return 0;
+  }
+
+  friend void swap (hb_pair_t& a, hb_pair_t& b)
+  {
+    hb_swap (a.first, b.first);
+    hb_swap (a.second, b.second);
+  }
+
+
   T1 first;
   T2 second;
 };
-#define hb_pair_t(T1,T2) hb_pair_t
 template  static inline hb_pair_t
 hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); }
 
@@ -551,14 +573,14 @@ struct
 {
   template  constexpr auto
   operator () (T&& a, T2&& b) const HB_AUTO_RETURN
-  (a <= b ? std::forward (a) : std::forward (b))
+  (a <= b ? a : b)
 }
 HB_FUNCOBJ (hb_min);
 struct
 {
   template  constexpr auto
   operator () (T&& a, T2&& b) const HB_AUTO_RETURN
-  (a >= b ? std::forward (a) : std::forward (b))
+  (a >= b ? a : b)
 }
 HB_FUNCOBJ (hb_max);
 struct
@@ -569,17 +591,6 @@ struct
 }
 HB_FUNCOBJ (hb_clamp);
 
-struct
-{
-  template  void
-  operator () (T& a, T& b) const
-  {
-    using std::swap; // allow ADL
-    swap (a, b);
-  }
-}
-HB_FUNCOBJ (hb_swap);
-
 /*
  * Bithacks.
  */
@@ -589,13 +600,17 @@ template 
 static inline unsigned int
 hb_popcount (T v)
 {
-#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+#if hb_has_builtin(__builtin_popcount)
   if (sizeof (T) <= sizeof (unsigned int))
     return __builtin_popcount (v);
+#endif
 
+#if hb_has_builtin(__builtin_popcountl)
   if (sizeof (T) <= sizeof (unsigned long))
     return __builtin_popcountl (v);
+#endif
 
+#if hb_has_builtin(__builtin_popcountll)
   if (sizeof (T) <= sizeof (unsigned long long))
     return __builtin_popcountll (v);
 #endif
@@ -632,13 +647,17 @@ hb_bit_storage (T v)
 {
   if (unlikely (!v)) return 0;
 
-#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+#if hb_has_builtin(__builtin_clz)
   if (sizeof (T) <= sizeof (unsigned int))
     return sizeof (unsigned int) * 8 - __builtin_clz (v);
+#endif
 
+#if hb_has_builtin(__builtin_clzl)
   if (sizeof (T) <= sizeof (unsigned long))
     return sizeof (unsigned long) * 8 - __builtin_clzl (v);
+#endif
 
+#if hb_has_builtin(__builtin_clzll)
   if (sizeof (T) <= sizeof (unsigned long long))
     return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
 #endif
@@ -706,13 +725,17 @@ hb_ctz (T v)
 {
   if (unlikely (!v)) return 8 * sizeof (T);
 
-#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+#if hb_has_builtin(__builtin_ctz)
   if (sizeof (T) <= sizeof (unsigned int))
     return __builtin_ctz (v);
+#endif
 
+#if hb_has_builtin(__builtin_ctzl)
   if (sizeof (T) <= sizeof (unsigned long))
     return __builtin_ctzl (v);
+#endif
 
+#if hb_has_builtin(__builtin_ctzll)
   if (sizeof (T) <= sizeof (unsigned long long))
     return __builtin_ctzll (v);
 #endif
@@ -848,19 +871,14 @@ hb_in_range (T u, T lo, T hi)
   return (T)(u - lo) <= (T)(hi - lo);
 }
 template  static inline bool
-hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
+hb_in_ranges (T u, T lo1, T hi1)
 {
-  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
+  return hb_in_range (u, lo1, hi1);
 }
-template  static inline bool
-hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
-{
-  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
-}
-template  static inline bool
-hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4)
+template  static inline bool
+hb_in_ranges (T u, T lo1, T hi1, Ts... ds)
 {
-  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3) || hb_in_range (u, lo4, hi4);
+  return hb_in_range (u, lo1, hi1) || hb_in_ranges (u, ds...);
 }
 
 
@@ -868,10 +886,18 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4)
  * Overflow checking.
  */
 
-/* Consider __builtin_mul_overflow use here also */
 static inline bool
-hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
+hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *result = nullptr)
 {
+#if hb_has_builtin(__builtin_mul_overflow)
+  unsigned stack_result;
+  if (!result)
+    result = &stack_result;
+  return __builtin_mul_overflow (count, size, result);
+#endif
+
+  if (result)
+    *result = count * size;
   return (size > 0) && (count >= ((unsigned int) -1) / size);
 }
 
@@ -972,7 +998,7 @@ void hb_qsort(void *base, size_t nel, size_t width,
               [void *arg]);
 */
 
-#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
+#define SORT_R_SWAP(a,b,tmp) ((void) ((tmp) = (a)), (void) ((a) = (b)), (b) = (tmp))
 
 /* swap a and b */
 /* a and b must not be equal! */
@@ -1163,9 +1189,12 @@ hb_qsort (void *base, size_t nel, size_t width,
 }
 
 
-template  static inline void
-hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2)
+template  static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2 = nullptr)
 {
+  static_assert (hb_is_trivially_copy_assignable (T), "");
+  static_assert (hb_is_trivially_copy_assignable (T3), "");
+
   for (unsigned int i = 1; i < len; i++)
   {
     unsigned int j = i;
@@ -1188,12 +1217,6 @@ hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *)
   }
 }
 
-template  static inline void
-hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
-{
-  hb_stable_sort (array, len, compar, (int *) nullptr);
-}
-
 static inline hb_bool_t
 hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
 {
@@ -1321,47 +1344,62 @@ struct
 HB_FUNCOBJ (hb_dec);
 
 
-/* Compiler-assisted vectorization. */
-
-/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
- * basically a fixed-size bitset. */
-template 
-struct hb_vector_size_t
+/* Adapted from kurbo implementation with extra parameters added,
+ * and finding for a particular range instead of 0.
+ *
+ * For documentation and implementation see:
+ *
+ * [ITP method]: https://en.wikipedia.org/wiki/ITP_Method
+ * [An Enhancement of the Bisection Method Average Performance Preserving Minmax Optimality]: https://dl.acm.org/doi/10.1145/3423597
+ * https://docs.rs/kurbo/0.8.1/kurbo/common/fn.solve_itp.html
+ * https://github.com/linebender/kurbo/blob/fd839c25ea0c98576c7ce5789305822675a89938/src/common.rs#L162-L248
+ */
+template 
+double solve_itp (func_t f,
+                  double a, double b,
+                  double epsilon,
+                  double min_y, double max_y,
+                  double &ya, double &yb, double &y)
 {
-  elt_t& operator [] (unsigned int i) { return v[i]; }
-  const elt_t& operator [] (unsigned int i) const { return v[i]; }
-
-  void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); }
-
-  template 
-  hb_vector_size_t process (const Op& op) const
+  unsigned n1_2 = (unsigned) (hb_max (ceil (log2 ((b - a) / epsilon)) - 1.0, 0.0));
+  const unsigned n0 = 1; // Hardwired
+  const double k1 = 0.2 / (b - a); // Hardwired.
+  unsigned nmax = n0 + n1_2;
+  double scaled_epsilon = epsilon * double (1llu << nmax);
+  double _2_epsilon = 2.0 * epsilon;
+  while (b - a > _2_epsilon)
   {
-    hb_vector_size_t r;
-    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
-      r.v[i] = op (v[i]);
-    return r;
-  }
-  template 
-  hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const
-  {
-    hb_vector_size_t r;
-    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
-      r.v[i] = op (v[i], o.v[i]);
-    return r;
+    double x1_2 = 0.5 * (a + b);
+    double r = scaled_epsilon - 0.5 * (b - a);
+    double xf = (yb * a - ya * b) / (yb - ya);
+    double sigma = x1_2 - xf;
+    double b_a = b - a;
+    // This has k2 = 2 hardwired for efficiency.
+    double b_a_k2 = b_a * b_a;
+    double delta = k1 * b_a_k2;
+    int sigma_sign = sigma >= 0 ? +1 : -1;
+    double xt = delta <= fabs (x1_2 - xf) ? xf + delta * sigma_sign : x1_2;
+    double xitp = fabs (xt - x1_2) <= r ? xt : x1_2 - r * sigma_sign;
+    double yitp = f (xitp);
+    if (yitp > max_y)
+    {
+      b = xitp;
+      yb = yitp;
+    }
+    else if (yitp < min_y)
+    {
+      a = xitp;
+      ya = yitp;
+    }
+    else
+    {
+      y = yitp;
+      return xitp;
+    }
+    scaled_epsilon *= 0.5;
   }
-  hb_vector_size_t operator | (const hb_vector_size_t &o) const
-  { return process (hb_bitwise_or, o); }
-  hb_vector_size_t operator & (const hb_vector_size_t &o) const
-  { return process (hb_bitwise_and, o); }
-  hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
-  { return process (hb_bitwise_xor, o); }
-  hb_vector_size_t operator ~ () const
-  { return process (hb_bitwise_neg); }
-
-  private:
-  static_assert (0 == byte_size % sizeof (elt_t), "");
-  elt_t v[byte_size / sizeof (elt_t)];
-};
+  return 0.5 * (a + b);
+}
 
 
 #endif /* HB_ALGS_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
index 0a92392d319..08b25987061 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-array.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
@@ -100,10 +100,17 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&>
   /* Ouch. The operator== compares the contents of the array.  For range-based for loops,
    * it's best if we can just compare arrayZ, though comparing contents is still fast,
    * but also would require that Type has operator==.  As such, we optimize this operator
-   * for range-based for loop and just compare arrayZ.  No need to compare length, as we
-   * assume we're only compared to .end(). */
+   * for range-based for loop and just compare arrayZ and length.
+   *
+   * The above comment is outdated now because we implemented separate begin/end to
+   * objects that were using hb_array_t for range-based loop before. */
   bool operator != (const hb_array_t& o) const
-  { return arrayZ != o.arrayZ; }
+  { return this->arrayZ != o.arrayZ || this->length != o.length; }
+
+  /* Faster range-based for loop without bounds-check. */
+  Type *begin () const { return arrayZ; }
+  Type *end () const { return arrayZ + length; }
+
 
   /* Extra operators.
    */
@@ -113,11 +120,11 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&>
 
   HB_INTERNAL bool operator == (const hb_array_t &o) const;
 
-  uint32_t hash () const {
+  uint32_t hash () const
+  {
     uint32_t current = 0;
-    for (unsigned int i = 0; i < this->length; i++) {
-      current = current * 31 + hb_hash (this->arrayZ[i]);
-    }
+    for (auto &v : *this)
+      current = current * 31 + hb_hash (v);
     return current;
   }
 
@@ -185,23 +192,18 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&>
 
   hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*))
   {
+    //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), "");
     if (likely (length))
       hb_qsort (arrayZ, length, this->get_item_size (), cmp_);
     return hb_sorted_array_t (*this);
   }
   hb_sorted_array_t qsort ()
   {
+    //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), "");
     if (likely (length))
       hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
     return hb_sorted_array_t (*this);
   }
-  void qsort (unsigned int start, unsigned int end)
-  {
-    end = hb_min (end, length);
-    assert (start <= end);
-    if (likely (start < end))
-      hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
-  }
 
   /*
    * Other methods.
@@ -220,11 +222,8 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&>
     if (end < start + 2)
       return;
 
-    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) {
-      Type temp = arrayZ[rhs];
-      arrayZ[rhs] = arrayZ[lhs];
-      arrayZ[lhs] = temp;
-    }
+    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--)
+      hb_swap (arrayZ[rhs], arrayZ[lhs]);
   }
 
   hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
@@ -266,17 +265,31 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&>
   void fini ()
   { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
 
-  template 
+  template )))>
   hb_array_t copy (hb_serialize_context_t *c) const
   {
     TRACE_SERIALIZE (this);
     auto* out = c->start_embed (arrayZ);
-    if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ());
+    if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ());
     for (unsigned i = 0; i < length; i++)
       out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */
     return_trace (hb_array_t (out, length));
   }
 
+  template ))>
+  hb_array_t copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    auto* out = c->start_embed (arrayZ);
+    if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ());
+    hb_memcpy (out, arrayZ, get_size ());
+    return_trace (hb_array_t (out, length));
+  }
+
   template 
   bool sanitize (hb_sanitize_context_t *c) const
   { return c->check_array (arrayZ, length); }
@@ -291,6 +304,9 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&>
   unsigned int backwards_length = 0;
 };
 template  inline hb_array_t
+hb_array ()
+{ return hb_array_t (); }
+template  inline hb_array_t
 hb_array (T *array, unsigned int length)
 { return hb_array_t (array, length); }
 template  inline hb_array_t
@@ -299,8 +315,8 @@ hb_array (T (&array_)[length_])
 
 template 
 struct hb_sorted_array_t :
-        hb_iter_t, Type&>,
-        hb_array_t
+        hb_array_t,
+        hb_iter_t, Type&>
 {
   typedef hb_iter_t iter_base_t;
   HB_ITER_USING (iter_base_t);
@@ -320,17 +336,24 @@ struct hb_sorted_array_t :
   template 
   constexpr hb_sorted_array_t (const hb_array_t &o) :
-    hb_iter_t (),
-    hb_array_t (o) {}
+    hb_array_t (o),
+    hb_iter_t () {}
   template 
   hb_sorted_array_t& operator = (const hb_array_t &o)
   { hb_array_t (*this) = o; return *this; }
 
   /* Iterator implementation. */
+
+  /* See comment in hb_array_of::operator != */
   bool operator != (const hb_sorted_array_t& o) const
   { return this->arrayZ != o.arrayZ || this->length != o.length; }
 
+  /* Faster range-based for loop without bounds-check. */
+  Type *begin () const { return this->arrayZ; }
+  Type *end () const { return this->arrayZ + this->length; }
+
+
   hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
   { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); }
   hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
@@ -344,7 +367,7 @@ struct hb_sorted_array_t :
     unsigned int i;
     return bfind (x, &i) ? &this->arrayZ[i] : not_found;
   }
-  template 
+  template 
   const Type *bsearch (const T &x, const Type *not_found = nullptr) const
   {
     unsigned int i;
@@ -391,7 +414,7 @@ struct hb_sorted_array_t :
                             this->length,
                             sizeof (Type),
                             _hb_cmp_method,
-                            ds...);
+                            std::forward (ds)...);
   }
 };
 template  inline hb_sorted_array_t
@@ -423,18 +446,42 @@ inline bool hb_array_t::operator == (const hb_array_t
-inline uint32_t hb_array_t::hash () const {
+inline uint32_t hb_array_t::hash () const
+{
   uint32_t current = 0;
-  for (unsigned int i = 0; i < this->length; i++)
-    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
+  unsigned i = 0;
+
+#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
+    ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__))
+  struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+  for (; i + 4 <= this->length; i += 4)
+    current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v);
+#endif
+
+  for (; i < this->length; i++)
+    current = current * 31 + hb_hash (this->arrayZ[i]);
   return current;
 }
+
 template <>
-inline uint32_t hb_array_t::hash () const {
+inline uint32_t hb_array_t::hash () const
+{
   uint32_t current = 0;
-  for (unsigned int i = 0; i < this->length; i++)
-    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
+  unsigned i = 0;
+
+#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
+    ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__))
+  struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+  for (; i + 4 <= this->length; i += 4)
+    current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v);
+#endif
+
+  for (; i < this->length; i++)
+    current = current * 31 + hb_hash (this->arrayZ[i]);
   return current;
 }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
index 1267bc4fe20..57e94761e80 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
@@ -84,11 +84,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 #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 *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
-#define hb_atomic_int_impl_set_relaxed(AI, V)   (reinterpret_cast *> (AI)->store ((V), std::memory_order_relaxed))
-#define hb_atomic_int_impl_set(AI, V)           (reinterpret_cast *> (AI)->store ((V), std::memory_order_release))
-#define hb_atomic_int_impl_get_relaxed(AI)      (reinterpret_cast const *> (AI)->load (std::memory_order_relaxed))
-#define hb_atomic_int_impl_get(AI)              (reinterpret_cast 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))
@@ -111,6 +111,24 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 #endif
 
 
+/* This should never be disabled, even under HB_NO_MT.
+ * except that MSVC gives me an internal compiler error, so disabled there.
+ *
+ * https://github.com/harfbuzz/harfbuzz/pull/4119
+ */
+#ifndef _hb_compiler_memory_r_barrier
+#if defined(__ATOMIC_ACQUIRE) // gcc-like
+#define _hb_compiler_memory_r_barrier() asm volatile("": : :"memory")
+#elif !defined(_MSC_VER)
+#include 
+#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire)
+#else
+#define _hb_compiler_memory_r_barrier() do {} while (0)
+#endif
+#endif
+
+
+
 #ifndef _hb_memory_r_barrier
 #define _hb_memory_r_barrier()                  _hb_memory_barrier ()
 #endif
@@ -132,24 +150,47 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 #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; }
 #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; }
 #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
+{
+  hb_atomic_short_t () = default;
+  constexpr hb_atomic_short_t (short v) : v (v) {}
+
+  hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; }
+  operator short () 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); }
+
+  short v = 0;
+};
+
 struct hb_atomic_int_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 (int v_) { hb_atomic_int_impl_set (&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 () const { return hb_atomic_int_impl_get (&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); }
 
@@ -167,11 +208,11 @@ struct hb_atomic_ptr_t
   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 () const { return (T *) hb_atomic_ptr_impl_get ((void **) &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_); }
 
-  T * operator -> () const                    { return get (); }
-  template  operator C * () const { return get (); }
+  T * operator -> () const                    { return get_acquire (); }
+  template  operator C * () const { return get_acquire (); }
 
   T *v = nullptr;
 };
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh
index 8e8c988716d..9edefd97106 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh
@@ -83,9 +83,15 @@ struct hb_bimap_t
 
   unsigned int get_population () const { return forw_map.get_population (); }
 
+
   protected:
   hb_map_t  forw_map;
   hb_map_t  back_map;
+
+  public:
+  auto keys () const HB_AUTO_RETURN (+ forw_map.keys())
+  auto values () const HB_AUTO_RETURN (+ forw_map.values())
+  auto iter () const HB_AUTO_RETURN (+ forw_map.iter())
 };
 
 /* Inremental bimap: only lhs is given, rhs is incrementally assigned */
@@ -108,6 +114,9 @@ struct hb_inc_bimap_t : hb_bimap_t
   hb_codepoint_t skip ()
   { return next_value++; }
 
+  hb_codepoint_t skip (unsigned count)
+  { return next_value += count; }
+
   hb_codepoint_t get_next_value () const
   { return next_value; }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh
index 5629ddc4cea..81e2a4997bd 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh
@@ -30,32 +30,89 @@
 
 #include "hb.hh"
 
+
+/* Compiler-assisted vectorization. */
+
+/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
+ * basically a fixed-size bitset. We can't use the compiler type because hb_vector_t cannot
+ * guarantee alignment requirements. */
+template 
+struct hb_vector_size_t
+{
+  elt_t& operator [] (unsigned int i) { return v[i]; }
+  const elt_t& operator [] (unsigned int i) const { return v[i]; }
+
+  void init0 ()
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      v[i] = 0;
+  }
+  void init1 ()
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      v[i] = (elt_t) -1;
+  }
+
+  template 
+  hb_vector_size_t process (const Op& op) const
+  {
+    hb_vector_size_t r;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      r.v[i] = op (v[i]);
+    return r;
+  }
+  template 
+  hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const
+  {
+    hb_vector_size_t r;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      r.v[i] = op (v[i], o.v[i]);
+    return r;
+  }
+  hb_vector_size_t operator | (const hb_vector_size_t &o) const
+  { return process (hb_bitwise_or, o); }
+  hb_vector_size_t operator & (const hb_vector_size_t &o) const
+  { return process (hb_bitwise_and, o); }
+  hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
+  { return process (hb_bitwise_xor, o); }
+  hb_vector_size_t operator ~ () const
+  { return process (hb_bitwise_neg); }
+
+  hb_array_t iter () const
+  { return hb_array (v); }
+
+  private:
+  static_assert (0 == byte_size % sizeof (elt_t), "");
+  elt_t v[byte_size / sizeof (elt_t)];
+};
+
+
 struct hb_bit_page_t
 {
-  void init0 () { v.clear (); }
-  void init1 () { v.clear (0xFF); }
+  void init0 () { v.init0 (); }
+  void init1 () { v.init1 (); }
 
-  constexpr unsigned len () const
+  static inline constexpr unsigned len ()
   { return ARRAY_LENGTH_CONST (v); }
 
   bool is_empty () const
   {
-    for (unsigned i = 0; i < len (); i++)
-      if (v[i])
-        return false;
-    return true;
+    return
+    + hb_iter (v)
+    | hb_none
+    ;
   }
   uint32_t hash () const
   {
-    uint32_t h = 0;
-    for (unsigned i = 0; i < len (); i++)
-      h = h * 31 + hb_hash (v[i]);
-    return h;
+    return
+    + hb_iter (v)
+    | hb_reduce ([] (uint32_t h, const elt_t &_) { return h * 31 + hb_hash (_); }, (uint32_t) 0u)
+    ;
   }
 
   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 v) { if (v) add (g); else del (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); }
 
   void add_range (hb_codepoint_t a, hb_codepoint_t b)
@@ -69,7 +126,7 @@ struct hb_bit_page_t
       *la |= ~(mask (a) - 1);
       la++;
 
-      memset (la, 0xff, (char *) lb - (char *) la);
+      hb_memset (la, 0xff, (char *) lb - (char *) la);
 
       *lb |= ((mask (b) << 1) - 1);
     }
@@ -85,7 +142,7 @@ struct hb_bit_page_t
       *la &= mask (a) - 1;
       la++;
 
-      memset (la, 0, (char *) lb - (char *) la);
+      hb_memset (la, 0, (char *) lb - (char *) la);
 
       *lb &= ~((mask (b) << 1) - 1);
     }
@@ -101,13 +158,13 @@ struct hb_bit_page_t
                       hb_codepoint_t *p,
                       unsigned int    size) const
   {
-    unsigned int start_v = start_value >> ELT_BITS_LOG_2;
+    unsigned int start_v = start_value / ELT_BITS;
     unsigned int start_bit = start_value & ELT_MASK;
     unsigned int count = 0;
     for (unsigned i = start_v; i < len () && count < size; i++)
     {
       elt_t bits = v[i];
-      uint32_t v_base = base | (i << ELT_BITS_LOG_2);
+      uint32_t v_base = base | (i * ELT_BITS);
       for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++)
       {
         if ((elt_t(1) << j) & bits) {
@@ -132,13 +189,13 @@ struct hb_bit_page_t
                                unsigned int    size,
                                hb_codepoint_t *next_value) const
   {
-    unsigned int start_v = start_value >> ELT_BITS_LOG_2;
+    unsigned int start_v = start_value / ELT_BITS;
     unsigned int start_bit = start_value & ELT_MASK;
     unsigned int count = 0;
     for (unsigned i = start_v; i < len () && count < size; i++)
     {
       elt_t bits = v[i];
-      uint32_t v_offset = i << ELT_BITS_LOG_2;
+      uint32_t v_offset = i * ELT_BITS;
       for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++)
       {
         if ((elt_t(1) << j) & bits)
@@ -161,7 +218,10 @@ struct hb_bit_page_t
 
   bool is_equal (const hb_bit_page_t &other) const
   {
-    return 0 == hb_memcmp (&v, &other.v, sizeof (v));
+    for (unsigned i = 0; i < len (); i++)
+      if (v[i] != other.v[i])
+        return false;
+    return true;
   }
   bool is_subset (const hb_bit_page_t &larger_page) const
   {
@@ -173,10 +233,10 @@ struct hb_bit_page_t
 
   unsigned int get_population () const
   {
-    unsigned int pop = 0;
-    for (unsigned int i = 0; i < len (); i++)
-      pop += hb_popcount (v[i]);
-    return pop;
+    return
+    + hb_iter (v)
+    | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u)
+    ;
   }
 
   bool next (hb_codepoint_t *codepoint) const
@@ -250,10 +310,10 @@ struct hb_bit_page_t
   static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID;
 
   typedef unsigned long long elt_t;
-  static constexpr unsigned PAGE_BITS = 512;
-  static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
-  static constexpr unsigned PAGE_BITS_LOG_2 = 9;
+  static constexpr unsigned PAGE_BITS_LOG_2 = 9; // 512 bits
+  static constexpr unsigned PAGE_BITS = 1 << PAGE_BITS_LOG_2;
   static_assert (1 << PAGE_BITS_LOG_2 == PAGE_BITS, "");
+  static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
   static constexpr unsigned PAGE_BITMASK = PAGE_BITS - 1;
 
   static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); }
@@ -262,8 +322,6 @@ struct hb_bit_page_t
   typedef hb_vector_size_t vector_t;
 
   static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8;
-  static constexpr unsigned ELT_BITS_LOG_2 = 6;
-  static_assert (1 << ELT_BITS_LOG_2 == ELT_BITS, "");
   static constexpr unsigned ELT_MASK = ELT_BITS - 1;
 
   static constexpr unsigned BITS = sizeof (vector_t) * 8;
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh
index 9562ca36a32..bf5a0b446de 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh
@@ -74,6 +74,11 @@ struct hb_bit_set_invertible_t
       inverted = !inverted;
   }
 
+  bool is_inverted () const
+  {
+    return inverted;
+  }
+
   bool is_empty () const
   {
     hb_codepoint_t v = INVALID;
@@ -123,10 +128,8 @@ struct hb_bit_set_invertible_t
   bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; }
 
   /* Has interface. */
-  static constexpr bool SENTINEL = false;
-  typedef bool value_t;
-  value_t operator [] (hb_codepoint_t k) const { return get (k); }
-  bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; }
+  bool operator [] (hb_codepoint_t k) const { return get (k); }
+  bool has (hb_codepoint_t k) const { return (*this)[k]; }
   /* Predicate. */
   bool operator () (hb_codepoint_t k) const { return has (k); }
 
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 be244835a15..31ee52f6096 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh
@@ -38,7 +38,7 @@ struct hb_bit_set_t
   hb_bit_set_t () = default;
   ~hb_bit_set_t () = default;
 
-  hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other); }
+  hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); }
   hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); }
   hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; }
   hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; }
@@ -56,7 +56,7 @@ struct hb_bit_set_t
   {
     successful = true;
     population = 0;
-    last_page_lookup.set_relaxed (0);
+    last_page_lookup = 0;
     page_map.init ();
     pages.init ();
   }
@@ -85,12 +85,16 @@ struct hb_bit_set_t
   void err () { if (successful) successful = false; } /* TODO Remove */
   bool in_error () const { return !successful; }
 
-  bool resize (unsigned int count)
+  bool resize (unsigned int count, bool clear = true, bool exact_size = false)
   {
     if (unlikely (!successful)) return false;
-    if (unlikely (!pages.resize (count) || !page_map.resize (count)))
+
+    if (pages.length == 0 && count == 1)
+      exact_size = true; // Most sets are small and local
+
+    if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size)))
     {
-      pages.resize (page_map.length);
+      pages.resize (page_map.length, clear, exact_size);
       successful = false;
       return false;
     }
@@ -190,7 +194,7 @@ struct hb_bit_set_t
       unsigned int end = major_start (m + 1);
       do
       {
-        if (v || page) /* The v check is to optimize out the page check if v is true. */
+        if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */
           page->set (g, v);
 
         array = &StructAtOffsetUnaligned (array, stride);
@@ -234,7 +238,7 @@ struct hb_bit_set_t
         if (g < last_g) return false;
         last_g = g;
 
-        if (v || page) /* The v check is to optimize out the page check if v is true. */
+        if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */
           page->add (g);
 
         array = &StructAtOffsetUnaligned (array, stride);
@@ -330,10 +334,8 @@ struct hb_bit_set_t
   }
 
   /* Has interface. */
-  static constexpr bool SENTINEL = false;
-  typedef bool value_t;
-  value_t operator [] (hb_codepoint_t k) const { return get (k); }
-  bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; }
+  bool operator [] (hb_codepoint_t k) const { return get (k); }
+  bool has (hb_codepoint_t k) const { return (*this)[k]; }
   /* Predicate. */
   bool operator () (hb_codepoint_t k) const { return has (k); }
 
@@ -348,11 +350,11 @@ struct hb_bit_set_t
     hb_codepoint_t c = first - 1;
     return next (&c) && c <= last;
   }
-  void set (const hb_bit_set_t &other)
+  void set (const hb_bit_set_t &other, bool exact_size = false)
   {
     if (unlikely (!successful)) return;
     unsigned int count = other.pages.length;
-    if (unlikely (!resize (count)))
+    if (unlikely (!resize (count, false, exact_size)))
       return;
     population = other.population;
 
@@ -391,7 +393,7 @@ struct hb_bit_set_t
   bool is_subset (const hb_bit_set_t &larger_set) const
   {
     if (has_population () && larger_set.has_population () &&
-        population != larger_set.population)
+        population > larger_set.population)
       return false;
 
     uint32_t spi = 0;
@@ -424,7 +426,7 @@ struct hb_bit_set_t
   private:
   bool allocate_compact_workspace (hb_vector_t& workspace)
   {
-    if (unlikely (!workspace.resize (pages.length)))
+    if (unlikely (!workspace.resize_exact (pages.length)))
     {
       successful = false;
       return false;
@@ -465,12 +467,10 @@ struct hb_bit_set_t
   }
   public:
 
-  template 
-  void process (const Op& op, const hb_bit_set_t &other)
+  void process_ (hb_bit_page_t::vector_t (*op) (const hb_bit_page_t::vector_t &, const hb_bit_page_t::vector_t &),
+                 bool passthru_left, bool passthru_right,
+                 const hb_bit_set_t &other)
   {
-    const bool passthru_left = op (1, 0);
-    const bool passthru_right = op (0, 1);
-
     if (unlikely (!successful)) return;
 
     dirty ();
@@ -542,21 +542,21 @@ struct hb_bit_set_t
     b = nb;
     for (; a && b; )
     {
-      if (page_map[a - 1].major == other.page_map[b - 1].major)
+      if (page_map.arrayZ[a - 1].major == other.page_map.arrayZ[b - 1].major)
       {
         a--;
         b--;
         count--;
-        page_map[count] = page_map[a];
+        page_map.arrayZ[count] = page_map.arrayZ[a];
         page_at (count).v = op (page_at (a).v, other.page_at (b).v);
       }
-      else if (page_map[a - 1].major > other.page_map[b - 1].major)
+      else if (page_map.arrayZ[a - 1].major > other.page_map.arrayZ[b - 1].major)
       {
         a--;
         if (passthru_left)
         {
           count--;
-          page_map[count] = page_map[a];
+          page_map.arrayZ[count] = page_map.arrayZ[a];
         }
       }
       else
@@ -565,8 +565,8 @@ struct hb_bit_set_t
         if (passthru_right)
         {
           count--;
-          page_map[count].major = other.page_map[b].major;
-          page_map[count].index = next_page++;
+          page_map.arrayZ[count].major = other.page_map.arrayZ[b].major;
+          page_map.arrayZ[count].index = next_page++;
           page_at (count).v = other.page_at (b).v;
         }
       }
@@ -576,20 +576,29 @@ struct hb_bit_set_t
       {
         a--;
         count--;
-        page_map[count] = page_map [a];
+        page_map.arrayZ[count] = page_map.arrayZ[a];
       }
     if (passthru_right)
       while (b)
       {
         b--;
         count--;
-        page_map[count].major = other.page_map[b].major;
-        page_map[count].index = next_page++;
+        page_map.arrayZ[count].major = other.page_map.arrayZ[b].major;
+        page_map.arrayZ[count].index = next_page++;
         page_at (count).v = other.page_at (b).v;
       }
     assert (!count);
     resize (newCount);
   }
+  template 
+  static hb_bit_page_t::vector_t
+  op_ (const hb_bit_page_t::vector_t &a, const hb_bit_page_t::vector_t &b)
+  { return Op{} (a, b); }
+  template 
+  void process (const Op& op, const hb_bit_set_t &other)
+  {
+    process_ (op_, op (1, 0), op (0, 1), other);
+  }
 
   void union_ (const hb_bit_set_t &other) { process (hb_bitwise_or, other); }
   void intersect (const hb_bit_set_t &other) { process (hb_bitwise_and, other); }
@@ -598,8 +607,6 @@ struct hb_bit_set_t
 
   bool next (hb_codepoint_t *codepoint) const
   {
-    // TODO: this should be merged with prev() as both implementations
-    //       are very similar.
     if (unlikely (*codepoint == INVALID)) {
       *codepoint = get_min ();
       return *codepoint != INVALID;
@@ -607,7 +614,7 @@ struct hb_bit_set_t
 
     const auto* page_map_array = page_map.arrayZ;
     unsigned int major = get_major (*codepoint);
-    unsigned int i = last_page_lookup.get_relaxed ();
+    unsigned int i = last_page_lookup;
 
     if (unlikely (i >= page_map.length || page_map_array[i].major != major))
     {
@@ -625,7 +632,7 @@ struct hb_bit_set_t
       if (pages_array[current.index].next (codepoint))
       {
         *codepoint += current.major * page_t::PAGE_BITS;
-        last_page_lookup.set_relaxed (i);
+        last_page_lookup = i;
         return true;
       }
       i++;
@@ -633,16 +640,16 @@ struct hb_bit_set_t
 
     for (; i < page_map.length; i++)
     {
-      const page_map_t ¤t = page_map.arrayZ[i];
+      const page_map_t ¤t = page_map_array[i];
       hb_codepoint_t m = pages_array[current.index].get_min ();
       if (m != INVALID)
       {
         *codepoint = current.major * page_t::PAGE_BITS + m;
-        last_page_lookup.set_relaxed (i);
+        last_page_lookup = i;
         return true;
       }
     }
-    last_page_lookup.set_relaxed (0);
+    last_page_lookup = 0;
     *codepoint = INVALID;
     return false;
   }
@@ -656,21 +663,21 @@ struct hb_bit_set_t
     page_map_t map = {get_major (*codepoint), 0};
     unsigned int i;
     page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST);
-    if (i < page_map.length && page_map[i].major == map.major)
+    if (i < page_map.length && page_map.arrayZ[i].major == map.major)
     {
-      if (pages[page_map[i].index].previous (codepoint))
+      if (pages[page_map.arrayZ[i].index].previous (codepoint))
       {
-        *codepoint += page_map[i].major * page_t::PAGE_BITS;
+        *codepoint += page_map.arrayZ[i].major * page_t::PAGE_BITS;
         return true;
       }
     }
     i--;
     for (; (int) i >= 0; i--)
     {
-      hb_codepoint_t m = pages[page_map[i].index].get_max ();
+      hb_codepoint_t m = pages.arrayZ[page_map.arrayZ[i].index].get_max ();
       if (m != INVALID)
       {
-        *codepoint = page_map[i].major * page_t::PAGE_BITS + m;
+        *codepoint = page_map.arrayZ[i].major * page_t::PAGE_BITS + m;
         return true;
       }
     }
@@ -725,7 +732,7 @@ struct hb_bit_set_t
     {
       const auto* page_map_array = page_map.arrayZ;
       unsigned int major = get_major (codepoint);
-      unsigned int i = last_page_lookup.get_relaxed ();
+      unsigned int i = last_page_lookup;
       if (unlikely (i >= page_map.length || page_map_array[i].major != major))
       {
         page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST);
@@ -766,7 +773,7 @@ struct hb_bit_set_t
     {
       const auto* page_map_array = page_map.arrayZ;
       unsigned int major = get_major (codepoint);
-      unsigned int i = last_page_lookup.get_relaxed ();
+      unsigned int i = last_page_lookup;
       if (unlikely (i >= page_map.length || page_map_array[i].major != major))
       {
         page_map.bfind(major, &i, HB_NOT_FOUND_STORE_CLOSEST);
@@ -893,12 +900,12 @@ struct hb_bit_set_t
     /* The extra page_map length is necessary; can't just rely on vector here,
      * since the next check would be tricked because a null page also has
      * major==0, which we can't distinguish from an actualy major==0 page... */
-    unsigned i = last_page_lookup.get_relaxed ();
+    unsigned i = last_page_lookup;
     if (likely (i < page_map.length))
     {
       auto &cached_page = page_map.arrayZ[i];
       if (cached_page.major == major)
-        return &pages[cached_page.index];
+        return &pages.arrayZ[cached_page.index];
     }
 
     page_map_t map = {major, pages.length};
@@ -910,15 +917,15 @@ struct hb_bit_set_t
       if (unlikely (!resize (pages.length + 1)))
         return nullptr;
 
-      pages[map.index].init0 ();
-      memmove (page_map + i + 1,
-               page_map + i,
+      pages.arrayZ[map.index].init0 ();
+      memmove (page_map.arrayZ + i + 1,
+               page_map.arrayZ + i,
                (page_map.length - 1 - i) * page_map.item_size);
       page_map[i] = map;
     }
 
-    last_page_lookup.set_relaxed (i);
-    return &pages[page_map[i].index];
+    last_page_lookup = i;
+    return &pages.arrayZ[page_map.arrayZ[i].index];
   }
   const page_t *page_for (hb_codepoint_t g) const
   {
@@ -927,23 +934,31 @@ struct hb_bit_set_t
     /* The extra page_map length is necessary; can't just rely on vector here,
      * since the next check would be tricked because a null page also has
      * major==0, which we can't distinguish from an actualy major==0 page... */
-    unsigned i = last_page_lookup.get_relaxed ();
+    unsigned i = last_page_lookup;
     if (likely (i < page_map.length))
     {
       auto &cached_page = page_map.arrayZ[i];
       if (cached_page.major == major)
-        return &pages[cached_page.index];
+        return &pages.arrayZ[cached_page.index];
     }
 
     page_map_t key = {major};
     if (!page_map.bfind (key, &i))
       return nullptr;
 
-    last_page_lookup.set_relaxed (i);
-    return &pages[page_map[i].index];
+    last_page_lookup = i;
+    return &pages.arrayZ[page_map[i].index];
+  }
+  page_t &page_at (unsigned int i)
+  {
+    assert (i < page_map.length);
+    return pages.arrayZ[page_map.arrayZ[i].index];
+  }
+  const page_t &page_at (unsigned int i) const
+  {
+    assert (i < page_map.length);
+    return pages.arrayZ[page_map.arrayZ[i].index];
   }
-  page_t &page_at (unsigned int i) { return pages[page_map[i].index]; }
-  const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; }
   unsigned int get_major (hb_codepoint_t g) const { return g >> page_t::PAGE_BITS_LOG_2; }
   unsigned int page_remainder (hb_codepoint_t g) const { return g & page_t::PAGE_BITMASK; }
   hb_codepoint_t major_start (unsigned int major) const { return major << page_t::PAGE_BITS_LOG_2; }
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc
index 1ee4195db7f..2a43afa1a43 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc
@@ -99,7 +99,7 @@ hb_blob_create (const char        *data,
  * is zero. This is in contrast to hb_blob_create(), which returns the singleton
  * empty blob (as returned by hb_blob_get_empty()) if @length is zero.
  *
- * Return value: New blob, or %NULL if failed.  Destroy with hb_blob_destroy().
+ * Return value: New blob, or `NULL` if failed.  Destroy with hb_blob_destroy().
  *
  * Since: 2.8.2
  **/
@@ -263,8 +263,6 @@ hb_blob_destroy (hb_blob_t *blob)
 {
   if (!hb_object_destroy (blob)) return;
 
-  blob->fini_shallow ();
-
   hb_free (blob);
 }
 
@@ -278,7 +276,7 @@ hb_blob_destroy (hb_blob_t *blob)
  *
  * Attaches a user-data key/data pair to the specified blob.
  *
- * Return value: %true if success, %false otherwise
+ * Return value: `true` if success, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -305,7 +303,7 @@ hb_blob_set_user_data (hb_blob_t          *blob,
  * Since: 0.9.2
  **/
 void *
-hb_blob_get_user_data (hb_blob_t          *blob,
+hb_blob_get_user_data (const hb_blob_t    *blob,
                        hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (blob, key);
@@ -335,7 +333,7 @@ hb_blob_make_immutable (hb_blob_t *blob)
  *
  * Tests whether a blob is immutable.
  *
- * Return value: %true if @blob is immutable, %false otherwise
+ * Return value: `true` if @blob is immutable, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -394,7 +392,7 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
  * fails.
  *
  * Returns: (transfer none) (array length=length): Writable blob data,
- * or %NULL if failed.
+ * or `NULL` if failed.
  *
  * Since: 0.9.2
  **/
@@ -497,7 +495,7 @@ hb_blob_t::try_make_writable ()
 
   DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data);
 
-  memcpy (new_data, this->data, this->length);
+  hb_memcpy (new_data, this->data, this->length);
   this->destroy_user_data ();
   this->mode = HB_MEMORY_MODE_WRITABLE;
   this->data = new_data;
@@ -620,7 +618,7 @@ hb_blob_create_from_file (const char *file_name)
  * specified binary font file.
  *
  * Returns: An #hb_blob_t pointer with the content of the file,
- * or %NULL if failed.
+ * or `NULL` if failed.
  *
  * Since: 2.8.2
  **/
@@ -678,7 +676,7 @@ hb_blob_create_from_file_or_fail (const char *file_name)
   wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size);
   if (unlikely (!wchar_file_name)) goto fail_without_close;
   mbstowcs (wchar_file_name, file_name, size);
-#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
   {
     CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };
     ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
@@ -699,7 +697,7 @@ hb_blob_create_from_file_or_fail (const char *file_name)
 
   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
 
-#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
   {
     LARGE_INTEGER length;
     GetFileSizeEx (fd, &length);
@@ -712,7 +710,7 @@ hb_blob_create_from_file_or_fail (const char *file_name)
 #endif
   if (unlikely (!file->mapping)) goto fail;
 
-#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
   file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);
 #else
   file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.h b/src/java.desktop/share/native/libharfbuzz/hb-blob.h
index 860b09bd088..1761e6534ca 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-blob.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.h
@@ -63,7 +63,7 @@ HB_BEGIN_DECLS
  *   HarfBuzz and doing that just once (no reuse!),
  *
  * - If the font is mmap()ed, it's okay to use
- *   @HB_MEMORY_READONLY_MAY_MAKE_WRITABLE, however, using that mode
+ *   @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, however, using that mode
  *   correctly is very tricky.  Use @HB_MEMORY_MODE_READONLY instead.
  **/
 typedef enum {
@@ -135,7 +135,7 @@ hb_blob_set_user_data (hb_blob_t          *blob,
 
 
 HB_EXTERN void *
-hb_blob_get_user_data (hb_blob_t          *blob,
+hb_blob_get_user_data (const hb_blob_t    *blob,
                        hb_user_data_key_t *key);
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh
index a3683a681e7..b1b3b94d3dd 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh
@@ -38,7 +38,7 @@
 
 struct hb_blob_t
 {
-  void fini_shallow () { destroy_user_data (); }
+  ~hb_blob_t () { destroy_user_data (); }
 
   void destroy_user_data ()
   {
@@ -61,12 +61,12 @@ struct hb_blob_t
   public:
   hb_object_header_t header;
 
-  const char *data;
-  unsigned int length;
-  hb_memory_mode_t mode;
+  const char *data = nullptr;
+  unsigned int length = 0;
+  hb_memory_mode_t mode = (hb_memory_mode_t) 0;
 
-  void *user_data;
-  hb_destroy_func_t destroy;
+  void *user_data = nullptr;
+  hb_destroy_func_t destroy = nullptr;
 };
 
 
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 970fae7dfa6..7b9fc557f73 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,35 +32,38 @@
 #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, 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, 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
+        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, 9u, 125u,
+        9u, 123u, 0u, 0u, 0
 };
 
 static const char _deserialize_json_key_spans[] = {
         0, 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, 50, 49,
-        59, 117, 59, 117, 117, 1, 50, 49,
-        117, 85, 115, 0
+        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, 117,
+        115, 0
 };
 
 static const short _deserialize_json_index_offsets[] = {
         0, 0, 116, 143, 165, 168, 170, 221,
-        271, 282, 400, 518, 636, 638, 689, 739,
-        750, 868, 986, 988, 990, 1041, 1091, 1209,
-        1327, 1330, 1332, 1383, 1433, 1444, 1562, 1680,
-        1682, 1733, 1783, 1794, 1912, 2030, 2032, 2083,
-        2133, 2193, 2311, 2371, 2489, 2607, 2609, 2660,
-        2710, 2828, 2914, 3030
+        271, 282, 400, 518, 604, 722, 724, 775,
+        825, 836, 954, 1072, 1074, 1076, 1127, 1177,
+        1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648,
+        1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118,
+        2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560,
+        2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137,
+        3255, 3371
 };
 
 static const char _deserialize_json_indicies[] = {
@@ -82,28 +85,28 @@ static const char _deserialize_json_indicies[] = {
         3, 3, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 3, 1, 4, 1,
-        5, 1, 6, 7, 1, 1, 8, 1,
+        5, 1, 6, 7, 1, 8, 9, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 9, 1, 10, 11,
-        1, 12, 1, 12, 12, 12, 12, 12,
+        1, 1, 1, 1, 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, 12, 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, 13, 1, 13, 13,
-        13, 13, 13, 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, 13, 1, 1,
+        1, 1, 1, 1, 1, 14, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 14, 1, 1, 15, 16, 16,
-        16, 16, 16, 16, 16, 16, 16, 1,
-        17, 18, 18, 18, 18, 18, 18, 18,
-        18, 18, 1, 19, 19, 19, 19, 19,
+        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, 19, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 20, 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, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
@@ -113,11 +116,11 @@ 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, 21,
-        1, 22, 22, 22, 22, 22, 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,
-        22, 1, 1, 1, 1, 1, 1, 1,
+        23, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 3, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
@@ -128,85 +131,94 @@ 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, 23, 1, 19,
-        19, 19, 19, 19, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 24, 1, 25,
+        25, 25, 25, 25, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 19, 1,
+        1, 1, 1, 1, 1, 1, 25, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 20, 1, 1, 1, 18, 18,
-        18, 18, 18, 18, 18, 18, 18, 18,
+        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, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 27, 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, 21, 1, 24, 1, 24,
-        24, 24, 24, 24, 1, 1, 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,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        25, 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, 26, 1,
-        1, 27, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 1, 29, 30, 30, 30,
-        30, 30, 30, 30, 30, 30, 1, 31,
-        31, 31, 31, 31, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 31, 1,
+        1, 22, 1, 28, 1, 28, 28, 28,
+        28, 28, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 32, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 28, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 29, 1,
+        29, 29, 29, 29, 29, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 29,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 30, 1, 1, 31,
+        32, 32, 32, 32, 32, 32, 32, 32,
+        32, 1, 33, 34, 34, 34, 34, 34,
+        34, 34, 34, 34, 1, 35, 35, 35,
+        35, 35, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 35, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        36, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 33, 1, 31, 31, 31,
-        31, 31, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 31, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        32, 1, 1, 1, 30, 30, 30, 30,
-        30, 30, 30, 30, 30, 30, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 37, 1, 35, 35, 35, 35, 35,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 35, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 36, 1,
+        1, 1, 34, 34, 34, 34, 34, 34,
+        34, 34, 34, 34, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 33, 1, 34, 1, 35, 1, 35,
-        35, 35, 35, 35, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 35, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        36, 1, 36, 36, 36, 36, 36, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 37,
+        1, 38, 1, 39, 1, 39, 39, 39,
+        39, 39, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 36, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 39, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 37, 38, 38, 38, 38, 38, 38,
-        38, 38, 38, 1, 39, 39, 39, 39,
-        39, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 39, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 40, 1,
+        40, 40, 40, 40, 40, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 40,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 41,
+        42, 42, 42, 42, 42, 42, 42, 42,
+        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, 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,
@@ -215,43 +227,42 @@ 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,
-        41, 1, 39, 39, 39, 39, 39, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 45, 1,
+        43, 43, 43, 43, 43, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 39, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 40, 1, 1,
-        1, 42, 42, 42, 42, 42, 42, 42,
-        42, 42, 42, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 43,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 44, 1, 1, 1, 46,
+        46, 46, 46, 46, 46, 46, 46, 46,
+        46, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 41, 1,
-        43, 44, 1, 45, 1, 45, 45, 45,
-        45, 45, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 45, 1, 1, 1,
+        1, 1, 1, 1, 45, 1, 47, 48,
+        1, 49, 1, 49, 49, 49, 49, 49,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 46, 1,
-        46, 46, 46, 46, 46, 1, 1, 1,
+        1, 1, 49, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 46,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 47, 1, 1, 48,
-        49, 49, 49, 49, 49, 49, 49, 49,
-        49, 1, 50, 51, 51, 51, 51, 51,
-        51, 51, 51, 51, 1, 52, 52, 52,
-        52, 52, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 50, 1, 50, 50,
+        50, 50, 50, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 52, 1, 1, 1,
+        1, 1, 1, 1, 1, 50, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        53, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 51, 1, 1, 52, 53, 53,
+        53, 53, 53, 53, 53, 53, 53, 1,
+        54, 55, 55, 55, 55, 55, 55, 55,
+        55, 55, 1, 56, 56, 56, 56, 56,
         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, 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,
@@ -259,42 +270,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, 54, 1, 52, 52, 52, 52, 52,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 52, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 53, 1,
-        1, 1, 51, 51, 51, 51, 51, 51,
-        51, 51, 51, 51, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 58,
+        1, 56, 56, 56, 56, 56, 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, 57, 1, 1, 1,
+        55, 55, 55, 55, 55, 55, 55, 55,
+        55, 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, 54,
-        1, 55, 1, 55, 55, 55, 55, 55,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 55, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 58, 1, 59,
+        1, 59, 59, 59, 59, 59, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 56, 1, 56, 56,
-        56, 56, 56, 1, 1, 1, 1, 1,
+        59, 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, 57, 1, 1, 58, 59, 59,
-        59, 59, 59, 59, 59, 59, 59, 1,
-        60, 61, 61, 61, 61, 61, 61, 61,
-        61, 61, 1, 62, 62, 62, 62, 62,
+        1, 1, 60, 1, 60, 60, 60, 60,
+        60, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 60, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 62, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 63, 1,
+        61, 1, 1, 62, 63, 63, 63, 63,
+        63, 63, 63, 63, 63, 1, 64, 65,
+        65, 65, 65, 65, 65, 65, 65, 65,
+        1, 66, 66, 66, 66, 66, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        66, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 67, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
@@ -302,48 +314,42 @@ 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, 64,
-        1, 62, 62, 62, 62, 62, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        62, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 63, 1, 1, 1,
-        61, 61, 61, 61, 61, 61, 61, 61,
-        61, 61, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 68, 1, 66,
+        66, 66, 66, 66, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 66, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 67, 1, 1, 1, 65, 65,
+        65, 65, 65, 65, 65, 65, 65, 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, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 64, 1, 65,
-        1, 65, 65, 65, 65, 65, 1, 1,
         1, 1, 1, 1, 1, 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, 68, 1, 69, 1, 70,
+        1, 70, 70, 70, 70, 70, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        70, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 66, 1, 66, 66, 66, 66,
-        66, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 66, 1, 67, 1, 1,
+        1, 1, 71, 1, 71, 71, 71, 71,
+        71, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 68, 69, 69, 69, 69,
-        69, 69, 69, 69, 69, 1, 71, 70,
-        70, 70, 70, 70, 70, 70, 70, 70,
-        70, 70, 70, 70, 70, 70, 70, 70,
-        70, 70, 70, 70, 70, 70, 70, 70,
-        70, 70, 70, 70, 70, 70, 70, 70,
-        70, 70, 70, 70, 70, 70, 70, 70,
-        70, 70, 70, 70, 70, 70, 70, 70,
-        70, 70, 70, 70, 70, 70, 70, 70,
-        72, 70, 73, 73, 73, 73, 73, 1,
+        1, 1, 1, 71, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 72, 73, 73, 73, 73,
+        73, 73, 73, 73, 73, 1, 74, 74,
+        74, 74, 74, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 73, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 74, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 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,
@@ -352,86 +358,126 @@ 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, 75, 1,
-        70, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 76, 1, 74, 74, 74, 74,
+        74, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 74, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 75,
+        1, 1, 1, 77, 77, 77, 77, 77,
+        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, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        76, 1, 78, 1, 78, 78, 78, 78,
+        78, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 78, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 79, 1, 79,
+        79, 79, 79, 79, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 79, 1,
+        80, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 81, 82,
+        82, 82, 82, 82, 82, 82, 82, 82,
+        1, 84, 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,
+        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, 85, 83, 86, 86, 86,
+        86, 86, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 86, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        87, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 70, 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, 77,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 88, 1, 83, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 83, 1, 89,
+        89, 89, 89, 89, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 89, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 90, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        78, 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, 77, 1, 1,
-        1, 79, 79, 79, 79, 79, 79, 79,
-        79, 79, 79, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 91, 1, 89, 89, 89,
+        89, 89, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 78, 1,
-        80, 1, 80, 80, 80, 80, 80, 1,
+        1, 1, 1, 1, 89, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        90, 1, 1, 1, 92, 92, 92, 92,
+        92, 92, 92, 92, 92, 92, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 80, 1, 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, 81, 81, 81,
-        81, 81, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 81, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 82, 83, 83, 83,
-        83, 83, 83, 83, 83, 83, 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, 77, 1, 1, 1, 84, 84,
-        84, 84, 84, 84, 84, 84, 84, 84,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 91, 1, 93, 1, 93, 93, 93,
+        93, 93, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 93, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 94, 1,
+        94, 94, 94, 94, 94, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 94,
         1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 95,
+        96, 96, 96, 96, 96, 96, 96, 96,
+        96, 1, 89, 89, 89, 89, 89, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 78, 1, 85, 85, 85,
-        85, 85, 1, 1, 1, 1, 1, 1,
+        1, 89, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 90, 1, 1,
+        1, 97, 97, 97, 97, 97, 97, 97,
+        97, 97, 97, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 85, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        86, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1,
-        1, 87, 1, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 91, 1,
+        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, 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, 1, 1,
@@ -442,46 +488,49 @@ 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, 2, 1, 1,
-        0
+        1, 1, 2, 1, 1, 0
 };
 
 static const char _deserialize_json_trans_targs[] = {
-        1, 0, 2, 2, 3, 4, 18, 24,
-        37, 45, 5, 12, 6, 7, 8, 9,
-        11, 9, 11, 10, 2, 49, 10, 49,
-        13, 14, 15, 16, 17, 16, 17, 10,
-        2, 49, 19, 20, 21, 22, 23, 10,
-        2, 49, 23, 25, 31, 26, 27, 28,
-        29, 30, 29, 30, 10, 2, 49, 32,
-        33, 34, 35, 36, 35, 36, 10, 2,
-        49, 38, 39, 40, 43, 44, 40, 41,
-        42, 10, 2, 49, 10, 2, 49, 44,
-        46, 47, 43, 48, 48, 49, 50, 51
+        1, 0, 2, 2, 3, 4, 19, 25,
+        38, 44, 52, 5, 13, 6, 7, 8,
+        9, 12, 9, 12, 10, 2, 11, 10,
+        11, 11, 56, 57, 14, 15, 16, 17,
+        18, 17, 18, 10, 2, 11, 20, 21,
+        22, 23, 24, 10, 2, 11, 24, 26,
+        32, 27, 28, 29, 30, 31, 30, 31,
+        10, 2, 11, 33, 34, 35, 36, 37,
+        36, 37, 10, 2, 11, 39, 40, 41,
+        42, 43, 10, 2, 11, 43, 45, 46,
+        47, 50, 51, 47, 48, 49, 10, 2,
+        11, 10, 2, 11, 51, 53, 54, 50,
+        55, 55
 };
 
 static const char _deserialize_json_trans_actions[] = {
         0, 0, 1, 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, 2, 14, 14, 0, 15,
-        0, 16, 16, 17, 18, 18, 19, 15,
-        0, 0, 20, 20, 21, 0, 0, 0
+        0, 0, 0, 0, 0, 0, 0, 2,
+        2, 2, 0, 0, 3, 3, 4, 0,
+        5, 0, 0, 0, 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
 };
 
 static const int deserialize_json_start = 1;
-static const int deserialize_json_first_final = 49;
+static const int deserialize_json_first_final = 56;
 static const int deserialize_json_error = 0;
 
 static const int deserialize_json_en_main = 1;
 
 
-#line 108 "hb-buffer-deserialize-json.rl"
+#line 111 "hb-buffer-deserialize-json.rl"
 
 
 static hb_bool_t
@@ -499,21 +548,19 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
   while (p < pe && ISSPACE (*p))
     p++;
   if (p < pe && *p == (buffer->len ? ',' : '['))
-  {
     *end_ptr = ++p;
-  }
 
   const char *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
 
-#line 512 "hb-buffer-deserialize-json.hh"
+#line 552 "hb-buffer-deserialize-json.hh"
         {
         cs = deserialize_json_start;
         }
 
-#line 517 "hb-buffer-deserialize-json.hh"
+#line 555 "hb-buffer-deserialize-json.hh"
         {
         int _slen;
         int _trans;
@@ -541,8 +588,8 @@ _resume:
         case 1:
 #line 38 "hb-buffer-deserialize-json.rl"
         {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
+        hb_memset (&info, 0, sizeof (info));
+        hb_memset (&pos , 0, sizeof (pos ));
 }
         break;
         case 5:
@@ -561,25 +608,25 @@ _resume:
         tok = p;
 }
         break;
-        case 15:
+        case 17:
 #line 55 "hb-buffer-deserialize-json.rl"
         { if (unlikely (!buffer->ensure_glyphs ())) return false; }
         break;
-        case 21:
+        case 23:
 #line 56 "hb-buffer-deserialize-json.rl"
         { if (unlikely (!buffer->ensure_unicode ())) return false; }
         break;
-        case 16:
+        case 18:
 #line 58 "hb-buffer-deserialize-json.rl"
         {
         /* TODO Unescape \" and \\ if found. */
         if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
+                                        tok+1, p - tok - 2, /* Skip "" */
                                         &info.codepoint))
           return false;
 }
         break;
-        case 18:
+        case 20:
 #line 66 "hb-buffer-deserialize-json.rl"
         { if (!parse_uint (tok, p, &info.codepoint)) return false; }
         break;
@@ -604,6 +651,10 @@ _resume:
         { 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;
@@ -611,7 +662,7 @@ _resume:
 #line 55 "hb-buffer-deserialize-json.rl"
         { if (unlikely (!buffer->ensure_glyphs ())) return false; }
         break;
-        case 20:
+        case 22:
 #line 51 "hb-buffer-deserialize-json.rl"
         {
         tok = p;
@@ -619,12 +670,12 @@ _resume:
 #line 56 "hb-buffer-deserialize-json.rl"
         { if (unlikely (!buffer->ensure_unicode ())) return false; }
         break;
-        case 17:
+        case 19:
 #line 58 "hb-buffer-deserialize-json.rl"
         {
         /* TODO Unescape \" and \\ if found. */
         if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
+                                        tok+1, p - tok - 2, /* Skip "" */
                                         &info.codepoint))
           return false;
 }
@@ -637,7 +688,7 @@ _resume:
         *end_ptr = p;
 }
         break;
-        case 19:
+        case 21:
 #line 66 "hb-buffer-deserialize-json.rl"
         { if (!parse_uint (tok, p, &info.codepoint)) return false; }
 #line 43 "hb-buffer-deserialize-json.rl"
@@ -709,7 +760,19 @@ _resume:
         *end_ptr = p;
 }
         break;
-#line 713 "hb-buffer-deserialize-json.hh"
+        case 15:
+#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 733 "hb-buffer-deserialize-json.hh"
         }
 
 _again:
@@ -721,7 +784,7 @@ _again:
         _out: {}
         }
 
-#line 136 "hb-buffer-deserialize-json.rl"
+#line 137 "hb-buffer-deserialize-json.rl"
 
 
   *end_ptr = p;
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
new file mode 100644
index 00000000000..cf9c281e86e
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh
@@ -0,0 +1,692 @@
+
+#line 1 "hb-buffer-deserialize-text-glyphs.rl"
+/*
+ * Copyright © 2013  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.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH
+
+#include "hb.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
+};
+
+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
+};
+
+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
+};
+
+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,
+        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
+};
+
+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
+};
+
+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
+};
+
+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
+};
+
+static const int deserialize_text_glyphs_start = 14;
+static const int deserialize_text_glyphs_first_final = 14;
+static const int deserialize_text_glyphs_error = 0;
+
+static const int deserialize_text_glyphs_en_main = 14;
+
+
+#line 98 "hb-buffer-deserialize-text-glyphs.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer,
+                                    const char *buf,
+                                    unsigned int buf_len,
+                                    const char **end_ptr,
+                                    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = 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 346 "hb-buffer-deserialize-text-glyphs.hh"
+        {
+        cs = deserialize_text_glyphs_start;
+        }
+
+#line 349 "hb-buffer-deserialize-text-glyphs.hh"
+        {
+        int _slen;
+        int _trans;
+        const unsigned char *_keys;
+        const char *_inds;
+        if ( p == pe )
+                goto _test_eof;
+        if ( cs == 0 )
+                goto _out;
+_resume:
+        _keys = _deserialize_text_glyphs_trans_keys + (cs<<1);
+        _inds = _deserialize_text_glyphs_indicies + _deserialize_text_glyphs_index_offsets[cs];
+
+        _slen = _deserialize_text_glyphs_key_spans[cs];
+        _trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+                (*p) <= _keys[1] ?
+                (*p) - _keys[0] : _slen ];
+
+        cs = _deserialize_text_glyphs_trans_targs[_trans];
+
+        if ( _deserialize_text_glyphs_trans_actions[_trans] == 0 )
+                goto _again;
+
+        switch ( _deserialize_text_glyphs_trans_actions[_trans] ) {
+        case 1:
+#line 51 "hb-buffer-deserialize-text-glyphs.rl"
+        {
+        tok = p;
+}
+        break;
+        case 7:
+#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;
+}
+        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"
+        {
+        /* 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);
+        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"
+        {
+        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;
+}
+        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 516 "hb-buffer-deserialize-text-glyphs.hh"
+        }
+
+_again:
+        if ( cs == 0 )
+                goto _out;
+        if ( ++p != pe )
+                goto _resume;
+        _test_eof: {}
+        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);
+        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 616 "hb-buffer-deserialize-text-glyphs.hh"
+        }
+        }
+
+        _out: {}
+        }
+
+#line 136 "hb-buffer-deserialize-text-glyphs.rl"
+
+
+  if (pe < orig_pe && *pe == ']')
+  {
+    pe++;
+    if (p == pe)
+      p++;
+  }
+
+  *end_ptr = p;
+
+  return p == pe;
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH */
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
new file mode 100644
index 00000000000..f0c94654533
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh
@@ -0,0 +1,332 @@
+
+#line 1 "hb-buffer-deserialize-text-unicode.rl"
+/*
+ * Copyright © 2013  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.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH
+
+#include "hb.hh"
+
+
+#line 33 "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
+};
+
+static const char _deserialize_text_unicode_key_spans[] = {
+        0, 109, 60, 55, 10, 116, 116, 116,
+        116
+};
+
+static const short _deserialize_text_unicode_index_offsets[] = {
+        0, 0, 110, 171, 227, 238, 355, 472,
+        589
+};
+
+static const char _deserialize_text_unicode_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,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 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, 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, 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, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 10, 1, 11, 11, 11, 11,
+        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,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 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
+};
+
+static const char _deserialize_text_unicode_trans_targs[] = {
+        1, 0, 2, 3, 5, 7, 8, 6,
+        5, 4, 1, 6, 6, 1, 8
+};
+
+static const char _deserialize_text_unicode_trans_actions[] = {
+        0, 0, 1, 0, 2, 2, 2, 3,
+        0, 4, 3, 0, 5, 5, 0
+};
+
+static const char _deserialize_text_unicode_eof_actions[] = {
+        0, 0, 0, 0, 0, 3, 0, 5,
+        5
+};
+
+static const int deserialize_text_unicode_start = 1;
+static const int deserialize_text_unicode_first_final = 5;
+static const int deserialize_text_unicode_error = 0;
+
+static const int deserialize_text_unicode_en_main = 1;
+
+
+#line 79 "hb-buffer-deserialize-text-unicode.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer,
+                                     const char *buf,
+                                     unsigned int buf_len,
+                                     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 *tok = nullptr;
+  int cs;
+  hb_glyph_info_t info = {0};
+  const hb_glyph_position_t pos = {0};
+
+#line 194 "hb-buffer-deserialize-text-unicode.hh"
+        {
+        cs = deserialize_text_unicode_start;
+        }
+
+#line 197 "hb-buffer-deserialize-text-unicode.hh"
+        {
+        int _slen;
+        int _trans;
+        const unsigned char *_keys;
+        const char *_inds;
+        if ( p == pe )
+                goto _test_eof;
+        if ( cs == 0 )
+                goto _out;
+_resume:
+        _keys = _deserialize_text_unicode_trans_keys + (cs<<1);
+        _inds = _deserialize_text_unicode_indicies + _deserialize_text_unicode_index_offsets[cs];
+
+        _slen = _deserialize_text_unicode_key_spans[cs];
+        _trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+                (*p) <= _keys[1] ?
+                (*p) - _keys[0] : _slen ];
+
+        cs = _deserialize_text_unicode_trans_targs[_trans];
+
+        if ( _deserialize_text_unicode_trans_actions[_trans] == 0 )
+                goto _again;
+
+        switch ( _deserialize_text_unicode_trans_actions[_trans] ) {
+        case 1:
+#line 38 "hb-buffer-deserialize-text-unicode.rl"
+        {
+        hb_memset (&info, 0, sizeof (info));
+}
+        break;
+        case 2:
+#line 51 "hb-buffer-deserialize-text-unicode.rl"
+        {
+        tok = p;
+}
+        break;
+        case 4:
+#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; }
+#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;
+#line 256 "hb-buffer-deserialize-text-unicode.hh"
+        }
+
+_again:
+        if ( cs == 0 )
+                goto _out;
+        if ( ++p != pe )
+                goto _resume;
+        _test_eof: {}
+        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);
+        if (unlikely (!buffer->successful))
+          return false;
+        if (buffer->have_positions)
+          buffer->pos[buffer->len - 1] = pos;
+        *end_ptr = p;
+}
+        break;
+#line 289 "hb-buffer-deserialize-text-unicode.hh"
+        }
+        }
+
+        _out: {}
+        }
+
+#line 115 "hb-buffer-deserialize-text-unicode.rl"
+
+
+  if (pe < orig_pe && *pe == '>')
+  {
+    pe++;
+    if (p == pe)
+      p++;
+  }
+
+  *end_ptr = p;
+
+  return p == pe;
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text.hh
deleted file mode 100644
index d34d5a41a4a..00000000000
--- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text.hh
+++ /dev/null
@@ -1,853 +0,0 @@
-
-#line 1 "hb-buffer-deserialize-text.rl"
-/*
- * Copyright © 2013  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.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
-#define HB_BUFFER_DESERIALIZE_TEXT_HH
-
-#include "hb.hh"
-
-
-#line 36 "hb-buffer-deserialize-text.hh"
-static const unsigned char _deserialize_text_trans_keys[] = {
-        0u, 0u, 9u, 91u, 85u, 85u, 43u, 43u, 48u, 102u, 9u, 85u, 48u, 57u, 45u, 57u,
-        48u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u,
-        43u, 124u, 45u, 57u, 48u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 9u, 85u, 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, 9u, 124u, 0
-};
-
-static const char _deserialize_text_key_spans[] = {
-        0, 83, 1, 1, 55, 77, 10, 13,
-        10, 10, 13, 10, 1, 13, 10, 14,
-        82, 13, 10, 116, 116, 0, 77, 116,
-        116, 116, 116, 116, 116, 116, 116, 116,
-        116, 116, 116, 116, 116
-};
-
-static const short _deserialize_text_index_offsets[] = {
-        0, 0, 84, 86, 88, 144, 222, 233,
-        247, 258, 269, 283, 294, 296, 310, 321,
-        336, 419, 433, 444, 561, 678, 679, 757,
-        874, 991, 1108, 1225, 1342, 1459, 1576, 1693,
-        1810, 1927, 2044, 2161, 2278
-};
-
-static const char _deserialize_text_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,
-        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, 1, 1, 1, 1,
-        1, 1, 1, 3, 1, 4, 1, 5,
-        1, 6, 6, 6, 6, 6, 6, 6,
-        6, 6, 6, 1, 1, 1, 1, 1,
-        1, 1, 6, 6, 6, 6, 6, 6,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 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,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 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, 1, 8,
-        9, 9, 9, 9, 9, 9, 9, 9,
-        9, 1, 10, 1, 1, 11, 12, 12,
-        12, 12, 12, 12, 12, 12, 12, 1,
-        13, 14, 14, 14, 14, 14, 14, 14,
-        14, 14, 1, 15, 16, 16, 16, 16,
-        16, 16, 16, 16, 16, 1, 17, 1,
-        1, 18, 19, 19, 19, 19, 19, 19,
-        19, 19, 19, 1, 20, 21, 21, 21,
-        21, 21, 21, 21, 21, 21, 1, 22,
-        1, 23, 1, 1, 24, 25, 25, 25,
-        25, 25, 25, 25, 25, 25, 1, 26,
-        27, 27, 27, 27, 27, 27, 27, 27,
-        27, 1, 22, 1, 1, 1, 21, 21,
-        21, 21, 21, 21, 21, 21, 21, 21,
-        1, 28, 28, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 28, 1, 1, 28, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 28, 28, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 28, 1, 29, 1, 1, 30,
-        31, 31, 31, 31, 31, 31, 31, 31,
-        31, 1, 32, 33, 33, 33, 33, 33,
-        33, 33, 33, 33, 1, 34, 34, 34,
-        34, 34, 1, 1, 1, 1, 1, 1,
-        1, 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, 35, 35, 35, 35,
-        35, 35, 35, 35, 35, 35, 1, 1,
-        1, 36, 37, 1, 1, 35, 35, 35,
-        35, 35, 35, 1, 1, 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, 35, 35,
-        35, 35, 35, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        38, 1, 39, 39, 39, 39, 39, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 39, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 40,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 41, 1, 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, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 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, 1, 42, 42,
-        42, 42, 42, 1, 1, 1, 1, 1,
-        1, 1, 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,
-        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, 44, 1, 42, 42, 42, 42, 42,
-        1, 1, 1, 1, 1, 1, 1, 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, 45, 45, 45, 45, 45, 45,
-        45, 45, 45, 45, 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, 44, 1,
-        47, 47, 47, 47, 47, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 47,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 48, 1, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 49, 46, 46, 50,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 51, 52, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 53, 46, 54, 54, 54,
-        54, 54, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 54, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 55,
-        1, 28, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        28, 56, 28, 28, 57, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        58, 59, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        28, 28, 28, 28, 28, 28, 28, 28,
-        60, 28, 61, 61, 61, 61, 61, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 61, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 62, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 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, 64, 1, 65,
-        65, 65, 65, 65, 1, 1, 1, 1,
-        1, 1, 1, 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, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 40, 1, 1, 1, 1,
-        1, 1, 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, 67, 67, 67,
-        67, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 67, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 48, 1,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        49, 46, 46, 50, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 51,
-        52, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 46,
-        46, 46, 46, 46, 46, 46, 46, 53,
-        46, 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, 69, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        70, 1, 1, 1, 1, 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, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 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,
-        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, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 74, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 75, 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, 73, 1, 1,
-        1, 1, 27, 27, 27, 27, 27, 27,
-        27, 27, 27, 27, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 75, 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, 69, 1, 1, 1, 1, 76,
-        76, 76, 76, 76, 76, 76, 76, 76,
-        76, 1, 1, 1, 1, 1, 1, 70,
-        1, 1, 1, 1, 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, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 71, 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, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 78, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        79, 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, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 33, 33, 33, 33, 33, 33, 33,
-        33, 33, 33, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 78, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 79, 1, 61,
-        61, 61, 61, 61, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 1, 1, 1, 1, 61, 1,
-        1, 1, 1, 1, 1, 1, 1, 1,
-        1, 1, 62, 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, 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, 64, 1, 0
-};
-
-static const char _deserialize_text_trans_targs[] = {
-        1, 0, 2, 25, 3, 4, 19, 5,
-        23, 24, 8, 27, 36, 27, 36, 30,
-        33, 11, 12, 15, 12, 15, 13, 14,
-        31, 32, 31, 32, 26, 18, 34, 35,
-        34, 35, 20, 19, 6, 21, 22, 20,
-        21, 22, 20, 21, 22, 24, 26, 26,
-        7, 9, 10, 16, 21, 29, 26, 7,
-        9, 10, 16, 21, 29, 28, 17, 21,
-        29, 28, 29, 29, 28, 7, 10, 29,
-        28, 7, 21, 29, 33, 28, 21, 29
-};
-
-static const char _deserialize_text_trans_actions[] = {
-        0, 0, 0, 0, 1, 0, 2, 0,
-        2, 2, 3, 4, 4, 5, 5, 4,
-        4, 3, 3, 3, 0, 0, 6, 3,
-        4, 4, 5, 5, 5, 3, 4, 4,
-        5, 5, 7, 8, 9, 7, 7, 0,
-        0, 0, 10, 10, 10, 8, 12, 13,
-        14, 14, 14, 15, 11, 11, 17, 18,
-        18, 18, 0, 16, 16, 19, 20, 19,
-        19, 0, 0, 13, 10, 21, 21, 10,
-        22, 23, 22, 22, 5, 24, 24, 24
-};
-
-static const char _deserialize_text_eof_actions[] = {
-        0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 7, 0, 0, 0, 10,
-        10, 11, 16, 19, 0, 11, 10, 22,
-        22, 10, 24, 24, 19
-};
-
-static const int deserialize_text_start = 1;
-static const int deserialize_text_first_final = 19;
-static const int deserialize_text_error = 0;
-
-static const int deserialize_text_en_main = 1;
-
-
-#line 114 "hb-buffer-deserialize-text.rl"
-
-
-static hb_bool_t
-_hb_buffer_deserialize_text (hb_buffer_t *buffer,
-                                    const char *buf,
-                                    unsigned int buf_len,
-                                    const char **end_ptr,
-                                    hb_font_t *font)
-{
-  const char *p = buf, *pe = buf + buf_len;
-
-  /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
-
-  while (p < pe && ISSPACE (*p))
-    p++;
-
-  const char *eof = pe, *tok = nullptr;
-  int cs;
-  hb_glyph_info_t info = {0};
-  hb_glyph_position_t pos = {0};
-
-#line 428 "hb-buffer-deserialize-text.hh"
-        {
-        cs = deserialize_text_start;
-        }
-
-#line 433 "hb-buffer-deserialize-text.hh"
-        {
-        int _slen;
-        int _trans;
-        const unsigned char *_keys;
-        const char *_inds;
-        if ( p == pe )
-                goto _test_eof;
-        if ( cs == 0 )
-                goto _out;
-_resume:
-        _keys = _deserialize_text_trans_keys + (cs<<1);
-        _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs];
-
-        _slen = _deserialize_text_key_spans[cs];
-        _trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
-                (*p) <= _keys[1] ?
-                (*p) - _keys[0] : _slen ];
-
-        cs = _deserialize_text_trans_targs[_trans];
-
-        if ( _deserialize_text_trans_actions[_trans] == 0 )
-                goto _again;
-
-        switch ( _deserialize_text_trans_actions[_trans] ) {
-        case 1:
-#line 38 "hb-buffer-deserialize-text.rl"
-        {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
-}
-        break;
-        case 3:
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-        break;
-        case 5:
-#line 55 "hb-buffer-deserialize-text.rl"
-        { if (unlikely (!buffer->ensure_glyphs ())) return false; }
-        break;
-        case 8:
-#line 56 "hb-buffer-deserialize-text.rl"
-        { if (unlikely (!buffer->ensure_unicode ())) return false; }
-        break;
-        case 18:
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-        break;
-        case 9:
-#line 66 "hb-buffer-deserialize-text.rl"
-        {if (!parse_hex (tok, p, &info.codepoint )) return false; }
-        break;
-        case 21:
-#line 68 "hb-buffer-deserialize-text.rl"
-        { if (!parse_uint (tok, p, &info.cluster )) return false; }
-        break;
-        case 6:
-#line 69 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.x_offset )) return false; }
-        break;
-        case 23:
-#line 70 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-        break;
-        case 20:
-#line 71 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-        break;
-        case 15:
-#line 38 "hb-buffer-deserialize-text.rl"
-        {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-        break;
-        case 4:
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-        { if (unlikely (!buffer->ensure_glyphs ())) return false; }
-        break;
-        case 2:
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-#line 56 "hb-buffer-deserialize-text.rl"
-        { if (unlikely (!buffer->ensure_unicode ())) return false; }
-        break;
-        case 16:
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 7:
-#line 66 "hb-buffer-deserialize-text.rl"
-        {if (!parse_hex (tok, p, &info.codepoint )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 10:
-#line 68 "hb-buffer-deserialize-text.rl"
-        { if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 22:
-#line 70 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 19:
-#line 71 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 24:
-#line 72 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 12:
-#line 38 "hb-buffer-deserialize-text.rl"
-        {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-        { if (unlikely (!buffer->ensure_glyphs ())) return false; }
-        break;
-        case 14:
-#line 38 "hb-buffer-deserialize-text.rl"
-        {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-        break;
-        case 17:
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-        { if (unlikely (!buffer->ensure_glyphs ())) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 11:
-#line 38 "hb-buffer-deserialize-text.rl"
-        {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 13:
-#line 38 "hb-buffer-deserialize-text.rl"
-        {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-        { if (unlikely (!buffer->ensure_glyphs ())) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-#line 722 "hb-buffer-deserialize-text.hh"
-        }
-
-_again:
-        if ( cs == 0 )
-                goto _out;
-        if ( ++p != pe )
-                goto _resume;
-        _test_eof: {}
-        if ( p == eof )
-        {
-        switch ( _deserialize_text_eof_actions[cs] ) {
-        case 16:
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 7:
-#line 66 "hb-buffer-deserialize-text.rl"
-        {if (!parse_hex (tok, p, &info.codepoint )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 10:
-#line 68 "hb-buffer-deserialize-text.rl"
-        { if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 22:
-#line 70 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 19:
-#line 71 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 24:
-#line 72 "hb-buffer-deserialize-text.rl"
-        { if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-        case 11:
-#line 38 "hb-buffer-deserialize-text.rl"
-        {
-        memset (&info, 0, sizeof (info));
-        memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-        {
-        tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-        {
-        /* TODO Unescape delimiters. */
-        if (!hb_font_glyph_from_string (font,
-                                        tok, p - tok,
-                                        &info.codepoint))
-          return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-        {
-        buffer->add_info (info);
-        if (unlikely (!buffer->successful))
-          return false;
-        buffer->pos[buffer->len - 1] = pos;
-        *end_ptr = p;
-}
-        break;
-#line 839 "hb-buffer-deserialize-text.hh"
-        }
-        }
-
-        _out: {}
-        }
-
-#line 138 "hb-buffer-deserialize-text.rl"
-
-
-  *end_ptr = p;
-
-  return p == pe && *(p-1) != ']';
-}
-
-#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */
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 4797d6a79fb..bb2cddcb655 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc
@@ -56,7 +56,7 @@ hb_buffer_serialize_list_formats ()
 /**
  * hb_buffer_serialize_format_from_string:
  * @str: (array length=len) (element-type uint8_t): a string to parse
- * @len: length of @str, or -1 if string is %NULL terminated
+ * @len: length of @str, or -1 if string is `NULL` terminated
  *
  * Parses a string into an #hb_buffer_serialize_format_t. Does not check if
  * @str is a valid buffer serialization format, use
@@ -78,11 +78,11 @@ hb_buffer_serialize_format_from_string (const char *str, int len)
  * hb_buffer_serialize_format_to_string:
  * @format: an #hb_buffer_serialize_format_t to convert.
  *
- * Converts @format to the string corresponding it, or %NULL if it is not a valid
+ * Converts @format to the string corresponding it, or `NULL` if it is not a valid
  * #hb_buffer_serialize_format_t.
  *
  * Return value: (transfer none):
- * A %NULL terminated string corresponding to @format. Should not be freed.
+ * A `NULL` terminated string corresponding to @format. Should not be freed.
  *
  * Since: 0.9.7
  **/
@@ -183,7 +183,7 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
     unsigned int l = p - b;
     if (buf_size > l)
     {
-      memcpy (buf, b, l);
+      hb_memcpy (buf, b, l);
       buf += l;
       buf_size -= l;
       *buf_consumed += l;
@@ -241,7 +241,7 @@ _hb_buffer_serialize_unicode_json (hb_buffer_t *buffer,
     unsigned int l = p - b;
     if (buf_size > l)
     {
-      memcpy (buf, b, l);
+      hb_memcpy (buf, b, l);
       buf += l;
       buf_size -= l;
       *buf_consumed += l;
@@ -329,7 +329,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
     unsigned int l = p - b;
     if (buf_size > l)
     {
-      memcpy (buf, b, l);
+      hb_memcpy (buf, b, l);
       buf += l;
       buf_size -= l;
       *buf_consumed += l;
@@ -381,7 +381,7 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
     unsigned int l = p - b;
     if (buf_size > l)
     {
-      memcpy (buf, b, l);
+      hb_memcpy (buf, b, l);
       buf += l;
       buf_size -= l;
       *buf_consumed += l;
@@ -400,9 +400,9 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
  * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
  *       write serialized buffer into.
  * @buf_size: the size of @buf.
- * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes written into @buf.
+ * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf.
  * @font: (nullable): the #hb_font_t used to shape this buffer, needed to
- *        read glyph names and extents. If %NULL, an empty font will be used.
+ *        read glyph names and extents. If `NULL`, an empty font will be used.
  * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
  * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
  *         to serialize.
@@ -514,7 +514,7 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
  * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
  *       write serialized buffer into.
  * @buf_size: the size of @buf.
- * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes written into @buf.
+ * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf.
  * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
  * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
  *         to serialize.
@@ -637,9 +637,9 @@ _hb_buffer_serialize_invalid (hb_buffer_t *buffer,
  * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
  *       write serialized buffer into.
  * @buf_size: the size of @buf.
- * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes written into @buf.
+ * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf.
  * @font: (nullable): the #hb_font_t used to shape this buffer, needed to
- *        read glyph names and extents. If %NULL, an empty font will be used.
+ *        read glyph names and extents. If `NULL`, an empty font will be used.
  * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
  * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
  *         to serialize.
@@ -721,13 +721,14 @@ parse_hex (const char *pp, const char *end, uint32_t *pv)
 }
 
 #include "hb-buffer-deserialize-json.hh"
-#include "hb-buffer-deserialize-text.hh"
+#include "hb-buffer-deserialize-text-glyphs.hh"
+#include "hb-buffer-deserialize-text-unicode.hh"
 
 /**
  * hb_buffer_deserialize_glyphs:
  * @buffer: an #hb_buffer_t buffer.
  * @buf: (array length=buf_len): string to deserialize
- * @buf_len: the size of @buf, or -1 if it is %NULL-terminated
+ * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated
  * @end_ptr: (out) (optional): output pointer to the character after last
  *                               consumed one.
  * @font: (nullable): font for getting glyph IDs
@@ -736,7 +737,8 @@ 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 @buf is not fully consumed, %false otherwise.
+ * Return value: `true` if parse was successful, `false` if an error
+ * occurred.
  *
  * Since: 0.9.7
  **/
@@ -779,9 +781,9 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
   switch (format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
-      return _hb_buffer_deserialize_text (buffer,
-                                          buf, buf_len, end_ptr,
-                                          font);
+      return _hb_buffer_deserialize_text_glyphs (buffer,
+                                                 buf, buf_len, end_ptr,
+                                                 font);
 
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:
       return _hb_buffer_deserialize_json (buffer,
@@ -800,7 +802,7 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
  * hb_buffer_deserialize_unicode:
  * @buffer: an #hb_buffer_t buffer.
  * @buf: (array length=buf_len): string to deserialize
- * @buf_len: the size of @buf, or -1 if it is %NULL-terminated
+ * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated
  * @end_ptr: (out) (optional): output pointer to the character after last
  *                               consumed one.
  * @format: the #hb_buffer_serialize_format_t of the input @buf
@@ -808,7 +810,8 @@ 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 @buf is not fully consumed, %false otherwise.
+ * Return value: `true` if parse was successful, `false` if an error
+ * occurred.
  *
  * Since: 2.7.3
  **/
@@ -849,9 +852,9 @@ hb_buffer_deserialize_unicode (hb_buffer_t *buffer,
   switch (format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
-      return _hb_buffer_deserialize_text (buffer,
-                                          buf, buf_len, end_ptr,
-                                          font);
+      return _hb_buffer_deserialize_text_unicode (buffer,
+                                                  buf, buf_len, end_ptr,
+                                                  font);
 
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:
       return _hb_buffer_deserialize_json (buffer,
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 1b96fafb0d4..f2fef3137e5 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc
@@ -150,7 +150,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t  *buffer,
     assert (text_start < text_end);
 
     if (0)
-      printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end);
+      printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
 
     hb_buffer_clear_contents (fragment);
 
@@ -186,7 +186,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t  *buffer,
 
   bool ret = true;
   hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
-  if (diff)
+  if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
   {
     buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed.");
     ret = false;
@@ -292,7 +292,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t        *buffer,
       assert (text_start < text_end);
 
       if (0)
-        printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end);
+        printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
 
 #if 0
       hb_buffer_flags_t flags = hb_buffer_get_flags (fragment);
@@ -313,7 +313,6 @@ buffer_verify_unsafe_to_concat (hb_buffer_t        *buffer,
 
   bool ret = true;
   hb_buffer_diff_flags_t diff;
-
   /*
    * Shape the two fragment streams.
    */
@@ -382,7 +381,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t        *buffer,
    * Diff results.
    */
   diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
-  if (diff)
+  if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
   {
     buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed.");
     ret = false;
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc
index e743e18125a..69d8c961930 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc
@@ -40,6 +40,11 @@
  * Buffers serve a dual role in HarfBuzz; before shaping, they hold
  * the input characters that are passed to hb_shape(), and after
  * shaping they hold the output glyphs.
+ *
+ * The input buffer is a sequence of Unicode codepoints, with
+ * associated attributes such as direction and script.  The output
+ * buffer is a sequence of glyphs, with associated attributes such
+ * as position and cluster.
  **/
 
 
@@ -51,7 +56,7 @@
  * Checks the equality of two #hb_segment_properties_t's.
  *
  * Return value:
- * %true if all properties of @a equal those of @b, %false otherwise.
+ * `true` if all properties of @a equal those of @b, `false` otherwise.
  *
  * Since: 0.9.7
  **/
@@ -172,12 +177,13 @@ hb_buffer_t::enlarge (unsigned int size)
   while (size >= new_allocated)
     new_allocated += (new_allocated >> 1) + 32;
 
-  static_assert ((sizeof (info[0]) == sizeof (pos[0])), "");
-  if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]))))
+  unsigned new_bytes;
+  if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]), &new_bytes)))
     goto done;
 
-  new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_allocated * sizeof (pos[0]));
-  new_info = (hb_glyph_info_t *) hb_realloc (info, new_allocated * sizeof (info[0]));
+  static_assert (sizeof (info[0]) == sizeof (pos[0]), "");
+  new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_bytes);
+  new_info = (hb_glyph_info_t *) hb_realloc (info, new_bytes);
 
 done:
   if (unlikely (!new_pos || !new_info))
@@ -208,7 +214,7 @@ hb_buffer_t::make_room_for (unsigned int num_in,
     assert (have_output);
 
     out_info = (hb_glyph_info_t *) pos;
-    memcpy (out_info, info, out_len * sizeof (out_info[0]));
+    hb_memcpy (out_info, info, out_len * sizeof (out_info[0]));
   }
 
   return true;
@@ -229,7 +235,7 @@ hb_buffer_t::shift_forward (unsigned int count)
      * Ideally, we should at least set Default_Ignorable bits on
      * these, as well as consistent cluster values.  But the former
      * is layering violation... */
-    memset (info + len, 0, (idx + count - len) * sizeof (info[0]));
+    hb_memset (info + len, 0, (idx + count - len) * sizeof (info[0]));
   }
   len += count;
   idx += count;
@@ -298,8 +304,8 @@ hb_buffer_t::clear ()
   out_len = 0;
   out_info = info;
 
-  memset (context, 0, sizeof context);
-  memset (context_len, 0, sizeof context_len);
+  hb_memset (context, 0, sizeof context);
+  hb_memset (context_len, 0, sizeof context_len);
 
   deallocate_var_all ();
   serial = 0;
@@ -313,15 +319,14 @@ hb_buffer_t::enter ()
   serial = 0;
   shaping_failed = false;
   scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
-  if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR)))
+  unsigned mul;
+  if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR, &mul)))
   {
-    max_len = hb_max (len * HB_BUFFER_MAX_LEN_FACTOR,
-                      (unsigned) HB_BUFFER_MAX_LEN_MIN);
+    max_len = hb_max (mul, (unsigned) HB_BUFFER_MAX_LEN_MIN);
   }
-  if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR)))
+  if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR, &mul)))
   {
-    max_ops = hb_max (len * HB_BUFFER_MAX_OPS_FACTOR,
-                      (unsigned) HB_BUFFER_MAX_OPS_MIN);
+    max_ops = hb_max (mul, (unsigned) HB_BUFFER_MAX_OPS_MIN);
   }
 }
 void
@@ -345,7 +350,7 @@ hb_buffer_t::add (hb_codepoint_t  codepoint,
 
   glyph = &info[len];
 
-  memset (glyph, 0, sizeof (*glyph));
+  hb_memset (glyph, 0, sizeof (*glyph));
   glyph->codepoint = codepoint;
   glyph->mask = 0;
   glyph->cluster = cluster;
@@ -387,9 +392,11 @@ hb_buffer_t::clear_positions ()
   hb_memset (pos, 0, sizeof (pos[0]) * len);
 }
 
-void
+bool
 hb_buffer_t::sync ()
 {
+  bool ret = false;
+
   assert (have_output);
 
   assert (idx <= len);
@@ -403,12 +410,39 @@ hb_buffer_t::sync ()
     info = out_info;
   }
   len = out_len;
+  ret = true;
 
 reset:
   have_output = false;
   out_len = 0;
   out_info = info;
   idx = 0;
+
+  return ret;
+}
+
+int
+hb_buffer_t::sync_so_far ()
+{
+  bool had_output = have_output;
+  unsigned out_i = out_len;
+  unsigned i = idx;
+  unsigned old_idx = idx;
+
+  if (sync ())
+    idx = out_i;
+  else
+    idx = i;
+
+  if (had_output)
+  {
+    have_output = true;
+    out_len = idx;
+  }
+
+  assert (idx <= len);
+
+  return idx - old_idx;
 }
 
 bool
@@ -493,15 +527,17 @@ hb_buffer_t::merge_clusters_impl (unsigned int start,
     cluster = hb_min (cluster, info[i].cluster);
 
   /* Extend end */
-  while (end < len && info[end - 1].cluster == info[end].cluster)
-    end++;
+  if (cluster != info[end - 1].cluster)
+    while (end < len && info[end - 1].cluster == info[end].cluster)
+      end++;
 
   /* Extend start */
-  while (idx < start && info[start - 1].cluster == info[start].cluster)
-    start--;
+  if (cluster != info[start].cluster)
+    while (idx < start && info[start - 1].cluster == info[start].cluster)
+      start--;
 
   /* If we hit the start of buffer, continue in out-buffer. */
-  if (idx == start)
+  if (idx == start && info[start].cluster != cluster)
     for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
       set_cluster (out_info[i - 1], cluster);
 
@@ -576,6 +612,53 @@ hb_buffer_t::delete_glyph ()
   skip_glyph ();
 }
 
+void
+hb_buffer_t::delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info))
+{
+  /* Merge clusters and delete filtered glyphs.
+   * NOTE! We can't use out-buffer as we have positioning data. */
+  unsigned int j = 0;
+  unsigned int count = len;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    if (filter (&info[i]))
+    {
+      /* Merge clusters.
+       * Same logic as delete_glyph(), but for in-place removal. */
+
+      unsigned int cluster = info[i].cluster;
+      if (i + 1 < count && cluster == info[i + 1].cluster)
+        continue; /* Cluster survives; do nothing. */
+
+      if (j)
+      {
+        /* Merge cluster backward. */
+        if (cluster < info[j - 1].cluster)
+        {
+          unsigned int mask = info[i].mask;
+          unsigned int old_cluster = info[j - 1].cluster;
+          for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
+            set_cluster (info[k - 1], cluster, mask);
+        }
+        continue;
+      }
+
+      if (i + 1 < count)
+        merge_clusters (i, i + 2); /* Merge cluster forward. */
+
+      continue;
+    }
+
+    if (j != i)
+    {
+      info[j] = info[i];
+      pos[j] = pos[i];
+    }
+    j++;
+  }
+  len = j;
+}
+
 void
 hb_buffer_t::guess_segment_properties ()
 {
@@ -643,9 +726,9 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) =
  * Return value: (transfer full):
  * A newly allocated #hb_buffer_t with a reference count of 1. The initial
  * reference count should be released with hb_buffer_destroy() when you are done
- * using the #hb_buffer_t. This function never returns %NULL. If memory cannot
+ * using the #hb_buffer_t. This function never returns `NULL`. If memory cannot
  * be allocated, a special #hb_buffer_t object will be returned on which
- * hb_buffer_allocation_successful() returns %false.
+ * hb_buffer_allocation_successful() returns `false`.
  *
  * Since: 0.9.2
  **/
@@ -775,7 +858,7 @@ hb_buffer_destroy (hb_buffer_t *buffer)
  *
  * Attaches a user-data key/data pair to the specified buffer.
  *
- * Return value: %true if success, %false otherwise
+ * Return value: `true` if success, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -802,7 +885,7 @@ hb_buffer_set_user_data (hb_buffer_t        *buffer,
  * Since: 0.9.2
  **/
 void *
-hb_buffer_get_user_data (hb_buffer_t        *buffer,
+hb_buffer_get_user_data (const hb_buffer_t  *buffer,
                          hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (buffer, key);
@@ -817,6 +900,32 @@ hb_buffer_get_user_data (hb_buffer_t        *buffer,
  * Sets the type of @buffer contents. Buffers are either empty, contain
  * characters (before shaping), or contain glyphs (the result of shaping).
  *
+ * You rarely need to call this function, since a number of other
+ * functions transition the content type for you. Namely:
+ *
+ * - A newly created buffer starts with content type
+ *   %HB_BUFFER_CONTENT_TYPE_INVALID. Calling hb_buffer_reset(),
+ *   hb_buffer_clear_contents(), as well as calling hb_buffer_set_length()
+ *   with an argument of zero all set the buffer content type to invalid
+ *   as well.
+ *
+ * - Calling hb_buffer_add_utf8(), hb_buffer_add_utf16(),
+ *   hb_buffer_add_utf32(), hb_buffer_add_codepoints() and
+ *   hb_buffer_add_latin1() expect that buffer is either empty and
+ *   have a content type of invalid, or that buffer content type is
+ *   %HB_BUFFER_CONTENT_TYPE_UNICODE, and they also set the content
+ *   type to Unicode if they added anything to an empty buffer.
+ *
+ * - Finally hb_shape() and hb_shape_full() expect that the buffer
+ *   is either empty and have content type of invalid, or that buffer
+ *   content type is %HB_BUFFER_CONTENT_TYPE_UNICODE, and upon
+ *   success they set the buffer content type to
+ *   %HB_BUFFER_CONTENT_TYPE_GLYPHS.
+ *
+ * The above transitions are designed such that one can use a buffer
+ * in a loop of "reset : add-text : shape" without needing to ever
+ * modify the content type manually.
+ *
  * Since: 0.9.5
  **/
 void
@@ -904,7 +1013,6 @@ hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer)
 void
 hb_buffer_set_direction (hb_buffer_t    *buffer,
                          hb_direction_t  direction)
-
 {
   if (unlikely (hb_object_is_immutable (buffer)))
     return;
@@ -1278,7 +1386,7 @@ hb_buffer_clear_contents (hb_buffer_t *buffer)
  * Pre allocates memory for @buffer to fit at least @size number of items.
  *
  * Return value:
- * %true if @buffer memory allocation succeeded, %false otherwise
+ * `true` if @buffer memory allocation succeeded, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1295,7 +1403,7 @@ hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
  * Check if allocating memory for the buffer succeeded.
  *
  * Return value:
- * %true if @buffer memory allocation succeeded, %false otherwise.
+ * `true` if @buffer memory allocation succeeded, `false` otherwise.
  *
  * Since: 0.9.2
  **/
@@ -1340,7 +1448,7 @@ hb_buffer_add (hb_buffer_t    *buffer,
  * end.
  *
  * Return value:
- * %true if @buffer memory allocation succeeded, %false otherwise.
+ * `true` if @buffer memory allocation succeeded, `false` otherwise.
  *
  * Since: 0.9.2
  **/
@@ -1356,9 +1464,9 @@ hb_buffer_set_length (hb_buffer_t  *buffer,
 
   /* Wipe the new space */
   if (length > buffer->len) {
-    memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
+    hb_memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
     if (buffer->have_positions)
-      memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
+      hb_memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
   }
 
   buffer->len = length;
@@ -1426,7 +1534,7 @@ hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
  * If buffer did not have positions before, the positions will be
  * initialized to zeros, unless this function is called from
  * within a buffer message callback (see hb_buffer_set_message_func()),
- * in which case %NULL is returned.
+ * in which case `NULL` is returned.
  *
  * Return value: (transfer none) (array length=length):
  * The @buffer glyph position array.
@@ -1461,7 +1569,7 @@ hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
  * and cleared of position data when hb_buffer_clear_contents() is called.
  *
  * Return value:
- * %true if the @buffer has position array, %false otherwise.
+ * `true` if the @buffer has position array, `false` otherwise.
  *
  * Since: 2.7.3
  **/
@@ -1645,10 +1753,10 @@ hb_buffer_add_utf (hb_buffer_t  *buffer,
  * @buffer: An #hb_buffer_t
  * @text: (array length=text_length) (element-type uint8_t): An array of UTF-8
  *               characters to append.
- * @text_length: The length of the @text, or -1 if it is %NULL terminated.
+ * @text_length: The length of the @text, or -1 if it is `NULL` terminated.
  * @item_offset: The offset of the first character to add to the @buffer.
  * @item_length: The number of characters to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated).
+ *               end of @text (assuming it is `NULL` terminated).
  *
  * See hb_buffer_add_codepoints().
  *
@@ -1671,10 +1779,10 @@ hb_buffer_add_utf8 (hb_buffer_t  *buffer,
  * hb_buffer_add_utf16:
  * @buffer: An #hb_buffer_t
  * @text: (array length=text_length): An array of UTF-16 characters to append
- * @text_length: The length of the @text, or -1 if it is %NULL terminated
+ * @text_length: The length of the @text, or -1 if it is `NULL` terminated
  * @item_offset: The offset of the first character to add to the @buffer
  * @item_length: The number of characters to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated)
+ *               end of @text (assuming it is `NULL` terminated)
  *
  * See hb_buffer_add_codepoints().
  *
@@ -1697,10 +1805,10 @@ hb_buffer_add_utf16 (hb_buffer_t    *buffer,
  * hb_buffer_add_utf32:
  * @buffer: An #hb_buffer_t
  * @text: (array length=text_length): An array of UTF-32 characters to append
- * @text_length: The length of the @text, or -1 if it is %NULL terminated
+ * @text_length: The length of the @text, or -1 if it is `NULL` terminated
  * @item_offset: The offset of the first character to add to the @buffer
  * @item_length: The number of characters to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated)
+ *               end of @text (assuming it is `NULL` terminated)
  *
  * See hb_buffer_add_codepoints().
  *
@@ -1724,10 +1832,10 @@ hb_buffer_add_utf32 (hb_buffer_t    *buffer,
  * @buffer: An #hb_buffer_t
  * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8
  *               characters to append
- * @text_length: the length of the @text, or -1 if it is %NULL terminated
+ * @text_length: the length of the @text, or -1 if it is `NULL` terminated
  * @item_offset: the offset of the first character to add to the @buffer
  * @item_length: the number of characters to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated)
+ *               end of @text (assuming it is `NULL` terminated)
  *
  * Similar to hb_buffer_add_codepoints(), but allows only access to first 256
  * Unicode code points that can fit in 8-bit strings.
@@ -1750,10 +1858,10 @@ hb_buffer_add_latin1 (hb_buffer_t   *buffer,
  * hb_buffer_add_codepoints:
  * @buffer: a #hb_buffer_t to append characters to.
  * @text: (array length=text_length): an array of Unicode code points to append.
- * @text_length: the length of the @text, or -1 if it is %NULL terminated.
+ * @text_length: the length of the @text, or -1 if it is `NULL` terminated.
  * @item_offset: the offset of the first code point to add to the @buffer.
  * @item_length: the number of code points to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated).
+ *               end of @text (assuming it is `NULL` terminated).
  *
  * Appends characters from @text array to @buffer. The @item_offset is the
  * position of the first character from @text that will be appended, and
@@ -1766,7 +1874,9 @@ hb_buffer_add_latin1 (hb_buffer_t   *buffer,
  * marks at stat of run.
  *
  * This function does not check the validity of @text, it is up to the caller
- * to ensure it contains a valid Unicode code points.
+ * to ensure it contains a valid Unicode scalar values.  In contrast,
+ * hb_buffer_add_utf32() can be used that takes similar input but performs
+ * sanity-check on the input.
  *
  * Since: 0.9.31
  **/
@@ -1829,9 +1939,9 @@ hb_buffer_append (hb_buffer_t *buffer,
 
   hb_segment_properties_overlay (&buffer->props, &source->props);
 
-  memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
+  hb_memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
   if (buffer->have_positions)
-    memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
+    hb_memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
 
   if (source->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE)
   {
@@ -2019,7 +2129,7 @@ hb_buffer_diff (hb_buffer_t *buffer,
       result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH;
     if (buf_info->cluster != ref_info->cluster)
       result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH;
-    if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED))
+    if ((buf_info->mask ^ ref_info->mask) & HB_GLYPH_FLAG_DEFINED)
       result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH;
     if (contains && ref_info->codepoint == dottedcircle_glyph)
       result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
@@ -2074,6 +2184,13 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
                             hb_buffer_message_func_t func,
                             void *user_data, hb_destroy_func_t destroy)
 {
+  if (unlikely (hb_object_is_immutable (buffer)))
+  {
+    if (destroy)
+      destroy (user_data);
+    return;
+  }
+
   if (buffer->message_destroy)
     buffer->message_destroy (buffer->message_data);
 
@@ -2090,8 +2207,16 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
 bool
 hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap)
 {
+  assert (!have_output || (out_info == info && out_len == idx));
+
+  message_depth++;
+
   char buf[100];
   vsnprintf (buf, sizeof (buf), fmt, ap);
-  return (bool) this->message_func (this, font, buf, this->message_data);
+  bool ret = (bool) this->message_func (this, font, buf, this->message_data);
+
+  message_depth--;
+
+  return ret;
 }
 #endif
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h
index d5c2b25f852..9b21ffb10fa 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h
@@ -142,6 +142,15 @@ typedef struct hb_glyph_info_t {
  *                                 shaping, otherwise the buffer flag will not be
  *                                 reliably produced.
  *                                 Since: 4.0.0
+ * @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL: In scripts that use elongation (Arabic,
+                                   Mongolian, Syriac, etc.), this flag signifies
+                                   that it is safe to insert a U+0640 TATWEEL
+                                   character before this cluster for elongation.
+                                   This flag does not determine the
+                                   script-specific elongation places, but only
+                                   when it is safe to do the elongation without
+                                   interrupting text shaping.
+                                   Since: 5.1.0
  * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
  *
  * Flags for #hb_glyph_info_t.
@@ -149,10 +158,11 @@ typedef struct hb_glyph_info_t {
  * Since: 1.5.0
  */
 typedef enum { /*< flags >*/
-  HB_GLYPH_FLAG_UNSAFE_TO_BREAK         = 0x00000001,
-  HB_GLYPH_FLAG_UNSAFE_TO_CONCAT        = 0x00000002,
+  HB_GLYPH_FLAG_UNSAFE_TO_BREAK                 = 0x00000001,
+  HB_GLYPH_FLAG_UNSAFE_TO_CONCAT                = 0x00000002,
+  HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL          = 0x00000004,
 
-  HB_GLYPH_FLAG_DEFINED                 = 0x00000003 /* OR of all defined flags */
+  HB_GLYPH_FLAG_DEFINED                         = 0x00000007 /* OR of all defined flags */
 } hb_glyph_flags_t;
 
 HB_EXTERN hb_glyph_flags_t
@@ -266,7 +276,7 @@ hb_buffer_set_user_data (hb_buffer_t        *buffer,
                          hb_bool_t           replace);
 
 HB_EXTERN void *
-hb_buffer_get_user_data (hb_buffer_t        *buffer,
+hb_buffer_get_user_data (const hb_buffer_t  *buffer,
                          hb_user_data_key_t *key);
 
 
@@ -373,6 +383,10 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer);
  *                      flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT
  *                      glyph-flag should be produced by the shaper. By default
  *                      it will not be produced since it incurs a cost. Since: 4.0.0
+ * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL:
+ *                      flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL
+ *                      glyph-flag should be produced by the shaper. By default
+ *                      it will not be produced. Since: 5.1.0
  * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0
  *
  * Flags for #hb_buffer_t.
@@ -388,8 +402,9 @@ typedef enum { /*< flags >*/
   HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE    = 0x00000010u,
   HB_BUFFER_FLAG_VERIFY                         = 0x00000020u,
   HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT       = 0x00000040u,
+  HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL = 0x00000080u,
 
-  HB_BUFFER_FLAG_DEFINED                        = 0x0000007Fu
+  HB_BUFFER_FLAG_DEFINED                        = 0x000000FFu
 } hb_buffer_flags_t;
 
 HB_EXTERN void
@@ -748,23 +763,23 @@ hb_buffer_diff (hb_buffer_t *buffer,
 
 
 /*
- * Debugging.
+ * Tracing.
  */
 
 /**
  * hb_buffer_message_func_t:
  * @buffer: An #hb_buffer_t to work upon
  * @font: The #hb_font_t the @buffer is shaped with
- * @message: %NULL-terminated message passed to the function
+ * @message: `NULL`-terminated message passed to the function
  * @user_data: User data pointer passed by the caller
  *
  * A callback method for #hb_buffer_t. The method gets called with the
  * #hb_buffer_t it was set on, the #hb_font_t the buffer is shaped with and a
  * message describing what step of the shaping process will be performed.
- * Returning %false from this method will skip this shaping step and move to
+ * Returning `false` from this method will skip this shaping step and move to
  * the next one.
  *
- * Return value: %true to perform the shaping step, %false to skip it.
+ * Return value: `true` to perform the shaping step, `false` to skip it.
  *
  * Since: 1.1.3
  */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh
index 44f30d0a5dc..c1476364de5 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh
@@ -32,28 +32,9 @@
 
 #include "hb.hh"
 #include "hb-unicode.hh"
+#include "hb-set-digest.hh"
 
 
-#ifndef HB_BUFFER_MAX_LEN_FACTOR
-#define HB_BUFFER_MAX_LEN_FACTOR 64
-#endif
-#ifndef HB_BUFFER_MAX_LEN_MIN
-#define HB_BUFFER_MAX_LEN_MIN 16384
-#endif
-#ifndef HB_BUFFER_MAX_LEN_DEFAULT
-#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
-#endif
-
-#ifndef HB_BUFFER_MAX_OPS_FACTOR
-#define HB_BUFFER_MAX_OPS_FACTOR 1024
-#endif
-#ifndef HB_BUFFER_MAX_OPS_MIN
-#define HB_BUFFER_MAX_OPS_MIN 16384
-#endif
-#ifndef HB_BUFFER_MAX_OPS_DEFAULT
-#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */
-#endif
-
 static_assert ((sizeof (hb_glyph_info_t) == 20), "");
 static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), "");
 
@@ -207,6 +188,14 @@ struct hb_buffer_t
   hb_glyph_info_t &prev ()      { return out_info[out_len ? out_len - 1 : 0]; }
   hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
 
+  hb_set_digest_t digest () const
+  {
+    hb_set_digest_t d;
+    d.init ();
+    d.add_array (&info[0].codepoint, len, sizeof (info[0]));
+    return d;
+  }
+
   HB_INTERNAL void similar (const hb_buffer_t &src);
   HB_INTERNAL void reset ();
   HB_INTERNAL void clear ();
@@ -288,7 +277,8 @@ struct hb_buffer_t
 
   HB_INTERNAL void guess_segment_properties ();
 
-  HB_INTERNAL void sync ();
+  HB_INTERNAL bool sync ();
+  HB_INTERNAL int sync_so_far ();
   HB_INTERNAL void clear_output ();
   HB_INTERNAL void clear_positions ();
 
@@ -401,6 +391,8 @@ struct hb_buffer_t
   HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end);
   /* Merge clusters for deleting current glyph, and skip it. */
   HB_INTERNAL void delete_glyph ();
+  HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info));
+
 
 
   /* Adds glyph flags in mask to infos with clusters between start and end.
@@ -461,6 +453,17 @@ struct hb_buffer_t
                       start, end,
                       true);
   }
+  void safe_to_insert_tatweel (unsigned int start = 0, unsigned int end = -1)
+  {
+    if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0)
+    {
+      unsafe_to_break (start, end);
+      return;
+    }
+    _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL,
+                      start, end,
+                      true);
+  }
   void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1)
   {
     if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0))
@@ -555,15 +558,11 @@ struct hb_buffer_t
     if (likely (!messaging ()))
       return true;
 
-    message_depth++;
-
     va_list ap;
     va_start (ap, fmt);
     bool ret = message_impl (font, fmt, ap);
     va_end (ap);
 
-    message_depth--;
-
     return ret;
 #endif
   }
@@ -582,21 +581,59 @@ struct hb_buffer_t
                           unsigned int cluster,
                           hb_mask_t mask)
   {
-    for (unsigned int i = start; i < end; i++)
-      if (cluster != infos[i].cluster)
+    if (unlikely (start == end))
+      return;
+
+    unsigned cluster_first = infos[start].cluster;
+    unsigned cluster_last = infos[end - 1].cluster;
+
+    if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS ||
+        (cluster != cluster_first && cluster != cluster_last))
+    {
+      for (unsigned int i = start; i < end; i++)
+        if (cluster != infos[i].cluster)
+        {
+          scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
+          infos[i].mask |= mask;
+        }
+      return;
+    }
+
+    /* Monotone clusters */
+
+    if (cluster == cluster_first)
+    {
+      for (unsigned int i = end; start < i && infos[i - 1].cluster != cluster_first; i--)
+      {
+        scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
+        infos[i - 1].mask |= mask;
+      }
+    }
+    else /* cluster == cluster_last */
+    {
+      for (unsigned int i = start; i < end && infos[i].cluster != cluster_last; i++)
       {
         scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
         infos[i].mask |= mask;
       }
+    }
   }
-  static unsigned
+  unsigned
   _infos_find_min_cluster (const hb_glyph_info_t *infos,
                            unsigned start, unsigned end,
                            unsigned cluster = UINT_MAX)
   {
-    for (unsigned int i = start; i < end; i++)
-      cluster = hb_min (cluster, infos[i].cluster);
-    return cluster;
+    if (unlikely (start == end))
+      return cluster;
+
+    if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+    {
+      for (unsigned int i = start; i < end; i++)
+        cluster = hb_min (cluster, infos[i].cluster);
+      return cluster;
+    }
+
+    return hb_min (cluster, hb_min (infos[start].cluster, infos[end - 1].cluster));
   }
 
   void clear_glyph_flags (hb_mask_t mask = 0)
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh
index de8a808ec45..f40c8610dbb 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh
@@ -30,29 +30,53 @@
 #include "hb.hh"
 
 
-/* Implements a lockfree cache for int->int functions. */
+/* Implements a lockfree cache for int->int functions.
+ *
+ * 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 index is used to index into the array.  The rest is used
+ * to store 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.
+ */
 
-template 
+template 
 struct hb_cache_t
 {
+  using item_t = typename std::conditional::type,
+                                           typename std::conditional::type
+                                          >::type;
+
   static_assert ((key_bits >= cache_bits), "");
-  static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (hb_atomic_int_t)), "");
-  static_assert (sizeof (hb_atomic_int_t) == sizeof (unsigned int), "");
+  static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), "");
+
+  hb_cache_t () { init (); }
 
   void init () { clear (); }
-  void fini () {}
 
   void clear ()
   {
     for (unsigned i = 0; i < ARRAY_LENGTH (values); i++)
-      values[i].set_relaxed (-1);
+      values[i] = -1;
   }
 
   bool get (unsigned int key, unsigned int *value) const
   {
     unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits))
       return false;
     *value = v & ((1u<>cache_bits)< hb_cmap_cache_t;
-typedef hb_cache_t<16, 24, 8> hb_advance_cache_t;
-
 
 #endif /* HB_CACHE_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh
index 2c980c60279..e92e8140fd0 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh
@@ -284,65 +284,56 @@ struct UnsizedByteStr : UnsizedArrayOf 
 /* A byte string associated with the current offset and an error condition */
 struct byte_str_ref_t
 {
-  byte_str_ref_t () { init (); }
-
-  void init ()
-  {
-    str = hb_ubytes_t ();
-    offset = 0;
-    error = false;
-  }
-
-  void fini () {}
+  byte_str_ref_t ()
+    : str () {}
 
   byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0)
-    : str (str_), offset (offset_), error (false) {}
+    : str (str_) { set_offset (offset_); }
 
   void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0)
   {
     str = str_;
-    offset = offset_;
-    error = false;
+    set_offset (offset_);
   }
 
   const unsigned char& operator [] (int i) {
-    if (unlikely ((unsigned int) (offset + i) >= str.length))
+    if (unlikely ((unsigned int) (get_offset () + i) >= str.length))
     {
       set_error ();
       return Null (unsigned char);
     }
-    return str[offset + i];
+    return str.arrayZ[get_offset () + i];
   }
 
+  unsigned char head_unchecked () const { return str.arrayZ[get_offset ()]; }
+
   /* Conversion to hb_ubytes_t */
-  operator hb_ubytes_t () const { return str.sub_array (offset, str.length - offset); }
+  operator hb_ubytes_t () const { return str.sub_array (get_offset ()); }
 
   hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const
   { return str.sub_array (offset_, len_); }
 
   bool avail (unsigned int count=1) const
-  { return (!in_error () && offset + count <= str.length); }
+  { return get_offset () + count <= str.length; }
   void inc (unsigned int count=1)
   {
-    if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length)))
-    {
-      offset += count;
-    }
-    else
-    {
-      offset = str.length;
-      set_error ();
-    }
+    /* Automatically puts us in error if count is out-of-range. */
+    set_offset (get_offset () + count);
   }
 
-  void set_error ()      { error = true; }
-  bool in_error () const { return error; }
+  /* We (ab)use ubytes backwards_length as a cursor (called offset),
+   * as well as to store error condition. */
 
-  hb_ubytes_t       str;
-  unsigned int  offset; /* beginning of the sub-string within str */
+  unsigned get_offset () const { return str.backwards_length; }
+  void set_offset (unsigned offset) { str.backwards_length = offset; }
+
+  void set_error ()      { str.backwards_length = str.length + 1; }
+  bool in_error () const { return str.backwards_length > str.length; }
+
+  unsigned total_size () const { return str.length; }
 
   protected:
-  bool    error;
+  hb_ubytes_t       str;
 };
 
 using byte_str_array_t = hb_vector_t;
@@ -491,8 +482,15 @@ struct arg_stack_t : cff_stack_t
 /* an operator prefixed by its operands in a byte string */
 struct op_str_t
 {
-  hb_ubytes_t str;
-  op_code_t  op;
+  /* This used to have a hb_ubytes_t. Using a pointer and length
+   * in a particular order, saves 8 bytes in this struct and more
+   * in our parsed_cs_op_t subclass. */
+
+  const unsigned char *ptr = nullptr;
+
+  op_code_t  op = OpCode_Invalid;
+
+  uint8_t length = 0;
 };
 
 /* base of OP_SERIALIZER */
@@ -503,9 +501,11 @@ struct op_serializer_t
   {
     TRACE_SERIALIZE (this);
 
-    HBUINT8 *d = c->allocate_size (opstr.str.length);
+    unsigned char *d = c->allocate_size (opstr.length);
     if (unlikely (!d)) return_trace (false);
-    memcpy (d, &opstr.str[0], opstr.str.length);
+    /* Faster than hb_memcpy for small strings. */
+    for (unsigned i = 0; i < opstr.length; i++)
+      d[i] = opstr.ptr[i];
     return_trace (true);
   }
 };
@@ -522,23 +522,17 @@ struct parsed_values_t
 
   void alloc (unsigned n)
   {
-    values.alloc (n);
+    values.alloc (n, true);
   }
 
-  void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
-  {
-    VAL *val = values.push ();
-    val->op = op;
-    val->str = str_ref.str.sub_array (opStart, str_ref.offset - opStart);
-    opStart = str_ref.offset;
-  }
-
-  void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v)
+  void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ())
   {
     VAL *val = values.push (v);
     val->op = op;
-    val->str = str_ref.sub_array ( opStart, str_ref.offset - opStart);
-    opStart = str_ref.offset;
+    auto arr = str_ref.sub_array (opStart, str_ref.get_offset () - opStart);
+    val->ptr = arr.arrayZ;
+    val->length = arr.length;
+    opStart = str_ref.get_offset ();
   }
 
   bool has_op (op_code_t op) const
@@ -549,8 +543,7 @@ struct parsed_values_t
   }
 
   unsigned get_count () const { return values.length; }
-  const VAL &get_value (unsigned int i)   const { return values[i]; }
-  const VAL &operator [] (unsigned int i) const { return get_value (i); }
+  const VAL &operator [] (unsigned int i) const { return values[i]; }
 
   unsigned int       opStart;
   hb_vector_t   values;
@@ -565,23 +558,23 @@ struct interp_env_t
     str_ref.reset (str_);
   }
   bool in_error () const
-  { return error || str_ref.in_error () || argStack.in_error (); }
+  { return str_ref.in_error () || argStack.in_error (); }
 
-  void set_error () { error = true; }
+  void set_error () { str_ref.set_error (); }
 
   op_code_t fetch_op ()
   {
     op_code_t  op = OpCode_Invalid;
     if (unlikely (!str_ref.avail ()))
       return OpCode_Invalid;
-    op = (op_code_t)(unsigned char)str_ref[0];
+    op = (op_code_t) str_ref.head_unchecked ();
+    str_ref.inc ();
     if (op == OpCode_escape) {
       if (unlikely (!str_ref.avail ()))
         return OpCode_Invalid;
-      op = Make_OpCode_ESC(str_ref[1]);
+      op = Make_OpCode_ESC (str_ref.head_unchecked ());
       str_ref.inc ();
     }
-    str_ref.inc ();
     return op;
   }
 
@@ -596,8 +589,6 @@ struct interp_env_t
                 str_ref;
   arg_stack_t
                 argStack;
-  protected:
-  bool          error = false;
 };
 
 using num_interp_env_t =  interp_env_t<>;
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh
index f4f41370495..52b52c3f769 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh
@@ -881,7 +881,13 @@ struct cs_interpreter_t : interpreter_t
   {
     SUPER::env.set_endchar (false);
 
+    unsigned max_ops = HB_CFF_MAX_OPS;
     for (;;) {
+      if (unlikely (!--max_ops))
+      {
+        SUPER::env.set_error ();
+        break;
+      }
       OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
       if (unlikely (SUPER::env.in_error ()))
         return false;
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh
index 041585a89a6..0c0cec9986a 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh
@@ -35,10 +35,8 @@ using namespace OT;
 /* an opstr and the parsed out dict value(s) */
 struct dict_val_t : op_str_t
 {
-  void init () { single_val.set_int (0); }
+  void init () {}
   void fini () {}
-
-  number_t            single_val;
 };
 
 typedef dict_val_t num_dict_val_t;
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh
index cfcf442ee7d..fbb31c9ee16 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh
@@ -38,7 +38,8 @@ typedef biased_subrs_t   cff1_biased_subrs_t;
 struct cff1_cs_interp_env_t : cs_interp_env_t
 {
   template 
-  cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd)
+  cff1_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)
   {
     processed_width = false;
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 791f3aab56a..c970633e064 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
@@ -40,20 +40,22 @@ struct blend_arg_t : number_t
   void set_real (double v) { reset_blends (); number_t::set_real (v); }
 
   void set_blends (unsigned int numValues_, unsigned int valueIndex_,
-                   unsigned int numBlends, hb_array_t blends_)
+                   hb_array_t blends_)
   {
     numValues = numValues_;
     valueIndex = valueIndex_;
-    deltas.resize (numBlends);
+    unsigned numBlends = blends_.length;
+    if (unlikely (!deltas.resize_exact (numBlends)))
+      return;
     for (unsigned int i = 0; i < numBlends; i++)
-      deltas[i] = blends_[i];
+      deltas.arrayZ[i] = blends_.arrayZ[i];
   }
 
   bool blending () const { return deltas.length > 0; }
   void reset_blends ()
   {
     numValues = valueIndex = 0;
-    deltas.resize (0);
+    deltas.shrink (0);
   }
 
   unsigned int numValues;
@@ -61,7 +63,6 @@ struct blend_arg_t : number_t
   hb_vector_t deltas;
 };
 
-typedef interp_env_t BlendInterpEnv;
 typedef biased_subrs_t   cff2_biased_subrs_t;
 
 template 
@@ -117,7 +118,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t
       region_count = varStore->varStore.get_region_index_count (get_ivs ());
       if (do_blend)
       {
-        if (unlikely (!scalars.resize (region_count)))
+        if (unlikely (!scalars.resize_exact (region_count)))
           SUPER::set_error ();
         else
           varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords,
@@ -154,13 +155,16 @@ struct cff2_cs_interp_env_t : cs_interp_env_t
     {
       if (likely (scalars.length == deltas.length))
       {
-        for (unsigned int i = 0; i < scalars.length; i++)
-          v += (double) scalars[i] * deltas[i].to_real ();
+        unsigned count = scalars.length;
+        for (unsigned i = 0; i < count; i++)
+          v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real ();
       }
     }
     return v;
   }
 
+  bool have_coords () const { return num_coords; }
+
   protected:
   const int     *coords;
   unsigned int  num_coords;
@@ -220,7 +224,10 @@ struct cff2_cs_opset_t : cs_opset_t, PAR
                                  const hb_array_t blends,
                                  unsigned n, unsigned i)
   {
-    arg.set_blends (n, i, blends.length, blends);
+    if (env.have_coords ())
+      arg.set_int (round (arg.to_real () + env.blend_deltas (blends)));
+    else
+      arg.set_blends (n, i, blends);
   }
   template 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc
index 310053e89df..8b94dcb366d 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc
@@ -29,32 +29,6 @@
 #include "hb.hh"
 #include "hb-machinery.hh"
 
-#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE))
-#define HB_NO_SETLOCALE 1
-#endif
-
-#ifndef HB_NO_SETLOCALE
-
-#include 
-#ifdef HAVE_XLOCALE_H
-#include  // Needed on BSD/OS X for uselocale
-#endif
-
-#ifdef WIN32
-#define hb_locale_t _locale_t
-#else
-#define hb_locale_t locale_t
-#endif
-#define hb_setlocale setlocale
-#define hb_uselocale uselocale
-
-#else
-
-#define hb_locale_t void *
-#define hb_setlocale(Category, Locale) "C"
-#define hb_uselocale(Locale) ((hb_locale_t) 0)
-
-#endif
 
 /**
  * SECTION:hb-common
@@ -99,7 +73,7 @@ _hb_options_init ()
   }
 
   /* This is idempotent and threadsafe. */
-  _hb_options.set_relaxed (u.i);
+  _hb_options = u.i;
 }
 
 
@@ -108,7 +82,7 @@ _hb_options_init ()
 /**
  * hb_tag_from_string:
  * @str: (array length=len) (element-type uint8_t): String to convert
- * @len: Length of @str, or -1 if it is %NULL-terminated
+ * @len: Length of @str, or -1 if it is `NULL`-terminated
  *
  * Converts a string into an #hb_tag_t. Valid tags
  * are four characters. Shorter input strings will be
@@ -170,7 +144,7 @@ static const char direction_strings[][4] = {
 /**
  * hb_direction_from_string:
  * @str: (array length=len) (element-type uint8_t): String to convert
- * @len: Length of @str, or -1 if it is %NULL-terminated
+ * @len: Length of @str, or -1 if it is `NULL`-terminated
  *
  * Converts a string to an #hb_direction_t.
  *
@@ -285,7 +259,7 @@ struct hb_language_item_t {
     lang = (hb_language_t) hb_malloc(len);
     if (likely (lang))
     {
-      memcpy((unsigned char *) lang, s, len);
+      hb_memcpy((unsigned char *) lang, s, len);
       for (unsigned char *p = (unsigned char *) lang; *p; p++)
         *p = canon_map[*p];
     }
@@ -357,7 +331,7 @@ lang_find_or_insert (const char *key)
  * hb_language_from_string:
  * @str: (array length=len) (element-type uint8_t): a string representing
  *       a BCP 47 language tag
- * @len: length of the @str, or -1 if it is %NULL-terminated.
+ * @len: length of the @str, or -1 if it is `NULL`-terminated.
  *
  * Converts @str representing a BCP 47 language tag to the corresponding
  * #hb_language_t.
@@ -379,7 +353,7 @@ hb_language_from_string (const char *str, int len)
     /* NUL-terminate it. */
     char strbuf[64];
     len = hb_min (len, (int) sizeof (strbuf) - 1);
-    memcpy (strbuf, str, len);
+    hb_memcpy (strbuf, str, len);
     strbuf[len] = '\0';
     item = lang_find_or_insert (strbuf);
   }
@@ -396,7 +370,7 @@ hb_language_from_string (const char *str, int len)
  * Converts an #hb_language_t to a string.
  *
  * Return value: (transfer none):
- * A %NULL-terminated string representing the @language. Must not be freed by
+ * A `NULL`-terminated string representing the @language. Must not be freed by
  * the caller.
  *
  * Since: 0.9.2
@@ -441,6 +415,38 @@ hb_language_get_default ()
   return language;
 }
 
+/**
+ * hb_language_matches:
+ * @language: The #hb_language_t to work on
+ * @specific: Another #hb_language_t
+ *
+ * Check whether a second language tag is the same or a more
+ * specific version of the provided language tag.  For example,
+ * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".
+ *
+ * Return value: `true` if languages match, `false` otherwise.
+ *
+ * Since: 5.0.0
+ **/
+hb_bool_t
+hb_language_matches (hb_language_t language,
+                     hb_language_t specific)
+{
+  if (language == specific) return true;
+  if (!language || !specific) return false;
+
+  const char *l = language->s;
+  const char *s = specific->s;
+  unsigned ll = strlen (l);
+  unsigned sl = strlen (s);
+
+  if (ll > sl)
+    return false;
+
+  return strncmp (l, s, ll) == 0 &&
+         (s[ll] == '\0' || s[ll] == '-');
+}
+
 
 /* hb_script_t */
 
@@ -498,7 +504,7 @@ hb_script_from_iso15924_tag (hb_tag_t tag)
  * hb_script_from_string:
  * @str: (array length=len) (element-type uint8_t): a string representing an
  *       ISO 15924 tag.
- * @len: length of the @str, or -1 if it is %NULL-terminated.
+ * @len: length of the @str, or -1 if it is `NULL`-terminated.
  *
  * Converts a string @str representing an ISO 15924 script tag to a
  * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
@@ -626,6 +632,7 @@ hb_script_get_horizontal_direction (hb_script_t script)
     case HB_SCRIPT_OLD_HUNGARIAN:
     case HB_SCRIPT_OLD_ITALIC:
     case HB_SCRIPT_RUNIC:
+    case HB_SCRIPT_TIFINAGH:
 
       return HB_DIRECTION_INVALID;
   }
@@ -693,8 +700,8 @@ hb_version_string ()
  * Tests the library version against a minimum value,
  * as three integer components.
  *
- * Return value: %true if the library is equal to or greater than
- * the test value, %false otherwise
+ * Return value: `true` if the library is equal to or greater than
+ * the test value, `false` otherwise
  *
  * Since: 0.9.30
  **/
@@ -881,7 +888,7 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
 /**
  * hb_feature_from_string:
  * @str: (array length=len) (element-type uint8_t): a string to parse
- * @len: length of @str, or -1 if string is %NULL terminated
+ * @len: length of @str, or -1 if string is `NULL` terminated
  * @feature: (out): the #hb_feature_t to initialize with the parsed values
  *
  * Parses a string into a #hb_feature_t.
@@ -923,7 +930,7 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
  * 
  *
  * Return value:
- * %true if @str is successfully parsed, %false otherwise
+ * `true` if @str is successfully parsed, `false` otherwise
  *
  * Since: 0.9.5
  **/
@@ -944,7 +951,7 @@ hb_feature_from_string (const char *str, int len,
   }
 
   if (feature)
-    memset (feature, 0, sizeof (*feature));
+    hb_memset (feature, 0, sizeof (*feature));
   return false;
 }
 
@@ -954,7 +961,7 @@ hb_feature_from_string (const char *str, int len,
  * @buf: (array length=size) (out): output string
  * @size: the allocated size of @buf
  *
- * Converts a #hb_feature_t into a %NULL-terminated string in the format
+ * Converts a #hb_feature_t into a `NULL`-terminated string in the format
  * understood by hb_feature_from_string(). The client in responsible for
  * allocating big enough size for @buf, 128 bytes is more than enough.
  *
@@ -993,7 +1000,7 @@ hb_feature_to_string (hb_feature_t *feature,
   }
   assert (len < ARRAY_LENGTH (s));
   len = hb_min (len, size - 1);
-  memcpy (buf, s, len);
+  hb_memcpy (buf, s, len);
   buf[len] = '\0';
 }
 
@@ -1022,7 +1029,7 @@ parse_one_variation (const char **pp, const char *end, hb_variation_t *variation
 /**
  * hb_variation_from_string:
  * @str: (array length=len) (element-type uint8_t): a string to parse
- * @len: length of @str, or -1 if string is %NULL terminated
+ * @len: length of @str, or -1 if string is `NULL` terminated
  * @variation: (out): the #hb_variation_t to initialize with the parsed values
  *
  * Parses a string into a #hb_variation_t.
@@ -1035,7 +1042,7 @@ parse_one_variation (const char **pp, const char *end, hb_variation_t *variation
  * number. For example `wght=500`, or `slnt=-7.5`.
  *
  * Return value:
- * %true if @str is successfully parsed, %false otherwise
+ * `true` if @str is successfully parsed, `false` otherwise
  *
  * Since: 1.4.2
  */
@@ -1056,7 +1063,7 @@ hb_variation_from_string (const char *str, int len,
   }
 
   if (variation)
-    memset (variation, 0, sizeof (*variation));
+    hb_memset (variation, 0, sizeof (*variation));
   return false;
 }
 
@@ -1104,10 +1111,10 @@ get_C_locale ()
 /**
  * hb_variation_to_string:
  * @variation: an #hb_variation_t to convert
- * @buf: (array length=size) (out): output string
+ * @buf: (array length=size) (out caller-allocates): output string
  * @size: the allocated size of @buf
  *
- * Converts an #hb_variation_t into a %NULL-terminated string in the format
+ * Converts an #hb_variation_t into a `NULL`-terminated string in the format
  * understood by hb_variation_from_string(). The client in responsible for
  * allocating big enough size for @buf, 128 bytes is more than enough.
  *
@@ -1134,7 +1141,7 @@ hb_variation_to_string (hb_variation_t *variation,
 
   assert (len < ARRAY_LENGTH (s));
   len = hb_min (len, size - 1);
-  memcpy (buf, s, len);
+  hb_memcpy (buf, s, len);
   buf[len] = '\0';
 }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.h b/src/java.desktop/share/native/libharfbuzz/hb-common.h
index 95daf0ed6a2..ebdeadd1fd1 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-common.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-common.h
@@ -326,6 +326,9 @@ hb_language_to_string (hb_language_t language);
 HB_EXTERN hb_language_t
 hb_language_get_default (void);
 
+HB_EXTERN hb_bool_t
+hb_language_matches (hb_language_t language,
+                     hb_language_t specific);
 
 /**
  * hb_script_t:
@@ -492,6 +495,8 @@ hb_language_get_default (void);
  * @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_INVALID: No script set
  *
  * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding
@@ -713,6 +718,12 @@ typedef enum
    */
   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*/
+
   /* No script set. */
   HB_SCRIPT_INVALID                     = HB_TAG_NONE,
 
@@ -886,6 +897,32 @@ HB_EXTERN uint8_t
 hb_color_get_blue (hb_color_t color);
 #define hb_color_get_blue(color)        (((color) >> 24) & 0xFF)
 
+/**
+ * hb_glyph_extents_t:
+ * @x_bearing: Distance from the x-origin to the left extremum of the glyph.
+ * @y_bearing: Distance from the top extremum of the glyph to the y-origin.
+ * @width: Distance from the left extremum of the glyph to the right extremum.
+ * @height: Distance from the top extremum of the glyph to the bottom extremum.
+ *
+ * Glyph extent values, measured in font units.
+ *
+ * Note that @height is negative, in coordinate systems that grow up.
+ **/
+typedef struct hb_glyph_extents_t {
+  hb_position_t x_bearing;
+  hb_position_t y_bearing;
+  hb_position_t width;
+  hb_position_t height;
+} hb_glyph_extents_t;
+
+/**
+ * hb_font_t:
+ *
+ * Data type for holding fonts.
+ *
+ */
+typedef struct hb_font_t hb_font_t;
+
 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 2578231d238..52adaad4384 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-config.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh
@@ -35,6 +35,11 @@
 #include "config.h"
 #endif
 
+#ifndef HB_EXPERIMENTAL_API
+#define HB_NO_BEYOND_64K
+#define HB_NO_CUBIC_GLYF
+#define HB_NO_VAR_COMPOSITES
+#endif
 
 #ifdef HB_TINY
 #define HB_LEAN
@@ -68,6 +73,7 @@
 #define HB_NO_LANGUAGE_PRIVATE_SUBTAG
 #define HB_NO_LAYOUT_FEATURE_PARAMS
 #define HB_NO_LAYOUT_COLLECT_GLYPHS
+#define HB_NO_LAYOUT_RARELY_USED
 #define HB_NO_LAYOUT_UNUSED
 #define HB_NO_MATH
 #define HB_NO_META
@@ -75,11 +81,13 @@
 #define HB_NO_MMAP
 #define HB_NO_NAME
 #define HB_NO_OPEN
-#define HB_NO_SETLOCALE
 #define HB_NO_OT_FONT_GLYPH_NAMES
 #define HB_NO_OT_SHAPE_FRACTIONS
+#define HB_NO_PAINT
+#define HB_NO_SETLOCALE
 #define HB_NO_STYLE
 #define HB_NO_SUBSET_LAYOUT
+#define HB_NO_VERTICAL
 #define HB_NO_VAR
 #endif
 
@@ -98,12 +106,22 @@
 
 /* Closure of options. */
 
+#ifdef HB_NO_BORING_EXPANSION
+#define HB_NO_BEYOND_64K
+#define HB_NO_AVAR2
+#endif
+
 #ifdef HB_DISABLE_DEPRECATED
 #define HB_IF_NOT_DEPRECATED(x)
 #else
 #define HB_IF_NOT_DEPRECATED(x) x
 #endif
 
+#ifdef HB_NO_SHAPER
+#define HB_NO_OT_SHAPE
+#define HB_NO_AAT_SHAPE
+#endif
+
 #ifdef HB_NO_AAT
 #define HB_NO_OT_NAME_LANGUAGE_AAT
 #define HB_NO_AAT_SHAPE
@@ -118,6 +136,10 @@
 #define HB_NO_SUBSET_CFF
 #endif
 
+#ifdef HB_NO_DRAW
+#define HB_NO_OUTLINE
+#endif
+
 #ifdef HB_NO_GETENV
 #define HB_NO_UNISCRIBE_BUG_COMPATIBLE
 #endif
@@ -150,6 +172,7 @@
 #define HB_NO_OT_SHAPER_HEBREW_FALLBACK
 #define HB_NO_OT_SHAPER_THAI_FALLBACK
 #define HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS
+#define HB_NO_OT_SHAPER_MYANMAR_ZAWGYI
 #endif
 
 #ifdef NDEBUG
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh b/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh
index adfbc3c11bf..eac4ff03ed5 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh
@@ -69,9 +69,9 @@ struct shared_ptr
   operator T * () const { return p; }
   T& operator * () const { return *get (); }
   T* operator -> () const { return get (); }
-  operator bool () { return p; }
-  bool operator == (const shared_ptr &o) { return p == o.p; }
-  bool operator != (const shared_ptr &o) { return p != o.p; }
+  operator bool () const { return p; }
+  bool operator == (const shared_ptr &o) const { return p == o.p; }
+  bool operator != (const shared_ptr &o) const { return p != o.p; }
 
   static T* get_empty() { return v::get_empty (); }
   T* reference() { return v::reference (p); }
@@ -130,7 +130,7 @@ template 
 struct vtable_t
 {
@@ -160,14 +160,43 @@ HB_DEFINE_VTABLE (map);
 HB_DEFINE_VTABLE (set);
 HB_DEFINE_VTABLE (shape_plan);
 HB_DEFINE_VTABLE (unicode_funcs);
+HB_DEFINE_VTABLE (draw_funcs);
+HB_DEFINE_VTABLE (paint_funcs);
 
 #undef HB_DEFINE_VTABLE
 
 
+#ifdef HB_SUBSET_H
+
+#define HB_DEFINE_VTABLE(name) \
+        template<> \
+        struct vtable \
+             : vtable_t {}
+
+
+HB_DEFINE_VTABLE (subset_input);
+HB_DEFINE_VTABLE (subset_plan);
+
+#undef HB_DEFINE_VTABLE
+
+#endif
+
+
 } // namespace hb
 
+/* Workaround for GCC < 7, see:
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
+ * https://stackoverflow.com/a/25594741 */
+namespace std {
+
+
 template
-struct std::hash>
+struct hash>
 {
     std::size_t operator()(const hb::shared_ptr& v) const noexcept
     {
@@ -177,7 +206,7 @@ struct std::hash>
 };
 
 template
-struct std::hash>
+struct hash>
 {
     std::size_t operator()(const hb::unique_ptr& v) const noexcept
     {
@@ -187,6 +216,8 @@ struct std::hash>
 };
 
 
+} // namespace std
+
 #endif /* __cplusplus */
 
 #endif /* HB_CPLUSPLUS_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh
index 09c407e415d..91a24a71521 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh
@@ -67,12 +67,12 @@ hb_options ()
 #endif
   /* Make a local copy, so we can access bitfield threadsafely. */
   hb_options_union_t u;
-  u.i = _hb_options.get_relaxed ();
+  u.i = _hb_options;
 
   if (unlikely (!u.i))
   {
     _hb_options_init ();
-    u.i = _hb_options.get_relaxed ();
+    u.i = _hb_options;
   }
 
   return u.opts;
@@ -113,7 +113,7 @@ _hb_print_func (const char *func)
     const char *paren = strchr (func, '(');
     if (paren)
       func_len = paren - func;
-    fprintf (stderr, "%.*s", func_len, func);
+    fprintf (stderr, "%.*s", (int) func_len, func);
   }
 }
 
@@ -142,9 +142,9 @@ _hb_debug_msg_va (const char *what,
   fprintf (stderr, "%-10s", what ? what : "");
 
   if (obj)
-    fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj);
+    fprintf (stderr, "(%*p) ", (int) (2 * sizeof (void *)), obj);
   else
-    fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
+    fprintf (stderr, " %*s  ", (int) (2 * sizeof (void *)), "");
 
   if (indented) {
 #define VBAR    "\342\224\202"  /* U+2502 BOX DRAWINGS LIGHT VERTICAL */
@@ -306,7 +306,7 @@ struct hb_auto_trace_t
     }
 
     _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1,
-                              "return %s (line %d)",
+                              "return %s (line %u)",
                               hb_printer_t>().print (v), line);
     if (plevel) --*plevel;
     plevel = nullptr;
@@ -373,6 +373,10 @@ struct hb_no_trace_t {
 #define HB_DEBUG_FT (HB_DEBUG+0)
 #endif
 
+#ifndef HB_DEBUG_JUSTIFY
+#define HB_DEBUG_JUSTIFY (HB_DEBUG+0)
+#endif
+
 #ifndef HB_DEBUG_OBJECT
 #define HB_DEBUG_OBJECT (HB_DEBUG+0)
 #endif
@@ -396,7 +400,7 @@ struct hb_no_trace_t {
 #define TRACE_APPLY(this) \
         hb_auto_trace_t trace \
         (&c->debug_depth, c->get_name (), this, HB_FUNC, \
-         "idx %d gid %u lookup %d", \
+         "idx %u gid %u lookup %d", \
          c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index)
 #else
 #define TRACE_APPLY(this) hb_no_trace_t trace
@@ -454,10 +458,15 @@ struct hb_no_trace_t {
 #define TRACE_DISPATCH(this, format) \
         hb_auto_trace_t trace \
         (&c->debug_depth, c->get_name (), this, HB_FUNC, \
-         "format %d", (int) format)
+         "format %u", (unsigned) format)
 #else
 #define TRACE_DISPATCH(this, format) hb_no_trace_t trace
 #endif
 
 
+#ifndef HB_BUFFER_MESSAGE_MORE
+#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1)
+#endif
+
+
 #endif /* HB_DEBUG_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h
index 55f1e1205df..a9e63de853d 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h
@@ -93,7 +93,7 @@ HB_BEGIN_DECLS
  * This method should retrieve the glyph ID for a specified Unicode code point
  * font, with an optional variation selector.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  * Deprecated: 1.2.3
  *
  **/
@@ -102,11 +102,22 @@ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data,
                                                hb_codepoint_t *glyph,
                                                void *user_data);
 
-HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void
+HB_DEPRECATED_FOR (hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func)
+HB_EXTERN void
 hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
                               hb_font_get_glyph_func_t func,
                               void *user_data, hb_destroy_func_t destroy);
 
+/* https://github.com/harfbuzz/harfbuzz/pull/4207 */
+/**
+ * HB_UNICODE_COMBINING_CLASS_CCC133:
+ *
+ * [Tibetan]
+ *
+ * Deprecated: 7.2.0
+ **/
+#define HB_UNICODE_COMBINING_CLASS_CCC133 133
+
 /**
  * hb_unicode_eastasian_width_func_t:
  * @ufuncs: A Unicode-functions structure
@@ -246,6 +257,7 @@ hb_font_get_glyph_v_kerning (hb_font_t *font,
 
 #endif
 
+
 HB_END_DECLS
 
 #endif /* HB_DEPRECATED_H */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc
index 6a9ab012409..f2821578b65 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc
@@ -35,6 +35,8 @@
  * @include: hb.h
  *
  * Functions for drawing (extracting) glyph shapes.
+ *
+ * The #hb_draw_funcs_t struct can be used with hb_font_draw_glyph().
  **/
 
 static void
@@ -80,6 +82,56 @@ hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UN
                         void *user_data HB_UNUSED) {}
 
 
+static bool
+_hb_draw_funcs_set_preamble (hb_draw_funcs_t    *dfuncs,
+                             bool                func_is_null,
+                             void              **user_data,
+                             hb_destroy_func_t  *destroy)
+{
+  if (hb_object_is_immutable (dfuncs))
+  {
+    if (*destroy)
+      (*destroy) (*user_data);
+    return false;
+  }
+
+  if (func_is_null)
+  {
+    if (*destroy)
+      (*destroy) (*user_data);
+    *destroy = nullptr;
+    *user_data = nullptr;
+  }
+
+  return true;
+}
+
+static bool
+_hb_draw_funcs_set_middle (hb_draw_funcs_t   *dfuncs,
+                           void              *user_data,
+                           hb_destroy_func_t  destroy)
+{
+  if (user_data && !dfuncs->user_data)
+  {
+    dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data));
+    if (unlikely (!dfuncs->user_data))
+      goto fail;
+  }
+  if (destroy && !dfuncs->destroy)
+  {
+    dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy));
+    if (unlikely (!dfuncs->destroy))
+      goto fail;
+  }
+
+  return true;
+
+fail:
+  if (destroy)
+    (destroy) (user_data);
+  return false;
+}
+
 #define HB_DRAW_FUNC_IMPLEMENT(name)                                            \
                                                                                 \
 void                                                                            \
@@ -88,42 +140,24 @@ hb_draw_funcs_set_##name##_func (hb_draw_funcs_t         *dfuncs,
                                  void                    *user_data,            \
                                  hb_destroy_func_t        destroy)              \
 {                                                                               \
-  if (hb_object_is_immutable (dfuncs))                                          \
-    return;                                                                     \
+  if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\
+      return;                                                            \
                                                                                 \
   if (dfuncs->destroy && dfuncs->destroy->name)                                 \
     dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \
                                                                          \
-  if (user_data && !dfuncs->user_data)                                   \
-  {                                                                      \
-    dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); \
-    if (unlikely (!dfuncs->user_data))                                   \
-      goto fail;                                                         \
-  }                                                                      \
-  if (destroy && !dfuncs->destroy)                                       \
-  {                                                                      \
-    dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); \
-    if (unlikely (!dfuncs->destroy))                                     \
-      goto fail;                                                         \
-  }                                                                      \
+  if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy))           \
+      return;                                                            \
                                                                         \
-  if (func) {                                                           \
+  if (func)                                                             \
     dfuncs->func.name = func;                                           \
-    if (dfuncs->user_data)                                              \
-      dfuncs->user_data->name = user_data;                              \
-    if (dfuncs->destroy)                                                \
-      dfuncs->destroy->name = destroy;                                  \
-  } else {                                                              \
+  else                                                                  \
     dfuncs->func.name = hb_draw_##name##_nil;                           \
-    if (dfuncs->user_data)                                              \
-      dfuncs->user_data->name = nullptr;                                \
-    if (dfuncs->destroy)                                                \
-      dfuncs->destroy->name = nullptr;                                  \
-  }                                                                     \
-                                                                         \
-fail:                                                                    \
-  if (destroy)                                                           \
-    destroy (user_data);                                                 \
+                                                                        \
+  if (dfuncs->user_data)                                                \
+    dfuncs->user_data->name = user_data;                                \
+  if (dfuncs->destroy)                                                  \
+    dfuncs->destroy->name = destroy;                                    \
 }
 
 HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
@@ -137,7 +171,7 @@ HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
  * Return value: (transfer full):
  * A newly allocated #hb_draw_funcs_t with a reference count of 1. The initial
  * reference count should be released with hb_draw_funcs_destroy when you are
- * done using the #hb_draw_funcs_t. This function never returns %NULL. If
+ * done using the #hb_draw_funcs_t. This function never returns `NULL`. If
  * memory cannot be allocated, a special singleton #hb_draw_funcs_t object will
  * be returned.
  *
@@ -166,13 +200,29 @@ DEFINE_NULL_INSTANCE (hb_draw_funcs_t) =
   }
 };
 
+/**
+ * hb_draw_funcs_get_empty:
+ *
+ * Fetches the singleton empty draw-functions structure.
+ *
+ * Return value: (transfer full): The empty draw-functions structure
+ *
+ * Since: 7.0.0
+ **/
+hb_draw_funcs_t *
+hb_draw_funcs_get_empty ()
+{
+  return const_cast (&Null (hb_draw_funcs_t));
+}
 
 /**
  * hb_draw_funcs_reference: (skip)
  * @dfuncs: draw functions
  *
- * Increases the reference count on @dfuncs by one. This prevents @buffer from
- * being destroyed until a matching call to hb_draw_funcs_destroy() is made.
+ * Increases the reference count on @dfuncs by one.
+ *
+ * This prevents @dfuncs from being destroyed until a matching
+ * call to hb_draw_funcs_destroy() is made.
  *
  * Return value: (transfer full):
  * The referenced #hb_draw_funcs_t.
@@ -208,9 +258,55 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs)
 #undef HB_DRAW_FUNC_IMPLEMENT
   }
 
+  hb_free (dfuncs->destroy);
+  hb_free (dfuncs->user_data);
+
   hb_free (dfuncs);
 }
 
+/**
+ * hb_draw_funcs_set_user_data: (skip)
+ * @dfuncs: The draw-functions structure
+ * @key: The user-data key
+ * @data: A pointer to the user data
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
+ * @replace: Whether to replace an existing data with the same key
+ *
+ * Attaches a user-data key/data pair to the specified draw-functions structure.
+ *
+ * Return value: `true` if success, `false` otherwise
+ *
+ * Since: 7.0.0
+ **/
+hb_bool_t
+hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs,
+                             hb_user_data_key_t *key,
+                             void *              data,
+                             hb_destroy_func_t   destroy,
+                             hb_bool_t           replace)
+{
+  return hb_object_set_user_data (dfuncs, key, data, destroy, replace);
+}
+
+/**
+ * hb_draw_funcs_get_user_data: (skip)
+ * @dfuncs: The draw-functions structure
+ * @key: The user-data key to query
+ *
+ * Fetches the user-data associated with the specified key,
+ * attached to the specified draw-functions structure.
+ *
+ * Return value: (transfer none): A pointer to the user data
+ *
+ * Since: 7.0.0
+ **/
+void *
+hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs,
+                             hb_user_data_key_t       *key)
+{
+  return hb_object_get_user_data (dfuncs, key);
+}
+
 /**
  * hb_draw_funcs_make_immutable:
  * @dfuncs: draw functions
@@ -234,7 +330,7 @@ hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs)
  *
  * Checks whether @dfuncs is immutable.
  *
- * Return value: %true if @dfuncs is immutable, %false otherwise
+ * Return value: `true` if @dfuncs is immutable, `false` otherwise
  *
  * Since: 4.0.0
  **/
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.h b/src/java.desktop/share/native/libharfbuzz/hb-draw.h
index 364db5f7f56..eba791d10c0 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-draw.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.h
@@ -92,11 +92,11 @@ typedef struct hb_draw_funcs_t hb_draw_funcs_t;
 /**
  * hb_draw_move_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_move_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "move-to" draw
  * operation.
@@ -112,11 +112,11 @@ typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data
 /**
  * hb_draw_line_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_line_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "line-to" draw
  * operation.
@@ -132,13 +132,13 @@ typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data
 /**
  * hb_draw_quadratic_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @control_x: X component of control point
  * @control_y: Y component of control point
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_quadratic_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "quadratic-to" draw
  * operation.
@@ -155,7 +155,7 @@ typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw
 /**
  * hb_draw_cubic_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @control1_x: X component of first control point
  * @control1_y: Y component of first control point
@@ -163,7 +163,7 @@ typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw
  * @control2_y: Y component of second control point
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_cubic_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "cubic-to" draw
  * operation.
@@ -181,9 +181,9 @@ typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_dat
 /**
  * hb_draw_close_path_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_close_path_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "close-path" draw
  * operation.
@@ -279,12 +279,27 @@ hb_draw_funcs_set_close_path_func (hb_draw_funcs_t           *dfuncs,
 HB_EXTERN hb_draw_funcs_t *
 hb_draw_funcs_create (void);
 
+HB_EXTERN hb_draw_funcs_t *
+hb_draw_funcs_get_empty (void);
+
 HB_EXTERN hb_draw_funcs_t *
 hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs);
 
 HB_EXTERN void
 hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs);
 
+HB_EXTERN hb_bool_t
+hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs,
+                             hb_user_data_key_t *key,
+                             void *              data,
+                             hb_destroy_func_t   destroy,
+                             hb_bool_t           replace);
+
+
+HB_EXTERN void *
+hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs,
+                             hb_user_data_key_t       *key);
+
 HB_EXTERN void
 hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs);
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc
new file mode 100644
index 00000000000..ff723ac7000
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc
@@ -0,0 +1,246 @@
+/*
+ * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 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
+ */
+
+#include "hb.hh"
+
+#include "hb-face.hh"
+
+#include "hb-map.hh"
+#include "hb-open-file.hh"
+#include "hb-serialize.hh"
+
+
+/*
+ * face-builder: A face that has add_table().
+ */
+
+struct face_table_info_t
+{
+  hb_blob_t* data;
+  signed order;
+};
+
+struct hb_face_builder_data_t
+{
+  hb_hashmap_t tables;
+};
+
+static int compare_entries (const void* pa, const void* pb)
+{
+  const auto& a = * (const hb_pair_t *) pa;
+  const auto& b = * (const hb_pair_t *) pb;
+
+  /* Order by blob size first (smallest to largest) and then table tag */
+
+  if (a.second.order != b.second.order)
+    return a.second.order < b.second.order ? -1 : +1;
+
+  if (a.second.data->length != b.second.data->length)
+    return a.second.data->length < b.second.data->length ? -1 : +1;
+
+  return a.first < b.first ? -1 : a.first == b.first ? 0 : +1;
+}
+
+static hb_face_builder_data_t *
+_hb_face_builder_data_create ()
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t));
+  if (unlikely (!data))
+    return nullptr;
+
+  data->tables.init ();
+
+  return data;
+}
+
+static void
+_hb_face_builder_data_destroy (void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  for (auto info : data->tables.values())
+    hb_blob_destroy (info.data);
+
+  data->tables.fini ();
+
+  hb_free (data);
+}
+
+static hb_blob_t *
+_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
+{
+
+  unsigned int table_count = data->tables.get_population ();
+  unsigned int face_length = table_count * 16 + 12;
+
+  for (auto info : data->tables.values())
+    face_length += hb_ceil_to_4 (hb_blob_get_length (info.data));
+
+  char *buf = (char *) hb_malloc (face_length);
+  if (unlikely (!buf))
+    return nullptr;
+
+  hb_serialize_context_t c (buf, face_length);
+  c.propagate_error (data->tables);
+  OT::OpenTypeFontFile *f = c.start_serialize ();
+
+  bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' '))
+                 || data->tables.has (HB_TAG ('C','F','F','2')));
+  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
+
+  // Sort the tags so that produced face is deterministic.
+  hb_vector_t> sorted_entries;
+  data->tables.iter () | hb_sink (sorted_entries);
+  if (unlikely (sorted_entries.in_error ()))
+  {
+    hb_free (buf);
+    return nullptr;
+  }
+
+  sorted_entries.qsort (compare_entries);
+
+  bool ret = f->serialize_single (&c,
+                                  sfnt_tag,
+                                  + sorted_entries.iter()
+                                  | hb_map ([&] (hb_pair_t _) {
+                                    return hb_pair_t (_.first, _.second.data);
+                                  }));
+
+  c.end_serialize ();
+
+  if (unlikely (!ret))
+  {
+    hb_free (buf);
+    return nullptr;
+  }
+
+  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free);
+}
+
+static hb_blob_t *
+_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  if (!tag)
+    return _hb_face_builder_data_reference_blob (data);
+
+  return hb_blob_reference (data->tables[tag].data);
+}
+
+
+/**
+ * hb_face_builder_create:
+ *
+ * Creates a #hb_face_t that can be used with hb_face_builder_add_table().
+ * After tables are added to the face, it can be compiled to a binary
+ * font file by calling hb_face_reference_blob().
+ *
+ * Return value: (transfer full): New face.
+ *
+ * Since: 1.9.0
+ **/
+hb_face_t *
+hb_face_builder_create ()
+{
+  hb_face_builder_data_t *data = _hb_face_builder_data_create ();
+  if (unlikely (!data)) return hb_face_get_empty ();
+
+  return hb_face_create_for_tables (_hb_face_builder_reference_table,
+                                    data,
+                                    _hb_face_builder_data_destroy);
+}
+
+/**
+ * hb_face_builder_add_table:
+ * @face: A face object created with hb_face_builder_create()
+ * @tag: The #hb_tag_t of the table to add
+ * @blob: The blob containing the table data to add
+ *
+ * Add table for @tag with data provided by @blob to the face.  @face must
+ * be created using hb_face_builder_create().
+ *
+ * Since: 1.9.0
+ **/
+hb_bool_t
+hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
+{
+  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
+    return false;
+
+  if (tag == HB_MAP_VALUE_INVALID)
+    return false;
+
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
+
+  hb_blob_t* previous = data->tables.get (tag).data;
+  if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1}))
+  {
+    hb_blob_destroy (blob);
+    return false;
+  }
+
+  hb_blob_destroy (previous);
+  return true;
+}
+
+/**
+ * hb_face_builder_sort_tables:
+ * @face: A face object created with hb_face_builder_create()
+ * @tags: (array zero-terminated=1): ordered list of table tags terminated by
+ *   %HB_TAG_NONE
+ *
+ * Set the ordering of tables for serialization. Any tables not
+ * specified in the tags list will be ordered after the tables in
+ * tags, ordered by the default sort ordering.
+ *
+ * Since: 5.3.0
+ **/
+void
+hb_face_builder_sort_tables (hb_face_t *face,
+                             const hb_tag_t  *tags)
+{
+  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
+    return;
+
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
+
+  // Sort all unspecified tables after any specified tables.
+  for (auto& info : data->tables.values_ref())
+    info.order = (unsigned) -1;
+
+  signed order = 0;
+  for (const hb_tag_t* tag = tags;
+       *tag;
+       tag++)
+  {
+    face_table_info_t* info;
+    if (!data->tables.has (*tag, &info)) continue;
+    info->order = order++;
+  }
+}
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc
index 43a32484701..bc19bb9d5c9 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc
@@ -33,7 +33,6 @@
 #include "hb-open-file.hh"
 #include "hb-ot-face.hh"
 #include "hb-ot-cmap-table.hh"
-#include "hb-map.hh"
 
 
 /**
@@ -48,6 +47,12 @@
  * More precisely, a font face represents a single face in a binary font file.
  * Font faces are typically built from a binary blob and a face index.
  * Font faces are used to create fonts.
+ *
+ * A font face can be created from a binary blob using hb_face_create().
+ * The face index is used to select a face from a binary blob that contains
+ * multiple faces.  For example, a binary blob that contains both a regular
+ * and a bold face can be used to create two font faces, one for each face
+ * index.
  **/
 
 
@@ -132,7 +137,7 @@ hb_face_create_for_tables (hb_reference_table_func_t  reference_table_func,
   face->user_data = user_data;
   face->destroy = destroy;
 
-  face->num_glyphs.set_relaxed (-1);
+  face->num_glyphs = -1;
 
   face->data.init0 (face);
   face->table.init0 (face);
@@ -198,7 +203,7 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
  * a face index into that blob.
  *
  * The face index is used for blobs of file formats such as TTC and
- * and DFont that can contain more than one face.  Face indices within
+ * DFont that can contain more than one face.  Face indices within
  * such collections are zero-based.
  *
  * Note: If the blob font format is not a collection, @index
@@ -288,6 +293,7 @@ hb_face_destroy (hb_face_t *face)
 {
   if (!hb_object_destroy (face)) return;
 
+#ifndef HB_NO_SHAPER
   for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
   {
     hb_face_t::plan_node_t *next = node->next;
@@ -295,6 +301,7 @@ hb_face_destroy (hb_face_t *face)
     hb_free (node);
     node = next;
   }
+#endif
 
   face->data.fini ();
   face->table.fini ();
@@ -315,7 +322,7 @@ hb_face_destroy (hb_face_t *face)
  *
  * Attaches a user-data key/data pair to the given face object.
  *
- * Return value: %true if success, %false otherwise
+ * Return value: `true` if success, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -342,7 +349,7 @@ hb_face_set_user_data (hb_face_t          *face,
  * Since: 0.9.2
  **/
 void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
                        hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (face, key);
@@ -371,7 +378,7 @@ hb_face_make_immutable (hb_face_t *face)
  *
  * Tests whether the given face object is immutable.
  *
- * Return value: %true is @face is immutable, %false otherwise
+ * Return value: `true` is @face is immutable, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -470,6 +477,8 @@ hb_face_get_index (const hb_face_t *face)
  *
  * Sets the units-per-em (upem) for a face object to the specified value.
  *
+ * This API is used in rare circumstances.
+ *
  * Since: 0.9.2
  **/
 void
@@ -479,14 +488,17 @@ hb_face_set_upem (hb_face_t    *face,
   if (hb_object_is_immutable (face))
     return;
 
-  face->upem.set_relaxed (upem);
+  face->upem = upem;
 }
 
 /**
  * hb_face_get_upem:
  * @face: A face object
  *
- * Fetches the units-per-em (upem) value of the specified face object.
+ * Fetches the units-per-em (UPEM) value of the specified face object.
+ *
+ * Typical UPEM values for fonts are 1000, or 2048, but any value
+ * in between 16 and 16,384 is allowed for OpenType fonts.
  *
  * Return value: The upem value of @face
  *
@@ -505,6 +517,8 @@ hb_face_get_upem (const hb_face_t *face)
  *
  * Sets the glyph count for a face object to the specified value.
  *
+ * This API is used in rare circumstances.
+ *
  * Since: 0.9.7
  **/
 void
@@ -514,7 +528,7 @@ hb_face_set_glyph_count (hb_face_t    *face,
   if (hb_object_is_immutable (face))
     return;
 
-  face->num_glyphs.set_relaxed (glyph_count);
+  face->num_glyphs = glyph_count;
 }
 
 /**
@@ -579,7 +593,7 @@ hb_face_get_table_tags (const hb_face_t *face,
 /**
  * hb_face_collect_unicodes:
  * @face: A face object
- * @out: The set to add Unicode characters to
+ * @out: (out): The set to add Unicode characters to
  *
  * Collects all of the Unicode characters covered by @face and adds
  * them to the #hb_set_t set @out.
@@ -592,10 +606,31 @@ hb_face_collect_unicodes (hb_face_t *face,
 {
   face->table.cmap->collect_unicodes (out, face->get_num_glyphs ());
 }
+/**
+ * hb_face_collect_nominal_glyph_mapping:
+ * @face: A face object
+ * @mapping: (out): The map to add Unicode-to-glyph mapping to
+ * @unicodes: (nullable) (out): The set to add Unicode characters to, or `NULL`
+ *
+ * Collects the mapping from Unicode characters to nominal glyphs of the @face,
+ * and optionally all of the Unicode characters covered by @face.
+ *
+ * Since: 7.0.0
+ */
+void
+hb_face_collect_nominal_glyph_mapping (hb_face_t *face,
+                                       hb_map_t  *mapping,
+                                       hb_set_t  *unicodes)
+{
+  hb_set_t stack_unicodes;
+  if (!unicodes)
+    unicodes = &stack_unicodes;
+  face->table.cmap->collect_mapping (unicodes, mapping, face->get_num_glyphs ());
+}
 /**
  * hb_face_collect_variation_selectors:
  * @face: A face object
- * @out: The set to add Variation Selector characters to
+ * @out: (out): The set to add Variation Selector characters to
  *
  * Collects all Unicode "Variation Selector" characters covered by @face and adds
  * them to the #hb_set_t set @out.
@@ -612,7 +647,7 @@ hb_face_collect_variation_selectors (hb_face_t *face,
  * hb_face_collect_variation_unicodes:
  * @face: A face object
  * @variation_selector: The Variation Selector to query
- * @out: The set to add Unicode characters to
+ * @out: (out): The set to add Unicode characters to
  *
  * Collects all Unicode characters for @variation_selector covered by @face and adds
  * them to the #hb_set_t set @out.
@@ -627,163 +662,3 @@ hb_face_collect_variation_unicodes (hb_face_t *face,
   face->table.cmap->collect_variation_unicodes (variation_selector, out);
 }
 #endif
-
-
-/*
- * face-builder: A face that has add_table().
- */
-
-struct hb_face_builder_data_t
-{
-  hb_hashmap_t tables;
-};
-
-static int compare_entries (const void* pa, const void* pb)
-{
-  const auto& a = * (const hb_pair_t *) pa;
-  const auto& b = * (const hb_pair_t *) pb;
-
-  /* Order by blob size first (smallest to largest) and then table tag */
-
-  if (a.second->length != b.second->length)
-    return a.second->length < b.second->length ? -1 : +1;
-
-  return a.first < b.first ? -1 : a.first == b.first ? 0 : +1;
-}
-
-static hb_face_builder_data_t *
-_hb_face_builder_data_create ()
-{
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t));
-  if (unlikely (!data))
-    return nullptr;
-
-  data->tables.init ();
-
-  return data;
-}
-
-static void
-_hb_face_builder_data_destroy (void *user_data)
-{
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
-
-  for (hb_blob_t* b : data->tables.values())
-    hb_blob_destroy (b);
-
-  data->tables.fini ();
-
-  hb_free (data);
-}
-
-static hb_blob_t *
-_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
-{
-
-  unsigned int table_count = data->tables.get_population ();
-  unsigned int face_length = table_count * 16 + 12;
-
-  for (hb_blob_t* b : data->tables.values())
-    face_length += hb_ceil_to_4 (hb_blob_get_length (b));
-
-  char *buf = (char *) hb_malloc (face_length);
-  if (unlikely (!buf))
-    return nullptr;
-
-  hb_serialize_context_t c (buf, face_length);
-  c.propagate_error (data->tables);
-  OT::OpenTypeFontFile *f = c.start_serialize ();
-
-  bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' '))
-                 || data->tables.has (HB_TAG ('C','F','F','2')));
-  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
-
-  // Sort the tags so that produced face is deterministic.
-  hb_vector_t> sorted_entries;
-  data->tables.iter () | hb_sink (sorted_entries);
-  if (unlikely (sorted_entries.in_error ()))
-  {
-    hb_free (buf);
-    return nullptr;
-  }
-
-  sorted_entries.qsort (compare_entries);
-  bool ret = f->serialize_single (&c, sfnt_tag, + sorted_entries.iter());
-
-  c.end_serialize ();
-
-  if (unlikely (!ret))
-  {
-    hb_free (buf);
-    return nullptr;
-  }
-
-  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free);
-}
-
-static hb_blob_t *
-_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
-{
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
-
-  if (!tag)
-    return _hb_face_builder_data_reference_blob (data);
-
-  return hb_blob_reference (data->tables[tag]);
-}
-
-
-/**
- * hb_face_builder_create:
- *
- * Creates a #hb_face_t that can be used with hb_face_builder_add_table().
- * After tables are added to the face, it can be compiled to a binary
- * font file by calling hb_face_reference_blob().
- *
- * Return value: (transfer full): New face.
- *
- * Since: 1.9.0
- **/
-hb_face_t *
-hb_face_builder_create ()
-{
-  hb_face_builder_data_t *data = _hb_face_builder_data_create ();
-  if (unlikely (!data)) return hb_face_get_empty ();
-
-  return hb_face_create_for_tables (_hb_face_builder_reference_table,
-                                    data,
-                                    _hb_face_builder_data_destroy);
-}
-
-/**
- * hb_face_builder_add_table:
- * @face: A face object created with hb_face_builder_create()
- * @tag: The #hb_tag_t of the table to add
- * @blob: The blob containing the table data to add
- *
- * Add table for @tag with data provided by @blob to the face.  @face must
- * be created using hb_face_builder_create().
- *
- * Since: 1.9.0
- **/
-hb_bool_t
-hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
-{
-  if (tag == HB_MAP_VALUE_INVALID)
-    return false;
-
-  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
-    return false;
-
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
-
-  hb_blob_t* previous = data->tables.get (tag);
-  if (!data->tables.set (tag, hb_blob_reference (blob)))
-  {
-    hb_blob_destroy (blob);
-    return false;
-  }
-
-  hb_blob_destroy (previous);
-  return true;
-}
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.h b/src/java.desktop/share/native/libharfbuzz/hb-face.h
index e74db2933b9..2e5fb150927 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-face.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-face.h
@@ -33,6 +33,7 @@
 
 #include "hb-common.h"
 #include "hb-blob.h"
+#include "hb-map.h"
 #include "hb-set.h"
 
 HB_BEGIN_DECLS
@@ -96,7 +97,7 @@ hb_face_set_user_data (hb_face_t          *face,
                        hb_bool_t           replace);
 
 HB_EXTERN void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
                        hb_user_data_key_t *key);
 
 HB_EXTERN void
@@ -149,6 +150,11 @@ HB_EXTERN void
 hb_face_collect_unicodes (hb_face_t *face,
                           hb_set_t  *out);
 
+HB_EXTERN void
+hb_face_collect_nominal_glyph_mapping (hb_face_t *face,
+                                       hb_map_t  *mapping,
+                                       hb_set_t  *unicodes);
+
 HB_EXTERN void
 hb_face_collect_variation_selectors (hb_face_t *face,
                                      hb_set_t  *out);
@@ -171,6 +177,10 @@ hb_face_builder_add_table (hb_face_t *face,
                            hb_tag_t   tag,
                            hb_blob_t *blob);
 
+HB_EXTERN void
+hb_face_builder_sort_tables (hb_face_t *face,
+                             const hb_tag_t  *tags);
+
 
 HB_END_DECLS
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-face.hh
index b4a74e9580f..4c6b252e88e 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-face.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-face.hh
@@ -65,7 +65,9 @@ struct hb_face_t
     hb_shape_plan_t *shape_plan;
     plan_node_t *next;
   };
+#ifndef HB_NO_SHAPER
   hb_atomic_ptr_t shape_plans;
+#endif
 
   hb_blob_t *reference_table (hb_tag_t tag) const
   {
@@ -74,7 +76,7 @@ struct hb_face_t
     if (unlikely (!reference_table_func))
       return hb_blob_get_empty ();
 
-    blob = reference_table_func (/*XXX*/const_cast (this), tag, user_data);
+    blob = reference_table_func (/*Oh, well.*/const_cast (this), tag, user_data);
     if (unlikely (!blob))
       return hb_blob_get_empty ();
 
@@ -83,7 +85,7 @@ struct hb_face_t
 
   unsigned int get_upem () const
   {
-    unsigned int ret = upem.get_relaxed ();
+    unsigned int ret = upem;
     if (unlikely (!ret))
     {
       return load_upem ();
@@ -93,7 +95,7 @@ struct hb_face_t
 
   unsigned int get_num_glyphs () const
   {
-    unsigned int ret = num_glyphs.get_relaxed ();
+    unsigned int ret = num_glyphs;
     if (unlikely (ret == UINT_MAX))
       return load_num_glyphs ();
     return ret;
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc
index cd1eaf5e21f..e37f90111eb 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc
@@ -75,16 +75,6 @@ _hb_fallback_shape (hb_shape_plan_t    *shape_plan HB_UNUSED,
                     const hb_feature_t *features HB_UNUSED,
                     unsigned int        num_features HB_UNUSED)
 {
-  /* TODO
-   *
-   * - Apply fallback kern.
-   * - Handle Variation Selectors?
-   * - Apply normalization?
-   *
-   * This will make the fallback shaper into a dumb "TrueType"
-   * shaper which many people unfortunately still request.
-   */
-
   hb_codepoint_t space;
   bool has_space = (bool) font->get_nominal_glyph (' ', &space);
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc
index b36b53613f8..5cfed3b0490 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc
@@ -30,6 +30,7 @@
 
 #include "hb-font.hh"
 #include "hb-draw.hh"
+#include "hb-paint.hh"
 #include "hb-machinery.hh"
 
 #include "hb-ot.h"
@@ -58,6 +59,11 @@
  *
  * HarfBuzz provides a built-in set of lightweight default
  * functions for each method in #hb_font_funcs_t.
+ *
+ * The default font functions are implemented in terms of the
+ * #hb_font_funcs_t methods of the parent font object.  This allows
+ * client programs to override only the methods they need to, and
+ * otherwise inherit the parent font's implementation, if any.
  **/
 
 
@@ -71,7 +77,7 @@ hb_font_get_font_h_extents_nil (hb_font_t         *font HB_UNUSED,
                                 hb_font_extents_t *extents,
                                 void              *user_data HB_UNUSED)
 {
-  memset (extents, 0, sizeof (*extents));
+  hb_memset (extents, 0, sizeof (*extents));
   return false;
 }
 
@@ -96,7 +102,7 @@ hb_font_get_font_v_extents_nil (hb_font_t         *font HB_UNUSED,
                                 hb_font_extents_t *extents,
                                 void              *user_data HB_UNUSED)
 {
-  memset (extents, 0, sizeof (*extents));
+  hb_memset (extents, 0, sizeof (*extents));
   return false;
 }
 
@@ -409,7 +415,7 @@ hb_font_get_glyph_extents_nil (hb_font_t          *font HB_UNUSED,
                                hb_glyph_extents_t *extents,
                                void               *user_data HB_UNUSED)
 {
-  memset (extents, 0, sizeof (*extents));
+  hb_memset (extents, 0, sizeof (*extents));
   return false;
 }
 
@@ -503,22 +509,34 @@ hb_font_get_glyph_from_name_default (hb_font_t      *font,
 }
 
 static void
-hb_font_get_glyph_shape_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)
+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 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)
+{
+}
 
-typedef struct hb_font_get_glyph_shape_default_adaptor_t {
+typedef struct hb_font_draw_glyph_default_adaptor_t {
   hb_draw_funcs_t *draw_funcs;
   void            *draw_data;
   float            x_scale;
   float            y_scale;
-} hb_font_get_glyph_shape_default_adaptor_t;
+  float            slant;
+} hb_font_draw_glyph_default_adaptor_t;
 
 static void
 hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED,
@@ -527,12 +545,13 @@ hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED,
                          float to_x, float to_y,
                          void *user_data HB_UNUSED)
 {
-  hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) 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;
 
   adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st,
-                                     x_scale * to_x, y_scale * to_y);
+                                     x_scale * to_x + slant * to_y, y_scale * to_y);
 }
 
 static void
@@ -541,15 +560,16 @@ hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data,
                          float to_x, float to_y,
                          void *user_data HB_UNUSED)
 {
-  hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) 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 *= x_scale;
-  st->current_y *= y_scale;
+  st->current_x = st->current_x * x_scale + st->current_y * slant;
+  st->current_y = st->current_y * y_scale;
 
   adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st,
-                                     x_scale * to_x, y_scale * to_y);
+                                     x_scale * to_x + slant * to_y, y_scale * to_y);
 }
 
 static void
@@ -559,16 +579,17 @@ hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data
                               float to_x, float to_y,
                               void *user_data HB_UNUSED)
 {
-  hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) 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 *= x_scale;
-  st->current_y *= y_scale;
+  st->current_x = st->current_x * x_scale + st->current_y * slant;
+  st->current_y = st->current_y * y_scale;
 
   adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st,
-                                          x_scale * control_x, y_scale * control_y,
-                                          x_scale * to_x, y_scale * to_y);
+                                          x_scale * control_x + slant * control_y, y_scale * control_y,
+                                          x_scale * to_x + slant * to_y, y_scale * to_y);
 }
 
 static void
@@ -579,17 +600,18 @@ hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data,
                           float to_x, float to_y,
                           void *user_data HB_UNUSED)
 {
-  hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) 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 *= x_scale;
-  st->current_y *= y_scale;
+  st->current_x = st->current_x * x_scale + st->current_y * slant;
+  st->current_y = st->current_y * y_scale;
 
   adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st,
-                                      x_scale * control1_x, y_scale * control1_y,
-                                      x_scale * control2_x, y_scale * control2_y,
-                                      x_scale * to_x, y_scale * to_y);
+                                      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);
 }
 
 static void
@@ -597,7 +619,7 @@ hb_draw_close_path_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data,
                             hb_draw_state_t *st,
                             void *user_data HB_UNUSED)
 {
-  hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data;
+  hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data;
 
   adaptor->draw_funcs->emit_close_path (adaptor->draw_data, *st);
 }
@@ -613,25 +635,50 @@ static const hb_draw_funcs_t _hb_draw_funcs_default = {
 };
 
 static void
-hb_font_get_glyph_shape_default (hb_font_t       *font,
+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)
 {
-  hb_font_get_glyph_shape_default_adaptor_t adaptor = {
+  hb_font_draw_glyph_default_adaptor_t adaptor = {
     draw_funcs,
     draw_data,
-    (float) font->x_scale / (float) font->parent->x_scale,
-    (float) font->y_scale / (float) font->parent->y_scale
+    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->get_glyph_shape (glyph,
+  font->parent->draw_glyph (glyph,
                                  const_cast (&_hb_draw_funcs_default),
                                  &adaptor);
 }
 
+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)
+{
+  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->paint_glyph (glyph, paint_funcs, paint_data, palette, foreground);
+
+  paint_funcs->pop_transform (paint_data);
+}
+
 DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
 {
   HB_OBJECT_HEADER_STATIC,
@@ -640,7 +687,7 @@ DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
   nullptr,
   {
     {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_nil,
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     }
@@ -654,7 +701,7 @@ static const hb_font_funcs_t _hb_font_funcs_default = {
   nullptr,
   {
     {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default,
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_default,
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     }
@@ -732,7 +779,7 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
 
   if (ffuncs->destroy)
   {
-#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy->name) \
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) if (ffuncs->destroy->name) \
     ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name);
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
@@ -754,7 +801,7 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
  *
  * Attaches a user-data key/data pair to the specified font-functions structure.
  *
- * Return value: %true if success, %false otherwise
+ * Return value: `true` if success, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -781,8 +828,8 @@ hb_font_funcs_set_user_data (hb_font_funcs_t    *ffuncs,
  * Since: 0.9.2
  **/
 void *
-hb_font_funcs_get_user_data (hb_font_funcs_t    *ffuncs,
-                             hb_user_data_key_t *key)
+hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs,
+                             hb_user_data_key_t    *key)
 {
   return hb_object_get_user_data (ffuncs, key);
 }
@@ -811,7 +858,7 @@ hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs)
  *
  * Tests whether a font-functions structure is immutable.
  *
- * Return value: %true if @ffuncs is immutable, %false otherwise
+ * Return value: `true` if @ffuncs is immutable, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -822,59 +869,82 @@ hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs)
 }
 
 
-#define HB_FONT_FUNC_IMPLEMENT(name) \
+static bool
+_hb_font_funcs_set_preamble (hb_font_funcs_t    *ffuncs,
+                             bool                func_is_null,
+                             void              **user_data,
+                             hb_destroy_func_t  *destroy)
+{
+  if (hb_object_is_immutable (ffuncs))
+  {
+    if (*destroy)
+      (*destroy) (*user_data);
+    return false;
+  }
+
+  if (func_is_null)
+  {
+    if (*destroy)
+      (*destroy) (*user_data);
+    *destroy = nullptr;
+    *user_data = nullptr;
+  }
+
+  return true;
+}
+
+static bool
+_hb_font_funcs_set_middle (hb_font_funcs_t   *ffuncs,
+                           void              *user_data,
+                           hb_destroy_func_t  destroy)
+{
+  if (user_data && !ffuncs->user_data)
+  {
+    ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data));
+    if (unlikely (!ffuncs->user_data))
+      goto fail;
+  }
+  if (destroy && !ffuncs->destroy)
+  {
+    ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy));
+    if (unlikely (!ffuncs->destroy))
+      goto fail;
+  }
+
+  return true;
+
+fail:
+  if (destroy)
+    (destroy) (user_data);
+  return false;
+}
+
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) \
                                                                          \
 void                                                                     \
 hb_font_funcs_set_##name##_func (hb_font_funcs_t             *ffuncs,    \
-                                 hb_font_get_##name##_func_t  func,      \
+                                 hb_font_##get_##name##_func_t func,     \
                                  void                        *user_data, \
                                  hb_destroy_func_t            destroy)   \
 {                                                                        \
-  if (hb_object_is_immutable (ffuncs))                                   \
-    goto fail;                                                           \
-                                                                         \
-  if (!func)                                                             \
-  {                                                                      \
-    if (destroy)                                                         \
-      destroy (user_data);                                               \
-    destroy = nullptr;                                                   \
-    user_data = nullptr;                                                 \
-  }                                                                      \
+  if (!_hb_font_funcs_set_preamble (ffuncs, !func, &user_data, &destroy))\
+      return;                                                            \
                                                                          \
   if (ffuncs->destroy && ffuncs->destroy->name)                          \
     ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); \
                                                                          \
-  if (user_data && !ffuncs->user_data)                                   \
-  {                                                                      \
-    ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); \
-    if (unlikely (!ffuncs->user_data))                                   \
-      goto fail;                                                         \
-  }                                                                      \
-  if (destroy && !ffuncs->destroy)                                       \
-  {                                                                      \
-    ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); \
-    if (unlikely (!ffuncs->destroy))                                     \
-      goto fail;                                                         \
-  }                                                                      \
+  if (!_hb_font_funcs_set_middle (ffuncs, user_data, destroy))           \
+      return;                                                            \
                                                                          \
-  if (func) {                                                            \
+  if (func)                                                              \
     ffuncs->get.f.name = func;                                           \
-    if (ffuncs->user_data)                                               \
-      ffuncs->user_data->name = user_data;                               \
-    if (ffuncs->destroy)                                                 \
-      ffuncs->destroy->name = destroy;                                   \
-  } else {                                                               \
-    ffuncs->get.f.name = hb_font_get_##name##_default;                   \
-    if (ffuncs->user_data)                                               \
-      ffuncs->user_data->name = nullptr;                                 \
-    if (ffuncs->destroy)                                                 \
-      ffuncs->destroy->name = nullptr;                                   \
-  }                                                                      \
-  return;                                                                \
+  else                                                                   \
+    ffuncs->get.f.name = hb_font_##get_##name##_default;                   \
                                                                          \
-fail:                                                                    \
-  if (destroy)                                                           \
-    destroy (user_data);                                                 \
+  if (ffuncs->user_data)                                                 \
+    ffuncs->user_data->name = user_data;                                 \
+  if (ffuncs->destroy)                                                   \
+    ffuncs->destroy->name = destroy;                                     \
 }
 
 HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
@@ -903,7 +973,7 @@ hb_font_t::has_func (unsigned int i)
  * Fetches the extents for a specified font, for horizontal
  * text segments.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 1.1.3
  **/
@@ -922,7 +992,7 @@ hb_font_get_h_extents (hb_font_t         *font,
  * Fetches the extents for a specified font, for vertical
  * text segments.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 1.1.3
  **/
@@ -946,7 +1016,7 @@ hb_font_get_v_extents (hb_font_t         *font,
  * If @variation_selector is 0, calls hb_font_get_nominal_glyph();
  * otherwise calls hb_font_get_variation_glyph().
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -974,7 +1044,7 @@ hb_font_get_glyph (hb_font_t      *font,
  * for code points modified by variation selectors. For variation-selector
  * support, user hb_font_get_variation_glyph() or use hb_font_get_glyph().
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 1.2.3
  **/
@@ -996,7 +1066,8 @@ hb_font_get_nominal_glyph (hb_font_t      *font,
  * @glyph_stride: The stride between successive glyph IDs
  *
  * Fetches the nominal glyph IDs for a sequence of Unicode code points. Glyph
- * IDs must be returned in a #hb_codepoint_t output parameter.
+ * IDs must be returned in a #hb_codepoint_t output parameter. Stopes at the
+ * first unsupported glyph ID.
  *
  * Return value: the number of code points processed
  *
@@ -1026,7 +1097,7 @@ hb_font_get_nominal_glyphs (hb_font_t *font,
  * by the specified variation-selector code point, in the specified
  * font.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 1.2.3
  **/
@@ -1136,7 +1207,7 @@ hb_font_get_glyph_v_advances (hb_font_t*            font,
  * Fetches the (X,Y) coordinates of the origin for a glyph ID
  * in the specified font, for horizontal text segments.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1159,7 +1230,7 @@ hb_font_get_glyph_h_origin (hb_font_t      *font,
  * Fetches the (X,Y) coordinates of the origin for a glyph ID
  * in the specified font, for vertical text segments.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1232,7 +1303,7 @@ hb_font_get_glyph_v_kerning (hb_font_t      *font,
  * Fetches the #hb_glyph_extents_t data for a glyph ID
  * in the specified font.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1255,7 +1326,7 @@ hb_font_get_glyph_extents (hb_font_t          *font,
  * Fetches the (x,y) coordinates of a specified contour-point index
  * in the specified glyph, within the specified font.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1278,7 +1349,10 @@ hb_font_get_glyph_contour_point (hb_font_t      *font,
  *
  * Fetches the glyph-name string for a glyph ID in the specified @font.
  *
- * Return value: %true if data found, %false otherwise
+ * According to the OpenType specification, glyph names are limited to 63
+ * characters and can only contain (a subset of) ASCII.
+ *
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1302,7 +1376,7 @@ hb_font_get_glyph_name (hb_font_t      *font,
  *
  * Note: @len == -1 means the name string is null-terminated.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1318,22 +1392,76 @@ hb_font_get_glyph_from_name (hb_font_t      *font,
 /**
  * hb_font_get_glyph_shape:
  * @font: #hb_font_t to work upon
- * @glyph: : The glyph ID
+ * @glyph: The glyph ID
  * @dfuncs: #hb_draw_funcs_t to draw to
  * @draw_data: User data to pass to draw callbacks
  *
  * Fetches the glyph shape that corresponds to a glyph in the specified @font.
- * The shape is returned by way of calls to the callsbacks of the @dfuncs
+ * The shape is returned by way of calls to the callbacks of the @dfuncs
  * objects, with @draw_data passed to them.
  *
  * Since: 4.0.0
- **/
+ * Deprecated: 7.0.0: Use hb_font_draw_glyph() instead
+ */
 void
 hb_font_get_glyph_shape (hb_font_t *font,
                          hb_codepoint_t glyph,
                          hb_draw_funcs_t *dfuncs, void *draw_data)
 {
-  font->get_glyph_shape (glyph, dfuncs, draw_data);
+  hb_font_draw_glyph (font, glyph, dfuncs, draw_data);
+}
+
+/**
+ * hb_font_draw_glyph:
+ * @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.
+ *
+ * The outline is returned by way of calls to the callbacks of the @dfuncs
+ * objects, with @draw_data passed to them.
+ *
+ * Since: 7.0.0
+ **/
+void
+hb_font_draw_glyph (hb_font_t *font,
+                         hb_codepoint_t glyph,
+                         hb_draw_funcs_t *dfuncs, void *draw_data)
+{
+  font->draw_glyph (glyph, dfuncs, draw_data);
+}
+
+/**
+ * hb_font_paint_glyph:
+ * @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 the 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.
+ *
+ * Since: 7.0.0
+ */
+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)
+{
+  font->paint_glyph (glyph, pfuncs, paint_data, palette_index, foreground);
 }
 
 /* A bit higher-level, and with fallback */
@@ -1537,7 +1665,7 @@ hb_font_get_glyph_kerning_for_direction (hb_font_t      *font,
  * Calls the appropriate direction-specific variant (horizontal
  * or vertical) depending on the value of @direction.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1566,7 +1694,7 @@ hb_font_get_glyph_extents_for_origin (hb_font_t          *font,
  * Calls the appropriate direction-specific variant (horizontal
  * or vertical) depending on the value of @direction.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1594,6 +1722,9 @@ hb_font_get_glyph_contour_point_for_origin (hb_font_t      *font,
  * If the glyph ID has no name in @font, a string of the form `gidDDD` is
  * generated, with `DDD` being the glyph ID.
  *
+ * According to the OpenType specification, glyph names are limited to 63
+ * characters and can only contain (a subset of) ASCII.
+ *
  * Since: 0.9.2
  **/
 void
@@ -1617,7 +1748,7 @@ hb_font_glyph_to_string (hb_font_t      *font,
  *
  * Note: @len == -1 means the string is null-terminated.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1647,8 +1778,13 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
 
   1000, /* x_scale */
   1000, /* y_scale */
-  0., /* slant */
-  0., /* slant_xy; */
+  0.f, /* x_embolden */
+  0.f, /* y_embolden */
+  true, /* embolden_in_place */
+  0, /* x_strength */
+  0, /* y_strength */
+  0.f, /* slant */
+  0.f, /* slant_xy; */
   1.f, /* x_multf */
   1.f, /* y_multf */
   1<<16, /* x_mult */
@@ -1658,6 +1794,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
   0, /* y_ppem */
   0, /* ptem */
 
+  HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */
   0, /* num_coords */
   nullptr, /* coords */
   nullptr, /* design_coords */
@@ -1675,6 +1812,7 @@ _hb_font_create (hb_face_t *face)
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
+
   if (!(font = hb_object_create ()))
     return hb_font_get_empty ();
 
@@ -1684,8 +1822,10 @@ _hb_font_create (hb_face_t *face)
   font->klass = hb_font_funcs_get_empty ();
   font->data.init0 (font);
   font->x_scale = font->y_scale = face->get_upem ();
+  font->embolden_in_place = true;
   font->x_multf = font->y_multf = 1.f;
   font->x_mult = font->y_mult = 1 << 16;
+  font->instance_index = HB_FONT_NO_VAR_NAMED_INSTANCE;
 
   return font;
 }
@@ -1767,6 +1907,9 @@ hb_font_create_sub_font (hb_font_t *parent)
 
   font->x_scale = parent->x_scale;
   font->y_scale = parent->y_scale;
+  font->x_embolden = parent->x_embolden;
+  font->y_embolden = parent->y_embolden;
+  font->embolden_in_place = parent->embolden_in_place;
   font->slant = parent->slant;
   font->x_ppem = parent->x_ppem;
   font->y_ppem = parent->y_ppem;
@@ -1779,8 +1922,8 @@ hb_font_create_sub_font (hb_font_t *parent)
     float *design_coords = (float *) hb_calloc (num_coords, sizeof (parent->design_coords[0]));
     if (likely (coords && design_coords))
     {
-      memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0]));
-      memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0]));
+      hb_memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0]));
+      hb_memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0]));
       _hb_font_adopt_var_coords (font, coords, design_coords, num_coords);
     }
     else
@@ -1866,7 +2009,7 @@ hb_font_destroy (hb_font_t *font)
  *
  * Attaches a user-data key/data pair to the specified font object.
  *
- * Return value: %true if success, %false otherwise
+ * Return value: `true` if success, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1896,7 +2039,7 @@ hb_font_set_user_data (hb_font_t          *font,
  * Since: 0.9.2
  **/
 void *
-hb_font_get_user_data (hb_font_t          *font,
+hb_font_get_user_data (const hb_font_t    *font,
                        hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (font, key);
@@ -1928,7 +2071,7 @@ hb_font_make_immutable (hb_font_t *font)
  *
  * Tests whether a font object is immutable.
  *
- * Return value: %true if @font is immutable, %false otherwise
+ * Return value: `true` if @font is immutable, `false` otherwise
  *
  * Since: 0.9.2
  **/
@@ -1948,7 +2091,7 @@ hb_font_is_immutable (hb_font_t *font)
  *
  * Return value: serial number
  *
- * Since: 4.4.0.
+ * Since: 4.4.0
  **/
 unsigned int
 hb_font_get_serial (hb_font_t *font)
@@ -1964,7 +2107,7 @@ hb_font_get_serial (hb_font_t *font)
  * This has the effect of increasing the serial as returned
  * by hb_font_get_serial(), which invalidates internal caches.
  *
- * Since: 4.4.0.
+ * Since: 4.4.0
  **/
 void
 hb_font_changed (hb_font_t *font)
@@ -2156,6 +2299,31 @@ hb_font_set_funcs_data (hb_font_t         *font,
  *
  * Sets the horizontal and vertical scale of a font.
  *
+ * The font scale is a number related to, but not the same as,
+ * font size. Typically the client establishes a scale factor
+ * to be used between the two. For example, 64, or 256, which
+ * would be the fractional-precision part of the font scale.
+ * This is necessary because #hb_position_t values are integer
+ * types and you need to leave room for fractional values
+ * in there.
+ *
+ * For example, to set the font size to 20, with 64
+ * levels of fractional precision you would call
+ * `hb_font_set_scale(font, 20 * 64, 20 * 64)`.
+ *
+ * In the example above, even what font size 20 means is up to
+ * you. It might be 20 pixels, or 20 points, or 20 millimeters.
+ * HarfBuzz does not care about that.  You can set the point
+ * size of the font using hb_font_set_ptem(), and the pixel
+ * size using hb_font_set_ppem().
+ *
+ * The choice of scale is yours but needs to be consistent between
+ * what you set here, and what you expect out of #hb_position_t
+ * as well has draw / paint API output values.
+ *
+ * Fonts default to a scale equal to the UPEM value of their face.
+ * A font with this setting is sometimes called an "unscaled" font.
+ *
  * Since: 0.9.2
  **/
 void
@@ -2201,7 +2369,11 @@ hb_font_get_scale (hb_font_t *font,
  * @x_ppem: Horizontal ppem value to assign
  * @y_ppem: Vertical ppem value to assign
  *
- * Sets the horizontal and vertical pixels-per-em (ppem) of a font.
+ * Sets the horizontal and vertical pixels-per-em (PPEM) of a font.
+ *
+ * These values are used for pixel-size-specific adjustment to
+ * shaping and draw results, though for the most part they are
+ * unused and can be left unset.
  *
  * Since: 0.9.2
  **/
@@ -2285,6 +2457,76 @@ hb_font_get_ptem (hb_font_t *font)
   return font->ptem;
 }
 
+/**
+ * hb_font_set_synthetic_bold:
+ * @font: #hb_font_t to work upon
+ * @x_embolden: the amount to embolden horizontally
+ * @y_embolden: the amount to embolden vertically
+ * @in_place: whether to embolden glyphs in-place
+ *
+ * Sets the "synthetic boldness" of a font.
+ *
+ * Positive values for @x_embolden / @y_embolden make a font
+ * bolder, negative values thinner. Typical values are in the
+ * 0.01 to 0.05 range. The default value is zero.
+ *
+ * Synthetic boldness is applied by offsetting the contour
+ * points of the glyph shape.
+ *
+ * Synthetic boldness is applied when rendering a glyph via
+ * hb_font_draw_glyph().
+ *
+ * If @in_place is `false`, then glyph advance-widths are also
+ * adjusted, otherwise they are not.  The in-place mode is
+ * useful for simulating [font grading](https://fonts.google.com/knowledge/glossary/grade).
+ *
+ *
+ * Since: 7.0.0
+ **/
+void
+hb_font_set_synthetic_bold (hb_font_t *font,
+                            float x_embolden,
+                            float y_embolden,
+                            hb_bool_t in_place)
+{
+  if (hb_object_is_immutable (font))
+    return;
+
+  if (font->x_embolden == x_embolden &&
+      font->y_embolden == y_embolden &&
+      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 ();
+}
+
+/**
+ * hb_font_get_synthetic_bold:
+ * @font: #hb_font_t to work upon
+ * @x_embolden: (out): return location for horizontal value
+ * @y_embolden: (out): return location for vertical value
+ * @in_place: (out): return location for in-place value
+ *
+ * Fetches the "synthetic boldness" parameters of a font.
+ *
+ * Since: 7.0.0
+ **/
+void
+hb_font_get_synthetic_bold (hb_font_t *font,
+                            float *x_embolden,
+                            float *y_embolden,
+                            hb_bool_t *in_place)
+{
+  if (x_embolden) *x_embolden = font->x_embolden;
+  if (y_embolden) *y_embolden = font->y_embolden;
+  if (in_place) *in_place = font->embolden_in_place;
+}
+
 /**
  * hb_font_set_synthetic_slant:
  * @font: #hb_font_t to work upon
@@ -2297,9 +2539,8 @@ hb_font_get_ptem (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_get_glyph_shape() is slanted to reflect this value
- * as well.
+ * Note: The glyph shape fetched via the hb_font_draw_glyph()
+ * function is slanted to reflect this value as well.
  *
  * Note: The slant value is a ratio.  For example, a
  * 20% slant would be represented as a 0.2 value.
@@ -2366,7 +2607,7 @@ hb_font_set_variations (hb_font_t            *font,
 
   font->serial_coords = ++font->serial;
 
-  if (!variations_length)
+  if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE)
   {
     hb_font_set_var_coords_normalized (font, nullptr, 0);
     return;
@@ -2386,20 +2627,104 @@ hb_font_set_variations (hb_font_t            *font,
     return;
   }
 
+  /* Initialize design coords. */
+  for (unsigned int i = 0; i < coords_length; i++)
+    design_coords[i] = axes[i].get_default ();
+  if (font->instance_index != HB_FONT_NO_VAR_NAMED_INSTANCE)
+  {
+    unsigned count = coords_length;
+    /* This may fail if index is out-of-range;
+     * That's why we initialize design_coords from fvar above
+     * unconditionally. */
+    hb_ot_var_named_instance_get_design_coords (font->face, font->instance_index,
+                                                &count, design_coords);
+  }
+
   for (unsigned int i = 0; i < variations_length; i++)
   {
     const auto tag = variations[i].tag;
     const auto v = variations[i].value;
     for (unsigned axis_index = 0; axis_index < coords_length; axis_index++)
       if (axes[axis_index].axisTag == tag)
-      {
         design_coords[axis_index] = v;
-        normalized[axis_index] = fvar.normalize_axis_value (axis_index, v);
-      }
   }
   font->face->table.avar->map_coords (normalized, coords_length);
 
+  hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
+  _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
+}
+
+/**
+ * hb_font_set_variation:
+ * @font: #hb_font_t to work upon
+ * @tag: The #hb_tag_t tag of the variation-axis name
+ * @value: The value of the variation axis
+ *
+ * Change the value of one variation axis on the font.
+ *
+ * Note: This function is expensive to be called repeatedly.
+ *   If you want to set multiple variation axes at the same time,
+ *   use hb_font_set_variations() instead.
+ *
+ * Since: 7.1.0
+ */
+void
+hb_font_set_variation (hb_font_t *font,
+                       hb_tag_t tag,
+                       float    value)
+{
+  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;
+  auto axes = fvar.get_axes ();
+  const unsigned coords_length = axes.length;
+
+  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;
+
+  if (unlikely (coords_length && !(normalized && design_coords)))
+  {
+    hb_free (normalized);
+    hb_free (design_coords);
+    return;
+  }
+
+  /* Initialize design coords. */
+  if (font->design_coords)
+  {
+    assert (coords_length == font->num_coords);
+    for (unsigned int i = 0; i < coords_length; i++)
+      design_coords[i] = font->design_coords[i];
+  }
+  else
+  {
+    for (unsigned int i = 0; i < coords_length; i++)
+      design_coords[i] = axes[i].get_default ();
+    if (font->instance_index != HB_FONT_NO_VAR_NAMED_INSTANCE)
+    {
+      unsigned count = coords_length;
+      /* This may fail if index is out-of-range;
+       * That's why we initialize design_coords from fvar above
+       * unconditionally. */
+      hb_ot_var_named_instance_get_design_coords (font->face, font->instance_index,
+                                                  &count, design_coords);
+    }
+  }
+
+  for (unsigned axis_index = 0; axis_index < coords_length; axis_index++)
+    if (axes[axis_index].axisTag == tag)
+      design_coords[axis_index] = value;
+
+  font->face->table.avar->map_coords (normalized, coords_length);
+
+  hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
   _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
+
 }
 
 /**
@@ -2438,7 +2763,7 @@ hb_font_set_var_coords_design (hb_font_t    *font,
   }
 
   if (coords_length)
-    memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0]));
+    hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0]));
 
   hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized);
   _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
@@ -2449,28 +2774,40 @@ hb_font_set_var_coords_design (hb_font_t    *font,
  * @font: a font.
  * @instance_index: named instance index.
  *
- * Sets design coords of a font from a named instance index.
+ * Sets design coords of a font from a named-instance index.
  *
  * Since: 2.6.0
  */
 void
 hb_font_set_var_named_instance (hb_font_t *font,
-                                unsigned instance_index)
+                                unsigned int instance_index)
 {
   if (hb_object_is_immutable (font))
     return;
 
-  font->serial_coords = ++font->serial;
+  if (font->instance_index == instance_index)
+    return;
 
-  unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr);
+  font->serial_coords = ++font->serial;
 
-  float *coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr;
-  if (unlikely (coords_length && !coords))
-    return;
+  font->instance_index = instance_index;
+  hb_font_set_variations (font, nullptr, 0);
+}
 
-  hb_ot_var_named_instance_get_design_coords (font->face, instance_index, &coords_length, coords);
-  hb_font_set_var_coords_design (font, coords, coords_length);
-  hb_free (coords);
+/**
+ * hb_font_get_var_named_instance:
+ * @font: a font.
+ *
+ * Returns the currently-set named-instance index of the font.
+ *
+ * Return value: Named-instance index or %HB_FONT_NO_VAR_NAMED_INSTANCE.
+ *
+ * Since: 7.0.0
+ **/
+unsigned int
+hb_font_get_var_named_instance (hb_font_t *font)
+{
+  return font->instance_index;
 }
 
 /**
@@ -2514,8 +2851,8 @@ hb_font_set_var_coords_normalized (hb_font_t    *font,
 
   if (coords_length)
   {
-    memcpy (copy, coords, coords_length * sizeof (coords[0]));
-    memcpy (unmapped, coords, coords_length * sizeof (coords[0]));
+    hb_memcpy (copy, coords, coords_length * sizeof (coords[0]));
+    hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0]));
   }
 
   /* Best effort design coords simulation */
@@ -2719,3 +3056,13 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t          *ffuncs,
                                           trampoline_destroy);
 }
 #endif
+
+
+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);
+}
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h
index a6aeeef4770..23301c13fc8 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-font.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h
@@ -34,18 +34,10 @@
 #include "hb-common.h"
 #include "hb-face.h"
 #include "hb-draw.h"
+#include "hb-paint.h"
 
 HB_BEGIN_DECLS
 
-/**
- * hb_font_t:
- *
- * Data type for holding fonts.
- *
- */
-typedef struct hb_font_t hb_font_t;
-
-
 /*
  * hb_font_funcs_t
  */
@@ -86,8 +78,8 @@ hb_font_funcs_set_user_data (hb_font_funcs_t    *ffuncs,
 
 
 HB_EXTERN void *
-hb_font_funcs_get_user_data (hb_font_funcs_t    *ffuncs,
-                             hb_user_data_key_t *key);
+hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs,
+                             hb_user_data_key_t    *key);
 
 
 HB_EXTERN void
@@ -97,7 +89,7 @@ HB_EXTERN hb_bool_t
 hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
 
 
-/* font and glyph extents */
+/* font extents */
 
 /**
  * hb_font_extents_t:
@@ -126,24 +118,6 @@ typedef struct hb_font_extents_t {
   hb_position_t reserved1;
 } hb_font_extents_t;
 
-/**
- * hb_glyph_extents_t:
- * @x_bearing: Distance from the x-origin to the left extremum of the glyph.
- * @y_bearing: Distance from the top extremum of the glyph to the y-origin.
- * @width: Distance from the left extremum of the glyph to the right extremum.
- * @height: Distance from the top extremum of the glyph to the bottom extremum.
- *
- * Glyph extent values, measured in font units.
- *
- * Note that @height is negative, in coordinate systems that grow up.
- **/
-typedef struct hb_glyph_extents_t {
-  hb_position_t x_bearing;
-  hb_position_t y_bearing;
-  hb_position_t width;
-  hb_position_t height;
-} hb_glyph_extents_t;
-
 /* func types */
 
 /**
@@ -198,7 +172,7 @@ typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t;
  * This method should retrieve the nominal glyph ID for a specified Unicode code
  * point. Glyph IDs must be returned in a #hb_codepoint_t output parameter.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  **/
 typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data,
@@ -221,7 +195,7 @@ typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *fo
  * followed by a specified Variation Selector code point. Glyph IDs must be
  * returned in a #hb_codepoint_t output parameter.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  **/
 typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data,
@@ -362,7 +336,7 @@ typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
  * origin for a glyph. Each coordinate must be returned in an #hb_position_t
  * output parameter.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  **/
 typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data,
@@ -434,7 +408,7 @@ typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
  * This method should retrieve the extents for a specified glyph. Extents must be
  * returned in an #hb_glyph_extents output parameter.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  **/
 typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data,
@@ -458,7 +432,7 @@ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *fo
  * specified contour point in a glyph. Each coordinate must be returned as
  * an #hb_position_t output parameter.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  **/
 typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data,
@@ -481,7 +455,7 @@ typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, vo
  * This method should retrieve the glyph name that corresponds to a
  * glyph ID. The name should be returned in a string output parameter.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  **/
 typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data,
@@ -503,7 +477,7 @@ typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_
  * This method should retrieve the glyph ID that corresponds to a glyph-name
  * string.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  **/
 typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data,
@@ -523,13 +497,53 @@ 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: 4.0.0
- *
+ * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead
  **/
 typedef void (*hb_font_get_glyph_shape_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_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
+ *
+ **/
+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
+ */
+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);
 
 /* func setters */
 
@@ -796,15 +810,50 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
  * @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_get_glyph_shape_func_t.
+ * Sets the implementation function for #hb_font_get_glyph_shape_func_t,
+ * which is the same as #hb_font_draw_glyph_func_t.
  *
  * Since: 4.0.0
+ * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead
  **/
 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_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,
+ * which is the same as #hb_font_get_glyph_shape_func_t.
+ *
+ * Since: 7.0.0
+ **/
+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
+ */
+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);
+
 /* func dispatch */
 
 HB_EXTERN hb_bool_t
@@ -890,6 +939,17 @@ hb_font_get_glyph_shape (hb_font_t *font,
                          hb_codepoint_t glyph,
                          hb_draw_funcs_t *dfuncs, void *draw_data);
 
+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 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);
 
 /* high-level funcs, with fallback */
 
@@ -993,7 +1053,7 @@ hb_font_set_user_data (hb_font_t          *font,
 
 
 HB_EXTERN void *
-hb_font_get_user_data (hb_font_t          *font,
+hb_font_get_user_data (const hb_font_t    *font,
                        hb_user_data_key_t *key);
 
 HB_EXTERN void
@@ -1069,6 +1129,16 @@ hb_font_set_ptem (hb_font_t *font, float ptem);
 HB_EXTERN float
 hb_font_get_ptem (hb_font_t *font);
 
+HB_EXTERN void
+hb_font_set_synthetic_bold (hb_font_t *font,
+                            float x_embolden, float y_embolden,
+                            hb_bool_t in_place);
+
+HB_EXTERN void
+hb_font_get_synthetic_bold (hb_font_t *font,
+                            float *x_embolden, float *y_embolden,
+                            hb_bool_t *in_place);
+
 HB_EXTERN void
 hb_font_set_synthetic_slant (hb_font_t *font, float slant);
 
@@ -1080,6 +1150,11 @@ hb_font_set_variations (hb_font_t *font,
                         const hb_variation_t *variations,
                         unsigned int variations_length);
 
+HB_EXTERN void
+hb_font_set_variation (hb_font_t *font,
+                       hb_tag_t tag,
+                       float    value);
+
 HB_EXTERN void
 hb_font_set_var_coords_design (hb_font_t *font,
                                const float *coords,
@@ -1098,10 +1173,23 @@ HB_EXTERN const int *
 hb_font_get_var_coords_normalized (hb_font_t *font,
                                    unsigned int *length);
 
+/**
+ * HB_FONT_NO_VAR_NAMED_INSTANCE:
+ *
+ * Constant signifying that a font does not have any
+ * named-instance index set.  This is the default of
+ * a font.
+ *
+ * Since: 7.0.0
+ */
+#define HB_FONT_NO_VAR_NAMED_INSTANCE 0xFFFFFFFF
+
 HB_EXTERN void
 hb_font_set_var_named_instance (hb_font_t *font,
-                                unsigned instance_index);
+                                unsigned int instance_index);
 
+HB_EXTERN unsigned int
+hb_font_get_var_named_instance (hb_font_t *font);
 
 HB_END_DECLS
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh
index 3af9d74012a..c3198830cd3 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh
@@ -40,24 +40,25 @@
  */
 
 #define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \
-  HB_FONT_FUNC_IMPLEMENT (font_h_extents) \
-  HB_FONT_FUNC_IMPLEMENT (font_v_extents) \
-  HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \
-  HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \
-  HB_FONT_FUNC_IMPLEMENT (variation_glyph) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \
-  HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_extents) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_name) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \
-  HB_FONT_FUNC_IMPLEMENT (glyph_shape) \
+  HB_FONT_FUNC_IMPLEMENT (get_,font_h_extents) \
+  HB_FONT_FUNC_IMPLEMENT (get_,font_v_extents) \
+  HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyph) \
+  HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyphs) \
+  HB_FONT_FUNC_IMPLEMENT (get_,variation_glyph) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advance) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advance) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advances) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \
+  HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \
+  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) \
   /* ^--- Add new callbacks here */
 
 struct hb_font_funcs_t
@@ -65,13 +66,13 @@ struct hb_font_funcs_t
   hb_object_header_t header;
 
   struct {
-#define HB_FONT_FUNC_IMPLEMENT(name) void *name;
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) void *name;
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   } *user_data;
 
   struct {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_destroy_func_t name;
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   } *destroy;
@@ -79,12 +80,12 @@ struct hb_font_funcs_t
   /* Don't access these directly.  Call font->get_*() instead. */
   union get_t {
     struct get_funcs_t {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name;
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_func_t name;
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     } f;
     void (*array[0
-#define HB_FONT_FUNC_IMPLEMENT(name) +1
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) +1
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
                 ]) ();
@@ -112,8 +113,16 @@ struct hb_font_t
 
   int32_t x_scale;
   int32_t y_scale;
+
+  float x_embolden;
+  float y_embolden;
+  bool embolden_in_place;
+  int32_t x_strength; /* x_embolden, in scaled units. */
+  int32_t y_strength; /* y_embolden, in scaled units. */
+
   float slant;
   float slant_xy;
+
   float x_multf;
   float y_multf;
   int64_t x_mult;
@@ -125,6 +134,7 @@ struct hb_font_t
   float ptem;
 
   /* Font variation coordinates. */
+  unsigned int instance_index;
   unsigned int num_coords;
   int *coords;
   float *design_coords;
@@ -179,6 +189,42 @@ struct hb_font_t
     *y = parent_scale_y_position (*y);
   }
 
+  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);
+
+    /* 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 (x_strength || y_strength)
+    {
+      /* Y */
+      int y_shift = y_strength;
+      if (y_scale < 0) y_shift = -y_shift;
+      extents->y_bearing += y_shift;
+      extents->height -= y_shift;
+
+      /* X */
+      int x_shift = x_strength;
+      if (x_scale < 0) x_shift = -x_shift;
+      if (embolden_in_place)
+        extents->x_bearing -= x_shift / 2;
+      extents->width += x_shift;
+    }
+  }
+
 
   /* Public getters */
 
@@ -186,7 +232,7 @@ struct hb_font_t
   HB_INTERNAL bool has_func_set (unsigned int i);
 
   /* has_* ... */
-#define HB_FONT_FUNC_IMPLEMENT(name) \
+#define HB_FONT_FUNC_IMPLEMENT(get_,name) \
   bool \
   has_##name##_func () \
   { \
@@ -206,14 +252,14 @@ struct hb_font_t
 
   hb_bool_t get_font_h_extents (hb_font_extents_t *extents)
   {
-    memset (extents, 0, sizeof (*extents));
+    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);
   }
   hb_bool_t get_font_v_extents (hb_font_extents_t *extents)
   {
-    memset (extents, 0, sizeof (*extents));
+    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);
@@ -342,7 +388,7 @@ struct hb_font_t
   hb_bool_t get_glyph_extents (hb_codepoint_t glyph,
                                hb_glyph_extents_t *extents)
   {
-    memset (extents, 0, sizeof (*extents));
+    hb_memset (extents, 0, sizeof (*extents));
     return klass->get.f.glyph_extents (this, user_data,
                                        glyph,
                                        extents,
@@ -380,15 +426,26 @@ struct hb_font_t
                                          !klass->user_data ? nullptr : klass->user_data->glyph_from_name);
   }
 
-  void get_glyph_shape (hb_codepoint_t glyph,
-                        hb_draw_funcs_t *draw_funcs, void *draw_data)
+  void draw_glyph (hb_codepoint_t glyph,
+                   hb_draw_funcs_t *draw_funcs, void *draw_data)
   {
-    klass->get.f.glyph_shape (this, user_data,
-                              glyph,
-                              draw_funcs, draw_data,
-                              !klass->user_data ? nullptr : klass->user_data->glyph_shape);
+    klass->get.f.draw_glyph (this, user_data,
+                             glyph,
+                             draw_funcs, draw_data,
+                             !klass->user_data ? nullptr : klass->user_data->draw_glyph);
   }
 
+  void paint_glyph (hb_codepoint_t glyph,
+                    hb_paint_funcs_t *paint_funcs, void *paint_data,
+                    unsigned int palette,
+                    hb_color_t foreground)
+  {
+    klass->get.f.paint_glyph (this, user_data,
+                              glyph,
+                              paint_funcs, paint_data,
+                              palette, foreground,
+                              !klass->user_data ? nullptr : klass->user_data->paint_glyph);
+  }
 
   /* A bit higher-level, and with fallback */
 
@@ -632,12 +689,17 @@ struct hb_font_t
   void mults_changed ()
   {
     float upem = face->get_upem ();
+
     x_multf = x_scale / upem;
     y_multf = y_scale / upem;
     bool x_neg = x_scale < 0;
     x_mult = (x_neg ? -((int64_t) -x_scale << 16) : ((int64_t) x_scale << 16)) / upem;
     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));
+
     slant_xy = y_scale ? slant * x_scale / y_scale : 0.f;
 
     data.fini ();
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc
index 291d1e8d70c..9b1fa8d53f2 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc
@@ -33,17 +33,22 @@
 
 #include "hb-ft.h"
 
+#include "hb-cache.hh"
 #include "hb-draw.hh"
 #include "hb-font.hh"
 #include "hb-machinery.hh"
-#include "hb-cache.hh"
 #include "hb-ot-os2-table.hh"
 #include "hb-ot-shaper-arabic-pua.hh"
+#include "hb-paint.hh"
 
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
 #include FT_OUTLINE_H
 #include FT_TRUETYPE_TABLES_H
+#include FT_SYNTHESIS_H
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
+#include FT_COLOR_H
+#endif
 
 
 /**
@@ -80,16 +85,19 @@
  */
 
 
+using hb_ft_advance_cache_t = hb_cache_t<16, 24, 8, false>;
+
 struct hb_ft_font_t
 {
   int load_flags;
   bool symbol; /* Whether selected cmap is symbol cmap. */
   bool unref; /* Whether to destroy ft_face when done. */
+  bool transform; /* Whether to apply FT_Face's transform. */
 
-  mutable hb_mutex_t lock;
+  mutable hb_mutex_t lock; /* Protects members below. */
   FT_Face ft_face;
   mutable unsigned cached_serial;
-  mutable hb_advance_cache_t advance_cache;
+  mutable hb_ft_advance_cache_t advance_cache;
 };
 
 static hb_ft_font_t *
@@ -122,8 +130,6 @@ _hb_ft_font_destroy (void *data)
 {
   hb_ft_font_t *ft_font = (hb_ft_font_t *) data;
 
-  ft_font->advance_cache.fini ();
-
   if (ft_font->unref)
     _hb_ft_face_destroy (ft_font->ft_face);
 
@@ -136,32 +142,59 @@ _hb_ft_font_destroy (void *data)
 /* hb_font changed, update FT_Face. */
 static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
 {
+  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
+
+  float x_mult = 1.f, y_mult = 1.f;
+
+  if (font->x_scale < 0) x_mult = -x_mult;
+  if (font->y_scale < 0) y_mult = -y_mult;
 
-  FT_Set_Char_Size (ft_face,
-                    abs (font->x_scale), abs (font->y_scale),
-                    0, 0);
+  if (FT_Set_Char_Size (ft_face,
+                        abs (font->x_scale), abs (font->y_scale),
+                        0, 0
 #if 0
                     font->x_ppem * 72 * 64 / font->x_scale,
-                    font->y_ppem * 72 * 64 / font->y_scale);
+                    font->y_ppem * 72 * 64 / font->y_scale
 #endif
-  if (font->x_scale < 0 || font->y_scale < 0)
+     ) && ft_face->num_fixed_sizes)
   {
-    FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
-                          0, font->y_scale < 0 ? -1 : +1};
+#ifdef HAVE_FT_GET_TRANSFORM
+    /* Bitmap font, eg. bitmap color emoji. */
+    /* Pick largest size? */
+    int x_scale  = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].x_ppem;
+    int y_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].y_ppem;
+    FT_Set_Char_Size (ft_face,
+                      x_scale, y_scale,
+                      0, 0);
+
+    /* This contains the sign that was previously in x_mult/y_mult. */
+    x_mult = (float) font->x_scale / x_scale;
+    y_mult = (float) font->y_scale / y_scale;
+#endif
+  }
+  else
+  { /* Shrug */ }
+
+
+  if (x_mult != 1.f || y_mult != 1.f)
+  {
+    FT_Matrix matrix = { (int) roundf (x_mult * (1<<16)), 0,
+                          0, (int) roundf (y_mult * (1<<16))};
     FT_Set_Transform (ft_face, &matrix, nullptr);
+    ft_font->transform = true;
   }
 
 #if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
   unsigned int num_coords;
-  const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
+  const float *coords = hb_font_get_var_coords_design (font, &num_coords);
   if (num_coords)
   {
     FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed));
     if (ft_coords)
     {
       for (unsigned int i = 0; i < num_coords; i++)
-        ft_coords[i] = coords[i] * 4;
-      FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
+          ft_coords[i] = coords[i] * 65536.f;
+      FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords);
       hb_free (ft_coords);
     }
   }
@@ -194,6 +227,9 @@ _hb_ft_hb_font_check_changed (hb_font_t *font,
  * For more information, see
  * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx
  *
+ * This function works with #hb_font_t objects created by
+ * hb_ft_font_create() or hb_ft_font_create_referenced().
+ *
  * Since: 1.0.5
  **/
 void
@@ -219,7 +255,10 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags)
  * For more information, see
  * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx
  *
- * Return value: FT_Load_Glyph flags found
+ * This function works with #hb_font_t objects created by
+ * hb_ft_font_create() or hb_ft_font_create_referenced().
+ *
+ * Return value: FT_Load_Glyph flags found, or 0
  *
  * Since: 1.0.5
  **/
@@ -241,7 +280,10 @@ hb_ft_font_get_load_flags (hb_font_t *font)
  * Fetches the FT_Face associated with the specified #hb_font_t
  * font object.
  *
- * Return value: (nullable): the FT_Face found or %NULL
+ * This function works with #hb_font_t objects created by
+ * hb_ft_font_create() or hb_ft_font_create_referenced().
+ *
+ * Return value: (nullable): the FT_Face found or `NULL`
  *
  * Since: 0.9.2
  **/
@@ -260,10 +302,15 @@ hb_ft_font_get_face (hb_font_t *font)
  * hb_ft_font_lock_face: (skip)
  * @font: #hb_font_t to work upon
  *
- * Gets the FT_Face associated with @font, This face will be kept around until
- * you call hb_ft_font_unlock_face().
+ * Gets the FT_Face associated with @font.
+ *
+ * This face will be kept around and access to the FT_Face object
+ * from other HarfBuzz API wil be blocked until you call hb_ft_font_unlock_face().
  *
- * Return value: (nullable): the FT_Face associated with @font or %NULL
+ * This function works with #hb_font_t objects created by
+ * hb_ft_font_create() or hb_ft_font_create_referenced().
+ *
+ * Return value: (nullable) (transfer none): the FT_Face associated with @font or `NULL`
  * Since: 2.6.5
  **/
 FT_Face
@@ -280,7 +327,7 @@ hb_ft_font_lock_face (hb_font_t *font)
 }
 
 /**
- * hb_ft_font_unlock_face:
+ * hb_ft_font_unlock_face: (skip)
  * @font: #hb_font_t to work upon
  *
  * Releases an FT_Face previously obtained with hb_ft_font_lock_face().
@@ -401,10 +448,24 @@ 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_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
   int load_flags = ft_font->load_flags;
-  int mult = font->x_scale < 0 ? -1 : +1;
+  float x_mult;
+#ifdef HAVE_FT_GET_TRANSFORM
+  if (ft_font->transform)
+  {
+    FT_Matrix matrix;
+    FT_Get_Transform (ft_face, &matrix, nullptr);
+    x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f;
+    x_mult *= font->x_scale < 0 ? -1 : +1;
+  }
+  else
+#endif
+  {
+    x_mult = font->x_scale < 0 ? -1 : +1;
+  }
 
   for (unsigned int i = 0; i < count; i++)
   {
@@ -417,13 +478,29 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
     else
     {
       FT_Get_Advance (ft_face, glyph, load_flags, &v);
+      /* Work around bug that FreeType seems to return negative advance
+       * for variable-set fonts if x_scale is negative! */
+      v = abs (v);
+      v = (int) (v * x_mult + (1<<9)) >> 10;
       ft_font->advance_cache.set (glyph, v);
     }
 
-    *first_advance = (v * mult + (1<<9)) >> 10;
+    *first_advance = v;
     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_VERTICAL
@@ -436,17 +513,31 @@ hb_ft_get_glyph_v_advance (hb_font_t *font,
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   hb_lock_t lock (ft_font->lock);
   FT_Fixed v;
+  float y_mult;
+#ifdef HAVE_FT_GET_TRANSFORM
+  if (ft_font->transform)
+  {
+    FT_Matrix matrix;
+    FT_Get_Transform (ft_font->ft_face, &matrix, nullptr);
+    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
+    y_mult *= font->y_scale < 0 ? -1 : +1;
+  }
+  else
+#endif
+  {
+    y_mult = font->y_scale < 0 ? -1 : +1;
+  }
 
   if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v)))
     return 0;
 
-  if (font->y_scale < 0)
-    v = -v;
+  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. */
 
-  return (-v + (1<<9)) >> 10;
+  hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
+  return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
 }
 #endif
 
@@ -462,6 +553,23 @@ hb_ft_get_glyph_v_origin (hb_font_t *font,
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
+  float x_mult, y_mult;
+#ifdef HAVE_FT_GET_TRANSFORM
+  if (ft_font->transform)
+  {
+    FT_Matrix matrix;
+    FT_Get_Transform (ft_face, &matrix, nullptr);
+    x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f;
+    x_mult *= font->x_scale < 0 ? -1 : +1;
+    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
+    y_mult *= font->y_scale < 0 ? -1 : +1;
+  }
+  else
+#endif
+  {
+    x_mult = font->x_scale < 0 ? -1 : +1;
+    y_mult = font->y_scale < 0 ? -1 : +1;
+  }
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
     return false;
@@ -471,10 +579,8 @@ hb_ft_get_glyph_v_origin (hb_font_t *font,
   *x = ft_face->glyph->metrics.horiBearingX -   ft_face->glyph->metrics.vertBearingX;
   *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY);
 
-  if (font->x_scale < 0)
-    *x = -*x;
-  if (font->y_scale < 0)
-    *y = -*y;
+  *x = (hb_position_t) (x_mult * *x);
+  *y = (hb_position_t) (y_mult * *y);
 
   return true;
 }
@@ -510,24 +616,63 @@ hb_ft_get_glyph_extents (hb_font_t *font,
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   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)
+  {
+    FT_Matrix matrix;
+    FT_Get_Transform (ft_face, &matrix, nullptr);
+    x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f;
+    x_mult *= font->x_scale < 0 ? -1 : +1;
+    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
+    y_mult *= font->y_scale < 0 ? -1 : +1;
+  }
+  else
+#endif
+  {
+    x_mult = font->x_scale < 0 ? -1 : +1;
+    y_mult = font->y_scale < 0 ? -1 : +1;
+  }
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
     return false;
 
-  extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
-  extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
-  extents->width = ft_face->glyph->metrics.width;
-  extents->height = -ft_face->glyph->metrics.height;
-  if (font->x_scale < 0)
+  /* Copied from hb_font_t::scale_glyph_extents. */
+
+  float x1 = x_mult * ft_face->glyph->metrics.horiBearingX;
+  float y1 = y_mult * ft_face->glyph->metrics.horiBearingY;
+  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)
   {
-    extents->x_bearing = -extents->x_bearing;
-    extents->width = -extents->width;
+    x1 += hb_min (y1 * slant_xy, y2 * slant_xy);
+    x2 += hb_max (y1 * slant_xy, y2 * slant_xy);
   }
-  if (font->y_scale < 0)
+
+  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)
   {
-    extents->y_bearing = -extents->y_bearing;
-    extents->height = -extents->height;
+    /* 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;
   }
+
   return true;
 }
 
@@ -620,16 +765,39 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED,
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
+  float y_mult;
+#ifdef HAVE_FT_GET_TRANSFORM
+  if (ft_font->transform)
+  {
+    FT_Matrix matrix;
+    FT_Get_Transform (ft_face, &matrix, nullptr);
+    y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f;
+    y_mult *= font->y_scale < 0 ? -1 : +1;
+  }
+  else
+#endif
+  {
+    y_mult = font->y_scale < 0 ? -1 : +1;
+  }
 
-  metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale);
-  metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale);
-  metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender);
-  if (font->y_scale < 0)
+  if (ft_face->units_per_EM != 0)
   {
-    metrics->ascender = -metrics->ascender;
-    metrics->descender = -metrics->descender;
-    metrics->line_gap = -metrics->line_gap;
+    metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale);
+    metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale);
+    metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender);
   }
+  else
+  {
+    /* Bitmap-only font, eg. color bitmap font. */
+    metrics->ascender = ft_face->size->metrics.ascender;
+    metrics->descender = ft_face->size->metrics.descender;
+    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->descender = (hb_position_t) (y_mult * metrics->descender);
+  metrics->line_gap  = (hb_position_t) (y_mult * metrics->line_gap);
+
   return true;
 }
 
@@ -637,16 +805,18 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED,
 
 static int
 _hb_ft_move_to (const FT_Vector *to,
-                hb_draw_session_t *drawing)
+                void *arg)
 {
+  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
   drawing->move_to (to->x, to->y);
   return FT_Err_Ok;
 }
 
 static int
 _hb_ft_line_to (const FT_Vector *to,
-                hb_draw_session_t *drawing)
+                void *arg)
 {
+  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
   drawing->line_to (to->x, to->y);
   return FT_Err_Ok;
 }
@@ -654,8 +824,9 @@ _hb_ft_line_to (const FT_Vector *to,
 static int
 _hb_ft_conic_to (const FT_Vector *control,
                  const FT_Vector *to,
-                 hb_draw_session_t *drawing)
+                 void *arg)
 {
+  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
   drawing->quadratic_to (control->x, control->y,
                          to->x, to->y);
   return FT_Err_Ok;
@@ -665,8 +836,9 @@ static int
 _hb_ft_cubic_to (const FT_Vector *control1,
                  const FT_Vector *control2,
                  const FT_Vector *to,
-                 hb_draw_session_t *drawing)
+                 void *arg)
 {
+  hb_draw_session_t *drawing = (hb_draw_session_t *) arg;
   drawing->cubic_to (control1->x, control1->y,
                      control2->x, control2->y,
                      to->x, to->y);
@@ -674,18 +846,16 @@ _hb_ft_cubic_to (const FT_Vector *control1,
 }
 
 static void
-hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED,
-                       void *font_data,
-                       hb_codepoint_t glyph,
-                       hb_draw_funcs_t *draw_funcs, void *draw_data,
-                       void *user_data HB_UNUSED)
+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)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
   hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
-  _hb_ft_hb_font_check_changed (font, ft_font);
-
   if (unlikely (FT_Load_Glyph (ft_face, glyph,
                                FT_LOAD_NO_BITMAP | ft_font->load_flags)))
     return;
@@ -694,22 +864,139 @@ hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED,
     return;
 
   const FT_Outline_Funcs outline_funcs = {
-    (FT_Outline_MoveToFunc) _hb_ft_move_to,
-    (FT_Outline_LineToFunc) _hb_ft_line_to,
-    (FT_Outline_ConicToFunc) _hb_ft_conic_to,
-    (FT_Outline_CubicToFunc) _hb_ft_cubic_to,
+    _hb_ft_move_to,
+    _hb_ft_line_to,
+    _hb_ft_conic_to,
+    _hb_ft_cubic_to,
     0, /* shift */
     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;
+      }
+    }
+  }
+
+
   FT_Outline_Decompose (&ft_face->glyph->outline,
                         &outline_funcs,
                         &draw_session);
 }
 #endif
 
+#ifndef HB_NO_PAINT
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
+
+#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)
+{
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
+  FT_Face ft_face = ft_font->ft_face;
+
+  /* We release the lock before calling into glyph callbacks, such that
+   * eg. draw API can call back into the face.*/
+
+  if (unlikely (FT_Load_Glyph (ft_face, gid,
+                               ft_font->load_flags | FT_LOAD_COLOR)))
+    return;
+
+  if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+  {
+    if (hb_ft_paint_glyph_colr (font, font_data, gid,
+                                paint_funcs, paint_data,
+                                palette_index, foreground,
+                                user_data))
+      return;
+
+    /* 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;
+  }
+
+  auto *glyph = ft_face->glyph;
+  if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
+  {
+    auto &bitmap = glyph->bitmap;
+    if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
+    {
+      if (bitmap.pitch != (signed) bitmap.width * 4)
+        return;
+
+      ft_font->lock.unlock ();
+
+      hb_blob_t *blob = hb_blob_create ((const char *) bitmap.buffer,
+                                        bitmap.pitch * bitmap.rows,
+                                        HB_MEMORY_MODE_DUPLICATE,
+                                        nullptr, nullptr);
+
+      hb_glyph_extents_t extents;
+      if (!hb_font_get_glyph_extents (font, gid, &extents))
+        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? */
+      }
+
+    out:
+      hb_blob_destroy (blob);
+      ft_font->lock.lock ();
+    }
+
+    return;
+  }
+}
+#endif
+#endif
+
 
 static inline void free_static_ft_funcs ();
 
@@ -743,7 +1030,13 @@ 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);
+#endif
 #endif
 
     hb_font_funcs_make_immutable (funcs);
@@ -818,6 +1111,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data
  *
  * Creates an #hb_face_t face object from the specified FT_Face.
  *
+ * Note that this is using the FT_Face object just to get at the underlying
+ * font data, and fonts created from the returned #hb_face_t will use the native
+ * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them.
+ *
  * This variant of the function does not provide any life-cycle management.
  *
  * Most client programs should use hb_ft_face_create_referenced()
@@ -862,6 +1159,10 @@ hb_ft_face_create (FT_Face           ft_face,
  *
  * Creates an #hb_face_t face object from the specified FT_Face.
  *
+ * Note that this is using the FT_Face object just to get at the underlying
+ * font data, and fonts created from the returned #hb_face_t will use the native
+ * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them.
+ *
  * This is the preferred variant of the hb_ft_face_create*
  * function family, because it calls FT_Reference_Face() on @ft_face,
  * ensuring that @ft_face remains alive as long as the resulting
@@ -882,8 +1183,9 @@ hb_ft_face_create_referenced (FT_Face ft_face)
 }
 
 static void
-hb_ft_face_finalize (FT_Face ft_face)
+hb_ft_face_finalize (void *arg)
 {
+  FT_Face ft_face = (FT_Face) arg;
   hb_face_destroy ((hb_face_t *) ft_face->generic.data);
 }
 
@@ -893,6 +1195,10 @@ hb_ft_face_finalize (FT_Face ft_face)
  *
  * Creates an #hb_face_t face object from the specified FT_Face.
  *
+ * Note that this is using the FT_Face object just to get at the underlying
+ * font data, and fonts created from the returned #hb_face_t will use the native
+ * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them.
+ *
  * This variant of the function caches the newly created #hb_face_t
  * face object, using the @generic pointer of @ft_face. Subsequent function
  * calls that are passed the same @ft_face parameter will have the same
@@ -915,7 +1221,7 @@ hb_ft_face_create_cached (FT_Face ft_face)
       ft_face->generic.finalizer (ft_face);
 
     ft_face->generic.data = hb_ft_face_create (ft_face, nullptr);
-    ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize;
+    ft_face->generic.finalizer = hb_ft_face_finalize;
   }
 
   return hb_face_reference ((hb_face_t *) ft_face->generic.data);
@@ -1028,6 +1334,9 @@ hb_ft_font_changed (hb_font_t *font)
 #endif
   }
 #endif
+
+  ft_font->advance_cache.clear ();
+  ft_font->cached_serial = font->serial;
 }
 
 /**
@@ -1121,8 +1430,9 @@ get_ft_library ()
 }
 
 static void
-_release_blob (FT_Face ft_face)
+_release_blob (void *arg)
 {
+  FT_Face ft_face = (FT_Face) arg;
   hb_blob_destroy ((hb_blob_t *) ft_face->generic.data);
 }
 
@@ -1139,10 +1449,14 @@ _release_blob (FT_Face ft_face)
  * created with hb_face_create(), and therefore was not
  * initially configured to use FreeType font functions.
  *
- * An #hb_face_t face object created with hb_ft_face_create()
+ * An #hb_font_t object created with hb_ft_font_create()
  * is preconfigured for FreeType font functions and does not
  * require this function to be used.
  *
+ * Note that if you modify the underlying #hb_font_t after
+ * calling this function, you need to call hb_ft_hb_font_changed()
+ * to update the underlying FT_Face.
+ *
  * Note: Internally, this function creates an FT_Face.
 * 
  *
@@ -1173,14 +1487,14 @@ hb_ft_font_set_funcs (hb_font_t *font)
   if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL))
     FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
 
-  _hb_ft_hb_font_changed (font, ft_face);
 
   ft_face->generic.data = blob;
-  ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
+  ft_face->generic.finalizer = _release_blob;
 
   _hb_ft_font_set_funcs (font, ft_face, true);
   hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
-}
 
+  _hb_ft_hb_font_changed (font, ft_face);
+}
 
 #endif
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh
index ff7a4c75895..bcd4eb8ebc4 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh
@@ -73,8 +73,10 @@ struct hb_iter_t
   /* Operators. */
   iter_t iter () const { return *thiz(); }
   iter_t operator + () const { return *thiz(); }
-  iter_t begin () const { return *thiz(); }
-  iter_t end () const { return thiz()->__end__ (); }
+  iter_t _begin () const { return *thiz(); }
+  iter_t begin () const { return _begin (); }
+  iter_t _end () const { return thiz()->__end__ (); }
+  iter_t end () const { return _end (); }
   explicit operator bool () const { return thiz()->__more__ (); }
   unsigned len () const { return thiz()->__len__ (); }
   /* The following can only be enabled if item_t is reference type.  Otherwise
@@ -118,7 +120,9 @@ struct hb_iter_t
 
 #define HB_ITER_USING(Name) \
   using item_t = typename Name::item_t; \
+  using Name::_begin; \
   using Name::begin; \
+  using Name::_end; \
   using Name::end; \
   using Name::get_item_size; \
   using Name::is_iterator; \
@@ -168,10 +172,16 @@ struct
 HB_FUNCOBJ (hb_iter);
 struct
 {
-  template  unsigned
-  operator () (T&& c) const
-  { return c.len (); }
+  template  auto
+  impl (T&& c, hb_priority<1>) const HB_RETURN (unsigned, c.len ())
+
+  template  auto
+  impl (T&& c, hb_priority<0>) const HB_RETURN (unsigned, c.len)
+
+  public:
 
+  template  auto
+  operator () (T&& c) const HB_RETURN (unsigned, impl (std::forward (c), hb_prioritize))
 }
 HB_FUNCOBJ (hb_len);
 
@@ -253,6 +263,8 @@ struct hb_is_iterator_of
 };
 #define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value
 #define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t)
+#define hb_is_sorted_iterator_of(Iter, Item) (hb_is_iterator_of::value && Iter::is_sorted_iterator)
+#define hb_is_sorted_iterator(Iter) hb_is_sorted_iterator_of (Iter, typename Iter::item_t)
 
 /* hb_is_iterable() */
 
@@ -375,7 +387,7 @@ struct hb_map_iter_t :
   void __forward__ (unsigned n) { it += n; }
   void __prev__ () { --it; }
   void __rewind__ (unsigned n) { it -= n; }
-  hb_map_iter_t __end__ () const { return hb_map_iter_t (it.end (), f); }
+  hb_map_iter_t __end__ () const { return hb_map_iter_t (it._end (), f); }
   bool operator != (const hb_map_iter_t& o) const
   { return it != o.it; }
 
@@ -438,7 +450,7 @@ struct hb_filter_iter_t :
   bool __more__ () const { return bool (it); }
   void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); }
   void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); }
-  hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it.end (), p, f); }
+  hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it._end (), p, f); }
   bool operator != (const hb_filter_iter_t& o) const
   { return it != o.it; }
 
@@ -551,7 +563,7 @@ struct hb_zip_iter_t :
   void __forward__ (unsigned n) { a += n; b += n; }
   void __prev__ () { --a; --b; }
   void __rewind__ (unsigned n) { a -= n; b -= n; }
-  hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); }
+  hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a._end (), b._end ()); }
   /* Note, we should stop if ANY of the iters reaches end.  As such two compare
    * unequal if both items are unequal, NOT if either is unequal. */
   bool operator != (const hb_zip_iter_t& o) const
@@ -635,7 +647,7 @@ struct hb_concat_iter_t :
     }
   }
 
-  hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a.end (), b.end ()); }
+  hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a._end (), b._end ()); }
   bool operator != (const hb_concat_iter_t& o) const
   {
     return a != o.a
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-limits.hh b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh
new file mode 100644
index 00000000000..0f60e9e2101
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh
@@ -0,0 +1,109 @@
+/*
+ * 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_LIMITS_HH
+#define HB_LIMITS_HH
+
+#include "hb.hh"
+
+
+#ifndef HB_BUFFER_MAX_LEN_FACTOR
+#define HB_BUFFER_MAX_LEN_FACTOR 64
+#endif
+#ifndef HB_BUFFER_MAX_LEN_MIN
+#define HB_BUFFER_MAX_LEN_MIN 16384
+#endif
+#ifndef HB_BUFFER_MAX_LEN_DEFAULT
+#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
+#endif
+
+#ifndef HB_BUFFER_MAX_OPS_FACTOR
+#define HB_BUFFER_MAX_OPS_FACTOR 1024
+#endif
+#ifndef HB_BUFFER_MAX_OPS_MIN
+#define HB_BUFFER_MAX_OPS_MIN 16384
+#endif
+#ifndef HB_BUFFER_MAX_OPS_DEFAULT
+#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */
+#endif
+
+
+#ifndef HB_MAX_NESTING_LEVEL
+#define HB_MAX_NESTING_LEVEL 64
+#endif
+
+
+#ifndef HB_MAX_CONTEXT_LENGTH
+#define HB_MAX_CONTEXT_LENGTH 64
+#endif
+
+#ifndef HB_CLOSURE_MAX_STAGES
+/*
+ * The maximum number of times a lookup can be applied during shaping.
+ * Used to limit the number of iterations of the closure algorithm.
+ * This must be larger than the number of times add_gsub_pause() is
+ * called in a collect_features call of any shaper.
+ */
+#define HB_CLOSURE_MAX_STAGES 12
+#endif
+
+#ifndef HB_MAX_SCRIPTS
+#define HB_MAX_SCRIPTS 500
+#endif
+
+#ifndef HB_MAX_LANGSYS
+#define HB_MAX_LANGSYS 2000
+#endif
+
+#ifndef HB_MAX_LANGSYS_FEATURE_COUNT
+#define HB_MAX_LANGSYS_FEATURE_COUNT 50000
+#endif
+
+#ifndef HB_MAX_FEATURE_INDICES
+#define HB_MAX_FEATURE_INDICES 1500
+#endif
+
+#ifndef HB_MAX_LOOKUP_VISIT_COUNT
+#define HB_MAX_LOOKUP_VISIT_COUNT 35000
+#endif
+
+
+#ifndef HB_GLYF_MAX_POINTS
+#define HB_GLYF_MAX_POINTS 20000
+#endif
+
+#ifndef HB_GLYF_MAX_EDGE_COUNT
+#define HB_GLYF_MAX_EDGE_COUNT 1024
+#endif
+
+#ifndef HB_CFF_MAX_OPS
+#define HB_CFF_MAX_OPS 10000
+#endif
+
+#ifndef HB_COLRV1_MAX_EDGE_COUNT
+#define HB_COLRV1_MAX_EDGE_COUNT 1024
+#endif
+
+
+#endif /* HB_LIMITS_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh
index 5938a25504a..3a048fc1242 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh
@@ -34,7 +34,6 @@
 
 #include "hb-dispatch.hh"
 #include "hb-sanitize.hh"
-#include "hb-serialize.hh"
 
 
 /*
@@ -136,6 +135,13 @@ static inline Type& StructAfter(TObject &X)
 
 /*
  * Lazy loaders.
+ *
+ * The lazy-loaders are thread-safe pointer-like objects that create their
+ * instead on-demand.  They also support access to a "data" object that is
+ * necessary for creating their instance.  The data object, if specified,
+ * is accessed via pointer math, located at a location before the position
+ * of the loader itself.  This avoids having to store a pointer to data
+ * for every lazy-loader.  Multiple lazy-loaders can access the same data.
  */
 
 template 
@@ -176,12 +182,12 @@ struct hb_lazy_loader_t : hb_data_wrapper_t
 
   void init0 () {} /* Init, when memory is already set to 0. No-op for us. */
   void init ()  { instance.set_relaxed (nullptr); }
-  void fini ()  { do_destroy (instance.get ()); init (); }
+  void fini ()  { do_destroy (instance.get_acquire ()); init (); }
 
   void free_instance ()
   {
   retry:
-    Stored *p = instance.get ();
+    Stored *p = instance.get_acquire ();
     if (unlikely (p && !cmpexch (p, nullptr)))
       goto retry;
     do_destroy (p);
@@ -203,7 +209,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t
   Stored * get_stored () const
   {
   retry:
-    Stored *p = this->instance.get ();
+    Stored *p = this->instance.get_acquire ();
     if (unlikely (!p))
     {
       if (unlikely (this->is_inert ()))
@@ -228,7 +234,8 @@ struct hb_lazy_loader_t : hb_data_wrapper_t
 
   bool cmpexch (Stored *current, Stored *value) const
   {
-    /* This *must* be called when there are no other threads accessing. */
+    /* This function can only be safely called directly if no
+     * other thread is accessing. */
     return this->instance.cmpexch (current, value);
   }
 
@@ -261,7 +268,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t
     hb_free (p);
   }
 
-//  private:
+  private:
   /* Must only have one pointer. */
   hb_atomic_ptr_t instance;
 };
@@ -283,7 +290,7 @@ struct hb_table_lazy_loader_t : hb_lazy_loader_t (face);
   }
   static void destroy (hb_blob_t *p) { hb_blob_destroy (p); }
@@ -297,22 +304,22 @@ struct hb_table_lazy_loader_t : hb_lazy_loader_tget_stored (); }
 };
 
-template 
-struct hb_font_funcs_lazy_loader_t : hb_lazy_loader_t
-{
-  static void destroy (hb_font_funcs_t *p)
-  { hb_font_funcs_destroy (p); }
-  static const hb_font_funcs_t *get_null ()
-  { return hb_font_funcs_get_empty (); }
-};
-template 
-struct hb_unicode_funcs_lazy_loader_t : hb_lazy_loader_t
-{
-  static void destroy (hb_unicode_funcs_t *p)
-  { hb_unicode_funcs_destroy (p); }
-  static const hb_unicode_funcs_t *get_null ()
-  { return hb_unicode_funcs_get_empty (); }
-};
+#define HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T(Type) \
+  template  \
+  struct hb_##Type##_funcs_lazy_loader_t : hb_lazy_loader_t \
+  { \
+    static void destroy (hb_##Type##_funcs_t *p) \
+    { hb_##Type##_funcs_destroy (p); } \
+    static const hb_##Type##_funcs_t *get_null () \
+    { return hb_##Type##_funcs_get_empty (); } \
+  }
+
+HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (font);
+HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (unicode);
+HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (draw);
+HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (paint);
+
+#undef HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T
 
 
 #endif /* HB_MACHINERY_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-map.cc
index af449bea411..48913a6a10d 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-map.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-map.cc
@@ -56,8 +56,6 @@ hb_map_create ()
   if (!(map = hb_object_create ()))
     return hb_map_get_empty ();
 
-  map->init_shallow ();
-
   return map;
 }
 
@@ -107,8 +105,6 @@ hb_map_destroy (hb_map_t *map)
 {
   if (!hb_object_destroy (map)) return;
 
-  map->fini_shallow ();
-
   hb_free (map);
 }
 
@@ -122,7 +118,7 @@ hb_map_destroy (hb_map_t *map)
  *
  * Attaches a user-data key/data pair to the specified map.
  *
- * Return value: %true if success, %false otherwise
+ * Return value: `true` if success, `false` otherwise
  *
  * Since: 1.7.7
  **/
@@ -149,7 +145,7 @@ hb_map_set_user_data (hb_map_t           *map,
  * Since: 1.7.7
  **/
 void *
-hb_map_get_user_data (hb_map_t           *map,
+hb_map_get_user_data (const hb_map_t     *map,
                       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (map, key);
@@ -162,7 +158,7 @@ hb_map_get_user_data (hb_map_t           *map,
  *
  * Tests whether memory allocation for a set was successful.
  *
- * Return value: %true if allocation succeeded, %false otherwise
+ * Return value: `true` if allocation succeeded, `false` otherwise
  *
  * Since: 1.7.7
  **/
@@ -178,7 +174,7 @@ hb_map_allocation_successful (const hb_map_t  *map)
  *
  * Allocate a copy of @map.
  *
- * Return value: Newly-allocated map.
+ * Return value: (transfer full): Newly-allocated map.
  *
  * Since: 4.4.0
  **/
@@ -186,9 +182,10 @@ hb_map_t *
 hb_map_copy (const hb_map_t *map)
 {
   hb_map_t *copy = hb_map_create ();
-  if (unlikely (!copy)) return nullptr;
-  copy->resize (map->population);
-  hb_copy (*map, *copy);
+  if (unlikely (copy->in_error ()))
+    return hb_map_get_empty ();
+
+  *copy = *map;
   return copy;
 }
 
@@ -251,7 +248,7 @@ hb_map_del (hb_map_t       *map,
  *
  * Tests whether @key is an element of @map.
  *
- * Return value: %true if @key is found in @map, %false otherwise
+ * Return value: `true` if @key is found in @map, `false` otherwise
  *
  * Since: 1.7.7
  **/
@@ -283,7 +280,7 @@ hb_map_clear (hb_map_t *map)
  *
  * Tests whether @map is empty (contains no elements).
  *
- * Return value: %true if @map is empty
+ * Return value: `true` if @map is empty
  *
  * Since: 1.7.7
  **/
@@ -317,7 +314,7 @@ hb_map_get_population (const hb_map_t *map)
  * Tests whether @map and @other are equal (contain the same
  * elements).
  *
- * Return value: %true if the two maps are equal, %false otherwise.
+ * Return value: `true` if the two maps are equal, `false` otherwise.
  *
  * Since: 4.3.0
  **/
@@ -339,9 +336,84 @@ hb_map_is_equal (const hb_map_t *map,
  *
  * Since: 4.4.0
  **/
-HB_EXTERN unsigned int
+unsigned int
 hb_map_hash (const hb_map_t *map)
 {
   return map->hash ();
 }
 
+/**
+ * hb_map_update:
+ * @map: A map
+ * @other: Another map
+ *
+ * Add the contents of @other to @map.
+ *
+ * Since: 7.0.0
+ **/
+HB_EXTERN void
+hb_map_update (hb_map_t *map,
+               const hb_map_t *other)
+{
+  map->update (*other);
+}
+
+/**
+ * hb_map_next:
+ * @map: A map
+ * @idx: (inout): Iterator internal state
+ * @key: (out): Key retrieved
+ * @value: (out): Value retrieved
+ *
+ * Fetches the next key/value paire in @map.
+ *
+ * Set @idx to -1 to get started.
+ *
+ * If the map is modified during iteration, the behavior is undefined.
+ *
+ * The order in which the key/values are returned is undefined.
+ *
+ * Return value: `true` if there was a next value, `false` otherwise
+ *
+ * Since: 7.0.0
+ **/
+hb_bool_t
+hb_map_next (const hb_map_t *map,
+             int *idx,
+             hb_codepoint_t *key,
+             hb_codepoint_t *value)
+{
+  return map->next (idx, key, value);
+}
+
+/**
+ * hb_map_keys:
+ * @map: A map
+ * @keys: A set
+ *
+ * Add the keys of @map to @keys.
+ *
+ * Since: 7.0.0
+ **/
+void
+hb_map_keys (const hb_map_t *map,
+             hb_set_t *keys)
+{
+  hb_copy (map->keys() , *keys);
+}
+
+/**
+ * hb_map_values:
+ * @map: A map
+ * @values: A set
+ *
+ * Add the values of @map to @values.
+ *
+ * Since: 7.0.0
+ **/
+void
+hb_map_values (const hb_map_t *map,
+               hb_set_t *values)
+{
+  hb_copy (map->values() , *values);
+}
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.h b/src/java.desktop/share/native/libharfbuzz/hb-map.h
index 27c19c2b589..12d8970f48f 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-map.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-map.h
@@ -32,6 +32,7 @@
 #define HB_MAP_H
 
 #include "hb-common.h"
+#include "hb-set.h"
 
 HB_BEGIN_DECLS
 
@@ -74,7 +75,7 @@ hb_map_set_user_data (hb_map_t           *map,
                       hb_bool_t           replace);
 
 HB_EXTERN void *
-hb_map_get_user_data (hb_map_t           *map,
+hb_map_get_user_data (const hb_map_t     *map,
                       hb_user_data_key_t *key);
 
 
@@ -118,6 +119,24 @@ HB_EXTERN hb_bool_t
 hb_map_has (const hb_map_t *map,
             hb_codepoint_t  key);
 
+HB_EXTERN void
+hb_map_update (hb_map_t *map,
+               const hb_map_t *other);
+
+/* Pass -1 in for idx to get started. */
+HB_EXTERN hb_bool_t
+hb_map_next (const hb_map_t *map,
+             int *idx,
+             hb_codepoint_t *key,
+             hb_codepoint_t *value);
+
+HB_EXTERN void
+hb_map_keys (const hb_map_t *map,
+             hb_set_t *keys);
+
+HB_EXTERN void
+hb_map_values (const hb_map_t *map,
+               hb_set_t *values);
 
 HB_END_DECLS
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-map.hh
index ff5a476d7a2..3b24dc9455a 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-map.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-map.hh
@@ -29,6 +29,8 @@
 
 #include "hb.hh"
 
+#include "hb-set.hh"
+
 
 /*
  * hb_hashmap_t
@@ -43,9 +45,9 @@ struct hb_hashmap_t
   hb_hashmap_t ()  { init (); }
   ~hb_hashmap_t () { fini (); }
 
-  hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (population); hb_copy (o, *this); }
+  hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (o.population); hb_copy (o, *this); }
   hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); }
-  hb_hashmap_t& operator= (const hb_hashmap_t& o)  { resize (population); hb_copy (o, *this); return *this; }
+  hb_hashmap_t& operator= (const hb_hashmap_t& o)  { reset (); resize (o.population); hb_copy (o, *this); return *this; }
   hb_hashmap_t& operator= (hb_hashmap_t&& o)  { hb_swap (*this, o); return *this; }
 
   hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t ()
@@ -71,6 +73,11 @@ struct hb_hashmap_t
     uint32_t is_tombstone_ : 1;
     V value;
 
+    item_t () : key (),
+                hash (0),
+                is_used_ (false), is_tombstone_ (false),
+                value () {}
+
     bool is_used () const { return is_used_; }
     void set_used (bool is_used) { is_used_ = is_used; }
     bool is_tombstone () const { return is_tombstone_; }
@@ -88,17 +95,8 @@ struct hb_hashmap_t
       return minus_1;
     };
 
-    void clear ()
-    {
-      new (std::addressof (key)) K ();
-      new (std::addressof (value)) V ();
-      hash = 0;
-      is_used_ = false;
-      is_tombstone_ = false;
-    }
-
-    bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); }
-    bool operator == (const item_t &o) { return *this == o.key; }
+    bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); }
+    bool operator == (const item_t &o) const { return *this == o.key; }
     hb_pair_t get_pair() const { return hb_pair_t (key, value); }
     hb_pair_t get_pair_ref() const { return hb_pair_t (key, value); }
 
@@ -107,8 +105,8 @@ struct hb_hashmap_t
   };
 
   hb_object_header_t header;
-  bool successful; /* Allocations successful */
-  unsigned int population; /* Not including tombstones. */
+  unsigned int successful : 1; /* Allocations successful */
+  unsigned int population : 31; /* Not including tombstones. */
   unsigned int occupancy; /* Including tombstones. */
   unsigned int mask;
   unsigned int prime;
@@ -118,27 +116,29 @@ struct hb_hashmap_t
   {
     if (unlikely (!a.successful || !b.successful))
       return;
-    hb_swap (a.population, b.population);
+    unsigned tmp = a.population;
+    a.population = b.population;
+    b.population = tmp;
+    //hb_swap (a.population, b.population);
     hb_swap (a.occupancy, b.occupancy);
     hb_swap (a.mask, b.mask);
     hb_swap (a.prime, b.prime);
     hb_swap (a.items, b.items);
   }
-  void init_shallow ()
+  void init ()
   {
+    hb_object_init (this);
+
     successful = true;
     population = occupancy = 0;
     mask = 0;
     prime = 0;
     items = nullptr;
   }
-  void init ()
-  {
-    hb_object_init (this);
-    init_shallow ();
-  }
-  void fini_shallow ()
+  void fini ()
   {
+    hb_object_fini (this);
+
     if (likely (items)) {
       unsigned size = mask + 1;
       for (unsigned i = 0; i < size; i++)
@@ -148,11 +148,6 @@ struct hb_hashmap_t
     }
     population = occupancy = 0;
   }
-  void fini ()
-  {
-    hb_object_fini (this);
-    fini_shallow ();
-  }
 
   void reset ()
   {
@@ -166,7 +161,9 @@ struct hb_hashmap_t
   {
     if (unlikely (!successful)) return false;
 
-    unsigned int power = hb_bit_storage (hb_max (population, new_population) * 2 + 8);
+    if (new_population != 0 && (new_population + new_population / 2) < mask) return true;
+
+    unsigned int power = hb_bit_storage (hb_max ((unsigned) population, new_population) * 2 + 8);
     unsigned int new_size = 1u << power;
     item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t));
     if (unlikely (!new_items))
@@ -175,9 +172,9 @@ struct hb_hashmap_t
       return false;
     }
     for (auto &_ : hb_iter (new_items, new_size))
-      _.clear ();
+      new (&_) item_t ();
 
-    unsigned int old_size = mask + 1;
+    unsigned int old_size = size ();
     item_t *old_items = items;
 
     /* Switch to new, empty, array. */
@@ -187,67 +184,102 @@ struct hb_hashmap_t
     items = new_items;
 
     /* Insert back old items. */
-    if (old_items)
-      for (unsigned int i = 0; i < old_size; i++)
+    for (unsigned int i = 0; i < old_size; i++)
+    {
+      if (old_items[i].is_real ())
       {
-        if (old_items[i].is_real ())
-        {
-          set_with_hash (old_items[i].key,
-                         old_items[i].hash,
-                         std::move (old_items[i].value));
-        }
-        old_items[i].~item_t ();
+        set_with_hash (std::move (old_items[i].key),
+                       old_items[i].hash,
+                       std::move (old_items[i].value));
       }
+      old_items[i].~item_t ();
+    }
 
     hb_free (old_items);
 
     return true;
   }
 
+  template 
+  bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool is_delete=false)
+  {
+    if (unlikely (!successful)) return false;
+    if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false;
+    item_t &item = item_for_hash (key, hash);
+
+    if (is_delete && !(item == key))
+      return true; /* Trying to delete non-existent key. */
+
+    if (item.is_used ())
+    {
+      occupancy--;
+      if (!item.is_tombstone ())
+        population--;
+    }
+
+    item.key = std::forward (key);
+    item.value = std::forward (value);
+    item.hash = hash;
+    item.set_used (true);
+    item.set_tombstone (is_delete);
+
+    occupancy++;
+    if (!is_delete)
+      population++;
+
+    return true;
+  }
+
+  template 
+  bool set (const K &key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward (value)); }
   template 
-  bool set (K key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward (value)); }
+  bool set (K &&key, VV&& value) { return set_with_hash (std::move (key), hb_hash (key), std::forward (value)); }
 
-  const V& get (K key) const
+  const V& get_with_hash (const K &key, uint32_t hash) const
   {
     if (unlikely (!items)) return item_t::default_value ();
-    unsigned int i = bucket_for (key);
-    return items[i].is_real () && items[i] == key ? items[i].value : item_t::default_value ();
+    auto &item = item_for_hash (key, hash);
+    return item.is_real () && item == key ? item.value : item_t::default_value ();
+  }
+  const V& get (const K &key) const
+  {
+    if (unlikely (!items)) return item_t::default_value ();
+    return get_with_hash (key, hb_hash (key));
   }
 
-  void del (K key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); }
+  void del (const K &key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); }
 
   /* Has interface. */
-  typedef const V& value_t;
-  value_t operator [] (K k) const { return get (k); }
-  bool has (K key, const V **vp = nullptr) const
+  const V& operator [] (K k) const { return get (k); }
+  template 
+  bool has (K key, VV **vp = nullptr) const
   {
     if (unlikely (!items))
-    {
-      if (vp) *vp = &item_t::default_value ();
       return false;
-    }
-    unsigned int i = bucket_for (key);
-    if (items[i].is_real () && items[i] == key)
+    auto &item = item_for_hash (key, hb_hash (key));
+    if (item.is_real () && item == key)
     {
-      if (vp) *vp = &items[i].value;
+      if (vp) *vp = std::addressof (item.value);
       return true;
     }
     else
-    {
-      if (vp) *vp = &item_t::default_value ();
       return false;
-    }
   }
   /* Projection. */
   V operator () (K k) const { return get (k); }
 
+  unsigned size () const { return mask ? mask + 1 : 0; }
+
   void clear ()
   {
     if (unlikely (!successful)) return;
 
-    if (items)
-      for (auto &_ : hb_iter (items, mask + 1))
-        _.clear ();
+    for (auto &_ : hb_iter (items, size ()))
+    {
+      /* Reconstruct items. */
+      _.~item_t ();
+      new (&_) item_t ();
+    }
 
     population = occupancy = 0;
   }
@@ -257,11 +289,10 @@ struct hb_hashmap_t
 
   uint32_t hash () const
   {
-    uint32_t h = 0;
-    for (const auto &item : + hb_array (items, mask ? mask + 1 : 0)
-                            | hb_filter (&item_t::is_real))
-      h ^= item.total_hash ();
-    return h;
+    return
+    + iter_items ()
+    | hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u)
+    ;
   }
 
   bool is_equal (const hb_hashmap_t &other) const
@@ -269,7 +300,7 @@ struct hb_hashmap_t
     if (population != other.population) return false;
 
     for (auto pair : iter ())
-      if (get (pair.first) != pair.second)
+      if (other.get (pair.first) != pair.second)
         return false;
 
     return true;
@@ -279,78 +310,90 @@ struct hb_hashmap_t
 
   unsigned int get_population () const { return population; }
 
+  void update (const hb_hashmap_t &other)
+  {
+    if (unlikely (!successful)) return;
+
+    hb_copy (other, *this);
+  }
+
   /*
    * Iterator
    */
-  auto iter () const HB_AUTO_RETURN
+
+  auto iter_items () const HB_AUTO_RETURN
   (
-    + hb_array (items, mask ? mask + 1 : 0)
+    + hb_iter (items, size ())
     | hb_filter (&item_t::is_real)
-    | hb_map (&item_t::get_pair)
   )
   auto iter_ref () const HB_AUTO_RETURN
   (
-    + hb_array (items, mask ? mask + 1 : 0)
-    | hb_filter (&item_t::is_real)
+    + iter_items ()
     | hb_map (&item_t::get_pair_ref)
   )
+  auto iter () const HB_AUTO_RETURN
+  (
+    + iter_items ()
+    | hb_map (&item_t::get_pair)
+  )
+  auto keys_ref () const HB_AUTO_RETURN
+  (
+    + iter_items ()
+    | hb_map (&item_t::key)
+  )
   auto keys () const HB_AUTO_RETURN
   (
-    + hb_array (items, mask ? mask + 1 : 0)
-    | hb_filter (&item_t::is_real)
+    + iter_items ()
     | hb_map (&item_t::key)
     | hb_map (hb_ridentity)
   )
+  auto values_ref () const HB_AUTO_RETURN
+  (
+    + iter_items ()
+    | hb_map (&item_t::value)
+  )
   auto values () const HB_AUTO_RETURN
   (
-    + hb_array (items, mask ? mask + 1 : 0)
-    | hb_filter (&item_t::is_real)
+    + iter_items ()
     | hb_map (&item_t::value)
     | hb_map (hb_ridentity)
   )
 
-  /* Sink interface. */
-  hb_hashmap_t& operator << (const hb_pair_t& v)
-  { set (v.first, v.second); return *this; }
-
-  protected:
-
-  template 
-  bool set_with_hash (K key, uint32_t hash, VV&& value, bool is_delete=false)
+  /* C iterator. */
+  bool next (int *idx,
+             K *key,
+             V *value) const
   {
-    if (unlikely (!successful)) return false;
-    if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false;
-    unsigned int i = bucket_for_hash (key, hash);
+    unsigned i = (unsigned) (*idx + 1);
 
-    if (is_delete && items[i].key != key)
-      return true; /* Trying to delete non-existent key. */
+    unsigned count = size ();
+    while (i < count && !items[i].is_real ())
+      i++;
 
-    if (items[i].is_used ())
+    if (i >= count)
     {
-      occupancy--;
-      if (!items[i].is_tombstone ())
-        population--;
+      *idx = -1;
+      return false;
     }
 
-    items[i].key = key;
-    items[i].value = std::forward (value);
-    items[i].hash = hash;
-    items[i].set_used (true);
-    items[i].set_tombstone (is_delete);
-
-    occupancy++;
-    if (!is_delete)
-      population++;
+    *key = items[i].key;
+    *value = items[i].value;
 
+    *idx = (signed) i;
     return true;
   }
 
-  unsigned int bucket_for (const K &key) const
-  {
-    return bucket_for_hash (key, hb_hash (key));
-  }
-
-  unsigned int bucket_for_hash (const K &key, uint32_t hash) const
+  /* Sink interface. */
+  hb_hashmap_t& operator << (const hb_pair_t& v)
+  { set (v.first, v.second); return *this; }
+  hb_hashmap_t& operator << (const hb_pair_t& v)
+  { set (v.first, std::move (v.second)); return *this; }
+  hb_hashmap_t& operator << (const hb_pair_t& v)
+  { set (std::move (v.first), v.second); return *this; }
+  hb_hashmap_t& operator << (const hb_pair_t& v)
+  { set (std::move (v.first), std::move (v.second)); return *this; }
+
+  item_t& item_for_hash (const K &key, uint32_t hash) const
   {
     hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
     unsigned int i = hash % prime;
@@ -359,12 +402,12 @@ struct hb_hashmap_t
     while (items[i].is_used ())
     {
       if (items[i].hash == hash && items[i] == key)
-        return i;
+        return items[i];
       if (tombstone == (unsigned) -1 && items[i].is_tombstone ())
         tombstone = i;
       i = (i + ++step) & mask;
     }
-    return tombstone == (unsigned) -1 ? i : tombstone;
+    return items[tombstone == (unsigned) -1 ? i : tombstone];
   }
 
   static unsigned int prime_for (unsigned int shift)
@@ -443,4 +486,5 @@ struct hb_map_t : hb_hashmap_t auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_ident
 template  using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize));
 
 
-/* TODO Add feature-parity to std::decay. */
-template  using hb_decay = hb_remove_const>;
+template  using hb_decay = typename std::decay::type;
 
 #define hb_is_convertible(From,To) std::is_convertible::value
 
@@ -133,6 +132,18 @@ struct
 
   template  constexpr auto
   operator () (T *v) const HB_AUTO_RETURN (*v)
+
+  template  constexpr auto
+  operator () (const hb::shared_ptr& v) const HB_AUTO_RETURN (*v)
+
+  template  constexpr auto
+  operator () (hb::shared_ptr& v) const HB_AUTO_RETURN (*v)
+
+  template  constexpr auto
+  operator () (const hb::unique_ptr& v) const HB_AUTO_RETURN (*v)
+
+  template  constexpr auto
+  operator () (hb::unique_ptr& v) const HB_AUTO_RETURN (*v)
 }
 HB_FUNCOBJ (hb_deref);
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-multimap.hh b/src/java.desktop/share/native/libharfbuzz/hb-multimap.hh
new file mode 100644
index 00000000000..b4a8cc62a3e
--- /dev/null
+++ b/src/java.desktop/share/native/libharfbuzz/hb-multimap.hh
@@ -0,0 +1,92 @@
+/*
+ * 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_MULTIMAP_HH
+#define HB_MULTIMAP_HH
+
+#include "hb.hh"
+#include "hb-map.hh"
+#include "hb-vector.hh"
+
+
+/*
+ * hb_multimap_t
+ */
+
+struct hb_multimap_t
+{
+  void add (hb_codepoint_t k, hb_codepoint_t v)
+  {
+    hb_codepoint_t *i;
+    if (multiples_indices.has (k, &i))
+    {
+      multiples_values[*i].push (v);
+      return;
+    }
+
+    hb_codepoint_t *old_v;
+    if (singulars.has (k, &old_v))
+    {
+      hb_codepoint_t old = *old_v;
+      singulars.del (k);
+
+      multiples_indices.set (k, multiples_values.length);
+      auto *vec = multiples_values.push ();
+
+      vec->push (old);
+      vec->push (v);
+
+      return;
+    }
+
+    singulars.set (k, v);
+  }
+
+  hb_array_t get (hb_codepoint_t k) const
+  {
+    const hb_codepoint_t *v;
+    if (singulars.has (k, &v))
+      return hb_array (v, 1);
+
+    hb_codepoint_t *i;
+    if (multiples_indices.has (k, &i))
+      return multiples_values[*i].as_array ();
+
+    return hb_array_t ();
+  }
+
+  bool in_error () const
+  {
+    return singulars.in_error () || multiples_indices.in_error () || multiples_values.in_error ();
+  }
+
+  protected:
+  hb_map_t singulars;
+  hb_map_t multiples_indices;
+  hb_vector_t> multiples_values;
+};
+
+
+
+#endif /* HB_MULTIMAP_HH */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh
index 8b62afaf94e..72012bd2e64 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh
@@ -60,7 +60,7 @@ typedef pthread_mutex_t hb_mutex_impl_t;
 #elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && defined(_WIN32)
 
 typedef CRITICAL_SECTION hb_mutex_impl_t;
-#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
 #define hb_mutex_impl_init(M)   InitializeCriticalSectionEx (M, 0, 0)
 #else
 #define hb_mutex_impl_init(M)   InitializeCriticalSection (M)
@@ -97,6 +97,9 @@ struct hb_mutex_t
   /* Create space for, but do not initialize m. */
   alignas(hb_mutex_impl_t) char m[sizeof (hb_mutex_impl_t)];
 
+  hb_mutex_t () { init (); }
+  ~hb_mutex_t () { fini (); }
+
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wcast-align"
   void init   () { hb_mutex_impl_init   ((hb_mutex_impl_t *) m); }
@@ -108,10 +111,11 @@ struct hb_mutex_t
 
 struct hb_lock_t
 {
-  hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); }
-  ~hb_lock_t () { mutex.unlock (); }
+  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 (); }
   private:
-  hb_mutex_t &mutex;
+  hb_mutex_t *mutex;
 };
 
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-null.hh b/src/java.desktop/share/native/libharfbuzz/hb-null.hh
index 4403867f5c3..8a9ebbfc2d7 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-null.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-null.hh
@@ -39,6 +39,24 @@
 
 #define HB_NULL_POOL_SIZE 448
 
+template 
+struct _hb_has_min_size : hb_false_type {};
+template 
+struct _hb_has_min_size>
+        : hb_true_type {};
+template 
+using hb_has_min_size = _hb_has_min_size;
+#define hb_has_min_size(T) hb_has_min_size::value
+
+template 
+struct _hb_has_null_size : hb_false_type {};
+template 
+struct _hb_has_null_size>
+        : hb_true_type {};
+template 
+using hb_has_null_size = _hb_has_null_size;
+#define hb_has_null_size(T) hb_has_null_size::value
+
 /* Use SFINAE to sniff whether T has min_size; in which case return the larger
  * of sizeof(T) and T::null_size, otherwise return sizeof(T).
  *
@@ -117,8 +135,19 @@ struct NullHelper
         }; \
         namespace Namespace { \
         static_assert (true, "") /* Require semicolon after. */
+#define DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1(Namespace, Type, Size) \
+        } /* Close namespace. */ \
+        extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Size]; \
+        template  \
+        struct Null> { \
+          static Namespace::Type const & get_null () { \
+            return *reinterpret_cast *> (_hb_Null_##Namespace##_##Type); \
+          } \
+        }; \
+        namespace Namespace { \
+        static_assert (true, "") /* Require semicolon after. */
 #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \
-        const unsigned char _hb_Null_##Namespace##_##Type[hb_null_size (Namespace::Type)]
+        const unsigned char _hb_Null_##Namespace##_##Type[sizeof (_hb_Null_##Namespace##_##Type)]
 
 /* Specializations for arbitrary-content Null objects expressed as struct initializer. */
 #define DECLARE_NULL_INSTANCE(Type) \
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh
index 9d2867e4835..07f3ebe0c7d 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh
@@ -31,7 +31,7 @@
 #include "hb.hh"
 
 
-#line 35 "hb-number-parser.hh"
+#line 32 "hb-number-parser.hh"
 static const unsigned char _double_parser_trans_keys[] = {
         0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u,
         46u, 101u, 0
@@ -135,12 +135,12 @@ strtod_rl (const char *p, const char **end_ptr /* IN/OUT */)
 
   int cs;
 
-#line 139 "hb-number-parser.hh"
+#line 132 "hb-number-parser.hh"
         {
         cs = double_parser_start;
         }
 
-#line 144 "hb-number-parser.hh"
+#line 135 "hb-number-parser.hh"
         {
         int _slen;
         int _trans;
@@ -198,7 +198,7 @@ _resume:
           exp_overflow = true;
 }
         break;
-#line 202 "hb-number-parser.hh"
+#line 187 "hb-number-parser.hh"
         }
 
 _again:
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number.cc b/src/java.desktop/share/native/libharfbuzz/hb-number.cc
index f4ce693d8ed..37bc8b87399 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-number.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-number.cc
@@ -24,7 +24,6 @@
  */
 
 #include "hb.hh"
-#include "hb-machinery.hh"
 #include "hb-number.hh"
 #include "hb-number-parser.hh"
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-object.hh b/src/java.desktop/share/native/libharfbuzz/hb-object.hh
index 436c37aa1b7..ef675f835f1 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-object.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-object.hh
@@ -69,7 +69,7 @@ struct hb_lockable_set_t
       item = items.push (v);
       l.unlock ();
     }
-    return item;
+    return items.in_error () ? nullptr : item;
   }
 
   template 
@@ -80,7 +80,7 @@ struct hb_lockable_set_t
     if (item)
     {
       item_t old = *item;
-      *item = items[items.length - 1];
+      *item = std::move (items.tail ());
       items.pop ();
       l.unlock ();
       old.fini ();
@@ -123,7 +123,7 @@ struct hb_lockable_set_t
     l.lock ();
     while (items.length)
     {
-      item_t old = items[items.length - 1];
+      item_t old = items.tail ();
       items.pop ();
       l.unlock ();
       old.fini ();
@@ -144,14 +144,14 @@ struct hb_reference_count_t
 {
   mutable hb_atomic_int_t ref_count;
 
-  void init (int v = 1) { ref_count.set_relaxed (v); }
-  int get_relaxed () const { return ref_count.get_relaxed (); }
+  void init (int v = 1) { ref_count = v; }
+  int get_relaxed () const { return ref_count; }
   int inc () const { return ref_count.inc (); }
   int dec () const { return ref_count.dec (); }
-  void fini () { ref_count.set_relaxed (-0x0000DEAD); }
+  void fini () { ref_count = -0x0000DEAD; }
 
-  bool is_inert () const { return !ref_count.get_relaxed (); }
-  bool is_valid () const { return ref_count.get_relaxed () > 0; }
+  bool is_inert () const { return !ref_count; }
+  bool is_valid () const { return ref_count > 0; }
 };
 
 
@@ -175,14 +175,34 @@ struct hb_user_data_array_t
 
   void init () { lock.init (); items.init (); }
 
-  HB_INTERNAL bool set (hb_user_data_key_t *key,
-                        void *              data,
-                        hb_destroy_func_t   destroy,
-                        hb_bool_t           replace);
+  void fini () { items.fini (lock); lock.fini (); }
+
+  bool set (hb_user_data_key_t *key,
+            void *              data,
+            hb_destroy_func_t   destroy,
+            hb_bool_t           replace)
+  {
+    if (!key)
+      return false;
 
-  HB_INTERNAL void *get (hb_user_data_key_t *key);
+    if (replace) {
+      if (!data && !destroy) {
+        items.remove (key, lock);
+        return true;
+      }
+    }
+    hb_user_data_item_t item = {key, data, destroy};
+    bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
 
-  void fini () { items.fini (lock); lock.fini (); }
+    return ret;
+  }
+
+  void *get (hb_user_data_key_t *key)
+  {
+    hb_user_data_item_t item = {nullptr, nullptr, nullptr};
+
+    return items.find (key, &item, lock) ? item.data : nullptr;
+  }
 };
 
 
@@ -214,23 +234,26 @@ static inline void hb_object_trace (const Type *obj, const char *function)
              obj ? obj->header.ref_count.get_relaxed () : 0);
 }
 
-template 
-static inline Type *hb_object_create ()
+template 
+static inline Type *hb_object_create (Ts... ds)
 {
   Type *obj = (Type *) hb_calloc (1, sizeof (Type));
 
   if (unlikely (!obj))
     return obj;
 
+  new (obj) Type (std::forward (ds)...);
+
   hb_object_init (obj);
   hb_object_trace (obj, HB_FUNC);
+
   return obj;
 }
 template 
 static inline void hb_object_init (Type *obj)
 {
   obj->header.ref_count.init ();
-  obj->header.writable.set_relaxed (true);
+  obj->header.writable = true;
   obj->header.user_data.init ();
 }
 template 
@@ -241,12 +264,12 @@ static inline bool hb_object_is_valid (const Type *obj)
 template 
 static inline bool hb_object_is_immutable (const Type *obj)
 {
-  return !obj->header.writable.get_relaxed ();
+  return !obj->header.writable;
 }
 template 
 static inline void hb_object_make_immutable (const Type *obj)
 {
-  obj->header.writable.set_relaxed (false);
+  obj->header.writable = false;
 }
 template 
 static inline Type *hb_object_reference (Type *obj)
@@ -269,18 +292,22 @@ static inline bool hb_object_destroy (Type *obj)
     return false;
 
   hb_object_fini (obj);
+
+  if (!std::is_trivially_destructible::value)
+    obj->~Type ();
+
   return true;
 }
 template 
 static inline void hb_object_fini (Type *obj)
 {
   obj->header.ref_count.fini (); /* Do this before user_data */
-  hb_user_data_array_t *user_data = obj->header.user_data.get ();
+  hb_user_data_array_t *user_data = obj->header.user_data.get_acquire ();
   if (user_data)
   {
     user_data->fini ();
     hb_free (user_data);
-    user_data = nullptr;
+    obj->header.user_data.set_relaxed (nullptr);
   }
 }
 template 
@@ -295,7 +322,7 @@ static inline bool hb_object_set_user_data (Type               *obj,
   assert (hb_object_is_valid (obj));
 
 retry:
-  hb_user_data_array_t *user_data = obj->header.user_data.get ();
+  hb_user_data_array_t *user_data = obj->header.user_data.get_acquire ();
   if (unlikely (!user_data))
   {
     user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1);
@@ -320,7 +347,7 @@ static inline void *hb_object_get_user_data (Type               *obj,
   if (unlikely (!obj || obj->header.is_inert ()))
     return nullptr;
   assert (hb_object_is_valid (obj));
-  hb_user_data_array_t *user_data = obj->header.user_data.get ();
+  hb_user_data_array_t *user_data = obj->header.user_data.get_acquire ();
   if (!user_data)
     return nullptr;
   return user_data->get (key);
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh
index 1dff7c414cc..c02eb41d100 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh
@@ -90,7 +90,7 @@ typedef struct OpenTypeOffsetTable
   {
     if (table_count)
     {
-      + tables.sub_array (start_offset, table_count)
+      + tables.as_array ().sub_array (start_offset, table_count)
       | hb_map (&TableRecord::tag)
       | hb_sink (hb_array (table_tags, *table_count))
       ;
@@ -158,7 +158,7 @@ typedef struct OpenTypeOffsetTable
         return_trace (false);
 
       if (likely (len))
-        memcpy (start, blob->data, len);
+        hb_memcpy (start, blob->data, len);
 
       /* 4-byte alignment. */
       c->align (4);
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 a4dfb3615e3..4639d80babc 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh
@@ -105,7 +105,7 @@ struct IntType
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
   protected:
   BEInt v;
@@ -141,27 +141,29 @@ typedef HBINT32 FWORD32;
 /* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */
 typedef HBUINT16 UFWORD;
 
-/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
-struct F2DOT14 : HBINT16
+template 
+struct HBFixed : Type
 {
-  F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; }
-  // 16384 means 1<<14
-  float to_float () const  { return ((int32_t) v) / 16384.f; }
-  void set_float (float f) { v = roundf (f * 16384.f); }
+  static constexpr float shift = (float) (1 << fraction_bits);
+  static_assert (Type::static_size * 8 > fraction_bits, "");
+
+  operator signed () const = delete;
+  operator unsigned () const = delete;
+  typename Type::type to_int () const { return Type::v; }
+  void set_int (typename Type::type i ) { Type::v = i; }
+  float to_float (float offset = 0) const  { return ((int32_t) Type::v + offset) / shift; }
+  void set_float (float f) { Type::v = roundf (f * shift); }
   public:
-  DEFINE_SIZE_STATIC (2);
+  DEFINE_SIZE_STATIC (Type::static_size);
 };
 
+/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
+using F2DOT14 = HBFixed;
+using F4DOT12 = HBFixed;
+using F6DOT10 = HBFixed;
+
 /* 32-bit signed fixed-point number (16.16). */
-struct HBFixed : HBINT32
-{
-  HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; }
-  // 65536 means 1<<16
-  float to_float () const  { return ((int32_t) v) / 65536.f; }
-  void set_float (float f) { v = roundf (f * 65536.f); }
-  public:
-  DEFINE_SIZE_STATIC (4);
-};
+using F16DOT16 = HBFixed;
 
 /* Date represented in number of seconds since 12:00 midnight, January 1,
  * 1904. The value is represented as a signed 64-bit integer. */
@@ -170,7 +172,7 @@ struct LONGDATETIME
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
   protected:
   HBINT32 major;
@@ -196,6 +198,10 @@ struct HBGlyphID16 : HBUINT16
 {
   HBGlyphID16& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; }
 };
+struct HBGlyphID24 : HBUINT24
+{
+  HBGlyphID24& operator = (uint32_t i) { HBUINT24::operator= (i); return *this; }
+};
 
 /* Script/language-system/feature index */
 struct Index : HBUINT16 {
@@ -208,6 +214,12 @@ typedef Index NameID;
 
 struct VarIdx : HBUINT32 {
   static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu;
+  static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, "");
+  static uint32_t add (uint32_t i, unsigned short v)
+  {
+    if (i == NO_VARIATION) return i;
+    return i + v;
+  }
   VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; }
 };
 DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx);
@@ -300,6 +312,10 @@ struct _hb_has_null
 template 
 struct OffsetTo : Offset
 {
+  // Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time.
+  static_assert (has_null == false ||
+                 (hb_has_null_size (Type) || !hb_has_min_size (Type)), "");
+
   HB_DELETE_COPY_ASSIGN (OffsetTo);
   OffsetTo () = default;
 
@@ -450,14 +466,16 @@ struct UnsizedArrayOf
   {
     unsigned int i = (unsigned int) i_;
     const Type *p = &arrayZ[i];
-    if (unlikely (p < arrayZ)) return Null (Type); /* Overflowed. */
+    if (unlikely ((const void *) p < (const void *) arrayZ)) return Null (Type); /* Overflowed. */
+    _hb_compiler_memory_r_barrier ();
     return *p;
   }
   Type& operator [] (int i_)
   {
     unsigned int i = (unsigned int) i_;
     Type *p = &arrayZ[i];
-    if (unlikely (p < arrayZ)) return Crap (Type); /* Overflowed. */
+    if (unlikely ((const void *) p < (const void *) arrayZ)) return Crap (Type); /* Overflowed. */
+    _hb_compiler_memory_r_barrier ();
     return *p;
   }
 
@@ -486,10 +504,10 @@ struct UnsizedArrayOf
   void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1)
   { as_array (len).qsort (start, end); }
 
-  bool serialize (hb_serialize_context_t *c, unsigned int items_len)
+  bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend (this, items_len))) return_trace (false);
+    if (unlikely (!c->extend_size (this, get_size (items_len), clear))) return_trace (false);
     return_trace (true);
   }
   template  *p = &this->arrayZ[i];
-    if (unlikely (p < this->arrayZ)) return Null (Type); /* Overflowed. */
+    if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Null (Type); /* Overflowed. */
+    _hb_compiler_memory_r_barrier ();
     return this+*p;
   }
   Type& operator [] (int i_)
   {
     unsigned int i = (unsigned int) i_;
     const OffsetTo *p = &this->arrayZ[i];
-    if (unlikely (p < this->arrayZ)) return Crap (Type); /* Overflowed. */
+    if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Crap (Type); /* Overflowed. */
+    _hb_compiler_memory_r_barrier ();
     return this+*p;
   }
 
@@ -608,12 +628,14 @@ struct ArrayOf
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= len)) return Null (Type);
+    _hb_compiler_memory_r_barrier ();
     return arrayZ[i];
   }
   Type& operator [] (int i_)
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= len)) return Crap (Type);
+    _hb_compiler_memory_r_barrier ();
     return arrayZ[i];
   }
 
@@ -635,14 +657,9 @@ struct ArrayOf
   operator   iter_t () const { return   iter (); }
   operator writer_t ()       { return writer (); }
 
-  hb_array_t sub_array (unsigned int start_offset, unsigned int count) const
-  { return as_array ().sub_array (start_offset, count); }
-  hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
-  { return as_array ().sub_array (start_offset, count); }
-  hb_array_t sub_array (unsigned int start_offset, unsigned int count)
-  { return as_array ().sub_array (start_offset, count); }
-  hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
-  { return as_array ().sub_array (start_offset, count); }
+  /* Faster range-based for loop. */
+  const Type *begin () const { return arrayZ; }
+  const Type *end () const { return arrayZ + len; }
 
   template 
   Type &lsearch (const T &x, Type ¬_found = Crap (Type))
@@ -656,15 +673,15 @@ struct ArrayOf
               unsigned int to_store = (unsigned int) -1) const
   { return as_array ().lfind (x, i, not_found, to_store); }
 
-  void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
-  { as_array ().qsort (start, end); }
+  void qsort ()
+  { as_array ().qsort (); }
 
-  HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len)
+  HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len, bool clear = true)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (this))) return_trace (false);
     c->check_assign (len, items_len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW);
-    if (unlikely (!c->extend (this))) return_trace (false);
+    if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false);
     return_trace (true);
   }
   template  using Array16Of = ArrayOf;
+template  using Array24Of = ArrayOf;
 template  using Array32Of = ArrayOf;
 using PString = ArrayOf;
 
@@ -738,26 +756,28 @@ template  using Array16OfOffset32To = ArrayOf using Array32OfOffset32To = ArrayOf, HBUINT32>;
 
 /* Array of offsets relative to the beginning of the array itself. */
-template 
-struct List16OfOffset16To : Array16OfOffset16To
+template 
+struct List16OfOffsetTo : ArrayOf, HBUINT16>
 {
   const Type& operator [] (int i_) const
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= this->len)) return Null (Type);
+    _hb_compiler_memory_r_barrier ();
     return this+this->arrayZ[i];
   }
   const Type& operator [] (int i_)
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= this->len)) return Crap (Type);
+    _hb_compiler_memory_r_barrier ();
     return this+this->arrayZ[i];
   }
 
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    struct List16OfOffset16To *out = c->serializer->embed (*this);
+    struct List16OfOffsetTo *out = c->serializer->embed (*this);
     if (unlikely (!out)) return_trace (false);
     unsigned int count = this->len;
     for (unsigned int i = 0; i < count; i++)
@@ -769,10 +789,13 @@ struct List16OfOffset16To : Array16OfOffset16To
   bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
   {
     TRACE_SANITIZE (this);
-    return_trace (Array16OfOffset16To::sanitize (c, this, std::forward (ds)...));
+    return_trace ((Array16Of>::sanitize (c, this, std::forward (ds)...)));
   }
 };
 
+template 
+using List16OfOffset16To = List16OfOffsetTo;
+
 /* An array starting at second element. */
 template 
 struct HeadlessArrayOf
@@ -785,12 +808,14 @@ struct HeadlessArrayOf
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= lenP1 || !i)) return Null (Type);
+    _hb_compiler_memory_r_barrier ();
     return arrayZ[i-1];
   }
   Type& operator [] (int i_)
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= lenP1 || !i)) return Crap (Type);
+    _hb_compiler_memory_r_barrier ();
     return arrayZ[i-1];
   }
   unsigned int get_size () const
@@ -809,21 +834,25 @@ struct HeadlessArrayOf
   operator   iter_t () const { return   iter (); }
   operator writer_t ()       { return writer (); }
 
-  bool serialize (hb_serialize_context_t *c, unsigned int items_len)
+  /* Faster range-based for loop. */
+  const Type *begin () const { return arrayZ; }
+  const Type *end () const { return arrayZ + get_length (); }
+
+  HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (this))) return_trace (false);
     c->check_assign (lenP1, items_len + 1, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW);
-    if (unlikely (!c->extend (this))) return_trace (false);
+    if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false);
     return_trace (true);
   }
   template 
-  bool serialize (hb_serialize_context_t *c, Iterator items)
+  HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items)
   {
     TRACE_SERIALIZE (this);
-    unsigned count = items.len ();
-    if (unlikely (!serialize (c, count))) return_trace (false);
+    unsigned count = hb_len (items);
+    if (unlikely (!serialize (c, count, false))) return_trace (false);
     /* TODO Umm. Just exhaust the iterator instead?  Being extra
      * cautious right now.. */
     for (unsigned i = 0; i < count; i++, ++items)
@@ -869,12 +898,14 @@ struct ArrayOfM1
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i > lenM1)) return Null (Type);
+    _hb_compiler_memory_r_barrier ();
     return arrayZ[i];
   }
   Type& operator [] (int i_)
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i > lenM1)) return Crap (Type);
+    _hb_compiler_memory_r_barrier ();
     return arrayZ[i];
   }
   unsigned int get_size () const
@@ -923,14 +954,9 @@ struct SortedArrayOf : ArrayOf
   operator   iter_t () const { return   iter (); }
   operator writer_t ()       { return writer (); }
 
-  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) const
-  { return as_array ().sub_array (start_offset, count); }
-  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
-  { return as_array ().sub_array (start_offset, count); }
-  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count)
-  { return as_array ().sub_array (start_offset, count); }
-  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
-  { return as_array ().sub_array (start_offset, count); }
+  /* Faster range-based for loop. */
+  const Type *begin () const { return this->arrayZ; }
+  const Type *end () const { return this->arrayZ + this->len; }
 
   bool serialize (hb_serialize_context_t *c, unsigned int items_len)
   {
@@ -961,6 +987,7 @@ struct SortedArrayOf : ArrayOf
 };
 
 template  using SortedArray16Of = SortedArrayOf;
+template  using SortedArray24Of = SortedArrayOf;
 template  using SortedArray32Of = SortedArrayOf;
 
 /*
@@ -1053,12 +1080,14 @@ struct VarSizedBinSearchArrayOf
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= get_length ())) return Null (Type);
+    _hb_compiler_memory_r_barrier ();
     return StructAtOffset (&bytesZ, i * header.unitSize);
   }
   Type& operator [] (int i_)
   {
     unsigned int i = (unsigned int) i_;
     if (unlikely (i >= get_length ())) return Crap (Type);
+    _hb_compiler_memory_r_barrier ();
     return StructAtOffset (&bytesZ, i * header.unitSize);
   }
   unsigned int get_length () const
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh
index 54c7ecdf294..d31a328e8ad 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh
@@ -66,95 +66,25 @@ struct CFFIndex
   {
     TRACE_SERIALIZE (this);
     unsigned int size = get_size ();
-    CFFIndex *out = c->allocate_size (size);
+    CFFIndex *out = c->allocate_size (size, false);
     if (likely (out))
-      memcpy (out, this, size);
+      hb_memcpy (out, this, size);
     return_trace (out);
   }
 
+  template 
   bool serialize (hb_serialize_context_t *c,
-                  unsigned int offSize_,
-                  const byte_str_array_t &byteArray)
+                  const Iterable &iterable)
   {
     TRACE_SERIALIZE (this);
-
-    if (byteArray.length == 0)
-    {
-      COUNT *dest = c->allocate_min ();
-      if (unlikely (!dest)) return_trace (false);
-      *dest = 0;
-      return_trace (true);
-    }
-
-    /* serialize CFFIndex header */
-    if (unlikely (!c->extend_min (this))) return_trace (false);
-    this->count = byteArray.length;
-    this->offSize = offSize_;
-    if (unlikely (!c->allocate_size (offSize_ * (byteArray.length + 1))))
-      return_trace (false);
-
-    /* serialize indices */
-    unsigned int  offset = 1;
-    unsigned int  i = 0;
-    for (; i < byteArray.length; i++)
-    {
-      set_offset_at (i, offset);
-      offset += byteArray[i].get_size ();
-    }
-    set_offset_at (i, offset);
-
-    /* serialize data */
-    for (unsigned int i = 0; i < byteArray.length; i++)
-    {
-      const hb_ubytes_t &bs = byteArray[i];
-      unsigned char *dest = c->allocate_size (bs.length);
-      if (unlikely (!dest)) return_trace (false);
-      memcpy (dest, &bs[0], bs.length);
-    }
-
-    return_trace (true);
-  }
-
-  bool serialize (hb_serialize_context_t *c,
-                  unsigned int offSize_,
-                  const str_buff_vec_t &buffArray)
-  {
-    byte_str_array_t  byteArray;
-    byteArray.init ();
-    byteArray.resize (buffArray.length);
-    for (unsigned int i = 0; i < byteArray.length; i++)
-      byteArray[i] = hb_ubytes_t (buffArray[i].arrayZ, buffArray[i].length);
-    bool result = this->serialize (c, offSize_, byteArray);
-    byteArray.fini ();
-    return result;
-  }
-
-  template 
-  bool serialize (hb_serialize_context_t *c,
-                  Iterator it)
-  {
-    TRACE_SERIALIZE (this);
-    serialize_header(c, + it | hb_map ([] (const hb_ubytes_t &_) { return _.length; }));
+    auto it = hb_iter (iterable);
+    serialize_header(c, + it | hb_map (hb_iter) | hb_map (hb_len));
     for (const auto &_ : +it)
-      _.copy (c);
+      hb_iter (_).copy (c);
     return_trace (true);
   }
 
-  bool serialize (hb_serialize_context_t *c,
-                  const byte_str_array_t &byteArray)
-  { return serialize (c, + hb_iter (byteArray)); }
-
-  bool serialize (hb_serialize_context_t *c,
-                  const str_buff_vec_t &buffArray)
-  {
-    auto it =
-    + hb_iter (buffArray)
-    | hb_map ([] (const str_buff_t &_) { return hb_ubytes_t (_.arrayZ, _.length); })
-    ;
-    return serialize (c, it);
-  }
-
   template 
   bool serialize_header (hb_serialize_context_t *c,
@@ -171,7 +101,7 @@ struct CFFIndex
     if (!this->count) return_trace (true);
     if (unlikely (!c->extend (this->offSize))) return_trace (false);
     this->offSize = off_size;
-    if (unlikely (!c->allocate_size (off_size * (this->count + 1))))
+    if (unlikely (!c->allocate_size (off_size * (this->count + 1), false)))
       return_trace (false);
 
     /* serialize indices */
@@ -179,14 +109,27 @@ struct CFFIndex
     unsigned int i = 0;
     for (unsigned _ : +it)
     {
-      CFFIndex::set_offset_at (i++, offset);
+      set_offset_at (i++, offset);
       offset += _;
     }
-    CFFIndex::set_offset_at (i, offset);
+    set_offset_at (i, offset);
 
     return_trace (true);
   }
 
+  template 
+  static unsigned total_size (const Iterable &iterable)
+  {
+    auto it = + hb_iter (iterable) | hb_map (hb_iter) | hb_map (hb_len);
+    if (!it) return 0;
+
+    unsigned total = + it | hb_reduce (hb_add, 0);
+    unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8;
+
+    return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total;
+  }
+
   void set_offset_at (unsigned int index, unsigned int offset)
   {
     assert (index <= count);
@@ -207,10 +150,14 @@ struct CFFIndex
 
     unsigned int size = offSize;
     const HBUINT8 *p = offsets + size * index;
-    unsigned int offset = 0;
-    for (; size; size--)
-      offset = (offset << 8) + *p++;
-    return offset;
+    switch (size)
+    {
+      case 1: return * (HBUINT8  *) p;
+      case 2: return * (HBUINT16 *) p;
+      case 3: return * (HBUINT24 *) p;
+      case 4: return * (HBUINT32 *) p;
+      default: return 0;
+    }
   }
 
   unsigned int length_at (unsigned int index) const
@@ -229,6 +176,7 @@ struct CFFIndex
   hb_ubytes_t operator [] (unsigned int index) const
   {
     if (unlikely (index >= count)) return hb_ubytes_t ();
+    _hb_compiler_memory_r_barrier ();
     unsigned length = length_at (index);
     if (unlikely (!length)) return hb_ubytes_t ();
     return hb_ubytes_t (data_base () + offset_at (index) - 1, length);
@@ -280,7 +228,7 @@ struct CFFIndexOf : CFFIndex
     if (unlikely (!c->extend_min (this))) return_trace (false);
     this->count = dataArrayLen;
     this->offSize = offSize_;
-    if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1))))
+    if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1), false)))
       return_trace (false);
 
     /* serialize indices */
@@ -288,10 +236,10 @@ struct CFFIndexOf : CFFIndex
     unsigned int  i = 0;
     for (; i < dataArrayLen; i++)
     {
-      CFFIndex::set_offset_at (i, offset);
+      this->set_offset_at (i, offset);
       offset += dataSizeArray[i];
     }
-    CFFIndex::set_offset_at (i, offset);
+    this->set_offset_at (i, offset);
 
     /* serialize data */
     for (unsigned int i = 0; i < dataArrayLen; i++)
@@ -324,13 +272,12 @@ struct Dict : UnsizedByteStr
   template 
   static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp)
   {
-    // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
-    if (/*unlikely*/ (!serialize_int (c, intOp, value)))
+    if (unlikely ((!serialize_int (c, intOp, value))))
       return false;
 
     TRACE_SERIALIZE (this);
     /* serialize the opcode */
-    HBUINT8 *p = c->allocate_size (OpCode_Size (op));
+    HBUINT8 *p = c->allocate_size (OpCode_Size (op), false);
     if (unlikely (!p)) return_trace (false);
     if (Is_OpCode_ESC (op))
     {
@@ -415,9 +362,8 @@ struct FDSelect0 {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this))))
       return_trace (false);
-    for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
-      if (unlikely (!fds[i].sanitize (c)))
-        return_trace (false);
+    if (unlikely (!c->check_array (fds, c->get_num_glyphs ())))
+      return_trace (false);
 
     return_trace (true);
   }
@@ -471,14 +417,20 @@ struct FDSelect3_4
     return_trace (true);
   }
 
-  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  static int _cmp_range (const void *_key, const void *_item)
   {
-    unsigned int i;
-    for (i = 1; i < nRanges (); i++)
-      if (glyph < ranges[i].first)
-        break;
+    hb_codepoint_t glyph = * (hb_codepoint_t *) _key;
+    FDSelect3_4_Range *range = (FDSelect3_4_Range *) _item;
 
-    return (hb_codepoint_t) ranges[i - 1].fd;
+    if (glyph < range[0].first) return -1;
+    if (glyph < range[1].first) return 0;
+    return +1;
+  }
+
+  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range);
+    return range ? range->fd : ranges[nRanges () - 1].fd;
   }
 
   GID_TYPE        &nRanges ()       { return ranges.len; }
@@ -501,9 +453,9 @@ struct FDSelect
   {
     TRACE_SERIALIZE (this);
     unsigned int size = src.get_size (num_glyphs);
-    FDSelect *dest = c->allocate_size (size);
+    FDSelect *dest = c->allocate_size (size, false);
     if (unlikely (!dest)) return_trace (false);
-    memcpy (dest, &src, size);
+    hb_memcpy (dest, &src, size);
     return_trace (true);
   }
 
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc
index 9173b9d8600..505af15e5c5 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc
@@ -422,8 +422,8 @@ bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph
   }
   else
   {
-    extents->x_bearing = font->em_scalef_x (bounds.min.x.to_real ());
-    extents->width = font->em_scalef_x (bounds.max.x.to_real ()) - extents->x_bearing;
+    extents->x_bearing = roundf (bounds.min.x.to_real ());
+    extents->width = roundf (bounds.max.x.to_real () - extents->x_bearing);
   }
   if (bounds.min.y >= bounds.max.y)
   {
@@ -432,10 +432,12 @@ bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph
   }
   else
   {
-    extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ());
-    extents->height = font->em_scalef_y (bounds.min.y.to_real ()) - extents->y_bearing;
+    extents->y_bearing = roundf (bounds.max.y.to_real ());
+    extents->height = roundf (bounds.min.y.to_real () - extents->y_bearing);
   }
 
+  font->scale_glyph_extents (extents);
+
   return true;
 }
 
@@ -551,6 +553,15 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
   return true;
 }
 
+bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
+{
+  funcs->push_clip_glyph (data, glyph, font);
+  funcs->color (data, true, foreground);
+  funcs->pop_clip (data);
+
+  return true;
+}
+
 bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
 {
 #ifdef HB_NO_OT_FONT_CFF
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 b773956662f..60bc308f90c 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
@@ -30,6 +30,7 @@
 #include "hb-ot-cff-common.hh"
 #include "hb-subset-cff1.hh"
 #include "hb-draw.hh"
+#include "hb-paint.hh"
 
 #define HB_STRING_ARRAY_NAME cff1_std_strings
 #define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh"
@@ -175,7 +176,7 @@ struct Encoding
     unsigned int size = src.get_size ();
     Encoding *dest = c->allocate_size (size);
     if (unlikely (!dest)) return_trace (false);
-    memcpy (dest, &src, size);
+    hb_memcpy (dest, &src, size);
     return_trace (true);
   }
 
@@ -471,7 +472,7 @@ struct Charset
     unsigned int size = src.get_size (num_glyphs);
     Charset *dest = c->allocate_size (size);
     if (unlikely (!dest)) return_trace (false);
-    memcpy (dest, &src, size);
+    hb_memcpy (dest, &src, size);
     return_trace (true);
   }
 
@@ -617,7 +618,6 @@ struct CFF1StringIndex : CFF1Index
     }
 
     byte_str_array_t bytesArray;
-    bytesArray.init ();
     if (!bytesArray.resize (sidmap.get_population ()))
       return_trace (false);
     for (unsigned int i = 0; i < strings.count; i++)
@@ -628,7 +628,6 @@ struct CFF1StringIndex : CFF1Index
     }
 
     bool result = CFF1Index::serialize (c, bytesArray);
-    bytesArray.fini ();
     return_trace (result);
   }
 };
@@ -813,7 +812,7 @@ struct cff1_top_dict_opset_t : top_dict_opset_t
         break;
 
       default:
-        env.last_offset = env.str_ref.offset;
+        env.last_offset = env.str_ref.get_offset ();
         top_dict_opset_t::process_op (op, env, dictval);
         /* Record this operand below if stack is empty, otherwise done */
         if (!env.argStack.is_empty ()) return;
@@ -903,8 +902,6 @@ struct cff1_private_dict_opset_t : dict_opset_t
       case OpCode_FamilyOtherBlues:
       case OpCode_StemSnapH:
       case OpCode_StemSnapV:
-        env.clear_args ();
-        break;
       case OpCode_StdHW:
       case OpCode_StdVW:
       case OpCode_BlueScale:
@@ -916,7 +913,6 @@ struct cff1_private_dict_opset_t : dict_opset_t
       case OpCode_initialRandomSeed:
       case OpCode_defaultWidthX:
       case OpCode_nominalWidthX:
-        val.single_val = env.argStack.pop_num ();
         env.clear_args ();
         break;
       case OpCode_Subrs:
@@ -1295,10 +1291,10 @@ struct cff1
     }
 
     protected:
-    hb_blob_t              *blob = nullptr;
     hb_sanitize_context_t   sc;
 
     public:
+    hb_blob_t               *blob = nullptr;
     const Encoding          *encoding = nullptr;
     const Charset           *charset = nullptr;
     const CFF1NameIndex     *nameIndex = nullptr;
@@ -1345,6 +1341,7 @@ struct cff1
     bool get_glyph_name (hb_codepoint_t glyph,
                          char *buf, unsigned int buf_len) const
     {
+      if (unlikely (glyph >= num_glyphs)) return false;
       if (unlikely (!is_valid ())) return false;
       if (is_CID()) return false;
       if (unlikely (!buf_len)) return true;
@@ -1379,7 +1376,7 @@ struct cff1
       if (unlikely (!len)) return false;
 
     retry:
-      hb_sorted_vector_t *names = glyph_names.get ();
+      hb_sorted_vector_t *names = glyph_names.get_acquire ();
       if (unlikely (!names))
       {
         names = (hb_sorted_vector_t *) hb_calloc (sizeof (hb_sorted_vector_t), 1);
@@ -1428,6 +1425,7 @@ struct cff1
     }
 
     HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
+    HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
     HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
 
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 1e757ebfba9..ed430ee59d5 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
@@ -124,8 +124,8 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
   }
   else
   {
-    extents->x_bearing = font->em_scalef_x (param.min_x.to_real ());
-    extents->width = font->em_scalef_x (param.max_x.to_real ()) - extents->x_bearing;
+    extents->x_bearing = roundf (param.min_x.to_real ());
+    extents->width = roundf (param.max_x.to_real () - extents->x_bearing);
   }
   if (param.min_y >= param.max_y)
   {
@@ -134,10 +134,21 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
   }
   else
   {
-    extents->y_bearing = font->em_scalef_y (param.max_y.to_real ());
-    extents->height = font->em_scalef_y (param.min_y.to_real ()) - extents->y_bearing;
+    extents->y_bearing = roundf (param.max_y.to_real ());
+    extents->height = roundf (param.min_y.to_real () - extents->y_bearing);
   }
 
+  font->scale_glyph_extents (extents);
+
+  return true;
+}
+
+bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
+{
+  funcs->push_clip_glyph (data, glyph, font);
+  funcs->color (data, true, foreground);
+  funcs->pop_clip (data);
+
   return true;
 }
 
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 7e1a3d0dba1..bfbc26b96ec 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
@@ -30,6 +30,7 @@
 #include "hb-ot-cff-common.hh"
 #include "hb-subset-cff2.hh"
 #include "hb-draw.hh"
+#include "hb-paint.hh"
 
 namespace CFF {
 
@@ -56,7 +57,7 @@ struct CFF2FDSelect
     unsigned int size = src.get_size (num_glyphs);
     CFF2FDSelect *dest = c->allocate_size (size);
     if (unlikely (!dest)) return_trace (false);
-    memcpy (dest, &src, size);
+    hb_memcpy (dest, &src, size);
     return_trace (true);
   }
 
@@ -124,7 +125,7 @@ struct CFF2VariationStore
     unsigned int size_ = varStore->get_size ();
     CFF2VariationStore *dest = c->allocate_size (size_);
     if (unlikely (!dest)) return_trace (false);
-    memcpy (dest, varStore, size_);
+    hb_memcpy (dest, varStore, size_);
     return_trace (true);
   }
 
@@ -282,9 +283,6 @@ struct cff2_private_dict_opset_t : dict_opset_t
       case OpCode_BlueFuzz:
       case OpCode_ExpansionFactor:
       case OpCode_LanguageGroup:
-        val.single_val = env.argStack.pop_num ();
-        env.clear_args ();
-        break;
       case OpCode_BlueValues:
       case OpCode_OtherBlues:
       case OpCode_FamilyBlues:
@@ -483,13 +481,18 @@ struct cff2
       blob = nullptr;
     }
 
+    hb_map_t *create_glyph_to_sid_map () const
+    {
+      return nullptr;
+    }
+
     bool is_valid () const { return blob; }
 
     protected:
-    hb_blob_t                   *blob = nullptr;
     hb_sanitize_context_t       sc;
 
     public:
+    hb_blob_t                   *blob = nullptr;
     cff2_top_dict_values_t      topDict;
     const CFF2Subrs             *globalSubrs = nullptr;
     const CFF2VariationStore    *varStore = nullptr;
@@ -511,6 +514,7 @@ struct cff2
     HB_INTERNAL bool get_extents (hb_font_t *font,
                                   hb_codepoint_t glyph,
                                   hb_glyph_extents_t *extents) const;
+    HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) 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 4b6e542fb87..eb1dd2bc0d1 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
@@ -31,6 +31,7 @@
 #include "hb-ot-shaper-arabic-pua.hh"
 #include "hb-open-type.hh"
 #include "hb-set.hh"
+#include "hb-cache.hh"
 
 /*
  * cmap -- Character to Glyph Index Mapping
@@ -403,7 +404,7 @@ struct CmapSubtableFormat4
                  unsigned distance) const
         {
           if (k > last) return +1;
-          if (k < (&last)[distance]) return -1;
+          if (k < (&last)[distance]/*first*/) return -1;
           return 0;
         }
         HBUINT16 last;
@@ -412,7 +413,7 @@ struct CmapSubtableFormat4
       const HBUINT16 *found = hb_bsearch (codepoint,
                                           this->endCount,
                                           this->segCount,
-                                          2,
+                                          sizeof (CustomRange),
                                           _hb_cmp_method,
                                           this->segCount + 1);
       if (unlikely (!found))
@@ -909,7 +910,7 @@ struct DefaultUVS : SortedArray32Of
       hb_codepoint_t first = arrayZ[i].startUnicodeValue;
       hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount),
                                     (hb_codepoint_t) HB_UNICODE_MAX);
-      out->add_range (first, hb_min (last, 0x10FFFFu));
+      out->add_range (first, last);
     }
   }
 
@@ -925,37 +926,75 @@ struct DefaultUVS : SortedArray32Of
     if (unlikely (!c->copy (len))) return nullptr;
     unsigned init_len = c->length ();
 
-    hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID;
-    int count = -1;
-
-    for (const UnicodeValueRange& _ : as_array ())
+    if (this->len > unicodes->get_population () * hb_bit_storage ((unsigned) this->len))
     {
-      for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1))
+      hb_codepoint_t start = HB_SET_VALUE_INVALID;
+      hb_codepoint_t end = HB_SET_VALUE_INVALID;
+
+      for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
+           unicodes->next (&u);)
       {
-        unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt;
-        if (!unicodes->has (curEntry)) continue;
-        count += 1;
-        if (lastCode == HB_MAP_VALUE_INVALID)
-          lastCode = curEntry;
-        else if (lastCode + count != curEntry)
+        if (!as_array ().bsearch (u))
+          continue;
+        if (start == HB_SET_VALUE_INVALID)
+        {
+          start = u;
+          end = start - 1;
+        }
+        if (end + 1 != u || end - start == 255)
         {
           UnicodeValueRange rec;
-          rec.startUnicodeValue = lastCode;
-          rec.additionalCount = count - 1;
+          rec.startUnicodeValue = start;
+          rec.additionalCount = end - start;
           c->copy (rec);
-
-          lastCode = curEntry;
-          count = 0;
+          start = u;
         }
+        end = u;
+      }
+      if (start != HB_SET_VALUE_INVALID)
+      {
+        UnicodeValueRange rec;
+        rec.startUnicodeValue = start;
+        rec.additionalCount = end - start;
+        c->copy (rec);
       }
-    }
 
-    if (lastCode != HB_MAP_VALUE_INVALID)
+    }
+    else
     {
-      UnicodeValueRange rec;
-      rec.startUnicodeValue = lastCode;
-      rec.additionalCount = count;
-      c->copy (rec);
+      hb_codepoint_t lastCode = HB_SET_VALUE_INVALID;
+      int count = -1;
+
+      for (const UnicodeValueRange& _ : *this)
+      {
+        hb_codepoint_t curEntry = (hb_codepoint_t) (_.startUnicodeValue - 1);
+        hb_codepoint_t end = curEntry + _.additionalCount + 2;
+
+        for (; unicodes->next (&curEntry) && curEntry < end;)
+        {
+          count += 1;
+          if (lastCode == HB_SET_VALUE_INVALID)
+            lastCode = curEntry;
+          else if (lastCode + count != curEntry)
+          {
+            UnicodeValueRange rec;
+            rec.startUnicodeValue = lastCode;
+            rec.additionalCount = count - 1;
+            c->copy (rec);
+
+            lastCode = curEntry;
+            count = 0;
+          }
+        }
+      }
+
+      if (lastCode != HB_MAP_VALUE_INVALID)
+      {
+        UnicodeValueRange rec;
+        rec.startUnicodeValue = lastCode;
+        rec.additionalCount = count;
+        c->copy (rec);
+      }
     }
 
     if (c->length () - init_len == 0)
@@ -1376,7 +1415,7 @@ struct CmapSubtable
     switch (format) {
     case  4: return u.format4.serialize (c, it);
     case 12: return u.format12.serialize (c, it);
-    case 14: return u.format14.serialize (c, plan->unicodes, plan->glyphs_requested, plan->glyph_map, base);
+    case 14: return u.format14.serialize (c, &plan->unicodes, &plan->glyphs_requested, plan->glyph_map, base);
     default: return;
     }
   }
@@ -1474,19 +1513,67 @@ struct EncodingRecord
   DEFINE_SIZE_STATIC (8);
 };
 
+struct cmap;
+
 struct SubtableUnicodesCache {
 
  private:
-  const void* base;
-  hb_hashmap_t> cached_unicodes;
+  hb_blob_ptr_t base_blob;
+  const char* base;
+  hb_hashmap_t> cached_unicodes;
 
  public:
+
+  static SubtableUnicodesCache* create (hb_blob_ptr_t source_table)
+  {
+    SubtableUnicodesCache* cache =
+        (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache));
+    new (cache) SubtableUnicodesCache (source_table);
+    return cache;
+  }
+
+  static void destroy (void* value) {
+    if (!value) return;
+
+    SubtableUnicodesCache* cache = (SubtableUnicodesCache*) value;
+    cache->~SubtableUnicodesCache ();
+    hb_free (cache);
+  }
+
   SubtableUnicodesCache(const void* cmap_base)
-      : base(cmap_base), cached_unicodes() {}
+      : base_blob(),
+        base ((const char*) cmap_base),
+        cached_unicodes ()
+  {}
+
+  SubtableUnicodesCache(hb_blob_ptr_t base_blob_)
+      : base_blob(base_blob_),
+        base ((const char *) base_blob.get()),
+        cached_unicodes ()
+  {}
+
+  ~SubtableUnicodesCache()
+  {
+    base_blob.destroy ();
+  }
 
-  hb_set_t* set_for (const EncodingRecord* record)
+  bool same_base(const void* other) const
   {
-    if (!cached_unicodes.has ((intptr_t) record))
+    return other == (const void*) base;
+  }
+
+  const hb_set_t* set_for (const EncodingRecord* record,
+                           SubtableUnicodesCache& mutable_cache) const
+  {
+    if (cached_unicodes.has ((unsigned) ((const char *) record - base)))
+      return cached_unicodes.get ((unsigned) ((const char *) record - base));
+
+    return mutable_cache.set_for (record);
+  }
+
+  const hb_set_t* set_for (const EncodingRecord* record)
+  {
+    if (!cached_unicodes.has ((unsigned) ((const char *) record - base)))
     {
       hb_set_t *s = hb_set_create ();
       if (unlikely (s->in_error ()))
@@ -1494,12 +1581,12 @@ struct SubtableUnicodesCache {
 
       (base+record->subtable).collect_unicodes (s);
 
-      if (unlikely (!cached_unicodes.set ((intptr_t) record, hb::unique_ptr {s})))
+      if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr {s})))
         return hb_set_get_empty ();
 
       return s;
     }
-    return cached_unicodes.get ((intptr_t) record);
+    return cached_unicodes.get ((unsigned) ((const char *) record - base));
   }
 
 };
@@ -1523,13 +1610,30 @@ struct cmap
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap;
 
+
+  static SubtableUnicodesCache* create_filled_cache(hb_blob_ptr_t source_table) {
+    const cmap* cmap = source_table.get();
+    auto it =
+    + hb_iter (cmap->encodingRecord)
+    | hb_filter ([&](const EncodingRecord& _) {
+      return cmap::filter_encoding_records_for_subset (cmap, _);
+    })
+    ;
+
+    SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table);
+    for (const EncodingRecord& _ : it)
+      cache->set_for(&_); // populate the cache for this encoding record.
+
+    return cache;
+  }
+
   template
   bool serialize (hb_serialize_context_t *c,
                   Iterator it,
                   EncodingRecIter encodingrec_iter,
                   const void *base,
-                  const hb_subset_plan_t *plan,
+                  hb_subset_plan_t *plan,
                   bool drop_format_4 = false)
   {
     if (unlikely (!c->extend_min ((*this))))  return false;
@@ -1538,7 +1642,14 @@ struct cmap
     unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
     auto snap = c->snapshot ();
 
-    SubtableUnicodesCache unicodes_cache (base);
+    SubtableUnicodesCache local_unicodes_cache (base);
+    const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache;
+
+    if (plan->accelerator &&
+        plan->accelerator->cmap_cache &&
+        plan->accelerator->cmap_cache->same_base (base))
+      unicodes_cache = plan->accelerator->cmap_cache;
+
     for (const EncodingRecord& _ : encodingrec_iter)
     {
       if (c->in_error ())
@@ -1547,7 +1658,7 @@ struct cmap
       unsigned format = (base+_.subtable).u.format;
       if (format != 4 && format != 12 && format != 14) continue;
 
-      hb_set_t* unicodes_set = unicodes_cache.set_for (&_);
+      const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache);
 
       if (!drop_format_4 && format == 4)
       {
@@ -1566,7 +1677,13 @@ struct cmap
 
       else if (format == 12)
       {
-        if (_can_drop (_, *unicodes_set, base, unicodes_cache, + it | hb_map (hb_first), encodingrec_iter)) continue;
+        if (_can_drop (_,
+                       *unicodes_set,
+                       base,
+                       *unicodes_cache,
+                       local_unicodes_cache,
+                       + it | hb_map (hb_first), encodingrec_iter))
+          continue;
         c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx);
       }
       else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx);
@@ -1585,7 +1702,8 @@ struct cmap
   bool _can_drop (const EncodingRecord& cmap12,
                   const hb_set_t& cmap12_unicodes,
                   const void* base,
-                  SubtableUnicodesCache& unicodes_cache,
+                  const SubtableUnicodesCache& unicodes_cache,
+                  SubtableUnicodesCache& local_unicodes_cache,
                   Iterator subset_unicodes,
                   EncodingRecordIterator encoding_records)
   {
@@ -1616,7 +1734,7 @@ struct cmap
           || (base+_.subtable).get_language() != target_language)
         continue;
 
-      hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_);
+      const hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_, local_unicodes_cache);
 
       auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes);
       auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes);
@@ -1653,17 +1771,9 @@ struct cmap
 
     auto encodingrec_iter =
     + hb_iter (encodingRecord)
-    | hb_filter ([&] (const EncodingRecord& _)
-                {
-                  if ((_.platformID == 0 && _.encodingID == 3) ||
-                      (_.platformID == 0 && _.encodingID == 4) ||
-                      (_.platformID == 3 && _.encodingID == 1) ||
-                      (_.platformID == 3 && _.encodingID == 10) ||
-                      (this + _.subtable).u.format == 14)
-                    return true;
-
-                  return false;
-                })
+    | hb_filter ([&](const EncodingRecord& _) {
+      return cmap::filter_encoding_records_for_subset (this, _);
+    })
     ;
 
     if (unlikely (!encodingrec_iter.len ())) return_trace (false);
@@ -1692,7 +1802,11 @@ struct cmap
                  { return (_.second != HB_MAP_VALUE_INVALID); })
     ;
 
-    return_trace (cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan));
+    return_trace (cmap_prime->serialize (c->serializer,
+                                         it,
+                                         encodingrec_iter,
+                                         this,
+                                         c->plan));
   }
 
   const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const
@@ -1728,6 +1842,8 @@ struct cmap
 
   struct accelerator_t
   {
+    using cache_t = hb_cache_t<21, 16, 8, true>;
+
     accelerator_t (hb_face_t *face)
     {
       this->table = hb_sanitize_context_t ().reference_table (face);
@@ -1782,26 +1898,43 @@ struct cmap
     }
     ~accelerator_t () { this->table.destroy (); }
 
+    inline bool _cached_get (hb_codepoint_t unicode,
+                             hb_codepoint_t *glyph,
+                             cache_t *cache) const
+    {
+      unsigned v;
+      if (cache && cache->get (unicode, &v))
+      {
+        *glyph = v;
+        return true;
+      }
+      bool ret  = this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph);
+
+      if (cache && ret)
+        cache->set (unicode, *glyph);
+      return ret;
+    }
+
     bool get_nominal_glyph (hb_codepoint_t  unicode,
-                            hb_codepoint_t *glyph) const
+                            hb_codepoint_t *glyph,
+                            cache_t *cache = nullptr) const
     {
-      if (unlikely (!this->get_glyph_funcZ)) return false;
-      return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph);
+      if (unlikely (!this->get_glyph_funcZ)) return 0;
+      return _cached_get (unicode, glyph, cache);
     }
+
     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) const
+                                     unsigned int glyph_stride,
+                                     cache_t *cache = nullptr) const
     {
       if (unlikely (!this->get_glyph_funcZ)) return 0;
 
-      hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ;
-      const void *get_glyph_data = this->get_glyph_data;
-
       unsigned int done;
       for (done = 0;
-           done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph);
+           done < count && _cached_get (*first_unicode, first_glyph, cache);
            done++)
       {
         first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride);
@@ -1812,7 +1945,8 @@ struct cmap
 
     bool get_variation_glyph (hb_codepoint_t  unicode,
                               hb_codepoint_t  variation_selector,
-                              hb_codepoint_t *glyph) const
+                              hb_codepoint_t *glyph,
+                              cache_t *cache = nullptr) const
     {
       switch (this->subtable_uvs->get_glyph_variant (unicode,
                                                      variation_selector,
@@ -1823,7 +1957,7 @@ struct cmap
         case GLYPH_VARIANT_USE_DEFAULT: break;
       }
 
-      return get_nominal_glyph (unicode, glyph);
+      return get_nominal_glyph (unicode, glyph, cache);
     }
 
     void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const
@@ -1928,6 +2062,19 @@ struct cmap
                   encodingRecord.sanitize (c, this));
   }
 
+ private:
+
+  static bool filter_encoding_records_for_subset(const cmap* cmap,
+                                                 const EncodingRecord& _)
+  {
+    return
+        (_.platformID == 0 && _.encodingID == 3) ||
+        (_.platformID == 0 && _.encodingID == 4) ||
+        (_.platformID == 3 && _.encodingID == 1) ||
+        (_.platformID == 3 && _.encodingID == 10) ||
+        (cmap + _.subtable).u.format == 14;
+  }
+
   protected:
   HBUINT16      version;        /* Table version number (0). */
   SortedArray16Of
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 5b45dbcd9fc..ef46fe5429a 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc
@@ -31,11 +31,11 @@
 
 #include "hb-ot.h"
 
-#include "hb-ot-color-cbdt-table.hh"
-#include "hb-ot-color-colr-table.hh"
-#include "hb-ot-color-cpal-table.hh"
-#include "hb-ot-color-sbix-table.hh"
-#include "hb-ot-color-svg-table.hh"
+#include "OT/Color/CBDT/CBDT.hh"
+#include "OT/Color/COLR/COLR.hh"
+#include "OT/Color/CPAL/CPAL.hh"
+#include "OT/Color/sbix/sbix.hh"
+#include "OT/Color/svg/svg.hh"
 
 
 /**
@@ -61,7 +61,7 @@
  *
  * Tests whether a face includes a `CPAL` color-palette table.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 2.1.0
  */
@@ -167,6 +167,10 @@ hb_ot_color_palette_get_flags (hb_face_t *face,
  * for allocating a buffer of suitable size before calling
  * hb_ot_color_palette_get_colors() a second time.
  *
+ * The RGBA values in the palette are unpremultiplied. See the
+ * OpenType spec [CPAL](https://learn.microsoft.com/en-us/typography/opentype/spec/cpal)
+ * section for details.
+ *
  * Return value: the total number of colors in the palette
  *
  * Since: 2.1.0
@@ -190,16 +194,53 @@ hb_ot_color_palette_get_colors (hb_face_t     *face,
  * hb_ot_color_has_layers:
  * @face: #hb_face_t to work upon
  *
- * Tests whether a face includes any `COLR` color layers.
+ * Tests whether a face includes a `COLR` table
+ * with data according to COLRv0.
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 2.1.0
  */
 hb_bool_t
 hb_ot_color_has_layers (hb_face_t *face)
 {
-  return face->table.COLR->has_data ();
+  return face->table.COLR->has_v0_data ();
+}
+
+/**
+ * hb_ot_color_has_paint:
+ * @face: #hb_face_t to work upon
+ *
+ * Tests where a face includes a `COLR` table
+ * with data according to COLRv1.
+ *
+ * Return value: `true` if data found, `false` otherwise
+ *
+ * Since: 7.0.0
+ */
+hb_bool_t
+hb_ot_color_has_paint (hb_face_t *face)
+{
+  return face->table.COLR->has_v1_data ();
+}
+
+/**
+ * hb_ot_color_glyph_has_paint:
+ * @face: #hb_face_t to work upon
+ * @glyph: The glyph index to query
+ *
+ * Tests where a face includes COLRv1 paint
+ * data for @glyph.
+ *
+ * Return value: `true` if data found, `false` otherwise
+ *
+ * Since: 7.0.0
+ */
+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);
 }
 
 /**
@@ -239,7 +280,7 @@ hb_ot_color_glyph_get_layers (hb_face_t           *face,
  *
  * Tests whether a face includes any `SVG` glyph images.
  *
- * Return value: %true if data found, %false otherwise.
+ * Return value: `true` if data found, `false` otherwise.
  *
  * Since: 2.1.0
  */
@@ -279,7 +320,7 @@ hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph)
  *
  * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables).
  *
- * Return value: %true if data found, %false otherwise
+ * Return value: `true` if data found, `false` otherwise
  *
  * Since: 2.1.0
  */
@@ -295,8 +336,8 @@ hb_ot_color_has_png (hb_face_t *face)
  * @glyph: a glyph index
  *
  * Fetches the PNG image for a glyph. This function takes a font object, not a face object,
- * as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font
- * object. If UPEM is unset, the blob returned will be the largest PNG available.
+ * as input. To get an optimally sized PNG blob, the PPEM values must be set on the @font
+ * object. If PPEM is unset, the blob returned will be the largest PNG available.
  *
  * If the glyph has no PNG image, the singleton empty blob is returned.
  *
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h
index ff659c7a458..7fa810bd96f 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h
@@ -102,6 +102,10 @@ hb_ot_color_has_layers (hb_face_t *face);
  *
  * Pairs of glyph and color index.
  *
+ * A color index of 0xFFFF does not refer to a palette
+ * color, but indicates that the foreground color should
+ * be used.
+ *
  * Since: 2.1.0
  **/
 typedef struct hb_ot_color_layer_t {
@@ -116,6 +120,15 @@ 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. */);
 
+/* COLRv1 */
+
+HB_EXTERN hb_bool_t
+hb_ot_color_has_paint (hb_face_t *face);
+
+HB_EXTERN hb_bool_t
+hb_ot_color_glyph_has_paint (hb_face_t      *face,
+                             hb_codepoint_t  glyph);
+
 /*
  * SVG
  */
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h
index da205d6713d..24656af53bb 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h
@@ -67,26 +67,30 @@ HB_BEGIN_DECLS
 
 
 /* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */
-HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t
+HB_DEPRECATED_FOR (hb_ot_layout_table_select_script)
+HB_EXTERN hb_bool_t
 hb_ot_layout_table_choose_script (hb_face_t      *face,
                                   hb_tag_t        table_tag,
                                   const hb_tag_t *script_tags,
                                   unsigned int   *script_index,
                                   hb_tag_t       *chosen_script);
 
-HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) hb_bool_t
+HB_DEPRECATED_FOR (hb_ot_layout_script_select_language)
+HB_EXTERN hb_bool_t
 hb_ot_layout_script_find_language (hb_face_t    *face,
                                    hb_tag_t      table_tag,
                                    unsigned int  script_index,
                                    hb_tag_t      language_tag,
                                    unsigned int *language_index);
 
-HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) void
+HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language)
+HB_EXTERN void
 hb_ot_tags_from_script (hb_script_t  script,
                         hb_tag_t    *script_tag_1,
                         hb_tag_t    *script_tag_2);
 
-HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t
+HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language)
+HB_EXTERN hb_tag_t
 hb_ot_tag_from_language (hb_language_t language);
 
 
@@ -121,13 +125,15 @@ typedef struct hb_ot_var_axis_t {
   float max_value;
 } hb_ot_var_axis_t;
 
-HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) unsigned int
+HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos)
+HB_EXTERN unsigned int
 hb_ot_var_get_axes (hb_face_t        *face,
                     unsigned int      start_offset,
                     unsigned int     *axes_count /* IN/OUT */,
                     hb_ot_var_axis_t *axes_array /* OUT */);
 
-HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) hb_bool_t
+HB_DEPRECATED_FOR (hb_ot_var_find_axis_info)
+HB_EXTERN hb_bool_t
 hb_ot_var_find_axis (hb_face_t        *face,
                      hb_tag_t          axis_tag,
                      unsigned int     *axis_index,
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 c05034b3bb5..b552dfdd9da 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
@@ -56,9 +56,9 @@ HB_OT_CORE_TABLE (OT, maxp)
 #if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT)
 HB_OT_ACCELERATOR (OT, cmap)
 #endif
-HB_OT_TABLE (OT, hhea)
+HB_OT_CORE_TABLE (OT, hhea)
 HB_OT_ACCELERATOR (OT, hmtx)
-HB_OT_TABLE (OT, OS2)
+HB_OT_CORE_TABLE (OT, OS2)
 #if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE)
 HB_OT_ACCELERATOR (OT, post)
 #endif
@@ -66,7 +66,7 @@ HB_OT_ACCELERATOR (OT, post)
 HB_OT_ACCELERATOR (OT, name)
 #endif
 #ifndef HB_NO_STYLE
-HB_OT_TABLE (OT, STAT)
+HB_OT_CORE_TABLE (OT, STAT)
 #endif
 #ifndef HB_NO_META
 HB_OT_ACCELERATOR (OT, meta)
@@ -74,9 +74,9 @@ HB_OT_ACCELERATOR (OT, meta)
 
 /* Vertical layout. */
 #ifndef HB_NO_VERTICAL
-HB_OT_TABLE (OT, vhea)
+HB_OT_CORE_TABLE (OT, vhea)
 HB_OT_ACCELERATOR (OT, vmtx)
-HB_OT_TABLE (OT, VORG)
+HB_OT_CORE_TABLE (OT, VORG)
 #endif
 
 /* TrueType outlines. */
@@ -91,15 +91,16 @@ HB_OT_ACCELERATOR (OT, cff2)
 
 /* OpenType variations. */
 #ifndef HB_NO_VAR
-HB_OT_TABLE (OT, fvar)
-HB_OT_TABLE (OT, avar)
+HB_OT_CORE_TABLE (OT, fvar)
+HB_OT_CORE_TABLE (OT, avar)
+HB_OT_CORE_TABLE (OT, cvar)
 HB_OT_ACCELERATOR (OT, gvar)
-HB_OT_TABLE (OT, MVAR)
+HB_OT_CORE_TABLE (OT, MVAR)
 #endif
 
 /* Legacy kern. */
 #ifndef HB_NO_OT_KERN
-HB_OT_TABLE (OT, kern)
+HB_OT_CORE_TABLE (OT, kern)
 #endif
 
 /* OpenType shaping. */
@@ -107,12 +108,12 @@ HB_OT_TABLE (OT, kern)
 HB_OT_ACCELERATOR (OT, GDEF)
 HB_OT_ACCELERATOR (OT, GSUB)
 HB_OT_ACCELERATOR (OT, GPOS)
-//HB_OT_TABLE (OT, JSTF)
+//HB_OT_CORE_TABLE (OT, JSTF)
 #endif
 
 /* OpenType baseline. */
 #ifndef HB_NO_BASE
-HB_OT_TABLE (OT, BASE)
+HB_OT_CORE_TABLE (OT, BASE)
 #endif
 
 /* AAT shaping. */
@@ -129,8 +130,8 @@ HB_OT_TABLE (AAT, feat)
 
 /* OpenType color fonts. */
 #ifndef HB_NO_COLOR
-HB_OT_TABLE (OT, COLR)
-HB_OT_TABLE (OT, CPAL)
+HB_OT_CORE_TABLE (OT, COLR)
+HB_OT_CORE_TABLE (OT, CPAL)
 HB_OT_ACCELERATOR (OT, CBDT)
 HB_OT_ACCELERATOR (OT, sbix)
 HB_OT_ACCELERATOR (OT, SVG)
@@ -138,7 +139,7 @@ HB_OT_ACCELERATOR (OT, SVG)
 
 /* OpenType math. */
 #ifndef HB_NO_MATH
-HB_OT_TABLE (OT, MATH)
+HB_OT_CORE_TABLE (OT, MATH)
 #endif
 
 
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 5ef8df43ce7..2243ee02874 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc
@@ -35,9 +35,9 @@
 #include "hb-ot-meta-table.hh"
 #include "hb-ot-name-table.hh"
 #include "hb-ot-post-table.hh"
-#include "hb-ot-color-cbdt-table.hh"
-#include "hb-ot-color-sbix-table.hh"
-#include "hb-ot-color-svg-table.hh"
+#include "OT/Color/CBDT/CBDT.hh"
+#include "OT/Color/sbix/sbix.hh"
+#include "OT/Color/svg/svg.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-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 2f3d4759118..884cea0f6b3 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc
@@ -34,18 +34,20 @@
 #include "hb-font.hh"
 #include "hb-machinery.hh"
 #include "hb-ot-face.hh"
+#include "hb-outline.hh"
 
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-cff1-table.hh"
 #include "hb-ot-cff2-table.hh"
 #include "hb-ot-hmtx-table.hh"
-#include "hb-ot-os2-table.hh"
 #include "hb-ot-post-table.hh"
 #include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-ot-vorg-table.hh"
-#include "hb-ot-color-cbdt-table.hh"
-#include "hb-ot-color-sbix-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"
 
 
 /**
@@ -59,13 +61,20 @@
  * 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>;
+
+static hb_user_data_key_t hb_ot_font_cmap_cache_user_data_key;
+
 struct hb_ot_font_t
 {
   const hb_ot_face_t *ot_face;
 
+  hb_ot_font_cmap_cache_t *cmap_cache;
+
   /* h_advance caching */
   mutable hb_atomic_int_t cached_coords_serial;
-  mutable hb_atomic_ptr_t advance_cache;
+  mutable hb_atomic_ptr_t advance_cache;
 };
 
 static hb_ot_font_t *
@@ -77,6 +86,33 @@ _hb_ot_font_create (hb_font_t *font)
 
   ot_font->ot_face = &font->face->table;
 
+  // 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;
+    cmap_cache->init ();
+    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;
+
   return ot_font;
 }
 
@@ -86,11 +122,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 ();
-  if (cache)
-  {
-    cache->fini ();
-    hb_free (cache);
-  }
+  hb_free (cache);
 
   hb_free (ot_font);
 }
@@ -104,7 +136,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;
-  return ot_face->cmap->get_nominal_glyph (unicode, glyph);
+  return ot_face->cmap->get_nominal_glyph (unicode, glyph, ot_font->cmap_cache);
 }
 
 static unsigned int
@@ -121,7 +153,8 @@ hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
   const hb_ot_face_t *ot_face = ot_font->ot_face;
   return ot_face->cmap->get_nominal_glyphs (count,
                                             first_unicode, unicode_stride,
-                                            first_glyph, glyph_stride);
+                                            first_glyph, glyph_stride,
+                                            ot_font->cmap_cache);
 }
 
 static hb_bool_t
@@ -134,7 +167,9 @@ 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;
-  return ot_face->cmap->get_variation_glyph (unicode, variation_selector, glyph);
+  return ot_face->cmap->get_variation_glyph (unicode,
+                                             variation_selector, glyph,
+                                             ot_font->cmap_cache);
 }
 
 static void
@@ -146,12 +181,15 @@ 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;
+
 #ifndef HB_NO_VAR
-  const OT::HVARVVAR &HVAR = *hmtx.var_table;
+  const OT::HVAR &HVAR = *hmtx.var_table;
   const OT::VariationStore &varStore = &HVAR + HVAR.varStore;
   OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr;
 
@@ -161,14 +199,14 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
   bool use_cache = false;
 #endif
 
-  hb_advance_cache_t *cache = nullptr;
+  hb_ot_font_advance_cache_t *cache = nullptr;
   if (use_cache)
   {
   retry:
-    cache = ot_font->advance_cache.get ();
+    cache = ot_font->advance_cache.get_acquire ();
     if (unlikely (!cache))
     {
-      cache = (hb_advance_cache_t *) hb_malloc (sizeof (hb_advance_cache_t));
+      cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t));
       if (unlikely (!cache))
       {
         use_cache = false;
@@ -181,7 +219,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
         hb_free (cache);
         goto retry;
       }
-      ot_font->cached_coords_serial.set (font->serial_coords);
+      ot_font->cached_coords_serial.set_release (font->serial_coords);
     }
   }
   out:
@@ -190,17 +228,17 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
   {
     for (unsigned int i = 0; i < count; i++)
     {
-      *first_advance = font->em_scale_x (hmtx.get_advance (*first_glyph, font, varStore_cache));
+      *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache));
       first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride);
       first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride);
     }
   }
   else
   { /* Use cache. */
-    if (ot_font->cached_coords_serial.get () != (int) font->serial_coords)
+    if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords)
     {
       ot_font->advance_cache->init ();
-      ot_font->cached_coords_serial.set (font->serial_coords);
+      ot_font->cached_coords_serial.set_release (font->serial_coords);
     }
 
     for (unsigned int i = 0; i < count; i++)
@@ -211,7 +249,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
         v = cv;
       else
       {
-        v = hmtx.get_advance (*first_glyph, font, varStore_cache);
+        v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache);
         ot_font->advance_cache->set (*first_glyph, v);
       }
       *first_advance = font->em_scale_x (v);
@@ -223,6 +261,18 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
 #ifndef HB_NO_VAR
   OT::VariationStore::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_VERTICAL
@@ -239,10 +289,12 @@ 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 ())
   {
 #ifndef HB_NO_VAR
-    const OT::HVARVVAR &VVAR = *vmtx.var_table;
+    const OT::VVAR &VVAR = *vmtx.var_table;
     const OT::VariationStore &varStore = &VVAR + VVAR.varStore;
     OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr;
 #else
@@ -251,7 +303,7 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
 
     for (unsigned int i = 0; i < count; i++)
     {
-      *first_advance = font->em_scale_y (-(int) vmtx.get_advance (*first_glyph, font, varStore_cache));
+      *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache));
       first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride);
       first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride);
     }
@@ -273,6 +325,18 @@ 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);
+    }
+  }
 }
 #endif
 
@@ -293,17 +357,28 @@ hb_ot_get_glyph_v_origin (hb_font_t *font,
   const OT::VORG &VORG = *ot_face->VORG;
   if (VORG.has_data ())
   {
-    *y = font->em_scale_y (VORG.get_y_origin (glyph));
+    float delta = 0;
+
+#ifndef HB_NO_VAR
+    const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
+    const OT::VVAR &VVAR = *vmtx.var_table;
+    if (font->num_coords)
+      VVAR.get_vorg_delta_unscaled (glyph,
+                                    font->coords, font->num_coords,
+                                    &delta);
+#endif
+
+    *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta);
     return true;
   }
 
   hb_glyph_extents_t extents = {0};
   if (ot_face->glyf->get_extents (font, glyph, &extents))
   {
-    if (ot_face->vmtx->has_data ())
+    const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
+    int tsb = 0;
+    if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb))
     {
-      const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
-      hb_position_t tsb = vmtx.get_side_bearing (font, glyph);
       *y = extents.y_bearing + font->em_scale_y (tsb);
       return true;
     }
@@ -336,17 +411,17 @@ hb_ot_get_glyph_extents (hb_font_t *font,
 
 #if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
   if (ot_face->sbix->get_extents (font, glyph, extents)) return true;
+  if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
+#endif
+#if !defined(HB_NO_COLOR) && !defined(HB_NO_PAINT)
+  if (ot_face->COLR->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
   if (ot_face->cff1->get_extents (font, glyph, extents)) return true;
   if (ot_face->cff2->get_extents (font, glyph, extents)) return true;
 #endif
-#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
-  if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
-#endif
 
-  // TODO Hook up side-bearings variations.
   return false;
 }
 
@@ -391,9 +466,16 @@ hb_ot_get_font_h_extents (hb_font_t *font,
                           hb_font_extents_t *metrics,
                           void *user_data HB_UNUSED)
 {
-  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);
+  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;
 }
 
 #ifndef HB_NO_VERTICAL
@@ -411,17 +493,62 @@ hb_ot_get_font_v_extents (hb_font_t *font,
 
 #ifndef HB_NO_DRAW
 static void
-hb_ot_get_glyph_shape (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_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)
+{
+  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);
+    if (!font->face->table.glyf->get_path (font, glyph, draw_session))
+#ifndef HB_NO_CFF
+    if (!font->face->table.cff1->get_path (font, glyph, draw_session))
+    if (!font->face->table.cff2->get_path (font, glyph, draw_session))
+#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);
+  }
+}
+#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)
 {
-  hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy);
-  if (font->face->table.glyf->get_path (font, glyph, draw_session)) return;
+#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;
+#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;
+#endif
+#endif
+  if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
 #ifndef HB_NO_CFF
-  if (font->face->table.cff1->get_path (font, glyph, draw_session)) return;
-  if (font->face->table.cff2->get_path (font, glyph, draw_session)) return;
+  if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
+  if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
 #endif
 }
 #endif
@@ -449,7 +576,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tface->table.glyf->get_side_bearing_var (font, glyph, is_vertical);
-}
-
-unsigned
-_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical)
-{
-  return font->face->table.glyf->get_advance_var (font, glyph, is_vertical);
-}
-#endif
-
-
 #endif
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh
index 84154467ed1..e13321ee6fa 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh
@@ -76,7 +76,7 @@ struct DeviceRecord
   HBUINT8                       maxWidth;       /* Maximum width. */
   UnsizedArrayOf       widthsZ;        /* Array of widths (numGlyphs is from the 'maxp' table). */
   public:
-  DEFINE_SIZE_ARRAY (2, widthsZ);
+  DEFINE_SIZE_UNBOUNDED (2);
 };
 
 
@@ -87,14 +87,6 @@ struct hdmx
   unsigned int get_size () const
   { return min_size + numRecords * sizeDeviceRecord; }
 
-  const DeviceRecord& operator [] (unsigned int i) const
-  {
-    /* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed.
-     * https://github.com/harfbuzz/harfbuzz/issues/1300 */
-    if (unlikely (i >= numRecords)) return Null (DeviceRecord);
-    return StructAtOffset (&this->firstDeviceRecord, i * sizeDeviceRecord);
-  }
-
   template
   bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it)
@@ -156,6 +148,7 @@ struct hdmx
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
                   !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) &&
+                  min_size + numRecords * sizeDeviceRecord > numRecords * sizeDeviceRecord &&
                   sizeDeviceRecord >= DeviceRecord::min_size &&
                   c->check_range (this, get_size ()));
   }
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh
index c87356ae16c..ab057fcfe4f 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh
@@ -63,7 +63,25 @@ struct head
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    return_trace (serialize (c->serializer));
+    head *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (false);
+
+    if (c->plan->normalized_coords)
+    {
+      if (unlikely (!c->serializer->check_assign (out->xMin, c->plan->head_maxp_info.xMin,
+                                                  HB_SERIALIZE_ERROR_INT_OVERFLOW)))
+        return_trace (false);
+      if (unlikely (!c->serializer->check_assign (out->xMax, c->plan->head_maxp_info.xMax,
+                                                  HB_SERIALIZE_ERROR_INT_OVERFLOW)))
+        return_trace (false);
+      if (unlikely (!c->serializer->check_assign (out->yMin, c->plan->head_maxp_info.yMin,
+                                                  HB_SERIALIZE_ERROR_INT_OVERFLOW)))
+        return_trace (false);
+      if (unlikely (!c->serializer->check_assign (out->yMax, c->plan->head_maxp_info.yMax,
+                                                  HB_SERIALIZE_ERROR_INT_OVERFLOW)))
+        return_trace (false);
+    }
+    return_trace (true);
   }
 
   enum mac_style_flag_t {
@@ -97,6 +115,7 @@ struct head
                                          * entire font as HBUINT32, then store
                                          * 0xB1B0AFBAu - sum. */
   HBUINT32      magicNumber;            /* Set to 0x5F0F3CF5u. */
+  public:
   HBUINT16      flags;                  /* Bit 0: Baseline for font at y=0;
                                          * Bit 1: Left sidebearing point at x=0;
                                          * Bit 2: Instructions may depend on point size;
@@ -141,6 +160,7 @@ struct head
                                          * encoded in the cmap subtables represent proper
                                          * support for those code points.
                                          * Bit 15: Reserved, set to 0. */
+  protected:
   HBUINT16      unitsPerEm;             /* Valid range is from 16 to 16384. This value
                                          * should be a power of 2 for fonts that have
                                          * TrueType outlines. */
@@ -148,10 +168,12 @@ struct head
                                            January 1, 1904. 64-bit integer */
   LONGDATETIME  modified;               /* Number of seconds since 12:00 midnight,
                                            January 1, 1904. 64-bit integer */
+  public:
   HBINT16       xMin;                   /* For all glyph bounding boxes. */
   HBINT16       yMin;                   /* For all glyph bounding boxes. */
   HBINT16       xMax;                   /* For all glyph bounding boxes. */
   HBINT16       yMax;                   /* For all glyph bounding boxes. */
+  protected:
   HBUINT16      macStyle;               /* Bit 0: Bold (if set to 1);
                                          * Bit 1: Italic (if set to 1)
                                          * Bit 2: Underline (if set to 1)
diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh
index 9883df20860..e830fd09cf0 100644
--- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh
+++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh
@@ -31,6 +31,7 @@
 #include "hb-ot-maxp-table.hh"
 #include "hb-ot-hhea-table.hh"
 #include "hb-ot-var-hvar-table.hh"
+#include "hb-ot-var-mvar-table.hh"
 #include "hb-ot-metrics.hh"
 
 /*
@@ -43,11 +44,14 @@
 #define HB_OT_TAG_vmtx HB_TAG('v','m','t','x')
 
 
-HB_INTERNAL int
-_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
+HB_INTERNAL bool
+_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb);
 
 HB_INTERNAL unsigned
-_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
+_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
+
+HB_INTERNAL bool
+_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb);
 
 
 namespace OT {
@@ -62,7 +66,7 @@ struct LongMetric
 };
 
 
-template 
+template 
 struct hmtxvmtx
 {
   bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
@@ -73,11 +77,15 @@ struct hmtxvmtx
     return_trace (true);
   }
 
+  const hb_hashmap_t>* get_mtx_map (const hb_subset_plan_t *plan) const
+  { return T::is_horizontal ? &plan->hmtx_map : &plan->vmtx_map; }
 
-  bool subset_update_header (hb_subset_plan_t *plan,
-                             unsigned int num_hmetrics) const
+  bool subset_update_header (hb_subset_context_t *c,
+                             unsigned int num_hmetrics,
+                             const hb_hashmap_t> *mtx_map,
+                             const hb_map_t *bounds_map) const
   {
-    hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (plan->source, H::tableTag);
+    hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (c->plan->source, H::tableTag);
     hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob);
     hb_blob_destroy (src_blob);
 
@@ -87,9 +95,58 @@ struct hmtxvmtx
 
     unsigned int length;
     H *table = (H *) hb_blob_get_data (dest_blob, &length);
-    table->numberOfLongMetrics = num_hmetrics;
+    c->serializer->check_assign (table->numberOfLongMetrics, num_hmetrics, HB_SERIALIZE_ERROR_INT_OVERFLOW);
+
+#ifndef HB_NO_VAR
+    if (c->plan->normalized_coords)
+    {
+      auto &MVAR = *c->plan->source->table.MVAR;
+      if (T::is_horizontal)
+      {
+        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE,   caretSlopeRise);
+        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN,    caretSlopeRun);
+        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET, caretOffset);
+      }
+      else
+      {
+        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RISE,     caretSlopeRise);
+        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RUN,      caretSlopeRun);
+        HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET,   caretOffset);
+      }
+
+      int min_lsb = 0x7FFF;
+      int min_rsb = 0x7FFF;
+      int max_extent = -0x7FFF;
+      unsigned max_adv = 0;
+      for (const auto _ : *mtx_map)
+      {
+        hb_codepoint_t gid = _.first;
+        unsigned adv = _.second.first;
+        int lsb = _.second.second;
+        max_adv = hb_max (max_adv, adv);
+
+        if (bounds_map->has (gid))
+        {
+          unsigned bound_width = bounds_map->get (gid);
+          int rsb = adv - lsb - bound_width;
+          int extent = lsb + bound_width;
+          min_lsb = hb_min (min_lsb, lsb);
+          min_rsb = hb_min (min_rsb, rsb);
+          max_extent = hb_max (max_extent, extent);
+        }
+      }
+
+      table->advanceMax = max_adv;
+      if (!bounds_map->is_empty ())
+      {
+        table->minLeadingBearing = min_lsb;
+        table->minTrailingBearing = min_rsb;
+        table->maxExtent = max_extent;
+      }
+    }
+#endif
 
-    bool result = plan->add_table (H::tableTag, dest_blob);
+    bool result = c->plan->add_table (H::tableTag, dest_blob);
     hb_blob_destroy (dest_blob);
 
     return result;
@@ -111,12 +168,19 @@ struct hmtxvmtx
         lm.sb = _.second;
         if (unlikely (!c->embed (&lm))) return;
       }
-      else
+      else if (idx < 0x10000u)
       {
         FWORD *sb = c->allocate_size (FWORD::static_size);
         if (unlikely (!sb)) return;
         *sb = _.second;
       }
+      else
+      {
+        // TODO: This does not do tail optimization.
+        UFWORD *adv = c->allocate_size (UFWORD::static_size);
+        if (unlikely (!adv)) return;
+        *adv = _.first;
+      }
       idx++;
     }
   }
@@ -130,14 +194,15 @@ struct hmtxvmtx
 
     accelerator_t _mtx (c->plan->source);
     unsigned num_long_metrics;
+    const hb_hashmap_t> *mtx_map = get_mtx_map (c->plan);
     {
       /* Determine num_long_metrics to encode. */
       auto& plan = c->plan;
-      num_long_metrics = plan->num_output_glyphs ();
-      hb_codepoint_t old_gid = 0;
-      unsigned int last_advance = plan->old_gid_for_new_gid (num_long_metrics - 1, &old_gid) ? _mtx.get_advance (old_gid) : 0;
+
+      num_long_metrics = hb_min (plan->num_output_glyphs (), 0xFFFFu);
+      unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx);
       while (num_long_metrics > 1 &&
-             last_advance == (plan->old_gid_for_new_gid (num_long_metrics - 2, &old_gid) ? _mtx.get_advance (old_gid) : 0))
+             last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx))
       {
         num_long_metrics--;
       }
@@ -145,12 +210,19 @@ struct hmtxvmtx
 
     auto it =
     + hb_range (c->plan->num_output_glyphs ())
-    | hb_map ([c, &_mtx] (unsigned _)
+    | hb_map ([c, &_mtx, mtx_map] (unsigned _)
               {
-                hb_codepoint_t old_gid;
-                if (!c->plan->old_gid_for_new_gid (_, &old_gid))
-                  return hb_pair (0u, 0);
-                return hb_pair (_mtx.get_advance (old_gid), _mtx.get_side_bearing (old_gid));
+                if (!mtx_map->has (_))
+                {
+                  hb_codepoint_t old_gid;
+                  if (!c->plan->old_gid_for_new_gid (_, &old_gid))
+                    return hb_pair (0u, 0);
+                  int lsb = 0;
+                  if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
+                    (void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb);
+                  return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb);
+                }
+                return mtx_map->get (_);
               })
     ;
 
@@ -160,7 +232,8 @@ struct hmtxvmtx
       return_trace (false);
 
     // Amend header num hmetrics
-    if (unlikely (!subset_update_header (c->plan, num_long_metrics)))
+    if (unlikely (!subset_update_header (c, num_long_metrics, mtx_map,
+                                         T::is_horizontal ? &c->plan->bounds_width_map : &c->plan->bounds_height_map)))
       return_trace (false);
 
     return_trace (true);
@@ -173,7 +246,7 @@ struct hmtxvmtx
     accelerator_t (hb_face_t *face)
     {
       table = hb_sanitize_context_t ().reference_table (face, T::tableTag);
-      var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag);
+      var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag);
 
       default_advance = T::is_horizontal ? hb_face_get_upem (face) / 2 : hb_face_get_upem (face);
 
@@ -221,36 +294,46 @@ struct hmtxvmtx
 
     bool has_data () const { return (bool) num_bearings; }
 
-    int get_side_bearing (hb_codepoint_t glyph) const
+    bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph,
+                                                   int *lsb) const
     {
       if (glyph < num_long_metrics)
-        return table->longMetricZ[glyph].sb;
+      {
+        *lsb = table->longMetricZ[glyph].sb;
+        return true;
+      }
 
       if (unlikely (glyph >= num_bearings))
-        return 0;
+        return false;
 
       const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
-      return bearings[glyph - num_long_metrics];
+      *lsb = bearings[glyph - num_long_metrics];
+      return true;
     }
 
-    int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const
+    bool get_leading_bearing_with_var_unscaled (hb_font_t *font,
+                                                hb_codepoint_t glyph,
+                                                int *lsb) const
     {
-      int side_bearing = get_side_bearing (glyph);
+      if (!font->num_coords)
+        return get_leading_bearing_without_var_unscaled (glyph, lsb);
 
 #ifndef HB_NO_VAR
-      if (unlikely (glyph >= num_bearings) || !font->num_coords)
-        return side_bearing;
-
-      if (var_table.get_length ())
-        return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords);
+      float delta;
+      if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) &&
+          get_leading_bearing_without_var_unscaled (glyph, lsb))
+      {
+        *lsb += roundf (delta);
+        return true;
+      }
 
-      return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
+      return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx, lsb);
 #else
-      return side_bearing;
+      return false;
 #endif
     }
 
-    unsigned int get_advance (hb_codepoint_t glyph) const
+    unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const
     {
       /* OpenType case. */
       if (glyph < num_bearings)
@@ -262,7 +345,7 @@ struct hmtxvmtx
       if (unlikely (!num_advances))
         return default_advance;
 
-#ifdef HB_NO_BORING_EXPANSION
+#ifdef HB_NO_BEYOND_64K
       return 0;
 #endif
 
@@ -272,10 +355,8 @@ struct hmtxvmtx
       /* num_bearings <= glyph < num_glyphs;
        * num_bearings <= num_advances */
 
-      /* TODO Optimize */
-
       if (num_bearings == num_advances)
-        return get_advance (num_bearings - 1);
+        return get_advance_without_var_unscaled (num_bearings - 1);
 
       const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
       const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics];
@@ -283,20 +364,22 @@ struct hmtxvmtx
       return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)];
     }
 
-    unsigned int get_advance (hb_codepoint_t  glyph,
-                              hb_font_t      *font,
-                              VariationStore::cache_t *store_cache = nullptr) const
+    unsigned get_advance_with_var_unscaled (hb_codepoint_t  glyph,
+                                            hb_font_t      *font,
+                                            VariationStore::cache_t *store_cache = nullptr) const
     {
-      unsigned int advance = get_advance (glyph);
+      unsigned int advance = get_advance_without_var_unscaled (glyph);
 
 #ifndef HB_NO_VAR
       if (unlikely (glyph >= num_bearings) || !font->num_coords)
         return advance;
 
       if (var_table.get_length ())
-        return advance + roundf (var_table->get_advance_var (glyph, font, store_cache)); // TODO Optimize?!
+        return advance + roundf (var_table->get_advance_delta_unscaled (glyph,
+                                                                        font->coords, font->num_coords,
+                                                                        store_cache));
 
-      return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
+      return _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
 #else
       return advance;
 #endif
@@ -313,9 +396,25 @@ struct hmtxvmtx
 
     public:
     hb_blob_ptr_t table;
-    hb_blob_ptr_t var_table;
+    hb_blob_ptr_t var_table;
   };
 
+  /* get advance: when no variations, call get_advance_without_var_unscaled.
+   * when there're variations, get advance value from mtx_map in subset_plan*/
+  unsigned get_new_gid_advance_unscaled (const hb_subset_plan_t *plan,
+                                         const hb_hashmap_t> *mtx_map,
+                                         unsigned new_gid,
+                                         const accelerator_t &_mtx) const
+  {
+    if (mtx_map->is_empty ())
+    {
+      hb_codepoint_t old_gid = 0;
+      return plan->old_gid_for_new_gid (new_gid, &old_gid) ?
+             _mtx.get_advance_without_var_unscaled (old_gid) : 0;
+    }
+    return mtx_map->get (new_gid).first;
+  }
+
   protected:
   UnsizedArrayOf
                 longMetricZ;    /* Paired advance width and leading
@@ -346,12 +445,12 @@ struct hmtxvmtx
   DEFINE_SIZE_ARRAY (0, longMetricZ);
 };
 
-struct hmtx : hmtxvmtx {
+struct hmtx : hmtxvmtx {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx;
   static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR;
   static constexpr bool is_horizontal = true;
 };
-struct vmtx : hmtxvmtx {
+struct vmtx : hmtxvmtx {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx;
   static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR;
   static constexpr bool is_horizontal = false;
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 b9b5b76a97c..bcf12221619 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
@@ -49,7 +49,7 @@ struct BaseCoordFormat1
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
   protected:
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 ed8acdbd62a..1ff697b1b63 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
@@ -35,69 +35,42 @@
 #include "hb-set.hh"
 #include "hb-bimap.hh"
 
+#include "OT/Layout/Common/Coverage.hh"
+#include "OT/Layout/types.hh"
 
-#ifndef HB_MAX_NESTING_LEVEL
-#define HB_MAX_NESTING_LEVEL    64
-#endif
-#ifndef HB_MAX_CONTEXT_LENGTH
-#define HB_MAX_CONTEXT_LENGTH   64
-#endif
-#ifndef HB_CLOSURE_MAX_STAGES
-/*
- * The maximum number of times a lookup can be applied during shaping.
- * Used to limit the number of iterations of the closure algorithm.
- * This must be larger than the number of times add_pause() is
- * called in a collect_features call of any shaper.
- */
-#define HB_CLOSURE_MAX_STAGES   32
-#endif
-
-#ifndef HB_MAX_SCRIPTS
-#define HB_MAX_SCRIPTS  500
-#endif
-
-#ifndef HB_MAX_LANGSYS
-#define HB_MAX_LANGSYS  2000
-#endif
-
-#ifndef HB_MAX_LANGSYS_FEATURE_COUNT
-#define HB_MAX_LANGSYS_FEATURE_COUNT 50000
-#endif
-
-#ifndef HB_MAX_FEATURES
-#define HB_MAX_FEATURES 750
-#endif
-
-#ifndef HB_MAX_FEATURE_INDICES
-#define HB_MAX_FEATURE_INDICES  1500
-#endif
-
-#ifndef HB_MAX_LOOKUP_VISIT_COUNT
-#define HB_MAX_LOOKUP_VISIT_COUNT       35000
-#endif
+// TODO(garretrieger): cleanup these after migration.
+using OT::Layout::Common::Coverage;
+using OT::Layout::Common::RangeRecord;
+using OT::Layout::SmallTypes;
+using OT::Layout::MediumTypes;
 
 
 namespace OT {
 
-
-#define NOT_COVERED             ((unsigned int) -1)
-
-
 template
-static inline void Coverage_serialize (hb_serialize_context_t *c,
+static inline bool ClassDef_serialize (hb_serialize_context_t *c,
                                        Iterator it);
 
-template
-static inline void ClassDef_serialize (hb_serialize_context_t *c,
-                                       Iterator it);
-
-static void ClassDef_remap_and_serialize (
+static bool ClassDef_remap_and_serialize (
     hb_serialize_context_t *c,
     const hb_set_t &klasses,
     bool use_class_zero,
     hb_sorted_vector_t> &glyph_and_klass, /* IN/OUT */
     hb_map_t *klass_map /*IN/OUT*/);
 
+struct hb_collect_feature_substitutes_with_var_context_t
+{
+  const hb_map_t *axes_index_tag_map;
+  const hb_hashmap_t *axes_location;
+  hb_hashmap_t> *record_cond_idx_map;
+  hb_hashmap_t *feature_substitutes_map;
+
+  // not stored in subset_plan
+  hb_set_t *feature_indices;
+  bool apply;
+  unsigned cur_record_idx;
+  hb_hashmap_t, unsigned> *conditionset_map;
+};
 
 struct hb_prune_langsys_context_t
 {
@@ -164,24 +137,40 @@ struct hb_subset_layout_context_t :
   const hb_map_t *lookup_index_map;
   const hb_hashmap_t> *script_langsys_map;
   const hb_map_t *feature_index_map;
+  const hb_hashmap_t *feature_substitutes_map;
+  hb_hashmap_t> *feature_record_cond_idx_map;
+
   unsigned cur_script_index;
+  unsigned cur_feature_var_record_idx;
 
   hb_subset_layout_context_t (hb_subset_context_t *c_,
-                              hb_tag_t tag_,
-                              hb_map_t *lookup_map_,
-                              hb_hashmap_t> *script_langsys_map_,
-                              hb_map_t *feature_index_map_) :
+                              hb_tag_t tag_) :
                                 subset_context (c_),
                                 table_tag (tag_),
-                                lookup_index_map (lookup_map_),
-                                script_langsys_map (script_langsys_map_),
-                                feature_index_map (feature_index_map_),
                                 cur_script_index (0xFFFFu),
+                                cur_feature_var_record_idx (0u),
                                 script_count (0),
                                 langsys_count (0),
                                 feature_index_count (0),
                                 lookup_index_count (0)
-  {}
+  {
+    if (tag_ == HB_OT_TAG_GSUB)
+    {
+      lookup_index_map = &c_->plan->gsub_lookups;
+      script_langsys_map = &c_->plan->gsub_langsys;
+      feature_index_map = &c_->plan->gsub_features;
+      feature_substitutes_map = &c_->plan->gsub_feature_substitutes_map;
+      feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gsub_feature_record_cond_idx_map;
+    }
+    else
+    {
+      lookup_index_map = &c_->plan->gpos_lookups;
+      script_langsys_map = &c_->plan->gpos_langsys;
+      feature_index_map = &c_->plan->gpos_features;
+      feature_substitutes_map = &c_->plan->gpos_feature_substitutes_map;
+      feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gpos_feature_record_cond_idx_map;
+    }
+  }
 
   private:
   unsigned script_count;
@@ -190,6 +179,7 @@ struct hb_subset_layout_context_t :
   unsigned lookup_index_count;
 };
 
+struct VariationStore;
 struct hb_collect_variation_indices_context_t :
        hb_dispatch_context_t
 {
@@ -198,15 +188,27 @@ struct hb_collect_variation_indices_context_t :
   static return_t default_return_value () { return hb_empty_t (); }
 
   hb_set_t *layout_variation_indices;
+  hb_hashmap_t> *varidx_delta_map;
+  hb_font_t *font;
+  const VariationStore *var_store;
   const hb_set_t *glyph_set;
   const hb_map_t *gpos_lookups;
+  float *store_cache;
 
   hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_,
+                                          hb_hashmap_t> *varidx_delta_map_,
+                                          hb_font_t *font_,
+                                          const VariationStore *var_store_,
                                           const hb_set_t *glyph_set_,
-                                          const hb_map_t *gpos_lookups_) :
+                                          const hb_map_t *gpos_lookups_,
+                                          float *store_cache_) :
                                         layout_variation_indices (layout_variation_indices_),
+                                        varidx_delta_map (varidx_delta_map_),
+                                        font (font_),
+                                        var_store (var_store_),
                                         glyph_set (glyph_set_),
-                                        gpos_lookups (gpos_lookups_) {}
+                                        gpos_lookups (gpos_lookups_),
+                                        store_cache (store_cache_) {}
 };
 
 template
@@ -315,6 +317,31 @@ struct subset_record_array_t
   const void *base;
 };
 
+template
+struct subset_record_array_arg_t
+{
+  subset_record_array_arg_t (hb_subset_layout_context_t *c_, OutputArray* out_,
+                             const void *base_,
+                             Arg &&arg_) : subset_layout_context (c_),
+                                           out (out_), base (base_), arg (arg_) {}
+
+  template 
+  void
+  operator () (T&& record)
+  {
+    auto snap = subset_layout_context->subset_context->serializer->snapshot ();
+    bool ret = record.subset (subset_layout_context, base, arg);
+    if (!ret) subset_layout_context->subset_context->serializer->revert (snap);
+    else out->len++;
+  }
+
+  private:
+  hb_subset_layout_context_t *subset_layout_context;
+  OutputArray *out;
+  const void *base;
+  Arg &&arg;
+};
+
 /*
  * Helper to subset a RecordList/record array. Subsets each Record in the array and
  * discards the record if the subset operation returns false.
@@ -326,6 +353,13 @@ struct
   operator () (hb_subset_layout_context_t *c, OutputArray* out,
                const void *base) const
   { return subset_record_array_t (c, out, base); }
+
+  /* Variant with one extra argument passed to subset */
+  template
+  subset_record_array_arg_t
+  operator () (hb_subset_layout_context_t *c, OutputArray* out,
+               const void *base, Arg &&arg) const
+  { return subset_record_array_arg_t (c, out, base, arg); }
 }
 HB_FUNCOBJ (subset_record_array);
 
@@ -377,166 +411,6 @@ HB_FUNCOBJ (serialize_math_record_array);
  * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList
  */
 
-struct Record_sanitize_closure_t {
-  hb_tag_t tag;
-  const void *list_base;
-};
-
-template 
-struct Record
-{
-  int cmp (hb_tag_t a) const { return tag.cmp (a); }
-
-  bool subset (hb_subset_layout_context_t *c, const void *base) const
-  {
-    TRACE_SUBSET (this);
-    auto *out = c->subset_context->serializer->embed (this);
-    if (unlikely (!out)) return_trace (false);
-    bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag);
-    return_trace (ret);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    const Record_sanitize_closure_t closure = {tag, base};
-    return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
-  }
-
-  Tag           tag;            /* 4-byte Tag identifier */
-  Offset16To
-                offset;         /* Offset from beginning of object holding
-                                 * the Record */
-  public:
-  DEFINE_SIZE_STATIC (6);
-};
-
-template 
-struct RecordArrayOf : SortedArray16Of>
-{
-  const Offset16To& get_offset (unsigned int i) const
-  { return (*this)[i].offset; }
-  Offset16To& get_offset (unsigned int i)
-  { return (*this)[i].offset; }
-  const Tag& get_tag (unsigned int i) const
-  { return (*this)[i].tag; }
-  unsigned int get_tags (unsigned int start_offset,
-                         unsigned int *record_count /* IN/OUT */,
-                         hb_tag_t     *record_tags /* OUT */) const
-  {
-    if (record_count)
-    {
-      + this->sub_array (start_offset, record_count)
-      | hb_map (&Record::tag)
-      | hb_sink (hb_array (record_tags, *record_count))
-      ;
-    }
-    return this->len;
-  }
-  bool find_index (hb_tag_t tag, unsigned int *index) const
-  {
-    return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
-  }
-};
-
-template 
-struct RecordListOf : RecordArrayOf
-{
-  const Type& operator [] (unsigned int i) const
-  { return this+this->get_offset (i); }
-
-  bool subset (hb_subset_context_t *c,
-               hb_subset_layout_context_t *l) const
-  {
-    TRACE_SUBSET (this);
-    auto *out = c->serializer->start_embed (*this);
-    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
-
-    + this->iter ()
-    | hb_apply (subset_record_array (l, out, this))
-    ;
-    return_trace (true);
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (RecordArrayOf::sanitize (c, this));
-  }
-};
-
-struct Feature;
-
-struct RecordListOfFeature : RecordListOf
-{
-  bool subset (hb_subset_context_t *c,
-               hb_subset_layout_context_t *l) const
-  {
-    TRACE_SUBSET (this);
-    auto *out = c->serializer->start_embed (*this);
-    if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
-
-    unsigned count = this->len;
-    + hb_zip (*this, hb_range (count))
-    | hb_filter (l->feature_index_map, hb_second)
-    | hb_map (hb_first)
-    | hb_apply (subset_record_array (l, out, this))
-    ;
-    return_trace (true);
-  }
-};
-
-struct Script;
-struct RecordListOfScript : RecordListOf