Merge branch 'master' into JDK-8348611

This commit is contained in:
Archie L. Cobbs 2025-05-07 12:09:10 -05:00
commit 102beb5ece
269 changed files with 8721 additions and 5351 deletions

View File

@ -411,6 +411,13 @@ instrumentation
special target <code>jcov-test</code> instead of <code>test</code>, e.g.
<code>make jcov-test TEST=jdk_lang</code>. This will make sure the JCov
image is built, and that JCov reporting is enabled.</p>
<p>To include JCov coverage for just a subset of all modules, you can
use the <code>--with-jcov-modules</code> arguments to
<code>configure</code>, e.g.
<code>--with-jcov-modules=jdk.compiler,java.desktop</code>.</p>
<p>For more fine-grained control, you can pass arbitrary filters to JCov
using <code>--with-jcov-filters</code>, and you can specify a specific
JDK to instrument using <code>--with-jcov-input-jdk</code>.</p>
<p>The JCov report is stored in
<code>build/$BUILD/test-results/jcov-output/report</code>.</p>
<p>Please note that running with JCov reporting can be very memory

View File

@ -345,6 +345,14 @@ The simplest way to run tests with JCov coverage report is to use the special
target `jcov-test` instead of `test`, e.g. `make jcov-test TEST=jdk_lang`. This
will make sure the JCov image is built, and that JCov reporting is enabled.
To include JCov coverage for just a subset of all modules, you can use the
`--with-jcov-modules` arguments to `configure`, e.g.
`--with-jcov-modules=jdk.compiler,java.desktop`.
For more fine-grained control, you can pass arbitrary filters to JCov using
`--with-jcov-filters`, and you can specify a specific JDK to instrument
using `--with-jcov-input-jdk`.
The JCov report is stored in `build/$BUILD/test-results/jcov-output/report`.
Please note that running with JCov reporting can be very memory intensive.

View File

@ -34,21 +34,28 @@ else
JCOV_INPUT_IMAGE_DIR := $(JDK_IMAGE_DIR)
endif
JCOV_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/jcov
#moving instrumented jdk image in and out of jcov_temp because of CODETOOLS-7902299
JCOV_TEMP := $(SUPPORT_OUTPUTDIR)/jcov_temp
JCOV_TEMP := $(JCOV_SUPPORT_DIR)/temp
ifneq ($(JCOV_MODULES), )
JCOV_MODULES_FILTER := $(foreach m, $(JCOV_MODULES), -include_module $m)
endif
$(JCOV_IMAGE_DIR)/release: $(JCOV_INPUT_IMAGE_DIR)/release
$(call LogWarn, Creating instrumented jdk image with JCov)
$(call MakeDir, $(JCOV_TEMP) $(IMAGES_OUTPUTDIR))
$(RM) -r $(JCOV_IMAGE_DIR) $(JCOV_TEMP)/*
$(CP) -r $(JCOV_INPUT_IMAGE_DIR) $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR)
$(JAVA) -Xmx3g -jar $(JCOV_HOME)/lib/jcov.jar JREInstr \
$(call ExecuteWithLog, $(JCOV_SUPPORT_DIR)/run-jcov, \
$(JAVA) -Xmx3g -jar $(JCOV_HOME)/lib/jcov.jar JREInstr \
-t $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR)/template.xml \
-rt $(JCOV_HOME)/lib/jcov_network_saver.jar \
-exclude 'java.lang.Object' \
-exclude jdk.test.Main -exclude '**\$Proxy*' \
$(JCOV_FILTERS) \
$(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR)
$(JCOV_MODULES_FILTER) $(JCOV_FILTERS) \
$(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR))
$(MV) $(JCOV_TEMP)/$(JCOV_IMAGE_SUBDIR) $(JCOV_IMAGE_DIR)
$(RMDIR) $(JCOV_TEMP)

View File

@ -115,6 +115,7 @@ JTREG_COV_OPTIONS :=
ifeq ($(TEST_OPTS_JCOV), true)
JCOV_OUTPUT_DIR := $(TEST_RESULTS_DIR)/jcov-output
JCOV_SUPPORT_DIR := $(TEST_SUPPORT_DIR)/jcov-support
JCOV_GRABBER_LOG := $(JCOV_OUTPUT_DIR)/grabber.log
JCOV_RESULT_FILE := $(JCOV_OUTPUT_DIR)/result.xml
JCOV_REPORT := $(JCOV_OUTPUT_DIR)/report
@ -1343,12 +1344,14 @@ TARGETS += run-all-tests pre-run-test post-run-test run-test-report run-test
ifeq ($(TEST_OPTS_JCOV), true)
JCOV_VM_OPTS := -Xmx4g -Djdk.xml.totalEntitySizeLimit=0 -Djdk.xml.maxGeneralEntitySizeLimit=0
jcov-do-start-grabber:
$(call MakeDir, $(JCOV_OUTPUT_DIR))
if $(JAVA) -jar $(JCOV_HOME)/lib/jcov.jar GrabberManager -status 1>/dev/null 2>&1 ; then \
$(JAVA) -jar $(JCOV_HOME)/lib/jcov.jar GrabberManager -stop -stoptimeout 3600 ; \
fi
$(JAVA) -Xmx4g -jar $(JCOV_HOME)/lib/jcov.jar Grabber -v -t \
$(JAVA) $(JCOV_VM_OPTS) -jar $(JCOV_HOME)/lib/jcov.jar Grabber -v -t \
$(JCOV_IMAGE_DIR)/template.xml -o $(JCOV_RESULT_FILE) \
1>$(JCOV_GRABBER_LOG) 2>&1 &
@ -1361,6 +1364,10 @@ ifeq ($(TEST_OPTS_JCOV), true)
$(JAVA) -jar $(JCOV_HOME)/lib/jcov.jar GrabberManager -stop -stoptimeout 3600
JCOV_REPORT_TITLE := JDK code coverage report<br/>
ifneq ($(JCOV_MODULES), )
JCOV_MODULES_FILTER := $(foreach m, $(JCOV_MODULES), -include_module $m)
JCOV_REPORT_TITLE += Included modules: $(JCOV_MODULES)<br>
endif
ifneq ($(JCOV_FILTERS), )
JCOV_REPORT_TITLE += Code filters: $(JCOV_FILTERS)<br>
endif
@ -1368,11 +1375,12 @@ ifeq ($(TEST_OPTS_JCOV), true)
jcov-gen-report: jcov-stop-grabber
$(call LogWarn, Generating JCov report ...)
$(JAVA) -Xmx4g -jar $(JCOV_HOME)/lib/jcov.jar RepGen -sourcepath \
$(call ExecuteWithLog, $(JCOV_SUPPORT_DIR)/run-jcov-repgen, \
$(JAVA) $(JCOV_VM_OPTS) -jar $(JCOV_HOME)/lib/jcov.jar RepGen -sourcepath \
`$(ECHO) $(TOPDIR)/src/*/share/classes/ | $(TR) ' ' ':'` -fmt html \
$(JCOV_FILTERS) \
$(JCOV_MODULES_FILTER) $(JCOV_FILTERS) \
-mainReportTitle "$(JCOV_REPORT_TITLE)" \
-o $(JCOV_REPORT) $(JCOV_RESULT_FILE)
-o $(JCOV_REPORT) $(JCOV_RESULT_FILE))
TARGETS += jcov-do-start-grabber jcov-start-grabber jcov-stop-grabber \
jcov-gen-report
@ -1392,7 +1400,7 @@ ifeq ($(TEST_OPTS_JCOV), true)
jcov-gen-diffcoverage: jcov-stop-grabber
$(call LogWarn, Generating diff coverage with changeset $(TEST_OPTS_JCOV_DIFF_CHANGESET) ... )
$(DIFF_COMMAND)
$(JAVA) -Xmx4g -jar $(JCOV_HOME)/lib/jcov.jar \
$(JAVA) $(JCOV_VM_OPTS) -jar $(JCOV_HOME)/lib/jcov.jar \
DiffCoverage -replaceDiff "src/.*/classes/:" -all \
$(JCOV_RESULT_FILE) $(JCOV_SOURCE_DIFF) > \
$(JCOV_DIFF_COVERAGE_REPORT)

View File

@ -217,9 +217,9 @@ else ifeq ($(OPENJDK_TARGET_OS), macosx)
else ifeq ($(OPENJDK_TARGET_OS), windows)
NUM_CORES := $(NUMBER_OF_PROCESSORS)
MEMORY_SIZE := $(shell \
$(EXPR) `wmic computersystem get totalphysicalmemory -value \
| $(GREP) = | $(SED) 's/\\r//g' \
| $(CUT) -d "=" -f 2-` / 1024 / 1024 \
$(EXPR) `powershell -Command \
"(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory" \
| $(SED) 's/\\r//g' ` / 1024 / 1024 \
)
endif
ifeq ($(NUM_CORES), )

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -180,11 +180,13 @@ AC_DEFUN([BOOTJDK_CHECK_JAVA_HOME],
# Test: Is there a java or javac in the PATH, which is a symlink to the JDK?
AC_DEFUN([BOOTJDK_CHECK_JAVA_IN_PATH_IS_SYMLINK],
[
UTIL_LOOKUP_PROGS(JAVAC_CHECK, javac, , NOFIXPATH)
UTIL_LOOKUP_PROGS(JAVA_CHECK, java, , NOFIXPATH)
BINARY="$JAVAC_CHECK"
if test "x$JAVAC_CHECK" = x; then
BINARY="$JAVA_CHECK"
UTIL_LOOKUP_PROGS(JAVAC_CHECK, javac)
UTIL_GET_EXECUTABLE(JAVAC_CHECK) # Will setup JAVAC_CHECK_EXECUTABLE
UTIL_LOOKUP_PROGS(JAVA_CHECK, java)
UTIL_GET_EXECUTABLE(JAVA_CHECK) # Will setup JAVA_CHECK_EXECUTABLE
BINARY="$JAVAC_CHECK_EXECUTABLE"
if test "x$JAVAC_CHECK_EXECUTABLE" = x; then
BINARY="$JAVA_CHECK_EXECUTABLE"
fi
if test "x$BINARY" != x; then
# So there is a java(c) binary, it might be part of a JDK.

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -75,7 +75,8 @@ AC_DEFUN([BPERF_CHECK_MEMORY_SIZE],
FOUND_MEM=yes
elif test "x$OPENJDK_BUILD_OS" = xwindows; then
# Windows, but without cygwin
MEMORY_SIZE=`wmic computersystem get totalphysicalmemory -value | grep = | cut -d "=" -f 2-`
MEMORY_SIZE=`powershell -Command \
"(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory" | $SED 's/\\r//g' `
MEMORY_SIZE=`expr $MEMORY_SIZE / 1024 / 1024`
FOUND_MEM=yes
fi

View File

@ -405,10 +405,19 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_CODE_COVERAGE],
JCOV_FILTERS="$with_jcov_filters"
fi
fi
UTIL_ARG_WITH(NAME: jcov-modules, TYPE: string,
DEFAULT: [], RESULT: JCOV_MODULES_COMMMA_SEPARATED,
DESC: [which modules to include in jcov (comma-separated)],
OPTIONAL: true)
# Replace "," with " ".
JCOV_MODULES=${JCOV_MODULES_COMMMA_SEPARATED//,/ }
AC_SUBST(JCOV_ENABLED)
AC_SUBST(JCOV_HOME)
AC_SUBST(JCOV_INPUT_JDK)
AC_SUBST(JCOV_FILTERS)
AC_SUBST(JCOV_MODULES)
])
################################################################################

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -454,6 +454,7 @@ JCOV_ENABLED := @JCOV_ENABLED@
JCOV_HOME := @JCOV_HOME@
JCOV_INPUT_JDK := @JCOV_INPUT_JDK@
JCOV_FILTERS := @JCOV_FILTERS@
JCOV_MODULES := @JCOV_MODULES@
# AddressSanitizer
ASAN_ENABLED := @ASAN_ENABLED@

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -186,7 +186,6 @@ AC_DEFUN([UTIL_CHECK_WINENV_EXEC_TYPE],
# it need to be in the PATH.
# $1: The name of the variable to fix
# $2: Where to look for the command (replaces $PATH)
# $3: set to NOFIXPATH to skip prefixing FIXPATH, even if needed on platform
AC_DEFUN([UTIL_FIXUP_EXECUTABLE],
[
input="[$]$1"
@ -282,10 +281,6 @@ AC_DEFUN([UTIL_FIXUP_EXECUTABLE],
fi
fi
if test "x$3" = xNOFIXPATH; then
fixpath_prefix=""
fi
# Now join together the path and the arguments once again
new_complete="$fixpath_prefix$new_path$arguments"
$1="$new_complete"
@ -379,7 +374,6 @@ AC_DEFUN([UTIL_SETUP_TOOL],
# $1: variable to set
# $2: executable name (or list of names) to look for
# $3: [path]
# $4: set to NOFIXPATH to skip prefixing FIXPATH, even if needed on platform
AC_DEFUN([UTIL_LOOKUP_PROGS],
[
UTIL_SETUP_TOOL($1, [
@ -421,10 +415,8 @@ AC_DEFUN([UTIL_LOOKUP_PROGS],
# If we have FIXPATH enabled, strip all instances of it and prepend
# a single one, to avoid double fixpath prefixing.
if test "x$4" != xNOFIXPATH; then
[ if [[ $FIXPATH != "" && $result =~ ^"$FIXPATH " ]]; then ]
result="\$FIXPATH ${result#"$FIXPATH "}"
fi
[ if [[ $FIXPATH != "" && $result =~ ^"$FIXPATH " ]]; then ]
result="\$FIXPATH ${result#"$FIXPATH "}"
fi
AC_MSG_RESULT([$result])
break 2;
@ -515,6 +507,24 @@ AC_DEFUN([UTIL_ADD_FIXPATH],
fi
])
################################################################################
# Return a path to the executable binary from a command line, stripping away
# any FIXPATH prefix or arguments. The resulting value can be checked for
# existence using "test -e". The result is returned in a variable named
# "$1_EXECUTABLE".
#
# $1: variable describing the command to get the binary for
AC_DEFUN([UTIL_GET_EXECUTABLE],
[
# Strip the FIXPATH prefix, if any
fixpath_stripped="[$]$1"
[ if [[ $FIXPATH != "" && $fixpath_stripped =~ ^"$FIXPATH " ]]; then ]
fixpath_stripped="${fixpath_stripped#"$FIXPATH "}"
fi
# Remove any arguments following the binary
$1_EXECUTABLE="${fixpath_stripped%% *}"
])
################################################################################
AC_DEFUN([UTIL_REMOVE_SYMBOLIC_LINKS],
[

View File

@ -241,10 +241,10 @@ var getJibProfilesCommon = function (input, data) {
// List of the main profile names used for iteration
common.main_profile_names = [
"linux-x64", "linux-x86", "macosx-x64", "macosx-aarch64",
"macosx-x64", "macosx-aarch64",
"windows-x64", "windows-aarch64",
"linux-aarch64", "linux-arm32", "linux-ppc64le", "linux-s390x",
"linux-riscv64"
"linux-x64", "linux-aarch64",
"linux-arm32", "linux-ppc64le", "linux-s390x", "linux-riscv64"
];
// These are the base settings for all the main build profiles.
@ -283,9 +283,6 @@ var getJibProfilesCommon = function (input, data) {
labels: "open"
};
common.configure_args_64bit = ["--with-target-bits=64"];
common.configure_args_32bit = ["--with-target-bits=32"];
/**
* Define common artifacts template for all main profiles
* @param o - Object containing data for artifacts
@ -412,58 +409,34 @@ var getJibProfilesProfiles = function (input, common, data) {
// Main SE profiles
var profiles = {
"linux-x64": {
target_os: "linux",
target_cpu: "x64",
dependencies: ["devkit", "gtest", "build_devkit", "graphviz", "pandoc", "tidy"],
configure_args: concat(
(input.build_cpu == "x64" ? common.configure_args_64bit
: "--openjdk-target=x86_64-linux-gnu"),
"--with-zlib=system", "--disable-dtrace",
(isWsl(input) ? [ "--host=x86_64-unknown-linux-gnu",
"--build=x86_64-unknown-linux-gnu" ] : [])),
},
"linux-x86": {
target_os: "linux",
target_cpu: "x86",
build_cpu: "x64",
dependencies: ["devkit", "gtest", "libffi"],
configure_args: concat(common.configure_args_32bit, [
"--with-jvm-variants=minimal,server",
"--with-zlib=system",
"--with-libffi=" + input.get("libffi", "home_path"),
"--enable-libffi-bundling",
"--enable-fallback-linker"
])
},
"macosx-x64": {
target_os: "macosx",
target_cpu: "x64",
dependencies: ["devkit", "gtest", "graphviz", "pandoc", "tidy"],
configure_args: concat(common.configure_args_64bit, "--with-zlib=system",
configure_args: [
"--with-zlib=system",
"--with-macosx-version-max=11.00.00",
"--enable-compatible-cds-alignment",
// Use system SetFile instead of the one in the devkit as the
// devkit one may not work on Catalina.
"SETFILE=/usr/bin/SetFile"),
"SETFILE=/usr/bin/SetFile"
],
},
"macosx-aarch64": {
target_os: "macosx",
target_cpu: "aarch64",
dependencies: ["devkit", "gtest", "graphviz", "pandoc", "tidy"],
configure_args: concat(common.configure_args_64bit,
"--with-macosx-version-max=11.00.00"),
configure_args: [
"--with-macosx-version-max=11.00.00"
],
},
"windows-x64": {
target_os: "windows",
target_cpu: "x64",
dependencies: ["devkit", "gtest", "pandoc"],
configure_args: concat(common.configure_args_64bit),
configure_args: [],
},
"windows-aarch64": {
@ -475,7 +448,19 @@ var getJibProfilesProfiles = function (input, common, data) {
],
},
"linux-aarch64": {
"linux-x64": {
target_os: "linux",
target_cpu: "x64",
dependencies: ["devkit", "gtest", "build_devkit", "graphviz", "pandoc", "tidy"],
configure_args: concat(
"--with-zlib=system",
"--disable-dtrace",
(cross_compiling ? [ "--openjdk-target=x86_64-linux-gnu" ] : []),
(isWsl(input) ? [ "--host=x86_64-unknown-linux-gnu",
"--build=x86_64-unknown-linux-gnu" ] : [])),
},
"linux-aarch64": {
target_os: "linux",
target_cpu: "aarch64",
dependencies: ["devkit", "gtest", "build_devkit", "graphviz", "pandoc", "tidy"],
@ -492,8 +477,10 @@ var getJibProfilesProfiles = function (input, common, data) {
build_cpu: "x64",
dependencies: ["devkit", "gtest", "build_devkit"],
configure_args: [
"--openjdk-target=arm-linux-gnueabihf", "--with-freetype=bundled",
"--with-abi-profile=arm-vfp-hflt", "--disable-warnings-as-errors"
"--openjdk-target=arm-linux-gnueabihf",
"--with-freetype=bundled",
"--with-abi-profile=arm-vfp-hflt",
"--disable-warnings-as-errors"
],
},
@ -503,7 +490,8 @@ var getJibProfilesProfiles = function (input, common, data) {
build_cpu: "x64",
dependencies: ["devkit", "gtest", "build_devkit"],
configure_args: [
"--openjdk-target=ppc64le-linux-gnu", "--with-freetype=bundled",
"--openjdk-target=ppc64le-linux-gnu",
"--with-freetype=bundled",
"--disable-warnings-as-errors"
],
},
@ -514,7 +502,8 @@ var getJibProfilesProfiles = function (input, common, data) {
build_cpu: "x64",
dependencies: ["devkit", "gtest", "build_devkit"],
configure_args: [
"--openjdk-target=s390x-linux-gnu", "--with-freetype=bundled",
"--openjdk-target=s390x-linux-gnu",
"--with-freetype=bundled",
"--disable-warnings-as-errors"
],
},
@ -525,7 +514,8 @@ var getJibProfilesProfiles = function (input, common, data) {
build_cpu: "x64",
dependencies: ["devkit", "gtest", "build_devkit"],
configure_args: [
"--openjdk-target=riscv64-linux-gnu", "--with-freetype=bundled",
"--openjdk-target=riscv64-linux-gnu",
"--with-freetype=bundled",
"--disable-warnings-as-errors"
],
},
@ -586,24 +576,24 @@ var getJibProfilesProfiles = function (input, common, data) {
target_os: "linux",
target_cpu: "x64",
dependencies: ["devkit", "gtest", "libffi"],
configure_args: concat(common.configure_args_64bit, [
configure_args: [
"--with-zlib=system",
"--with-jvm-variants=zero",
"--with-libffi=" + input.get("libffi", "home_path"),
"--enable-libffi-bundling",
])
]
},
"linux-aarch64-zero": {
target_os: "linux",
target_cpu: "aarch64",
dependencies: ["devkit", "gtest", "libffi"],
configure_args: concat(common.configure_args_64bit, [
configure_args: [
"--with-zlib=system",
"--with-jvm-variants=zero",
"--with-libffi=" + input.get("libffi", "home_path"),
"--enable-libffi-bundling"
])
]
},
"linux-x86-zero": {
@ -611,12 +601,13 @@ var getJibProfilesProfiles = function (input, common, data) {
target_cpu: "x86",
build_cpu: "x64",
dependencies: ["devkit", "gtest", "libffi"],
configure_args: concat(common.configure_args_32bit, [
configure_args: [
"--with-target-bits=32",
"--with-zlib=system",
"--with-jvm-variants=zero",
"--with-libffi=" + input.get("libffi", "home_path"),
"--enable-libffi-bundling"
])
]
}
}
profiles = concatObjects(profiles, zeroProfiles);
@ -635,8 +626,10 @@ var getJibProfilesProfiles = function (input, common, data) {
target_os: "linux",
target_cpu: "x64",
dependencies: ["devkit", "gtest"],
configure_args: concat(common.configure_args_64bit,
"--with-zlib=system", "--disable-precompiled-headers"),
configure_args: [
"--with-zlib=system",
"--disable-precompiled-headers"
],
},
};
profiles = concatObjects(profiles, noPchProfiles);
@ -693,9 +686,6 @@ var getJibProfilesProfiles = function (input, common, data) {
"linux-x64": {
platform: "linux-x64",
},
"linux-x86": {
platform: "linux-x86",
},
"macosx-x64": {
platform: "macos-x64",
jdk_subdir: "jdk-" + data.version + ".jdk/Contents/Home",

View File

@ -119,7 +119,6 @@ $(JMH_UNPACKED_JARS_DONE): $(JMH_RUNTIME_JARS)
$(foreach jar, $(JMH_RUNTIME_JARS), \
$$($(UNZIP) -oq $(jar) -d $(JMH_UNPACKED_DIR)))
$(RM) -r $(JMH_UNPACKED_DIR)/META-INF
$(RM) $(JMH_UNPACKED_DIR)/*.xml
$(TOUCH) $@
# Copy dependency files for inclusion in the benchmark JARs

View File

@ -180,6 +180,7 @@ bool CodeInstaller::pd_relocate(address pc, jint mark) {
case POLL_RETURN_FAR:
_instructions->relocate(pc, relocInfo::poll_return_type);
return true;
#if INCLUDE_ZGC
case Z_BARRIER_RELOCATION_FORMAT_LOAD_GOOD_BEFORE_TB_X:
_instructions->relocate(pc, barrier_Relocation::spec(), ZBarrierRelocationFormatLoadGoodBeforeTbX);
return true;
@ -192,6 +193,7 @@ bool CodeInstaller::pd_relocate(address pc, jint mark) {
case Z_BARRIER_RELOCATION_FORMAT_STORE_BAD_BEFORE_MOV:
_instructions->relocate(pc, barrier_Relocation::spec(), ZBarrierRelocationFormatStoreBadBeforeMov);
return true;
#endif
}
return false;

View File

@ -1904,8 +1904,14 @@ enum VectorMask {
INSN(vand_vv, 0b1010111, 0b000, 0b001001);
// Vector Single-Width Integer Add and Subtract
INSN(vsub_vv, 0b1010111, 0b000, 0b000010);
INSN(vadd_vv, 0b1010111, 0b000, 0b000000);
INSN(vsub_vv, 0b1010111, 0b000, 0b000010);
// Vector Saturating Integer Add and Subtract
INSN(vsadd_vv, 0b1010111, 0b000, 0b100001);
INSN(vsaddu_vv, 0b1010111, 0b000, 0b100000);
INSN(vssub_vv, 0b1010111, 0b000, 0b100011);
INSN(vssubu_vv, 0b1010111, 0b000, 0b100010);
// Vector Register Gather Instructions
INSN(vrgather_vv, 0b1010111, 0b000, 0b001100);

View File

@ -251,7 +251,6 @@ instruct vmaskcmp_fp(vRegMask dst, vReg src1, vReg src2, immI cond) %{
predicate(Matcher::vector_element_basic_type(n) == T_FLOAT ||
Matcher::vector_element_basic_type(n) == T_DOUBLE);
match(Set dst (VectorMaskCmp (Binary src1 src2) cond));
effect(TEMP_DEF dst);
format %{ "vmaskcmp_fp $dst, $src1, $src2, $cond" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
@ -651,6 +650,144 @@ instruct vsubL_vx_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{
ins_pipe(pipe_slow);
%}
// -------- vector saturating integer operations
// vector saturating signed integer addition
instruct vsadd(vReg dst, vReg src1, vReg src2) %{
predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned());
match(Set dst (SaturatingAddV src1 src2));
ins_cost(VEC_COST);
format %{ "vsadd $dst, $src1, $src2" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vsadd_vv(as_VectorRegister($dst$$reg),
as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg));
%}
ins_pipe(pipe_slow);
%}
// vector saturating unsigned integer addition
instruct vsaddu(vReg dst, vReg src1, vReg src2) %{
predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned());
match(Set dst (SaturatingAddV src1 src2));
ins_cost(VEC_COST);
format %{ "vsaddu $dst, $src1, $src2" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vsaddu_vv(as_VectorRegister($dst$$reg),
as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg));
%}
ins_pipe(pipe_slow);
%}
// vector saturating signed integer addition (predicated)
instruct vsadd_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{
predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned());
match(Set dst_src (SaturatingAddV (Binary dst_src src1) v0));
ins_cost(VEC_COST);
format %{ "vsadd_masked $dst_src, $dst_src, $src1, $v0" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vsadd_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg),
as_VectorRegister($src1$$reg), Assembler::v0_t);
%}
ins_pipe(pipe_slow);
%}
// vector saturating unsigned integer addition (predicated)
instruct vsaddu_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{
predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned());
match(Set dst_src (SaturatingAddV (Binary dst_src src1) v0));
ins_cost(VEC_COST);
format %{ "vsaddu_masked $dst_src, $dst_src, $src1, $v0" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vsaddu_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg),
as_VectorRegister($src1$$reg), Assembler::v0_t);
%}
ins_pipe(pipe_slow);
%}
// vector saturating signed integer subtraction
instruct vssub(vReg dst, vReg src1, vReg src2) %{
predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned());
match(Set dst (SaturatingSubV src1 src2));
ins_cost(VEC_COST);
format %{ "vssub $dst, $src1, $src2" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vssub_vv(as_VectorRegister($dst$$reg),
as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg));
%}
ins_pipe(pipe_slow);
%}
// vector saturating unsigned integer subtraction
instruct vssubu(vReg dst, vReg src1, vReg src2) %{
predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned());
match(Set dst (SaturatingSubV src1 src2));
ins_cost(VEC_COST);
format %{ "vssubu $dst, $src1, $src2" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vssubu_vv(as_VectorRegister($dst$$reg),
as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg));
%}
ins_pipe(pipe_slow);
%}
// vector saturating signed integer subtraction (predicated)
instruct vssub_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{
predicate(n->is_SaturatingVector() && !n->as_SaturatingVector()->is_unsigned());
match(Set dst_src (SaturatingSubV (Binary dst_src src1) v0));
ins_cost(VEC_COST);
format %{ "vssub_masked $dst_src, $dst_src, $src1, $v0" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vssub_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg),
as_VectorRegister($src1$$reg), Assembler::v0_t);
%}
ins_pipe(pipe_slow);
%}
// vector saturating unsigned integer subtraction (predicated)
instruct vssubu_masked(vReg dst_src, vReg src1, vRegMask_V0 v0) %{
predicate(n->is_SaturatingVector() && n->as_SaturatingVector()->is_unsigned());
match(Set dst_src (SaturatingSubV (Binary dst_src src1) v0));
ins_cost(VEC_COST);
format %{ "vssubu_masked $dst_src, $dst_src, $src1, $v0" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
assert(is_integral_type(bt), "unsupported type");
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vssubu_vv(as_VectorRegister($dst_src$$reg), as_VectorRegister($dst_src$$reg),
as_VectorRegister($src1$$reg), Assembler::v0_t);
%}
ins_pipe(pipe_slow);
%}
// vector and
instruct vand(vReg dst, vReg src1, vReg src2) %{

View File

@ -203,11 +203,14 @@ inline intptr_t* frame::interpreter_frame_expression_stack() const {
// Also begin is one past last monitor.
inline intptr_t* frame::interpreter_frame_top_frame_sp() {
return (intptr_t*)ijava_state()->top_frame_sp;
intptr_t n = *addr_at(_z_ijava_idx(top_frame_sp));
return &fp()[n]; // return relativized locals
}
inline void frame::interpreter_frame_set_top_frame_sp(intptr_t* top_frame_sp) {
ijava_state()->top_frame_sp = (intptr_t) top_frame_sp;
assert(is_interpreted_frame(), "interpreted frame expected");
// set relativized top_frame_sp
ijava_state()->top_frame_sp = (intptr_t) (top_frame_sp - fp());
}
inline void frame::interpreter_frame_set_sender_sp(intptr_t* sender_sp) {

View File

@ -576,7 +576,10 @@ void InterpreterMacroAssembler::prepare_to_jump_from_interpreted(Register method
// Satisfy interpreter calling convention (see generate_normal_entry()).
z_lgr(Z_R10, Z_SP); // Set sender sp (aka initial caller sp, aka unextended sp).
// Record top_frame_sp, because the callee might modify it, if it's compiled.
z_stg(Z_SP, _z_ijava_state_neg(top_frame_sp), Z_fp);
assert_different_registers(Z_R1, method);
z_sgrk(Z_R1, Z_SP, Z_fp);
z_srag(Z_R1, Z_R1, Interpreter::logStackElementSize);
z_stg(Z_R1, _z_ijava_state_neg(top_frame_sp), Z_fp);
save_bcp();
save_esp();
z_lgr(Z_method, method); // Set Z_method (kills Z_fp!).

View File

@ -637,6 +637,8 @@ address TemplateInterpreterGenerator::generate_return_entry_for (TosState state,
Register sp_before_i2c_extension = Z_bcp;
__ z_lg(Z_fp, _z_abi(callers_sp), Z_SP); // Restore frame pointer.
__ z_lg(sp_before_i2c_extension, Address(Z_fp, _z_ijava_state_neg(top_frame_sp)));
__ z_slag(sp_before_i2c_extension, sp_before_i2c_extension, Interpreter::logStackElementSize);
__ z_agr(sp_before_i2c_extension, Z_fp);
__ resize_frame_absolute(sp_before_i2c_extension, Z_locals/*tmp*/, true/*load_fp*/);
// TODO(ZASM): necessary??

View File

@ -5731,7 +5731,7 @@ void C2_MacroAssembler::vector_compress_expand_avx2(int opcode, XMMRegister dst,
// in a permute table row contains either a valid permute index or a -1 (default)
// value, this can potentially be used as a blending mask after
// compressing/expanding the source vector lanes.
vblendvps(dst, dst, xtmp, permv, vec_enc, false, permv);
vblendvps(dst, dst, xtmp, permv, vec_enc, true, permv);
}
void C2_MacroAssembler::vector_compress_expand(int opcode, XMMRegister dst, XMMRegister src, KRegister mask,

View File

@ -1030,6 +1030,7 @@ bool AOTClassLocationConfig::validate(bool has_aot_linked_classes, bool* has_ext
}
} else {
log_warning(cds)("%s%s", mismatch_msg, hint_msg);
MetaspaceShared::report_loading_error(nullptr);
}
}
return success;

View File

@ -313,7 +313,7 @@ address ArchiveBuilder::reserve_buffer() {
ReservedSpace rs = MemoryReserver::reserve(buffer_size,
MetaspaceShared::core_region_alignment(),
os::vm_page_size(),
mtClassShared);
mtNone);
if (!rs.is_reserved()) {
log_error(cds)("Failed to reserve %zu bytes of output buffer.", buffer_size);
MetaspaceShared::unrecoverable_writing_error();

View File

@ -647,6 +647,14 @@ bool CDSConfig::is_using_archive() {
return UseSharedSpaces;
}
bool CDSConfig::is_using_only_default_archive() {
return is_using_archive() &&
input_static_archive_path() != nullptr &&
default_archive_path() != nullptr &&
strcmp(input_static_archive_path(), default_archive_path()) == 0 &&
input_dynamic_archive_path() == nullptr;
}
bool CDSConfig::is_logging_lambda_form_invokers() {
return ClassListWriter::is_enabled() || is_dumping_dynamic_archive();
}

View File

@ -103,6 +103,7 @@ public:
// input archive(s)
static bool is_using_archive() NOT_CDS_RETURN_(false);
static bool is_using_only_default_archive() NOT_CDS_RETURN_(false);
// static_archive
static bool is_dumping_static_archive() { return CDS_ONLY(_is_dumping_static_archive) NOT_CDS(false); }

View File

@ -326,7 +326,7 @@ bool FileMapInfo::validate_class_location() {
if (header()->has_full_module_graph() && has_extra_module_paths) {
CDSConfig::stop_using_optimized_module_handling();
log_info(cds)("optimized module handling: disabled because extra module path(s) are specified");
MetaspaceShared::report_loading_error("optimized module handling: disabled because extra module path(s) are specified");
}
if (CDSConfig::is_dumping_dynamic_archive()) {
@ -1256,7 +1256,7 @@ char* FileMapInfo::map_bitmap_region() {
char* bitmap_base = map_memory(_fd, _full_path, r->file_offset(),
requested_addr, r->used_aligned(), read_only, allow_exec, mtClassShared);
if (bitmap_base == nullptr) {
log_info(cds)("failed to map relocation bitmap");
MetaspaceShared::report_loading_error("failed to map relocation bitmap");
return nullptr;
}
@ -1454,9 +1454,9 @@ void FileMapInfo::map_or_load_heap_region() {
success = ArchiveHeapLoader::load_heap_region(this);
} else {
if (!UseCompressedOops && !ArchiveHeapLoader::can_map()) {
log_info(cds)("Cannot use CDS heap data. Selected GC not compatible -XX:-UseCompressedOops");
MetaspaceShared::report_loading_error("Cannot use CDS heap data. Selected GC not compatible -XX:-UseCompressedOops");
} else {
log_info(cds)("Cannot use CDS heap data. UseEpsilonGC, UseG1GC, UseSerialGC, UseParallelGC, or UseShenandoahGC are required.");
MetaspaceShared::report_loading_error("Cannot use CDS heap data. UseEpsilonGC, UseG1GC, UseSerialGC, UseParallelGC, or UseShenandoahGC are required.");
}
}
}
@ -1468,8 +1468,10 @@ void FileMapInfo::map_or_load_heap_region() {
// all AOT-linked classes are visible.
//
// We get here because the heap is too small. The app will fail anyway. So let's quit.
MetaspaceShared::unrecoverable_loading_error("CDS archive has aot-linked classes but the archived "
"heap objects cannot be loaded. Try increasing your heap size.");
log_error(cds)("%s has aot-linked classes but the archived "
"heap objects cannot be loaded. Try increasing your heap size.",
CDSConfig::type_of_archive_being_loaded());
MetaspaceShared::unrecoverable_loading_error();
}
CDSConfig::stop_using_full_module_graph("archive heap loading failed");
}
@ -1635,7 +1637,7 @@ bool FileMapInfo::map_heap_region_impl() {
// allocate from java heap
HeapWord* start = G1CollectedHeap::heap()->alloc_archive_region(word_size, (HeapWord*)requested_start);
if (start == nullptr) {
log_info(cds)("UseSharedSpaces: Unable to allocate java heap region for archive heap.");
MetaspaceShared::report_loading_error("UseSharedSpaces: Unable to allocate java heap region for archive heap.");
return false;
}
@ -1670,7 +1672,7 @@ bool FileMapInfo::map_heap_region_impl() {
if (VerifySharedSpaces && !r->check_region_crc(base)) {
dealloc_heap_region();
log_info(cds)("UseSharedSpaces: mapped heap region is corrupt");
MetaspaceShared::report_loading_error("UseSharedSpaces: mapped heap region is corrupt");
return false;
}
}
@ -1694,7 +1696,7 @@ bool FileMapInfo::map_heap_region_impl() {
if (_heap_pointers_need_patching) {
char* bitmap_base = map_bitmap_region();
if (bitmap_base == nullptr) {
log_info(cds)("CDS heap cannot be used because bitmap region cannot be mapped");
MetaspaceShared::report_loading_error("CDS heap cannot be used because bitmap region cannot be mapped");
dealloc_heap_region();
_heap_pointers_need_patching = false;
return false;
@ -1807,16 +1809,16 @@ bool FileMapInfo::open_as_input() {
// are replaced at runtime by JVMTI ClassFileLoadHook. All of those classes are resolved
// during the JVMTI "early" stage, so we can still use CDS if
// JvmtiExport::has_early_class_hook_env() is false.
log_info(cds)("CDS is disabled because early JVMTI ClassFileLoadHook is in use.");
MetaspaceShared::report_loading_error("CDS is disabled because early JVMTI ClassFileLoadHook is in use.");
return false;
}
if (!open_for_read() || !init_from_file(_fd) || !validate_header()) {
if (_is_static) {
log_info(cds)("Loading static archive failed.");
MetaspaceShared::report_loading_error("Loading static archive failed.");
return false;
} else {
log_info(cds)("Loading dynamic archive failed.");
MetaspaceShared::report_loading_error("Loading dynamic archive failed.");
if (AutoCreateSharedArchive) {
CDSConfig::enable_dumping_dynamic_archive(_full_path);
}
@ -1831,29 +1833,34 @@ bool FileMapInfo::validate_aot_class_linking() {
// These checks need to be done after FileMapInfo::initialize(), which gets called before Universe::heap()
// is available.
if (header()->has_aot_linked_classes()) {
const char* archive_type = CDSConfig::type_of_archive_being_loaded();
CDSConfig::set_has_aot_linked_classes(true);
if (JvmtiExport::should_post_class_file_load_hook()) {
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.");
log_error(cds)("%s has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.",
archive_type);
return false;
}
if (JvmtiExport::has_early_vmstart_env()) {
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when JVMTI early vm start is in use.");
log_error(cds)("%s has aot-linked classes. It cannot be used when JVMTI early vm start is in use.",
archive_type);
return false;
}
if (!CDSConfig::is_using_full_module_graph()) {
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used.");
log_error(cds)("%s has aot-linked classes. It cannot be used when archived full module graph is not used.",
archive_type);
return false;
}
const char* prop = Arguments::get_property("java.security.manager");
if (prop != nullptr && strcmp(prop, "disallow") != 0) {
log_error(cds)("CDS archive has aot-linked classes. It cannot be used with -Djava.security.manager=%s.", prop);
log_error(cds)("%s has aot-linked classes. It cannot be used with -Djava.security.manager=%s.",
archive_type, prop);
return false;
}
#if INCLUDE_JVMTI
if (Arguments::has_jdwp_agent()) {
log_error(cds)("CDS archive has aot-linked classes. It cannot be used with JDWP agent");
log_error(cds)("%s has aot-linked classes. It cannot be used with JDWP agent", archive_type);
return false;
}
#endif
@ -1913,8 +1920,8 @@ bool FileMapHeader::validate() {
const char* prop = Arguments::get_property("java.system.class.loader");
if (prop != nullptr) {
if (has_aot_linked_classes()) {
log_error(cds)("CDS archive has aot-linked classes. It cannot be used when the "
"java.system.class.loader property is specified.");
log_error(cds)("%s has aot-linked classes. It cannot be used when the "
"java.system.class.loader property is specified.", CDSConfig::type_of_archive_being_loaded());
return false;
}
log_warning(cds)("Archived non-system classes are disabled because the "

View File

@ -937,7 +937,7 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS
if (CDSConfig::is_dumping_heap()) {
assert(CDSConfig::allow_only_single_java_thread(), "Required");
if (!HeapShared::is_archived_boot_layer_available(THREAD)) {
log_info(cds)("archivedBootLayer not available, disabling full module graph");
report_loading_error("archivedBootLayer not available, disabling full module graph");
CDSConfig::stop_dumping_full_module_graph();
}
// Do this before link_shared_classes(), as the following line may load new classes.
@ -1118,10 +1118,7 @@ bool MetaspaceShared::is_shared_static(void* p) {
// - When -XX:+RequireSharedSpaces is specified, AND the JVM cannot load the archive(s) due
// to version or classpath mismatch.
void MetaspaceShared::unrecoverable_loading_error(const char* message) {
log_error(cds)("An error has occurred while processing the %s.", CDSConfig::type_of_archive_being_loaded());
if (message != nullptr) {
log_error(cds)("%s", message);
}
report_loading_error(message);
if (CDSConfig::is_dumping_final_static_archive()) {
vm_exit_during_initialization("Must be a valid AOT configuration generated by the current JVM", AOTConfiguration);
@ -1132,6 +1129,30 @@ void MetaspaceShared::unrecoverable_loading_error(const char* message) {
}
}
void MetaspaceShared::report_loading_error(const char* format, ...) {
// If the user doesn't specify any CDS options, we will try to load the default CDS archive, which
// may fail due to incompatible VM options. Print at the info level to avoid excessive verbosity.
// However, if the user has specified a CDS archive (or AOT cache), they would be interested in
// knowing that the loading fails, so we print at the error level.
Log(cds) log;
LogStream ls_error(log.error());
LogStream ls_info(log.info());
LogStream& ls = (!CDSConfig::is_using_archive()) || CDSConfig::is_using_only_default_archive() ? ls_info : ls_error;
static bool printed_error = false;
if (!printed_error) { // No need for locks. Loading error checks happen only in main thread.
ls.print_cr("An error has occurred while processing the %s. Run with -Xlog:cds for details.", CDSConfig::type_of_archive_being_loaded());
printed_error = true;
}
if (format != nullptr) {
va_list ap;
va_start(ap, format);
ls.vprint_cr(format, ap);
va_end(ap);
}
}
// This function is called when the JVM is unable to write the specified CDS archive due to an
// unrecoverable error.
void MetaspaceShared::unrecoverable_writing_error(const char* message) {
@ -1193,11 +1214,14 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() {
// The base archive cannot be mapped. We cannot dump the dynamic shared archive.
AutoCreateSharedArchive = false;
CDSConfig::disable_dumping_dynamic_archive();
log_info(cds)("Unable to map shared spaces");
if (PrintSharedArchiveAndExit) {
MetaspaceShared::unrecoverable_loading_error("Unable to use shared archive.");
} else if (RequireSharedSpaces) {
MetaspaceShared::unrecoverable_loading_error("Unable to map shared spaces");
} else {
if (RequireSharedSpaces) {
MetaspaceShared::unrecoverable_loading_error("Unable to map shared spaces");
} else {
report_loading_error("Unable to map shared spaces");
}
}
}
@ -1712,8 +1736,8 @@ MapArchiveResult MetaspaceShared::map_archive(FileMapInfo* mapinfo, char* mapped
mapinfo->set_is_mapped(false);
if (mapinfo->core_region_alignment() != (size_t)core_region_alignment()) {
log_info(cds)("Unable to map CDS archive -- core_region_alignment() expected: %zu"
" actual: %zu", mapinfo->core_region_alignment(), core_region_alignment());
report_loading_error("Unable to map CDS archive -- core_region_alignment() expected: %zu"
" actual: %zu", mapinfo->core_region_alignment(), core_region_alignment());
return MAP_ARCHIVE_OTHER_FAILURE;
}

View File

@ -110,7 +110,8 @@ public:
static bool is_shared_dynamic(void* p) NOT_CDS_RETURN_(false);
static bool is_shared_static(void* p) NOT_CDS_RETURN_(false);
static void unrecoverable_loading_error(const char* message = nullptr);
static void unrecoverable_loading_error(const char* message = nullptr) ATTRIBUTE_PRINTF(1, 0);
static void report_loading_error(const char* format, ...) ATTRIBUTE_PRINTF(1, 0);
static void unrecoverable_writing_error(const char* message = nullptr);
static void writing_error(const char* message = nullptr);

View File

@ -604,21 +604,21 @@ void Modules::ArchivedProperty::runtime_check() const {
bool disable = false;
if (runtime_value == nullptr) {
if (_archived_value != nullptr) {
log_info(cds)("Mismatched values for property %s: %s specified during dump time but not during runtime", _prop, _archived_value);
MetaspaceShared::report_loading_error("Mismatched values for property %s: %s specified during dump time but not during runtime", _prop, _archived_value);
disable = true;
}
} else {
if (_archived_value == nullptr) {
log_info(cds)("Mismatched values for property %s: %s specified during runtime but not during dump time", _prop, runtime_value);
MetaspaceShared::report_loading_error("Mismatched values for property %s: %s specified during runtime but not during dump time", _prop, runtime_value);
disable = true;
} else if (strcmp(runtime_value, _archived_value) != 0) {
log_info(cds)("Mismatched values for property %s: runtime %s dump time %s", _prop, runtime_value, _archived_value);
MetaspaceShared::report_loading_error("Mismatched values for property %s: runtime %s dump time %s", _prop, runtime_value, _archived_value);
disable = true;
}
}
if (disable) {
log_info(cds)("Disabling optimized module handling");
MetaspaceShared::report_loading_error("Disabling optimized module handling");
CDSConfig::stop_using_optimized_module_handling();
}
}

View File

@ -48,6 +48,9 @@
#if INCLUDE_G1GC
#include "gc/g1/g1BarrierSetRuntime.hpp"
#endif
#if INCLUDE_SHENANDOAHGC
#include "gc/shenandoah/shenandoahRuntime.hpp"
#endif
#if INCLUDE_ZGC
#include "gc/z/zBarrierSetRuntime.hpp"
#endif
@ -976,9 +979,9 @@ ImmutableOopMapSet* AOTCodeReader::read_oop_map_set() {
// [_blobs_base, _blobs_base + _blobs_max -1],
// ...
// [_c_str_base, _c_str_base + _c_str_max -1],
#define _extrs_max 10
#define _extrs_max 13
#define _blobs_max 10
#define _all_max 20
#define _all_max 23
#define _extrs_base 0
#define _blobs_base (_extrs_base + _extrs_max)
@ -1012,6 +1015,11 @@ void AOTCodeAddressTable::init_extrs() {
SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_post_entry);
SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_pre_entry);
#endif
#if INCLUDE_SHENANDOAHGC
SET_ADDRESS(_extrs, ShenandoahRuntime::write_ref_field_pre);
SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom);
SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow);
#endif
#if INCLUDE_ZGC
SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr());
#if defined(AMD64)

View File

@ -28,6 +28,7 @@
#include "code/dependencies.hpp"
#include "code/nativeInst.hpp"
#include "code/nmethod.inline.hpp"
#include "code/relocInfo.hpp"
#include "code/scopeDesc.hpp"
#include "compiler/abstractCompiler.hpp"
#include "compiler/compilationLog.hpp"
@ -1323,8 +1324,9 @@ nmethod::nmethod(
_unwind_handler_offset = 0;
CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize));
int metadata_size = align_up(code_buffer->total_metadata_size(), wordSize);
JVMCI_ONLY( _jvmci_data_size = 0; )
uint16_t metadata_size;
CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize));
JVMCI_ONLY( _metadata_size = metadata_size; )
assert(_mutable_data_size == _relocation_size + metadata_size,
"wrong mutable data size: %d != %d + %d",
_mutable_data_size, _relocation_size, metadata_size);
@ -1497,9 +1499,10 @@ nmethod::nmethod(
}
CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize));
uint16_t metadata_size = (uint16_t)align_up(code_buffer->total_metadata_size(), wordSize);
JVMCI_ONLY(CHECKED_CAST(_jvmci_data_size, uint16_t, align_up(compiler->is_jvmci() ? jvmci_data->size() : 0, oopSize)));
int jvmci_data_size = 0 JVMCI_ONLY(+ _jvmci_data_size);
uint16_t metadata_size;
CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize));
JVMCI_ONLY( _metadata_size = metadata_size; )
int jvmci_data_size = 0 JVMCI_ONLY( + align_up(compiler->is_jvmci() ? jvmci_data->size() : 0, oopSize));
assert(_mutable_data_size == _relocation_size + metadata_size + jvmci_data_size,
"wrong mutable data size: %d != %d + %d + %d",
_mutable_data_size, _relocation_size, metadata_size, jvmci_data_size);
@ -1646,6 +1649,10 @@ void nmethod::maybe_print_nmethod(const DirectiveSet* directive) {
}
void nmethod::print_nmethod(bool printmethod) {
// Enter a critical section to prevent a race with deopts that patch code and updates the relocation info.
// Unfortunately, we have to lock the NMethodState_lock before the tty lock due to the deadlock rules and
// cannot lock in a more finely grained manner.
ConditionalMutexLocker ml(NMethodState_lock, !NMethodState_lock->owned_by_self(), Mutex::_no_safepoint_check_flag);
ttyLocker ttyl; // keep the following output all in one block
if (xtty != nullptr) {
xtty->begin_head("print_nmethod");
@ -2035,6 +2042,17 @@ bool nmethod::make_not_entrant(const char* reason) {
// cache call.
NativeJump::patch_verified_entry(entry_point(), verified_entry_point(),
SharedRuntime::get_handle_wrong_method_stub());
// Update the relocation info for the patched entry.
// First, get the old relocation info...
RelocIterator iter(this, verified_entry_point(), verified_entry_point() + 8);
if (iter.next() && iter.addr() == verified_entry_point()) {
Relocation* old_reloc = iter.reloc();
// ...then reset the iterator to update it.
RelocIterator iter(this, verified_entry_point(), verified_entry_point() + 8);
relocInfo::change_reloc_info_for_address(&iter, verified_entry_point(), old_reloc->type(),
relocInfo::relocType::runtime_call_type);
}
}
if (update_recompile_counts()) {

View File

@ -237,7 +237,9 @@ class nmethod : public CodeBlob {
uint16_t _oops_size;
#if INCLUDE_JVMCI
uint16_t _jvmci_data_size;
// _metadata_size is not specific to JVMCI. In the non-JVMCI case, it can be derived as:
// _metadata_size = mutable_data_size - relocation_size
uint16_t _metadata_size;
#endif
// Offset in immutable data section
@ -537,8 +539,8 @@ public:
// mutable data
Metadata** metadata_begin () const { return (Metadata**) (mutable_data_begin() + _relocation_size); }
#if INCLUDE_JVMCI
Metadata** metadata_end () const { return (Metadata**) (mutable_data_end() - _jvmci_data_size); }
address jvmci_data_begin () const { return mutable_data_end() - _jvmci_data_size; }
Metadata** metadata_end () const { return (Metadata**) (mutable_data_begin() + _relocation_size + _metadata_size); }
address jvmci_data_begin () const { return mutable_data_begin() + _relocation_size + _metadata_size; }
address jvmci_data_end () const { return mutable_data_end(); }
#else
Metadata** metadata_end () const { return (Metadata**) mutable_data_end(); }

View File

@ -33,21 +33,12 @@
inline bool nmethod::is_deopt_pc(address pc) { return is_deopt_entry(pc) || is_deopt_mh_entry(pc); }
// When using JVMCI the address might be off by the size of a call instruction.
inline bool nmethod::is_deopt_entry(address pc) {
return pc == deopt_handler_begin()
#if INCLUDE_JVMCI
|| (is_compiled_by_jvmci() && pc == (deopt_handler_begin() + NativeCall::byte_size()))
#endif
;
return pc == deopt_handler_begin();
}
inline bool nmethod::is_deopt_mh_entry(address pc) {
return pc == deopt_mh_handler_begin()
#if INCLUDE_JVMCI
|| (is_compiled_by_jvmci() && pc == (deopt_mh_handler_begin() + NativeCall::byte_size()))
#endif
;
return pc == deopt_mh_handler_begin();
}
// class ExceptionCache methods

View File

@ -471,7 +471,7 @@ void CompileTask::print_inlining_inner_message(outputStream* st, InliningResult
}
void CompileTask::print_ul(const char* msg){
LogTarget(Debug, jit, compilation) lt;
LogTarget(Info, jit, compilation) lt;
if (lt.is_enabled()) {
LogStream ls(lt);
print(&ls, msg, /* short form */ true, /* cr */ true);
@ -479,7 +479,7 @@ void CompileTask::print_ul(const char* msg){
}
void CompileTask::print_ul(const nmethod* nm, const char* msg) {
LogTarget(Debug, jit, compilation) lt;
LogTarget(Info, jit, compilation) lt;
if (lt.is_enabled()) {
LogStream ls(lt);
print_impl(&ls, nm->method(), nm->compile_id(),

View File

@ -98,6 +98,9 @@
#include "utilities/events.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/powerOfTwo.hpp"
#if INCLUDE_JVMCI
#include "jvmci/jvmci.hpp"
#endif
#if INCLUDE_JFR
#include "gc/shenandoah/shenandoahJfrSupport.hpp"
#endif
@ -2233,6 +2236,9 @@ void ShenandoahHeap::stw_unload_classes(bool full_gc) {
ShenandoahGCWorkerPhase worker_phase(phase);
bool unloading_occurred = SystemDictionary::do_unloading(gc_timer());
// Clean JVMCI metadata handles.
JVMCI_ONLY(JVMCI::do_unloading(unloading_occurred));
uint num_workers = _workers->active_workers();
ShenandoahClassUnloadingTask unlink_task(phase, num_workers, unloading_occurred);
_workers->run_task(&unlink_task);

View File

@ -48,6 +48,10 @@ JRT_LEAF(void, ShenandoahRuntime::write_ref_field_pre(oopDesc * orig, JavaThread
ShenandoahBarrierSet::satb_mark_queue_set().enqueue_known_active(queue, orig);
JRT_END
void ShenandoahRuntime::write_barrier_pre(oopDesc* orig) {
write_ref_field_pre(orig, JavaThread::current());
}
JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_strong(oopDesc* src, oop* load_addr))
return ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(src, load_addr);
JRT_END

View File

@ -37,6 +37,7 @@ public:
static void arraycopy_barrier_narrow_oop(narrowOop* src, narrowOop* dst, size_t length);
static void write_ref_field_pre(oopDesc* orig, JavaThread* thread);
static void write_barrier_pre(oopDesc* orig);
static oopDesc* load_reference_barrier_strong(oopDesc* src, oop* load_addr);
static oopDesc* load_reference_barrier_strong_narrow(oopDesc* src, narrowOop* load_addr);

View File

@ -115,7 +115,9 @@ static void deoptimize_allocation(JavaThread* thread) {
assert(caller_frame.is_compiled_frame(), "must be compiled");
const nmethod* const nm = caller_frame.cb()->as_nmethod();
if (nm->is_compiled_by_c2() && !caller_frame.is_deoptimized_frame()) {
if ((nm->is_compiled_by_c2() || nm->is_compiled_by_jvmci()) && !caller_frame.is_deoptimized_frame()) {
// The JIT might have elided barriers on this object so deoptimize the frame and let the
// intepreter deal with it.
Deoptimization::deoptimize_frame(thread, caller_frame.id());
}
}

View File

@ -111,6 +111,10 @@ class CompilerToVM {
#if INCLUDE_ZGC
static int sizeof_ZStoreBarrierEntry;
#endif
#if INCLUDE_SHENANDOAHGC
static address shenandoah_in_cset_fast_test_addr;
static int shenandoah_region_size_bytes_shift;
#endif
#ifdef X86
static int L1_line_size;

View File

@ -52,6 +52,10 @@
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/resourceHash.hpp"
#if INCLUDE_SHENANDOAHGC
#include "gc/shenandoah/shenandoahHeap.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
#endif
int CompilerToVM::Data::oopDesc_klass_offset_in_bytes;
int CompilerToVM::Data::arrayOopDesc_length_offset_in_bytes;
@ -86,6 +90,11 @@ address CompilerToVM::Data::ZPointerVectorLoadBadMask_address;
address CompilerToVM::Data::ZPointerVectorStoreBadMask_address;
address CompilerToVM::Data::ZPointerVectorStoreGoodMask_address;
#if INCLUDE_SHENANDOAHGC
address CompilerToVM::Data::shenandoah_in_cset_fast_test_addr;
int CompilerToVM::Data::shenandoah_region_size_bytes_shift;
#endif
bool CompilerToVM::Data::continuations_enabled;
#ifdef AARCH64
@ -227,12 +236,24 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) {
assert(base != nullptr, "unexpected byte_map_base");
cardtable_start_address = base;
cardtable_shift = CardTable::card_shift();
#if INCLUDE_SHENANDOAHGC
} else if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) {
cardtable_start_address = nullptr;
cardtable_shift = CardTable::card_shift();
#endif
} else {
// No card mark barriers
cardtable_start_address = nullptr;
cardtable_shift = 0;
}
#if INCLUDE_SHENANDOAHGC
if (UseShenandoahGC) {
shenandoah_in_cset_fast_test_addr = ShenandoahHeap::in_cset_fast_test_addr();
shenandoah_region_size_bytes_shift = ShenandoahHeapRegion::region_size_bytes_shift_jint();
}
#endif
#ifdef X86
L1_line_size = VM_Version::L1_line_size();
#endif

View File

@ -229,7 +229,7 @@ bool JVMCIGlobals::enable_jvmci_product_mode(JVMFlagOrigin origin, bool use_graa
}
bool JVMCIGlobals::gc_supports_jvmci() {
return UseSerialGC || UseParallelGC || UseG1GC || UseZGC || UseEpsilonGC;
return UseSerialGC || UseParallelGC || UseG1GC || UseZGC || UseShenandoahGC || UseEpsilonGC;
}
void JVMCIGlobals::check_jvmci_supported_gc() {

View File

@ -53,6 +53,10 @@
#include "gc/z/zBarrierSetRuntime.hpp"
#include "gc/z/zThreadLocalData.hpp"
#endif
#if INCLUDE_SHENANDOAHGC
#include "gc/shenandoah/shenandoahRuntime.hpp"
#include "gc/shenandoah/shenandoahThreadLocalData.hpp"
#endif
#define VM_STRUCTS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field) \
static_field(CompilerToVM::Data, oopDesc_klass_offset_in_bytes, int) \
@ -129,6 +133,8 @@
static_field(CompilerToVM::Data, sizeof_arrayOopDesc, int) \
static_field(CompilerToVM::Data, sizeof_BasicLock, int) \
ZGC_ONLY(static_field(CompilerToVM::Data, sizeof_ZStoreBarrierEntry, int)) \
SHENANDOAHGC_ONLY(static_field(CompilerToVM::Data, shenandoah_in_cset_fast_test_addr, address)) \
SHENANDOAHGC_ONLY(static_field(CompilerToVM::Data, shenandoah_region_size_bytes_shift,int)) \
\
static_field(CompilerToVM::Data, dsin, address) \
static_field(CompilerToVM::Data, dcos, address) \
@ -895,6 +901,13 @@
declare_function(JVMCIRuntime::load_and_clear_exception) \
G1GC_ONLY(declare_function(JVMCIRuntime::write_barrier_pre)) \
G1GC_ONLY(declare_function(JVMCIRuntime::write_barrier_post)) \
SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_strong)) \
SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_strong_narrow)) \
SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_weak)) \
SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_weak_narrow)) \
SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_phantom)) \
SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::load_reference_barrier_phantom_narrow)) \
SHENANDOAHGC_ONLY(declare_function(ShenandoahRuntime::write_barrier_pre)) \
declare_function(JVMCIRuntime::validate_object) \
\
declare_function(JVMCIRuntime::test_deoptimize_call_int)
@ -941,6 +954,15 @@
#endif // INCLUDE_ZGC
#if INCLUDE_SHENANDOAHGC
#define VM_INT_CONSTANTS_JVMCI_SHENANDOAH(declare_constant, declare_constant_with_value, declare_preprocessor_constant) \
declare_constant_with_value("ShenandoahThreadLocalData::gc_state_offset", in_bytes(ShenandoahThreadLocalData::gc_state_offset())) \
declare_constant_with_value("ShenandoahThreadLocalData::satb_mark_queue_index_offset", in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())) \
declare_constant_with_value("ShenandoahThreadLocalData::satb_mark_queue_buffer_offset", in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())) \
declare_constant_with_value("ShenandoahThreadLocalData::card_table_offset", in_bytes(ShenandoahThreadLocalData::card_table_offset())) \
#endif
#ifdef LINUX
@ -1063,6 +1085,11 @@ VMIntConstantEntry JVMCIVMStructs::localHotSpotVMIntConstants[] = {
GENERATE_VM_INT_CONSTANT_WITH_VALUE_ENTRY,
GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY)
#endif
#if INCLUDE_SHENANDOAHGC
VM_INT_CONSTANTS_JVMCI_SHENANDOAH(GENERATE_VM_INT_CONSTANT_ENTRY,
GENERATE_VM_INT_CONSTANT_WITH_VALUE_ENTRY,
GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY)
#endif
#ifdef VM_INT_CPU_FEATURE_CONSTANTS
VM_INT_CPU_FEATURE_CONSTANTS
#endif

View File

@ -5205,32 +5205,39 @@ IdealGraphPrinter* Compile::_debug_network_printer = nullptr;
// Called from debugger. Prints method to the default file with the default phase name.
// This works regardless of any Ideal Graph Visualizer flags set or not.
void igv_print() {
Compile::current()->igv_print_method_to_file();
// Use in debugger (gdb/rr): p igv_print($sp, $fp, $pc).
void igv_print(void* sp, void* fp, void* pc) {
frame fr(sp, fp, pc);
Compile::current()->igv_print_method_to_file(nullptr, false, &fr);
}
// Same as igv_print() above but with a specified phase name.
void igv_print(const char* phase_name) {
Compile::current()->igv_print_method_to_file(phase_name);
void igv_print(const char* phase_name, void* sp, void* fp, void* pc) {
frame fr(sp, fp, pc);
Compile::current()->igv_print_method_to_file(phase_name, false, &fr);
}
// Called from debugger. Prints method with the default phase name to the default network or the one specified with
// the network flags for the Ideal Graph Visualizer, or to the default file depending on the 'network' argument.
// This works regardless of any Ideal Graph Visualizer flags set or not.
void igv_print(bool network) {
// Use in debugger (gdb/rr): p igv_print(true, $sp, $fp, $pc).
void igv_print(bool network, void* sp, void* fp, void* pc) {
frame fr(sp, fp, pc);
if (network) {
Compile::current()->igv_print_method_to_network();
Compile::current()->igv_print_method_to_network(nullptr, &fr);
} else {
Compile::current()->igv_print_method_to_file();
Compile::current()->igv_print_method_to_file(nullptr, false, &fr);
}
}
// Same as igv_print(bool network) above but with a specified phase name.
void igv_print(bool network, const char* phase_name) {
// Same as igv_print(bool network, ...) above but with a specified phase name.
// Use in debugger (gdb/rr): p igv_print(true, "MyPhase", $sp, $fp, $pc).
void igv_print(bool network, const char* phase_name, void* sp, void* fp, void* pc) {
frame fr(sp, fp, pc);
if (network) {
Compile::current()->igv_print_method_to_network(phase_name);
Compile::current()->igv_print_method_to_network(phase_name, &fr);
} else {
Compile::current()->igv_print_method_to_file(phase_name);
Compile::current()->igv_print_method_to_file(phase_name, false, &fr);
}
}
@ -5242,16 +5249,20 @@ void igv_print_default() {
// Called from debugger, especially when replaying a trace in which the program state cannot be altered like with rr replay.
// A method is appended to an existing default file with the default phase name. This means that igv_append() must follow
// an earlier igv_print(*) call which sets up the file. This works regardless of any Ideal Graph Visualizer flags set or not.
void igv_append() {
Compile::current()->igv_print_method_to_file("Debug", true);
// Use in debugger (gdb/rr): p igv_append($sp, $fp, $pc).
void igv_append(void* sp, void* fp, void* pc) {
frame fr(sp, fp, pc);
Compile::current()->igv_print_method_to_file(nullptr, true, &fr);
}
// Same as igv_append() above but with a specified phase name.
void igv_append(const char* phase_name) {
Compile::current()->igv_print_method_to_file(phase_name, true);
// Same as igv_append(...) above but with a specified phase name.
// Use in debugger (gdb/rr): p igv_append("MyPhase", $sp, $fp, $pc).
void igv_append(const char* phase_name, void* sp, void* fp, void* pc) {
frame fr(sp, fp, pc);
Compile::current()->igv_print_method_to_file(phase_name, true, &fr);
}
void Compile::igv_print_method_to_file(const char* phase_name, bool append) {
void Compile::igv_print_method_to_file(const char* phase_name, bool append, const frame* fr) {
const char* file_name = "custom_debug.xml";
if (_debug_file_printer == nullptr) {
_debug_file_printer = new IdealGraphPrinter(C, file_name, append);
@ -5259,23 +5270,23 @@ void Compile::igv_print_method_to_file(const char* phase_name, bool append) {
_debug_file_printer->update_compiled_method(C->method());
}
tty->print_cr("Method %s to %s", append ? "appended" : "printed", file_name);
_debug_file_printer->print_graph(phase_name);
_debug_file_printer->print_graph(phase_name, fr);
}
void Compile::igv_print_method_to_network(const char* phase_name) {
void Compile::igv_print_method_to_network(const char* phase_name, const frame* fr) {
ResourceMark rm;
GrowableArray<const Node*> empty_list;
igv_print_graph_to_network(phase_name, empty_list);
igv_print_graph_to_network(phase_name, empty_list, fr);
}
void Compile::igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes) {
void Compile::igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes, const frame* fr) {
if (_debug_network_printer == nullptr) {
_debug_network_printer = new IdealGraphPrinter(C);
} else {
_debug_network_printer->update_compiled_method(C->method());
}
tty->print_cr("Method printed over network stream to IGV");
_debug_network_printer->print(name, C->root(), visible_nodes);
_debug_network_printer->print(name, C->root(), visible_nodes, fr);
}
#endif

View File

@ -673,13 +673,13 @@ public:
void init_igv();
void dump_igv(const char* graph_name, int level = 3) {
if (should_print_igv(level)) {
_igv_printer->print_graph(graph_name);
_igv_printer->print_graph(graph_name, nullptr);
}
}
void igv_print_method_to_file(const char* phase_name = "Debug", bool append = false);
void igv_print_method_to_network(const char* phase_name = "Debug");
void igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes);
void igv_print_method_to_file(const char* phase_name = nullptr, bool append = false, const frame* fr = nullptr);
void igv_print_method_to_network(const char* phase_name = nullptr, const frame* fr = nullptr);
void igv_print_graph_to_network(const char* name, GrowableArray<const Node*>& visible_nodes, const frame* fr);
static IdealGraphPrinter* debug_file_printer() { return _debug_file_printer; }
static IdealGraphPrinter* debug_network_printer() { return _debug_network_printer; }
#endif

View File

@ -27,8 +27,10 @@
#include "opto/idealGraphPrinter.hpp"
#include "opto/machnode.hpp"
#include "opto/parse.hpp"
#include "runtime/arguments.hpp"
#include "runtime/threadCritical.hpp"
#include "runtime/threadSMR.hpp"
#include "utilities/decoder.hpp"
#include "utilities/stringUtils.hpp"
#ifndef PRODUCT
@ -49,6 +51,13 @@ const char *IdealGraphPrinter::REMOVE_EDGE_ELEMENT = "removeEdge";
const char *IdealGraphPrinter::REMOVE_NODE_ELEMENT = "removeNode";
const char *IdealGraphPrinter::COMPILATION_ID_PROPERTY = "compilationId";
const char *IdealGraphPrinter::COMPILATION_OSR_PROPERTY = "osr";
const char *IdealGraphPrinter::COMPILATION_ARGUMENTS_PROPERTY = "arguments";
const char *IdealGraphPrinter::COMPILATION_MACHINE_PROPERTY = "machine";
const char *IdealGraphPrinter::COMPILATION_CPU_FEATURES_PROPERTY = "cpuFeatures";
const char *IdealGraphPrinter::COMPILATION_VM_VERSION_PROPERTY = "vm";
const char *IdealGraphPrinter::COMPILATION_DATE_TIME_PROPERTY = "dateTime";
const char *IdealGraphPrinter::COMPILATION_PROCESS_ID_PROPERTY = "processId";
const char *IdealGraphPrinter::COMPILATION_THREAD_ID_PROPERTY = "threadId";
const char *IdealGraphPrinter::METHOD_NAME_PROPERTY = "name";
const char *IdealGraphPrinter::METHOD_IS_PUBLIC_PROPERTY = "public";
const char *IdealGraphPrinter::METHOD_IS_STATIC_PROPERTY = "static";
@ -336,6 +345,42 @@ void IdealGraphPrinter::begin_method() {
print_prop(COMPILATION_ID_PROPERTY, C->compile_id());
stringStream args;
Arguments::print_jvm_args_on(&args);
print_prop(COMPILATION_ARGUMENTS_PROPERTY, args.freeze());
stringStream machine;
buffer[0] = 0;
os::print_summary_info(&machine, buffer, sizeof(buffer) - 1);
print_prop(COMPILATION_MACHINE_PROPERTY, machine.freeze());
print_prop(COMPILATION_CPU_FEATURES_PROPERTY, VM_Version::features_string());
stringStream version;
buffer[0] = 0;
JDK_Version::current().to_string(buffer, sizeof(buffer) - 1);
const char* runtime_name = JDK_Version::runtime_name() != nullptr ?
JDK_Version::runtime_name() : "";
const char* runtime_version = JDK_Version::runtime_version() != nullptr ?
JDK_Version::runtime_version() : "";
const char* vendor_version = JDK_Version::runtime_vendor_version() != nullptr ?
JDK_Version::runtime_vendor_version() : "";
const char* jdk_debug_level = VM_Version::printable_jdk_debug_level() != nullptr ?
VM_Version::printable_jdk_debug_level() : "";
version.print_cr("%s%s%s (%s) (%sbuild %s)", runtime_name,
(*vendor_version != '\0') ? " " : "", vendor_version,
buffer, jdk_debug_level, runtime_version);
print_prop(COMPILATION_VM_VERSION_PROPERTY, version.freeze());
stringStream time;
buffer[0] = 0;
os::print_date_and_time(&time, buffer, sizeof(buffer) - 1);
print_prop(COMPILATION_DATE_TIME_PROPERTY, time.freeze());
print_prop(COMPILATION_PROCESS_ID_PROPERTY, os::current_process_id());
print_prop(COMPILATION_THREAD_ID_PROPERTY, os::current_thread_id());
tail(PROPERTIES_ELEMENT);
_should_send_method = true;
@ -862,24 +907,91 @@ void IdealGraphPrinter::walk_nodes(Node* start, bool edges) {
}
}
void IdealGraphPrinter::print_graph(const char* name) {
// Whether the stack walk should skip the given frame when producing a C2 stack
// trace. We consider IGV- and debugger-specific frames uninteresting.
static bool should_skip_frame(const char* name) {
return strstr(name, "IdealGraphPrinter") != nullptr ||
strstr(name, "Compile::print_method") != nullptr ||
strstr(name, "Compile::igv_print_graph") != nullptr ||
strstr(name, "PrintBFS") != nullptr ||
strstr(name, "Node::dump_bfs") != nullptr;
}
// Whether the stack walk should be considered done when visiting a certain
// frame. The purpose of walking the stack is producing a C2 trace, so we
// consider all frames below (and including) C2Compiler::compile_method(..)
// uninteresting.
static bool should_end_stack_walk(const char* name) {
return strstr(name, "C2Compiler::compile_method") != nullptr;
}
void IdealGraphPrinter::print_stack(const frame* initial_frame, outputStream* graph_name) {
char buf[O_BUFLEN];
frame fr = initial_frame == nullptr ? os::current_frame() : *initial_frame;
int frame = 0;
for (int count = 0; count < StackPrintLimit && fr.pc() != nullptr; count++) {
int offset;
buf[0] = '\0';
bool found = os::dll_address_to_function_name(fr.pc(), buf, sizeof(buf), &offset);
if (!found || should_end_stack_walk(buf)) {
break;
}
if (!should_skip_frame(buf)) {
stringStream frame_loc;
frame_loc.print("%s", buf);
buf[0] = '\0';
int line_no;
if (Decoder::get_source_info(fr.pc(), buf, sizeof(buf), &line_no, count != 1)) {
frame_loc.print(" (%s:%d)", buf, line_no);
if (graph_name != nullptr) {
// Extract a debug graph name and return.
graph_name->print("%s:%d", buf, line_no);
return;
}
}
if (graph_name == nullptr) {
// Print frame as IGV property and continue to the next frame.
stringStream frame_number_str;
frame_number_str.print("frame %d:", frame);
print_prop(frame_number_str.freeze(), frame_loc.freeze());
frame++;
}
}
fr = frame::next_frame(fr, Thread::current_or_null());
}
}
void IdealGraphPrinter::print_graph(const char* name, const frame* fr) {
ResourceMark rm;
GrowableArray<const Node*> empty_list;
print(name, (Node*) C->root(), empty_list);
print(name, (Node*) C->root(), empty_list, fr);
}
// Print current ideal graph
void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const Node*>& visible_nodes) {
void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const Node*>& visible_nodes, const frame* fr) {
if (!_current_method || !_should_send_method || node == nullptr) return;
if (name == nullptr) {
stringStream graph_name;
print_stack(fr, &graph_name);
name = graph_name.freeze();
if (strlen(name) == 0) {
name = "Debug";
}
}
// Warning, unsafe cast?
_chaitin = (PhaseChaitin *)C->regalloc();
begin_head(GRAPH_ELEMENT);
print_attr(GRAPH_NAME_PROPERTY, (const char *)name);
print_attr(GRAPH_NAME_PROPERTY, name);
end_head();
head(PROPERTIES_ELEMENT);
print_stack(fr, nullptr);
tail(PROPERTIES_ELEMENT);
head(NODES_ELEMENT);
if (C->cfg() != nullptr) {
// Compute the maximum estimated frequency in the current graph.
@ -1047,7 +1159,6 @@ void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray<const
}
void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multiple_files) {
ThreadCritical tc;
if (use_multiple_files && _file_count != 0) {
assert(!_append, "append should only be used for debugging with a single file");
ResourceMark rm;

View File

@ -67,6 +67,13 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
static const char *ALL_PROPERTY;
static const char *COMPILATION_ID_PROPERTY;
static const char *COMPILATION_OSR_PROPERTY;
static const char *COMPILATION_ARGUMENTS_PROPERTY;
static const char *COMPILATION_MACHINE_PROPERTY;
static const char *COMPILATION_CPU_FEATURES_PROPERTY;
static const char *COMPILATION_VM_VERSION_PROPERTY;
static const char *COMPILATION_DATE_TIME_PROPERTY;
static const char *COMPILATION_PROCESS_ID_PROPERTY;
static const char *COMPILATION_THREAD_ID_PROPERTY;
static const char *METHOD_NAME_PROPERTY;
static const char *BLOCK_NAME_PROPERTY;
static const char *BLOCK_DOMINATOR_PROPERTY;
@ -110,6 +117,10 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
double _max_freq;
bool _append;
// Walk the native stack and print relevant C2 frames as IGV properties (if
// graph_name == nullptr) or the graph name based on the highest C2 frame (if
// graph_name != nullptr).
void print_stack(const frame* initial_frame, outputStream* graph_name);
void print_method(ciMethod* method, int bci, InlineTree* tree);
void print_inline_tree(InlineTree* tree);
void visit_node(Node* n, bool edges);
@ -149,8 +160,8 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
void print_inlining();
void begin_method();
void end_method();
void print_graph(const char* name);
void print(const char* name, Node* root, GrowableArray<const Node*>& hidden_nodes);
void print_graph(const char* name, const frame* fr = nullptr);
void print(const char* name, Node* root, GrowableArray<const Node*>& hidden_nodes, const frame* fr = nullptr);
void set_compile(Compile* compile) {C = compile; }
void update_compiled_method(ciMethod* current_method);
};

View File

@ -1774,8 +1774,8 @@ Node* Node::find(const int idx, bool only_ctrl) {
class PrintBFS {
public:
PrintBFS(const Node* start, const int max_distance, const Node* target, const char* options, outputStream* st)
: _start(start), _max_distance(max_distance), _target(target), _options(options), _output(st),
PrintBFS(const Node* start, const int max_distance, const Node* target, const char* options, outputStream* st, const frame* fr)
: _start(start), _max_distance(max_distance), _target(target), _options(options), _output(st), _frame(fr),
_dcc(this), _info_uid(cmpkey, hashkey) {}
void run();
@ -1796,6 +1796,7 @@ private:
const Node* _target;
const char* _options;
outputStream* _output;
const frame* _frame;
// options
bool _traverse_inputs = false;
@ -2057,7 +2058,7 @@ void PrintBFS::print() {
if (_print_igv) {
Compile* C = Compile::current();
C->init_igv();
C->igv_print_graph_to_network("PrintBFS", _print_list);
C->igv_print_graph_to_network(nullptr, _print_list, _frame);
}
} else {
_output->print_cr("No nodes to print.");
@ -2102,6 +2103,8 @@ void PrintBFS::print_options_help(bool print_examples) {
_output->print_cr(" B: print scheduling blocks (if available)");
_output->print_cr(" $: dump only, no header, no other columns");
_output->print_cr(" !: show nodes on IGV (sent over network stream)");
_output->print_cr(" (use preferably with dump_bfs(int, Node*, char*, void*, void*, void*)");
_output->print_cr(" to produce a C2 stack trace along with the graph dump, see examples below)");
_output->print_cr("");
_output->print_cr("recursively follow edges to nodes with permitted visit types,");
_output->print_cr("on the boundary additionally display nodes allowed in boundary types");
@ -2151,6 +2154,9 @@ void PrintBFS::print_options_help(bool print_examples) {
_output->print_cr(" find all paths (A) between two nodes of length at most 8");
_output->print_cr(" find_node(741)->dump_bfs(7, find_node(741), \"c+A\")");
_output->print_cr(" find all control loops for this node");
_output->print_cr(" find_node(741)->dump_bfs(7, find_node(741), \"c+A!\", $sp, $fp, $pc)");
_output->print_cr(" same as above, but printing the resulting subgraph");
_output->print_cr(" along with a C2 stack trace on IGV");
}
}
@ -2409,8 +2415,8 @@ void Node::dump_bfs(const int max_distance, Node* target, const char* options) c
}
// Used to dump to stream.
void Node::dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st) const {
PrintBFS bfs(this, max_distance, target, options, st);
void Node::dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st, const frame* fr) const {
PrintBFS bfs(this, max_distance, target, options, st, fr);
bfs.run();
}
@ -2419,6 +2425,13 @@ void Node::dump_bfs(const int max_distance) const {
dump_bfs(max_distance, nullptr, nullptr);
}
// Call this from debugger, with stack handling register arguments for IGV dumps.
// Example: p find_node(741)->dump_bfs(7, find_node(741), "c+A!", $sp, $fp, $pc).
void Node::dump_bfs(const int max_distance, Node* target, const char* options, void* sp, void* fp, void* pc) const {
frame fr(sp, fp, pc);
dump_bfs(max_distance, target, options, tty, &fr);
}
// -----------------------------dump_idx---------------------------------------
void Node::dump_idx(bool align, outputStream* st, DumpConfig* dc) const {
if (dc != nullptr) {

View File

@ -1298,9 +1298,10 @@ public:
public:
Node* find(int idx, bool only_ctrl = false); // Search the graph for the given idx.
Node* find_ctrl(int idx); // Search control ancestors for the given idx.
void dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st) const;
void dump_bfs(const int max_distance, Node* target, const char* options, outputStream* st, const frame* fr = nullptr) const;
void dump_bfs(const int max_distance, Node* target, const char* options) const; // directly to tty
void dump_bfs(const int max_distance) const; // dump_bfs(max_distance, nullptr, nullptr)
void dump_bfs(const int max_distance, Node* target, const char* options, void* sp, void* fp, void* pc) const;
class DumpConfig {
public:
// overridden to implement coloring of node idx

View File

@ -988,6 +988,10 @@ JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvm
self_tobj = Handle(current, thread_oop);
continue; // self suspend after all other suspends
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_oop);
java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread);
}
results[i] = suspend_thread(thread_oop, java_thread, /* single_suspend */ true);
}
}
@ -1114,6 +1118,10 @@ JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmt
continue;
}
}
if (java_lang_VirtualThread::is_instance(thread_oop)) {
oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_oop);
java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread);
}
results[i] = resume_thread(thread_oop, java_thread, /* single_resume */ true);
}
// per-thread resume results returned via results parameter

View File

@ -1398,8 +1398,7 @@ JvmtiEnvBase::is_vthread_suspended(oop vt_oop, JavaThread* jt) {
bool suspended = false;
if (java_lang_VirtualThread::is_instance(vt_oop)) {
suspended = JvmtiVTSuspender::is_vthread_suspended(vt_oop);
}
if (vt_oop->is_a(vmClasses::BoundVirtualThread_klass())) {
} else if (vt_oop->is_a(vmClasses::BoundVirtualThread_klass())) {
suspended = jt->is_suspended();
}
return suspended;
@ -1757,57 +1756,46 @@ JvmtiEnvBase::suspend_thread(oop thread_oop, JavaThread* java_thread, bool singl
Handle thread_h(current, thread_oop);
bool is_virtual = java_lang_VirtualThread::is_instance(thread_h());
if (is_virtual) {
if (single_suspend) {
if (JvmtiVTSuspender::is_vthread_suspended(thread_h())) {
return JVMTI_ERROR_THREAD_SUSPENDED;
}
JvmtiVTSuspender::register_vthread_suspend(thread_h());
// Check if virtual thread is mounted and there is a java_thread.
// A non-null java_thread is always passed in the !single_suspend case.
oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_h());
java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread);
// Unmounted vthread case.
if (is_virtual && java_thread == nullptr) {
assert(single_suspend, "sanity check");
if (JvmtiVTSuspender::is_vthread_suspended(thread_h())) {
return JVMTI_ERROR_THREAD_SUSPENDED;
}
// The java_thread can be still blocked in VTMS transition after a previous JVMTI resume call.
// There is no need to suspend the java_thread in this case. After vthread unblocking,
// it will check for ext_suspend request and suspend itself if necessary.
if (java_thread == nullptr || java_thread->is_suspended()) {
// We are done if the virtual thread is unmounted or
// the java_thread is externally suspended.
return JVMTI_ERROR_NONE;
}
// The virtual thread is mounted: suspend the java_thread.
JvmtiVTSuspender::register_vthread_suspend(thread_h());
return JVMTI_ERROR_NONE;
}
// Platform thread or mounted vthread cases.
assert(java_thread != nullptr, "sanity check");
assert(!java_thread->is_in_VTMS_transition(), "sanity check");
// Don't allow hidden thread suspend request.
if (java_thread->is_hidden_from_external_view()) {
return JVMTI_ERROR_NONE;
}
bool is_thread_carrying = is_thread_carrying_vthread(java_thread, thread_h());
// A case of non-virtual thread.
if (!is_virtual) {
// Thread.suspend() is used in some tests. It sets jt->is_suspended() only.
if (java_thread->is_carrier_thread_suspended() ||
(!is_thread_carrying && java_thread->is_suspended())) {
return JVMTI_ERROR_THREAD_SUSPENDED;
}
java_thread->set_carrier_thread_suspended();
}
assert(!java_thread->is_in_VTMS_transition(), "sanity check");
assert(!single_suspend || (!is_virtual && java_thread->is_carrier_thread_suspended()) ||
(is_virtual && JvmtiVTSuspender::is_vthread_suspended(thread_h())),
"sanity check");
// An attempt to handshake-suspend a thread carrying a virtual thread will result in
// suspension of mounted virtual thread. So, we just mark it as suspended
// and it will be actually suspended at virtual thread unmount transition.
if (!is_thread_carrying) {
bool is_thread_carrying = is_thread_carrying_vthread(java_thread, thread_h());
if (is_thread_carrying) {
return java_thread->set_carrier_thread_suspended() ? JVMTI_ERROR_NONE : JVMTI_ERROR_THREAD_SUSPENDED;
} else {
// Platform thread (not carrying vthread) or mounted vthread cases.
assert(thread_h() != nullptr, "sanity check");
assert(single_suspend || thread_h()->is_a(vmClasses::BaseVirtualThread_klass()),
"SuspendAllVirtualThreads should never suspend non-virtual threads");
// Case of mounted virtual or attached carrier thread.
if (!java_thread->java_suspend()) {
// Ideally we would just need to check java_thread->is_suspended(), but we have to
// consider the case of trying to suspend a thread that was previously suspended while
// carrying a vthread but has already unmounted it.
if (java_thread->is_suspended() || (!is_virtual && java_thread->is_carrier_thread_suspended())) {
return JVMTI_ERROR_THREAD_SUSPENDED;
}
if (!java_thread->java_suspend(is_virtual && single_suspend)) {
// Thread is already suspended or in process of exiting.
if (java_thread->is_exiting()) {
// The thread was in the process of exiting.
@ -1815,8 +1803,8 @@ JvmtiEnvBase::suspend_thread(oop thread_oop, JavaThread* java_thread, bool singl
}
return JVMTI_ERROR_THREAD_SUSPENDED;
}
return JVMTI_ERROR_NONE;
}
return JVMTI_ERROR_NONE;
}
// java_thread - protected by ThreadsListHandle
@ -1827,54 +1815,51 @@ JvmtiEnvBase::resume_thread(oop thread_oop, JavaThread* java_thread, bool single
Handle thread_h(current, thread_oop);
bool is_virtual = java_lang_VirtualThread::is_instance(thread_h());
if (is_virtual) {
if (single_resume) {
if (!JvmtiVTSuspender::is_vthread_suspended(thread_h())) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
JvmtiVTSuspender::register_vthread_resume(thread_h());
// Check if virtual thread is mounted and there is a java_thread.
// A non-null java_thread is always passed in the !single_resume case.
oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_h());
java_thread = carrier_thread == nullptr ? nullptr : java_lang_Thread::thread(carrier_thread);
// Unmounted vthread case.
if (is_virtual && java_thread == nullptr) {
assert(single_resume, "sanity check");
if (!JvmtiVTSuspender::is_vthread_suspended(thread_h())) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
// The java_thread can be still blocked in VTMS transition after a previous JVMTI suspend call.
// There is no need to resume the java_thread in this case. After vthread unblocking,
// it will check for is_vthread_suspended request and remain resumed if necessary.
if (java_thread == nullptr || !java_thread->is_suspended()) {
// We are done if the virtual thread is unmounted or
// the java_thread is not externally suspended.
return JVMTI_ERROR_NONE;
}
// The virtual thread is mounted and java_thread is supended: resume the java_thread.
JvmtiVTSuspender::register_vthread_resume(thread_h());
return JVMTI_ERROR_NONE;
}
// Platform thread or mounted vthread cases.
assert(java_thread != nullptr, "sanity check");
assert(!java_thread->is_in_VTMS_transition(), "sanity check");
// Don't allow hidden thread resume request.
if (java_thread->is_hidden_from_external_view()) {
return JVMTI_ERROR_NONE;
}
bool is_thread_carrying = is_thread_carrying_vthread(java_thread, thread_h());
if (is_thread_carrying) {
return java_thread->clear_carrier_thread_suspended() ? JVMTI_ERROR_NONE : JVMTI_ERROR_THREAD_NOT_SUSPENDED;
} else {
// Platform thread (not carrying vthread) or mounted vthread cases.
// A case of a non-virtual thread.
if (!is_virtual) {
if (!java_thread->is_carrier_thread_suspended() &&
(is_thread_carrying || !java_thread->is_suspended())) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
java_thread->clear_carrier_thread_suspended();
}
assert(!java_thread->is_in_VTMS_transition(), "sanity check");
if (!is_thread_carrying) {
assert(thread_h() != nullptr, "sanity check");
assert(single_resume || thread_h()->is_a(vmClasses::BaseVirtualThread_klass()),
"ResumeAllVirtualThreads should never resume non-virtual threads");
if (java_thread->is_suspended()) {
if (!java_thread->java_resume()) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
// Ideally we would not have to check this but we have to consider the case
// of trying to resume a thread that was previously suspended while carrying
// a vthread but has already unmounted it.
if (!is_virtual && java_thread->is_carrier_thread_suspended()) {
bool res = java_thread->clear_carrier_thread_suspended();
assert(res, "resume operations running concurrently?");
return JVMTI_ERROR_NONE;
}
if (!java_thread->java_resume(is_virtual && single_resume)) {
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
return JVMTI_ERROR_NONE;
}
return JVMTI_ERROR_NONE;
}
ResourceTracker::ResourceTracker(JvmtiEnv* env) {

View File

@ -658,7 +658,7 @@ vframe *VM_GetOrSetLocal::get_vframe() {
javaVFrame *VM_GetOrSetLocal::get_java_vframe() {
vframe* vf = get_vframe();
if (!(_self || _thread->is_carrier_thread_suspended())) {
if (!_self && !_thread->is_suspended() && !_thread->is_carrier_thread_suspended()) {
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
return nullptr;
}

View File

@ -254,7 +254,10 @@ JvmtiVTMSTransitionDisabler::print_info() {
// disable VTMS transitions for one virtual thread
// disable VTMS transitions for all threads if thread is nullptr or a platform thread
JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread)
: _is_SR(false), _thread(thread)
: _is_SR(false),
_is_virtual(false),
_is_self(false),
_thread(thread)
{
if (!Continuations::enabled()) {
return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads
@ -262,21 +265,26 @@ JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread)
if (Thread::current_or_null() == nullptr) {
return; // Detached thread, can be a call from Agent_OnLoad.
}
JavaThread* current = JavaThread::current();
oop thread_oop = JNIHandles::resolve_external_guard(thread);
_is_virtual = java_lang_VirtualThread::is_instance(thread_oop);
if (thread == nullptr ||
(!_is_virtual && thread_oop == current->threadObj()) ||
(_is_virtual && thread_oop == current->vthread())) {
_is_self = true;
return; // no need for current thread to disable and enable transitions for itself
}
if (!sync_protocol_enabled_permanently()) {
JvmtiVTMSTransitionDisabler::inc_sync_protocol_enabled_count();
}
oop thread_oop = JNIHandles::resolve_external_guard(thread);
// Target can be virtual or platform thread.
// If target is a platform thread then we have to disable VTMS transitions for all threads.
// It is by several reasons:
// - carrier threads can mount virtual threads which may cause incorrect behavior
// - there is no mechanism to disable transitions for a specific carrier thread yet
if (!java_lang_VirtualThread::is_instance(thread_oop)) {
_thread = nullptr; // target is a platform thread, switch to disabling VTMS transitions for all threads
}
if (_thread != nullptr) {
if (_is_virtual) {
VTMS_transition_disable_for_one(); // disable VTMS transitions for one virtual thread
} else {
VTMS_transition_disable_for_all(); // disable VTMS transitions for all virtual threads
@ -285,7 +293,10 @@ JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread)
// disable VTMS transitions for all virtual threads
JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(bool is_SR)
: _is_SR(is_SR), _thread(nullptr)
: _is_SR(is_SR),
_is_virtual(false),
_is_self(false),
_thread(nullptr)
{
if (!Continuations::enabled()) {
return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads
@ -309,7 +320,10 @@ JvmtiVTMSTransitionDisabler::~JvmtiVTMSTransitionDisabler() {
if (Thread::current_or_null() == nullptr) {
return; // Detached thread, can be a call from Agent_OnLoad.
}
if (_thread != nullptr) {
if (_is_self) {
return; // no need for current thread to disable and enable transitions for itself
}
if (_is_virtual) {
VTMS_transition_enable_for_one(); // enable VTMS transitions for one virtual thread
} else {
VTMS_transition_enable_for_all(); // enable VTMS transitions for all virtual threads
@ -684,7 +698,7 @@ JvmtiVTSuspender::_not_suspended_list = new VirtualThreadList();
void
JvmtiVTSuspender::register_all_vthreads_suspend() {
MonitorLocker ml(JvmtiVTMSTransition_lock);
MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag);
_SR_mode = SR_all;
_suspended_list->invalidate();
@ -693,7 +707,7 @@ JvmtiVTSuspender::register_all_vthreads_suspend() {
void
JvmtiVTSuspender::register_all_vthreads_resume() {
MonitorLocker ml(JvmtiVTMSTransition_lock);
MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag);
_SR_mode = SR_none;
_suspended_list->invalidate();
@ -703,7 +717,7 @@ JvmtiVTSuspender::register_all_vthreads_resume() {
void
JvmtiVTSuspender::register_vthread_suspend(oop vt) {
int64_t id = java_lang_Thread::thread_id(vt);
MonitorLocker ml(JvmtiVTMSTransition_lock);
MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag);
if (_SR_mode == SR_all) {
assert(_not_suspended_list->contains(id),
@ -720,7 +734,7 @@ JvmtiVTSuspender::register_vthread_suspend(oop vt) {
void
JvmtiVTSuspender::register_vthread_resume(oop vt) {
int64_t id = java_lang_Thread::thread_id(vt);
MonitorLocker ml(JvmtiVTMSTransition_lock);
MutexLocker ml(JvmtiVThreadSuspend_lock, Mutex::_no_safepoint_check_flag);
if (_SR_mode == SR_all) {
assert(!_not_suspended_list->contains(id),

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -86,6 +86,8 @@ class JvmtiVTMSTransitionDisabler {
static volatile bool _sync_protocol_enabled_permanently; // seen a suspender: JvmtiVTMSTransitionDisabler protocol is enabled permanently
bool _is_SR; // is suspender or resumer
bool _is_virtual; // target thread is virtual
bool _is_self; // JvmtiVTMSTransitionDisabler is a no-op for current platform, carrier or virtual thread
jthread _thread; // virtual thread to disable transitions for, no-op if it is a platform thread
DEBUG_ONLY(static void print_info();)

View File

@ -28,6 +28,7 @@
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/resourceArea.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "runtime/atomic.hpp"
#include "runtime/globals.hpp"
#include "runtime/handshake.hpp"
@ -729,7 +730,7 @@ class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure {
virtual bool is_suspend() { return true; }
};
bool HandshakeState::suspend_with_handshake() {
bool HandshakeState::suspend_with_handshake(bool register_vthread_SR) {
assert(_handshakee->threadObj() != nullptr, "cannot suspend with a null threadObj");
if (_handshakee->is_exiting()) {
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_handshakee));
@ -744,7 +745,7 @@ bool HandshakeState::suspend_with_handshake() {
// Target is going to wake up and leave suspension.
// Let's just stop the thread from doing that.
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_handshakee));
set_suspended(true);
set_suspended(true, register_vthread_SR);
return true;
}
}
@ -752,7 +753,7 @@ bool HandshakeState::suspend_with_handshake() {
assert(!is_suspended(), "cannot be suspended without a suspend request");
// Thread is safe, so it must execute the request, thus we can count it as suspended
// from this point.
set_suspended(true);
set_suspended(true, register_vthread_SR);
set_async_suspend_handshake(true);
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_handshakee));
ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake();
@ -762,17 +763,19 @@ bool HandshakeState::suspend_with_handshake() {
// This is the closure that synchronously honors the suspend request.
class SuspendThreadHandshake : public HandshakeClosure {
bool _register_vthread_SR;
bool _did_suspend;
public:
SuspendThreadHandshake() : HandshakeClosure("SuspendThread"), _did_suspend(false) {}
SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"),
_register_vthread_SR(register_vthread_SR), _did_suspend(false) {}
void do_thread(Thread* thr) {
JavaThread* target = JavaThread::cast(thr);
_did_suspend = target->handshake_state()->suspend_with_handshake();
_did_suspend = target->handshake_state()->suspend_with_handshake(_register_vthread_SR);
}
bool did_suspend() { return _did_suspend; }
};
bool HandshakeState::suspend() {
bool HandshakeState::suspend(bool register_vthread_SR) {
JVMTI_ONLY(assert(!_handshakee->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");)
JavaThread* self = JavaThread::current();
if (_handshakee == self) {
@ -780,31 +783,42 @@ bool HandshakeState::suspend() {
// and just suspend directly
ThreadBlockInVM tbivm(self);
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
set_suspended(true);
set_suspended(true, register_vthread_SR);
do_self_suspend();
return true;
} else {
SuspendThreadHandshake st;
SuspendThreadHandshake st(register_vthread_SR);
Handshake::execute(&st, _handshakee);
return st.did_suspend();
}
}
bool HandshakeState::resume() {
if (!is_suspended()) {
return false;
}
bool HandshakeState::resume(bool register_vthread_SR) {
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
if (!is_suspended()) {
assert(!_handshakee->is_suspended(), "cannot be suspended without a suspend request");
return false;
}
// Resume the thread.
set_suspended(false);
set_suspended(false, register_vthread_SR);
_lock.notify();
return true;
}
void HandshakeState::set_suspended(bool is_suspend, bool register_vthread_SR) {
#if INCLUDE_JVMTI
if (register_vthread_SR) {
assert(_handshakee->is_vthread_mounted(), "sanity check");
if (is_suspend) {
JvmtiVTSuspender::register_vthread_suspend(_handshakee->vthread());
} else {
JvmtiVTSuspender::register_vthread_resume(_handshakee->vthread());
}
}
#endif
Atomic::store(&_suspended, is_suspend);
}
void HandshakeState::handle_unsafe_access_error() {
if (is_suspended()) {
// A suspend handshake was added to the queue after the

View File

@ -173,18 +173,18 @@ class HandshakeState {
bool _async_suspend_handshake;
// Called from the suspend handshake.
bool suspend_with_handshake();
bool suspend_with_handshake(bool register_vthread_SR);
// Called from the async handshake (the trap)
// to stop a thread from continuing execution when suspended.
void do_self_suspend();
bool is_suspended() { return Atomic::load(&_suspended); }
void set_suspended(bool to) { return Atomic::store(&_suspended, to); }
void set_suspended(bool to, bool register_vthread_SR);
bool has_async_suspend_handshake() { return _async_suspend_handshake; }
void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; }
bool suspend();
bool resume();
bool suspend(bool register_vthread_SR);
bool resume(bool register_vthread_SR);
};
#endif // SHARE_RUNTIME_HANDSHAKE_HPP

View File

@ -1193,7 +1193,7 @@ void JavaThread::set_is_VTMS_transition_disabler(bool val) {
// - Target thread will not execute any new bytecode.
// - Target thread will not enter any new monitors.
//
bool JavaThread::java_suspend() {
bool JavaThread::java_suspend(bool register_vthread_SR) {
#if INCLUDE_JVMTI
// Suspending a JavaThread in VTMS transition or disabling VTMS transitions can cause deadlocks.
assert(!is_in_VTMS_transition(), "no suspend allowed in VTMS transition");
@ -1202,13 +1202,13 @@ bool JavaThread::java_suspend() {
guarantee(Thread::is_JavaThread_protected(/* target */ this),
"target JavaThread is not protected in calling context.");
return this->handshake_state()->suspend();
return this->handshake_state()->suspend(register_vthread_SR);
}
bool JavaThread::java_resume() {
bool JavaThread::java_resume(bool register_vthread_SR) {
guarantee(Thread::is_JavaThread_protected_by_TLH(/* target */ this),
"missing ThreadsListHandle in calling context.");
return this->handshake_state()->resume();
return this->handshake_state()->resume(register_vthread_SR);
}
// Wait for another thread to perform object reallocation and relocking on behalf of

View File

@ -698,8 +698,8 @@ private:
// Suspend/resume support for JavaThread
// higher-level suspension/resume logic called by the public APIs
bool java_suspend();
bool java_resume();
bool java_suspend(bool register_vthread_SR);
bool java_resume(bool register_vthread_SR);
bool is_suspended() { return _handshake.is_suspended(); }
// Check for async exception in addition to safepoint.
@ -710,11 +710,11 @@ private:
void wait_for_object_deoptimization();
#if INCLUDE_JVMTI
inline void set_carrier_thread_suspended();
inline void clear_carrier_thread_suspended();
inline bool set_carrier_thread_suspended();
inline bool clear_carrier_thread_suspended();
bool is_carrier_thread_suspended() const {
return _carrier_thread_suspended;
return Atomic::load(&_carrier_thread_suspended);
}
bool is_in_VTMS_transition() const { return _is_in_VTMS_transition; }

View File

@ -71,11 +71,11 @@ inline void JavaThread::clear_obj_deopt_flag() {
}
#if INCLUDE_JVMTI
inline void JavaThread::set_carrier_thread_suspended() {
_carrier_thread_suspended = true;
inline bool JavaThread::set_carrier_thread_suspended() {
return Atomic::cmpxchg(&_carrier_thread_suspended, false, true) == false;
}
inline void JavaThread::clear_carrier_thread_suspended() {
_carrier_thread_suspended = false;
inline bool JavaThread::clear_carrier_thread_suspended() {
return Atomic::cmpxchg(&_carrier_thread_suspended, true, false) == true;
}
#endif

View File

@ -50,6 +50,7 @@ Monitor* JNICritical_lock = nullptr;
Mutex* JvmtiThreadState_lock = nullptr;
Monitor* EscapeBarrier_lock = nullptr;
Monitor* JvmtiVTMSTransition_lock = nullptr;
Mutex* JvmtiVThreadSuspend_lock = nullptr;
Monitor* Heap_lock = nullptr;
#if INCLUDE_PARALLELGC
Mutex* PSOldGenExpand_lock = nullptr;
@ -260,6 +261,7 @@ void mutex_init() {
MUTEX_DEFN(DirectivesStack_lock , PaddedMutex , nosafepoint);
MUTEX_DEFN(JvmtiVTMSTransition_lock , PaddedMonitor, safepoint); // used for Virtual Thread Mount State transition management
MUTEX_DEFN(JvmtiVThreadSuspend_lock , PaddedMutex, nosafepoint-1);
MUTEX_DEFN(EscapeBarrier_lock , PaddedMonitor, nosafepoint); // Used to synchronize object reallocation/relocking triggered by JVMTI
MUTEX_DEFN(Management_lock , PaddedMutex , safepoint); // used for JVM management

View File

@ -48,6 +48,7 @@ extern Monitor* JNICritical_lock; // a lock used while synchroniz
extern Mutex* JvmtiThreadState_lock; // a lock on modification of JVMTI thread data
extern Monitor* EscapeBarrier_lock; // a lock to sync reallocating and relocking objects because of JVMTI access
extern Monitor* JvmtiVTMSTransition_lock; // a lock for Virtual Thread Mount State transition (VTMS transition) management
extern Mutex* JvmtiVThreadSuspend_lock; // a lock for virtual threads suspension
extern Monitor* Heap_lock; // a lock on the heap
#if INCLUDE_PARALLELGC
extern Mutex* PSOldGenExpand_lock; // a lock on expanding the heap

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024 SAP SE. All rights reserved.
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -33,7 +33,10 @@
// Useful if the function or method is known to do something special or even 'dangerous', for
// example causing desired signals/crashes.
#ifdef UNDEFINED_BEHAVIOR_SANITIZER
#if defined(__clang__) || defined(__GNUC__)
#if defined(__clang__)
#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined","float-divide-by-zero")))
#endif
#if defined(__GNUC__) && !defined(__clang__)
#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined")))
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -89,7 +89,6 @@ import java.util.stream.Stream;
* @see Process
* @since 9
*/
@jdk.internal.ValueBased
public interface ProcessHandle extends Comparable<ProcessHandle> {
/**
@ -114,7 +113,7 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
* @throws UnsupportedOperationException if the implementation
* does not support this operation
*/
public static Optional<ProcessHandle> of(long pid) {
static Optional<ProcessHandle> of(long pid) {
return ProcessHandleImpl.get(pid);
}
@ -126,7 +125,7 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
* @throws UnsupportedOperationException if the implementation
* does not support this operation
*/
public static ProcessHandle current() {
static ProcessHandle current() {
return ProcessHandleImpl.current();
}
@ -204,21 +203,21 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
* and actions if the value is available.
* @since 9
*/
public interface Info {
interface Info {
/**
* Returns the executable pathname of the process.
*
* @return an {@code Optional<String>} of the executable pathname
* of the process
*/
public Optional<String> command();
Optional<String> command();
/**
* Returns the command line of the process.
* <p>
* If {@link #command command()} and {@link #arguments arguments()} return
* non-empty optionals, this is simply a convenience method which concatenates
* the values of the two functions separated by spaces. Otherwise it will return a
* the values of the two functions separated by spaces. Otherwise, it will return a
* best-effort, platform dependent representation of the command line.
*
* @apiNote Note that the returned executable pathname and the
@ -233,7 +232,7 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
* @return an {@code Optional<String>} of the command line
* of the process
*/
public Optional<String> commandLine();
Optional<String> commandLine();
/**
* Returns an array of Strings of the arguments of the process.
@ -244,28 +243,28 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
*
* @return an {@code Optional<String[]>} of the arguments of the process
*/
public Optional<String[]> arguments();
Optional<String[]> arguments();
/**
* Returns the start time of the process.
*
* @return an {@code Optional<Instant>} of the start time of the process
*/
public Optional<Instant> startInstant();
Optional<Instant> startInstant();
/**
* Returns the total cputime accumulated of the process.
*
* @return an {@code Optional<Duration>} for the accumulated total cputime
*/
public Optional<Duration> totalCpuDuration();
Optional<Duration> totalCpuDuration();
/**
* Return the user of the process.
*
* @return an {@code Optional<String>} for the user of the process
*/
public Optional<String> user();
Optional<String> user();
}
/**
@ -285,13 +284,11 @@ public interface ProcessHandle extends Comparable<ProcessHandle> {
* {@link java.util.concurrent.CompletableFuture#isDone done} or to
* {@link java.util.concurrent.Future#get() wait} for it to terminate.
* {@link java.util.concurrent.Future#cancel(boolean) Cancelling}
* the CompleteableFuture does not affect the Process.
* the {@linkplain CompletableFuture CompletableFuture} does not affect the Process.
* @apiNote
* The process may be observed to have terminated with {@link #isAlive}
* before the ComputableFuture is completed and dependent actions are invoked.
*
* before the {@code CompletableFuture} is completed and dependent actions are invoked.
* @return a new {@code CompletableFuture<ProcessHandle>} for the ProcessHandle
*
* @throws IllegalStateException if the process is the current process
*/
CompletableFuture<ProcessHandle> onExit();

View File

@ -432,7 +432,7 @@ final class StringConcatHelper {
@ForceInline
static String doConcat(String s1, String s2) {
byte coder = (byte) (s1.coder() | s2.coder());
int newLength = (s1.length() + s2.length()) << coder;
int newLength = checkOverflow(s1.length() + s2.length()) << coder;
byte[] buf = newArray(newLength);
s1.getBytes(buf, 0, coder);
s2.getBytes(buf, s1.length(), coder);

View File

@ -2483,7 +2483,7 @@ public sealed interface CodeBuilder
var cpArgs = ref.bootstrapArgs();
List<LoadableConstantEntry> bsArguments = new ArrayList<>(cpArgs.length);
for (var constantValue : cpArgs) {
bsArguments.add(BytecodeHelpers.constantEntry(constantPool(), constantValue));
bsArguments.add(constantPool().loadableConstantEntry(requireNonNull(constantValue)));
}
BootstrapMethodEntry bm = constantPool().bsmEntry(bsMethod, bsArguments);
NameAndTypeEntry nameAndType = constantPool().nameAndTypeEntry(ref.invocationName(), ref.invocationType());
@ -2941,7 +2941,7 @@ public sealed interface CodeBuilder
* @see ConstantInstruction.LoadConstantInstruction
*/
default CodeBuilder ldc(ConstantDesc value) {
return ldc(BytecodeHelpers.constantEntry(constantPool(), value));
return ldc(constantPool().loadableConstantEntry(requireNonNull(value)));
}
/**

View File

@ -36,6 +36,7 @@ import java.lang.invoke.MethodHandleInfo;
import java.util.List;
import java.util.function.Consumer;
import jdk.internal.classfile.impl.AbstractPoolEntry;
import jdk.internal.classfile.impl.AbstractPoolEntry.ClassEntryImpl;
import jdk.internal.classfile.impl.ClassReaderImpl;
import jdk.internal.classfile.impl.SplitConstantPool;
@ -385,11 +386,13 @@ public sealed interface ConstantPoolBuilder
default MethodHandleEntry methodHandleEntry(DirectMethodHandleDesc descriptor) {
var owner = classEntry(descriptor.owner());
var nat = nameAndTypeEntry(utf8Entry(descriptor.methodName()), utf8Entry(descriptor.lookupDescriptor()));
return methodHandleEntry(descriptor.refKind(), switch (descriptor.kind()) {
var ret = methodHandleEntry(descriptor.refKind(), switch (descriptor.kind()) {
case GETTER, SETTER, STATIC_GETTER, STATIC_SETTER -> fieldRefEntry(owner, nat);
case INTERFACE_STATIC, INTERFACE_VIRTUAL, INTERFACE_SPECIAL -> interfaceMethodRefEntry(owner, nat);
case STATIC, VIRTUAL, SPECIAL, CONSTRUCTOR -> methodRefEntry(owner, nat);
});
((AbstractPoolEntry.MethodHandleEntryImpl) ret).sym = descriptor;
return ret;
}
/**
@ -414,7 +417,9 @@ public sealed interface ConstantPoolBuilder
* @see InvokeDynamicEntry#asSymbol() InvokeDynamicEntry::asSymbol
*/
default InvokeDynamicEntry invokeDynamicEntry(DynamicCallSiteDesc dcsd) {
return invokeDynamicEntry(bsmEntry((DirectMethodHandleDesc)dcsd.bootstrapMethod(), List.of(dcsd.bootstrapArgs())), nameAndTypeEntry(dcsd.invocationName(), dcsd.invocationType()));
var ret = invokeDynamicEntry(bsmEntry((DirectMethodHandleDesc)dcsd.bootstrapMethod(), List.of(dcsd.bootstrapArgs())), nameAndTypeEntry(dcsd.invocationName(), dcsd.invocationType()));
((AbstractPoolEntry.InvokeDynamicEntryImpl) ret).sym = dcsd;
return ret;
}
/**
@ -440,7 +445,9 @@ public sealed interface ConstantPoolBuilder
* @see ConstantDynamicEntry#asSymbol() ConstantDynamicEntry::asSymbol
*/
default ConstantDynamicEntry constantDynamicEntry(DynamicConstantDesc<?> dcd) {
return constantDynamicEntry(bsmEntry(dcd.bootstrapMethod(), List.of(dcd.bootstrapArgs())), nameAndTypeEntry(dcd.constantName(), dcd.constantType()));
var ret = constantDynamicEntry(bsmEntry(dcd.bootstrapMethod(), List.of(dcd.bootstrapArgs())), nameAndTypeEntry(dcd.constantName(), dcd.constantType()));
((AbstractPoolEntry.ConstantDynamicEntryImpl) ret).sym = dcd;
return ret;
}
/**

View File

@ -501,7 +501,7 @@ public final class MethodHandles {
* <tbody>
* <tr>
* <th scope="row">{@link java.lang.invoke.MethodHandles.Lookup#findGetter lookup.findGetter(C.class,"f",FT.class)}</th>
* <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
* <td>{@code FT f;}</td><td>{@code (FT) this.f;}</td>
* </tr>
* <tr>
* <th scope="row">{@link java.lang.invoke.MethodHandles.Lookup#findStaticGetter lookup.findStaticGetter(C.class,"f",FT.class)}</th>

View File

@ -295,9 +295,13 @@ public abstract sealed class AbstractPoolEntry {
@Override
public Utf8EntryImpl clone(ConstantPoolBuilder cp) {
return (state == State.STRING && rawBytes == null)
var ret = (state == State.STRING && rawBytes == null)
? (Utf8EntryImpl) cp.utf8Entry(stringValue)
: ((SplitConstantPool) cp).maybeCloneUtf8Entry(this);
var mySym = this.typeSym;
if (ret.typeSym == null && mySym != null)
ret.typeSym = mySym;
return ret;
}
@Override
@ -939,6 +943,8 @@ public abstract sealed class AbstractPoolEntry {
extends AbstractDynamicConstantPoolEntry
implements InvokeDynamicEntry {
public @Stable DynamicCallSiteDesc sym;
InvokeDynamicEntryImpl(ConstantPool cpm, int index, int hash, BootstrapMethodEntryImpl bootstrapMethod,
NameAndTypeEntryImpl nameAndType) {
super(cpm, index, hash, bootstrapMethod, nameAndType);
@ -957,13 +963,27 @@ public abstract sealed class AbstractPoolEntry {
@Override
public InvokeDynamicEntry clone(ConstantPoolBuilder cp) {
return cp.invokeDynamicEntry(bootstrap(), nameAndType());
var ret = (InvokeDynamicEntryImpl) cp.invokeDynamicEntry(bootstrap(), nameAndType());
var mySym = this.sym;
if (ret.sym == null && mySym != null)
ret.sym = mySym;
return ret;
}
@Override
public DynamicCallSiteDesc asSymbol() {
var cache = this.sym;
if (cache != null)
return cache;
return this.sym = InvokeDynamicEntry.super.asSymbol();
}
}
public static final class ConstantDynamicEntryImpl extends AbstractDynamicConstantPoolEntry
implements ConstantDynamicEntry {
public @Stable DynamicConstantDesc<?> sym;
ConstantDynamicEntryImpl(ConstantPool cpm, int index, int hash, BootstrapMethodEntryImpl bootstrapMethod,
NameAndTypeEntryImpl nameAndType) {
super(cpm, index, hash, bootstrapMethod, nameAndType);
@ -982,7 +1002,19 @@ public abstract sealed class AbstractPoolEntry {
@Override
public ConstantDynamicEntry clone(ConstantPoolBuilder cp) {
return cp.constantDynamicEntry(bootstrap(), nameAndType());
var ret = (ConstantDynamicEntryImpl) cp.constantDynamicEntry(bootstrap(), nameAndType());
var mySym = this.sym;
if (ret.sym == null && mySym != null)
ret.sym = mySym;
return ret;
}
@Override
public DynamicConstantDesc<?> asSymbol() {
var cache = this.sym;
if (cache != null)
return cache;
return this.sym = ConstantDynamicEntry.super.asSymbol();
}
}
@ -991,6 +1023,7 @@ public abstract sealed class AbstractPoolEntry {
private final int refKind;
private final AbstractPoolEntry.AbstractMemberRefEntry reference;
public @Stable DirectMethodHandleDesc sym;
MethodHandleEntryImpl(ConstantPool cpm, int index, int hash, int refKind, AbstractPoolEntry.AbstractMemberRefEntry
reference) {
@ -1023,7 +1056,14 @@ public abstract sealed class AbstractPoolEntry {
@Override
public DirectMethodHandleDesc asSymbol() {
return MethodHandleDesc.of(
var cache = this.sym;
if (cache != null)
return cache;
return computeSymbol();
}
private DirectMethodHandleDesc computeSymbol() {
return this.sym = MethodHandleDesc.of(
DirectMethodHandleDesc.Kind.valueOf(kind(), reference() instanceof InterfaceMethodRefEntry),
((MemberRefEntry) reference()).owner().asSymbol(),
((MemberRefEntry) reference()).nameAndType().name().stringValue(),
@ -1037,7 +1077,11 @@ public abstract sealed class AbstractPoolEntry {
@Override
public MethodHandleEntry clone(ConstantPoolBuilder cp) {
return cp.methodHandleEntry(refKind, reference);
var ret = (MethodHandleEntryImpl) cp.methodHandleEntry(refKind, reference);
var mySym = this.sym;
if (ret.sym == null && mySym != null)
ret.sym = mySym;
return ret;
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -29,12 +29,10 @@ import java.lang.classfile.BootstrapMethodEntry;
import java.lang.classfile.Opcode;
import java.lang.classfile.TypeKind;
import java.lang.classfile.constantpool.*;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.MethodHandleInfo;
import java.util.ArrayList;
import java.util.List;
@ -515,38 +513,6 @@ public class BytecodeHelpers {
: Opcode.LDC;
}
public static LoadableConstantEntry constantEntry(ConstantPoolBuilder constantPool,
ConstantDesc constantValue) {
// this method is invoked during JVM bootstrap - cannot use pattern switch
if (constantValue instanceof Integer value) {
return constantPool.intEntry(value);
}
if (constantValue instanceof String value) {
return constantPool.stringEntry(value);
}
if (constantValue instanceof ClassDesc value && !value.isPrimitive()) {
return constantPool.classEntry(value);
}
if (constantValue instanceof Long value) {
return constantPool.longEntry(value);
}
if (constantValue instanceof Float value) {
return constantPool.floatEntry(value);
}
if (constantValue instanceof Double value) {
return constantPool.doubleEntry(value);
}
if (constantValue instanceof MethodTypeDesc value) {
return constantPool.methodTypeEntry(value);
}
if (constantValue instanceof DirectMethodHandleDesc value) {
return handleDescToHandleInfo(constantPool, value);
} if (constantValue instanceof DynamicConstantDesc<?> value) {
return handleConstantDescToHandleInfo(constantPool, value);
}
throw new UnsupportedOperationException("not yet: " + requireNonNull(constantValue));
}
public static ConstantDesc intrinsicConstantValue(Opcode opcode) {
return switch (opcode) {
case ACONST_NULL -> ConstantDescs.NULL;

View File

@ -30,6 +30,7 @@ import java.lang.classfile.ClassReader;
import java.lang.classfile.attribute.BootstrapMethodsAttribute;
import java.lang.classfile.constantpool.*;
import java.lang.constant.ClassDesc;
import java.lang.constant.DynamicCallSiteDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.Arrays;
import java.util.List;
@ -521,6 +522,10 @@ public final class SplitConstantPool implements ConstantPoolBuilder {
AbstractPoolEntry.ClassEntryImpl cloneClassEntry(AbstractPoolEntry.ClassEntryImpl e) {
var ce = tryFindClassEntry(e.hashCode(), e.ref1);
if (ce != null) {
var mySym = e.sym;
if (ce.sym == null && mySym != null) {
ce.sym = mySym;
}
return ce;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -716,7 +716,7 @@ public final class StackMapGenerator {
case TAG_METHOD_TYPE ->
currentFrame.pushStack(Type.METHOD_TYPE);
case TAG_DYNAMIC ->
currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).asSymbol().constantType());
currentFrame.pushStack(cp.entryByIndex(index, ConstantDynamicEntry.class).typeSymbol());
default ->
throw generatorError("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag()));
}

View File

@ -154,7 +154,7 @@ java.launcher.X.usage=\n\
\ -Xmixed mixed mode execution (default)\n\
\ -Xmn<size> sets the initial and maximum size (in bytes) of the heap\n\
\ for the young generation (nursery)\n\
\ -Xms<size> set initial Java heap size\n\
\ -Xms<size> set minimum and initial Java heap size\n\
\ -Xmx<size> set maximum Java heap size\n\
\ -Xnoclassgc disable class garbage collection\n\
\ -Xrs reduce use of OS signals by Java/VM (see documentation)\n\

View File

@ -31,8 +31,6 @@ import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HexFormat;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Locale;
/**
@ -65,7 +63,7 @@ public class Debug {
}
if (args != null) {
args = marshal(args);
args = args.toLowerCase(Locale.ENGLISH);
if (args.equals("help")) {
Help();
} else if (args.contains("all")) {
@ -349,69 +347,6 @@ public class Debug {
return sb.toString();
}
/**
* change a string into lower case except permission classes and URLs.
*/
private static String marshal(String args) {
if (args != null) {
StringBuilder target = new StringBuilder();
StringBuilder source = new StringBuilder(args);
// obtain the "permission=<classname>" options
// the syntax of classname: IDENTIFIER.IDENTIFIER
// the regular express to match a class name:
// "[a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*"
String keyReg = "[Pp][Ee][Rr][Mm][Ii][Ss][Ss][Ii][Oo][Nn]=";
String keyStr = "permission=";
String reg = keyReg +
"[a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*";
Pattern pattern = Pattern.compile(reg);
Matcher matcher = pattern.matcher(source);
StringBuilder left = new StringBuilder();
while (matcher.find()) {
String matched = matcher.group();
target.append(matched.replaceFirst(keyReg, keyStr));
target.append(" ");
// delete the matched sequence
matcher.appendReplacement(left, "");
}
matcher.appendTail(left);
source = left;
// obtain the "codebase=<URL>" options
// the syntax of URL is too flexible, and here assumes that the
// URL contains no space, comma(','), and semicolon(';'). That
// also means those characters also could be used as separator
// after codebase option.
// However, the assumption is incorrect in some special situation
// when the URL contains comma or semicolon
keyReg = "[Cc][Oo][Dd][Ee][Bb][Aa][Ss][Ee]=";
keyStr = "codebase=";
reg = keyReg + "[^, ;]*";
pattern = Pattern.compile(reg);
matcher = pattern.matcher(source);
left = new StringBuilder();
while (matcher.find()) {
String matched = matcher.group();
target.append(matched.replaceFirst(keyReg, keyStr));
target.append(" ");
// delete the matched sequence
matcher.appendReplacement(left, "");
}
matcher.appendTail(left);
source = left;
// convert the rest to lower-case characters
target.append(source.toString().toLowerCase(Locale.ENGLISH));
return target.toString();
}
return null;
}
public static String toString(byte[] b) {
if (b == null) {
return "(null)";

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -261,6 +261,7 @@ VES=VES
VND=VND
VUV=VUV
WST=WST
XAD=XAD
XAF=XAF
XAG=XAG
XAU=XAU
@ -488,6 +489,7 @@ ves=Venezuelan Bolívar Soberano
vnd=Vietnamese Dong
vuv=Vanuatu Vatu
wst=Samoan Tala
xad=Arab Accounting Dinar
xaf=CFA Franc BEAC
xag=Silver
xau=Gold

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -32,7 +32,7 @@ formatVersion=3
# Version of the currency code information in this class.
# It is a serial number that accompanies with each amendment.
dataVersion=177
dataVersion=179
# List of all valid ISO 4217 currency codes.
# To ensure compatibility, do not remove codes.
@ -54,7 +54,7 @@ all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036
SBD090-SCR690-SDD736-SDG938-SEK752-SGD702-SHP654-SIT705-SKK703-SLE925-SLL694-SOS706-\
SRD968-SRG740-SSP728-STD678-STN930-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TMT934-TND788-TOP776-\
TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\
UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\
UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAD396-XAF950-XAG961-XAU959-XBA955-\
XBB956-XBC957-XBD958-XCD951-XCG532-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\
XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWG924-\
ZWL932-ZWN942-ZWR935

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024 SAP SE. All rights reserved.
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -32,7 +32,10 @@
* following function or method.
*/
#ifdef UNDEFINED_BEHAVIOR_SANITIZER
#if defined(__clang__) || defined(__GNUC__)
#if defined(__clang__)
#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined","float-divide-by-zero")))
#endif
#if defined(__GNUC__) && !defined(__clang__)
#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined")))
#endif
#endif

View File

@ -81,6 +81,8 @@ public enum SourceVersion {
* switch in second preview, module Import Declarations in second
* preview, simple source files and instance main in fourth
* preview, flexible constructor bodies in third preview)
* 25: module import declarations, compact source files and
* instance main methods,
*/
/**
@ -449,11 +451,18 @@ public enum SourceVersion {
* The version introduced by the Java Platform, Standard Edition
* 25.
*
* Additions in this release include module import declarations
* and compact source files and instance main methods.
*
* @since 25
*
* @see <a
* href="https://docs.oracle.com/javase/specs/jls/se25/html/index.html">
* <cite>The Java Language Specification, Java SE 25 Edition</cite></a>
* @see <a href="https://openjdk.org/jeps/511">
* JEP 511: Module Import Declarations</a>
* @see <a href="https://openjdk.org/jeps/512">
* JEP 512: Compact Source Files and Instance Main Methods</a>
*/
RELEASE_25,
; // Reduce code churn when appending new constants

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -778,6 +778,12 @@ public interface Elements {
* elements.getTypeElement("I"));
* }
*
* @apiNote This method examines the method's name, signature, subclass relationship, and accessibility
* in determining whether one method overrides another, as specified in JLS {@jls 8.4.8.1}.
* In addition, an implementation may have stricter checks including method modifiers, return types and
* exception types as described in JLS {@jls 8.4.8.1} and {@jls 8.4.8.3}.
* Note that such additional compile-time checks are not guaranteed and may vary between implementations.
*
* @param overrider the first method, possible overrider
* @param overridden the second method, possibly being overridden
* @param type the class or interface of which the first method is a member

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -124,9 +124,12 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses,
kAudioHardwarePropertyDevices, &size);
if (err == noErr) {
int count = size/sizeof(AudioDeviceID);
AudioDeviceID devices[count];
AudioDeviceID *devices = (AudioDeviceID *) malloc(size);
if (!devices) {
break;
}
err = GetAudioObjectProperty(kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal,
kAudioHardwarePropertyDevices, count*sizeof(AudioDeviceID), devices, 1);
kAudioHardwarePropertyDevices, size, devices, 1);
if (err == noErr) {
bool found = false;
for (int j = 0; j < count; j++) {
@ -139,6 +142,7 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses,
invalid = true;
}
}
free(devices);
}
break;
case kAudioObjectPropertyOwnedObjects:
@ -148,9 +152,12 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses,
kAudioObjectPropertyOwnedObjects, &size);
if (err == noErr) {
int count = size / sizeof(AudioObjectID);
AudioObjectID controlIDs[count];
AudioObjectID *controlIDs = (AudioObjectID *) malloc(size);
if (!controlIDs) {
break;
}
err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyOwnedObjects, count * sizeof(AudioObjectID), &controlIDs, 1);
kAudioObjectPropertyOwnedObjects, size, controlIDs, 1);
if (err == noErr) {
for (PortControl *ctrl = mixer->portControls; ctrl != NULL; ctrl = ctrl->next) {
for (int i = 0; i < ctrl->controlCount; i++) {
@ -168,6 +175,7 @@ OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses,
}
}
}
free(controlIDs);
}
}
}
@ -229,6 +237,9 @@ INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* mixer
void* PORT_Open(INT32 mixerIndex) {
TRACE1("\n>>PORT_Open (mixerIndex=%d)\n", (int)mixerIndex);
PortMixer *mixer = (PortMixer *)calloc(1, sizeof(PortMixer));
if (!mixer) {
return nullptr;
}
mixer->deviceID = deviceCache.GetDeviceID(mixerIndex);
if (mixer->deviceID != 0) {
@ -480,16 +491,20 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
mixer->deviceControlCount = size / sizeof(AudioObjectID);
TRACE1(" PORT_GetControls: detected %d owned objects\n", mixer->deviceControlCount);
AudioObjectID controlIDs[mixer->deviceControlCount];
AudioObjectID *controlIDs = (AudioObjectID *) malloc(size);
if (!controlIDs) {
return;
}
err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyOwnedObjects, sizeof(controlIDs), controlIDs, 1);
kAudioObjectPropertyOwnedObjects, size, controlIDs, 1);
if (err) {
OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject values", portIndex);
} else {
mixer->deviceControls = (AudioControl *)calloc(mixer->deviceControlCount, sizeof(AudioControl));
if (mixer->deviceControls == NULL) {
free(controlIDs);
return;
}
@ -513,6 +528,7 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
}
}
free(controlIDs);
}
}
@ -524,10 +540,14 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0);
// collect volume and mute controls
AudioControl* volumeControls[totalChannels+1]; // 0 - for master channel
memset(&volumeControls, 0, sizeof(AudioControl *) * (totalChannels+1));
AudioControl* muteControls[totalChannels+1]; // 0 - for master channel
memset(&muteControls, 0, sizeof(AudioControl *) * (totalChannels+1));
size_t totalAndMaster = totalChannels + 1; // 0 - for master channel
AudioControl **volumeControls = (AudioControl **) calloc(totalAndMaster, sizeof(AudioControl *));
AudioControl **muteControls = (AudioControl **) calloc(totalAndMaster, sizeof(AudioControl *));
if (!volumeControls || !muteControls) {
free(muteControls);
free(volumeControls);
return;
}
for (int i=0; i<mixer->deviceControlCount; i++) {
AudioControl *control = &mixer->deviceControls[i];
@ -626,6 +646,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
CFIndex length = CFStringGetLength(cfname) + 1;
channelName = (char *)malloc(length);
if (channelName == NULL) {
free(muteControls);
free(volumeControls);
return;
}
CFStringGetCString(cfname, channelName, length, kCFStringEncodingUTF8);
@ -633,6 +655,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
} else {
channelName = (char *)malloc(16);
if (channelName == NULL) {
free(muteControls);
free(volumeControls);
return;
}
snprintf(channelName, 16, "Ch %d", ch);
@ -657,7 +681,8 @@ void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
}
AddChangeListeners(mixer);
free(muteControls);
free(volumeControls);
TRACE1("<<PORT_GetControls (portIndex = %d)\n", portIndex);
}
@ -772,28 +797,35 @@ float PORT_GetFloatValue(void* controlIDV) {
PortControl *control = (PortControl *)controlIDV;
Float32 result = 0;
Float32 subVolumes[control->controlCount];
Float32 *subVolumes = (Float32 *) malloc(control->controlCount * sizeof(Float32));
if (!subVolumes) {
return DEFAULT_VOLUME_VALUE;
}
Float32 maxVolume;
switch (control->type) {
case PortControl::Volume:
if (!TestPortControlValidity(control)) {
return DEFAULT_VOLUME_VALUE;
result = DEFAULT_VOLUME_VALUE;
break;
}
if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
return DEFAULT_VOLUME_VALUE;
result = DEFAULT_VOLUME_VALUE;
break;
}
result = maxVolume;
break;
case PortControl::Balance:
if (!TestPortControlValidity(control)) {
return DEFAULT_BALANCE_VALUE;
result = DEFAULT_BALANCE_VALUE;
break;
}
// balance control always has 2 volume controls
if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
return DEFAULT_VOLUME_VALUE;
result = DEFAULT_VOLUME_VALUE;
break;
}
// calculate balance value
if (subVolumes[0] > subVolumes[1]) {
@ -806,9 +838,10 @@ float PORT_GetFloatValue(void* controlIDV) {
break;
default:
ERROR1("GetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
return 0;
result = 0;
break;
}
free(subVolumes);
TRACE1("<<PORT_GetFloatValue = %f\n", result);
return result;
}
@ -821,13 +854,16 @@ void PORT_SetFloatValue(void* controlIDV, float value) {
return;
}
Float32 subVolumes[control->controlCount];
Float32 *subVolumes = (Float32 *) malloc(sizeof(Float32) * control->controlCount);
if (!subVolumes) {
return;
}
Float32 maxVolume;
switch (control->type) {
case PortControl::Volume:
if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
return;
break;
}
// update the values
if (maxVolume > 0.001) {
@ -845,7 +881,7 @@ void PORT_SetFloatValue(void* controlIDV, float value) {
case PortControl::Balance:
// balance control always has 2 volume controls
if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
return;
break;
}
// calculate new values
if (value < 0.0f) {
@ -861,8 +897,9 @@ void PORT_SetFloatValue(void* controlIDV, float value) {
break;
default:
ERROR1("PORT_SetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
return;
break;
}
free(subVolumes);
}
#endif // USE_PORTS

View File

@ -127,24 +127,6 @@ public class PSPrinterJob extends RasterPrinterJob {
*/
private static final int MAX_PSSTR = (1024 * 64 - 1);
private static final int RED_MASK = 0x00ff0000;
private static final int GREEN_MASK = 0x0000ff00;
private static final int BLUE_MASK = 0x000000ff;
private static final int RED_SHIFT = 16;
private static final int GREEN_SHIFT = 8;
private static final int BLUE_SHIFT = 0;
private static final int LOWNIBBLE_MASK = 0x0000000f;
private static final int HINIBBLE_MASK = 0x000000f0;
private static final int HINIBBLE_SHIFT = 4;
private static final byte[] hexDigits = {
(byte)'0', (byte)'1', (byte)'2', (byte)'3',
(byte)'4', (byte)'5', (byte)'6', (byte)'7',
(byte)'8', (byte)'9', (byte)'A', (byte)'B',
(byte)'C', (byte)'D', (byte)'E', (byte)'F'
};
private static final int PS_XRES = 300;
private static final int PS_YRES = 300;
@ -267,14 +249,6 @@ public class PSPrinterJob extends RasterPrinterJob {
private double xres = PS_XRES;
private double yres = PS_YRES;
/* non-null if printing EPS for Java Plugin */
private EPSPrinter epsPrinter = null;
/**
* The metrics for the font currently set.
*/
FontMetrics mCurMetrics;
/**
* The output stream to which the generated PostScript
* is written.
@ -513,46 +487,43 @@ public class PSPrinterJob extends RasterPrinterJob {
OutputStream output = null;
if (epsPrinter == null) {
if (getPrintService() instanceof PSStreamPrintService) {
StreamPrintService sps = (StreamPrintService)getPrintService();
mDestType = RasterPrinterJob.STREAM;
if (sps.isDisposed()) {
throw new PrinterException("service is disposed");
}
output = sps.getOutputStream();
if (output == null) {
throw new PrinterException("Null output stream");
if (getPrintService() instanceof PSStreamPrintService) {
StreamPrintService sps = (StreamPrintService)getPrintService();
mDestType = RasterPrinterJob.STREAM;
if (sps.isDisposed()) {
throw new PrinterException("service is disposed");
}
output = sps.getOutputStream();
if (output == null) {
throw new PrinterException("Null output stream");
}
} else {
/* REMIND: This needs to be more maintainable */
mNoJobSheet = super.noJobSheet;
if (super.destinationAttr != null) {
mDestType = RasterPrinterJob.FILE;
mDestination = super.destinationAttr;
}
if (mDestType == RasterPrinterJob.FILE) {
try {
spoolFile = new File(mDestination);
output = new FileOutputStream(spoolFile);
} catch (IOException ex) {
abortDoc();
throw new PrinterIOException(ex);
}
} else {
/* REMIND: This needs to be more maintainable */
mNoJobSheet = super.noJobSheet;
if (super.destinationAttr != null) {
mDestType = RasterPrinterJob.FILE;
mDestination = super.destinationAttr;
}
if (mDestType == RasterPrinterJob.FILE) {
try {
spoolFile = new File(mDestination);
output = new FileOutputStream(spoolFile);
} catch (IOException ex) {
abortDoc();
throw new PrinterIOException(ex);
}
} else {
PrinterOpener po = new PrinterOpener();
po.run();
if (po.pex != null) {
throw po.pex;
}
output = po.result;
PrinterOpener po = new PrinterOpener();
po.run();
if (po.pex != null) {
throw po.pex;
}
output = po.result;
}
mPSStream = new PrintStream(new BufferedOutputStream(output));
mPSStream.println(ADOBE_PS_STR);
}
mPSStream = new PrintStream(new BufferedOutputStream(output));
mPSStream.println(ADOBE_PS_STR);
mPSStream.println("%%BeginProlog");
mPSStream.println(READIMAGEPROC);
mPSStream.println("/BD {bind def} bind def");
@ -619,45 +590,43 @@ public class PSPrinterJob extends RasterPrinterJob {
mPSStream.println("%%EndProlog");
mPSStream.println("%%BeginSetup");
if (epsPrinter == null) {
// Set Page Size using first page's format.
PageFormat pageFormat = getPageable().getPageFormat(0);
double paperHeight = pageFormat.getPaper().getHeight();
double paperWidth = pageFormat.getPaper().getWidth();
// Set Page Size using first page's format.
PageFormat pageFormat = getPageable().getPageFormat(0);
double paperHeight = pageFormat.getPaper().getHeight();
double paperWidth = pageFormat.getPaper().getWidth();
/* PostScript printers can always generate uncollated copies.
*/
mPSStream.print("<< /PageSize [" +
paperWidth + " "+ paperHeight+"]");
/* PostScript printers can always generate uncollated copies.
*/
mPSStream.print("<< /PageSize [" +
paperWidth + " "+ paperHeight+"]");
final PrintService pservice = getPrintService();
Boolean isPS = Boolean.TRUE;
try {
Class<?> psClass = Class.forName("sun.print.IPPPrintService");
if (psClass.isInstance(pservice)) {
Method isPSMethod = psClass.getMethod("isPostscript",
(Class[])null);
isPS = (Boolean)isPSMethod.invoke(pservice, (Object[])null);
}
} catch (Throwable t) {
final PrintService pservice = getPrintService();
Boolean isPS = Boolean.TRUE;
try {
Class<?> psClass = Class.forName("sun.print.IPPPrintService");
if (psClass.isInstance(pservice)) {
Method isPSMethod = psClass.getMethod("isPostscript",
(Class[])null);
isPS = (Boolean)isPSMethod.invoke(pservice, (Object[])null);
}
if (isPS) {
mPSStream.print(" /DeferredMediaSelection true");
}
mPSStream.print(" /ImagingBBox null /ManualFeed false");
mPSStream.print(isCollated() ? " /Collate true":"");
mPSStream.print(" /NumCopies " +getCopiesInt());
if (sidesAttr != Sides.ONE_SIDED) {
if (sidesAttr == Sides.TWO_SIDED_LONG_EDGE) {
mPSStream.print(" /Duplex true ");
} else if (sidesAttr == Sides.TWO_SIDED_SHORT_EDGE) {
mPSStream.print(" /Duplex true /Tumble true ");
}
}
mPSStream.println(" >> setpagedevice ");
} catch (Throwable t) {
}
if (isPS) {
mPSStream.print(" /DeferredMediaSelection true");
}
mPSStream.print(" /ImagingBBox null /ManualFeed false");
mPSStream.print(isCollated() ? " /Collate true":"");
mPSStream.print(" /NumCopies " +getCopiesInt());
if (sidesAttr != Sides.ONE_SIDED) {
if (sidesAttr == Sides.TWO_SIDED_LONG_EDGE) {
mPSStream.print(" /Duplex true ");
} else if (sidesAttr == Sides.TWO_SIDED_SHORT_EDGE) {
mPSStream.print(" /Duplex true /Tumble true ");
}
}
mPSStream.println(" >> setpagedevice ");
mPSStream.println("%%EndSetup");
}
@ -1175,36 +1144,6 @@ public class PSPrinterJob extends RasterPrinterJob {
return psFont;
}
private static String escapeParens(String str) {
if (str.indexOf('(') == -1 && str.indexOf(')') == -1 ) {
return str;
} else {
int count = 0;
int pos = 0;
while ((pos = str.indexOf('(', pos)) != -1) {
count++;
pos++;
}
pos = 0;
while ((pos = str.indexOf(')', pos)) != -1) {
count++;
pos++;
}
char []inArr = str.toCharArray();
char []outArr = new char[inArr.length+count];
pos = 0;
for (int i=0;i<inArr.length;i++) {
if (inArr[i] == '(' || inArr[i] == ')') {
outArr[pos++] = '\\';
}
outArr[pos++] = inArr[i];
}
return new String(outArr);
}
}
/* return of 0 means unsupported. Other return indicates the number
* of distinct PS fonts needed to draw this text. This saves us
* doing this processing one extra time.
@ -2099,198 +2038,4 @@ public class PSPrinterJob extends RasterPrinterJob {
return retArr;
}
/**
* PluginPrinter generates EPSF wrapped with a header and trailer
* comment. This conforms to the new requirements of Mozilla 1.7
* and FireFox 1.5 and later. Earlier versions of these browsers
* did not support plugin printing in the general sense (not just Java).
* A notable limitation of these browsers is that they handle plugins
* which would span page boundaries by scaling plugin content to fit on a
* single page. This means white space is left at the bottom of the
* previous page and its impossible to print these cases as they appear on
* the web page. This is contrast to how the same browsers behave on
* Windows where it renders as on-screen.
* Cases where the content fits on a single page do work fine, and they
* are the majority of cases.
* The scaling that the browser specifies to make the plugin content fit
* when it is larger than a single page can hold is non-uniform. It
* scales the axis in which the content is too large just enough to
* ensure it fits. For content which is extremely long this could lead
* to noticeable distortion. However that is probably rare enough that
* its not worth compensating for that here, but we can revisit that if
* needed, and compensate by making the scale for the other axis the
* same.
*/
public static class PluginPrinter implements Printable {
private EPSPrinter epsPrinter;
private Component applet;
private PrintStream stream;
private String epsTitle;
private int bx, by, bw, bh;
private int width, height;
/**
* This is called from the Java Plug-in to print an Applet's
* contents as EPS to a postscript stream provided by the browser.
* @param applet the applet component to print.
* @param stream the print stream provided by the plug-in
* @param x the x location of the applet panel in the browser window
* @param y the y location of the applet panel in the browser window
* @param w the width of the applet panel in the browser window
* @param h the width of the applet panel in the browser window
*/
@SuppressWarnings("deprecation")
public PluginPrinter(Component applet,
PrintStream stream,
int x, int y, int w, int h) {
this.applet = applet;
this.epsTitle = "Java Plugin Applet";
this.stream = stream;
bx = x;
by = y;
bw = w;
bh = h;
width = applet.size().width;
height = applet.size().height;
epsPrinter = new EPSPrinter(this, epsTitle, stream,
0, 0, width, height);
}
public void printPluginPSHeader() {
stream.println("%%BeginDocument: JavaPluginApplet");
}
public void printPluginApplet() {
try {
epsPrinter.print();
} catch (PrinterException e) {
}
}
public void printPluginPSTrailer() {
stream.println("%%EndDocument: JavaPluginApplet");
stream.flush();
}
public void printAll() {
printPluginPSHeader();
printPluginApplet();
printPluginPSTrailer();
}
public int print(Graphics g, PageFormat pf, int pgIndex) {
if (pgIndex > 0) {
return Printable.NO_SUCH_PAGE;
} else {
// "aware" client code can detect that its been passed a
// PrinterGraphics and could theoretically print
// differently. I think this is more likely useful than
// a problem.
applet.printAll(g);
return Printable.PAGE_EXISTS;
}
}
}
/*
* This class can take an application-client supplied printable object
* and send the result to a stream.
* The application does not need to send any postscript to this stream
* unless it needs to specify a translation etc.
* It assumes that its importing application obeys all the conventions
* for importation of EPS. See Appendix H - Encapsulated Postscript File
* Format - of the Adobe Postscript Language Reference Manual, 2nd edition.
* This class could be used as the basis for exposing the ability to
* generate EPSF from 2D graphics as a StreamPrintService.
* In that case a MediaPrintableArea attribute could be used to
* communicate the bounding box.
*/
public static class EPSPrinter implements Pageable {
private PageFormat pf;
private PSPrinterJob job;
private int llx, lly, urx, ury;
private Printable printable;
private PrintStream stream;
private String epsTitle;
public EPSPrinter(Printable printable, String title,
PrintStream stream,
int x, int y, int wid, int hgt) {
this.printable = printable;
this.epsTitle = title;
this.stream = stream;
llx = x;
lly = y;
urx = llx+wid;
ury = lly+hgt;
// construct a PageFormat with zero margins representing the
// exact bounds of the applet. ie construct a theoretical
// paper which happens to exactly match applet panel size.
Paper p = new Paper();
p.setSize((double)wid, (double)hgt);
p.setImageableArea(0.0,0.0, (double)wid, (double)hgt);
pf = new PageFormat();
pf.setPaper(p);
}
public void print() throws PrinterException {
stream.println("%!PS-Adobe-3.0 EPSF-3.0");
stream.println("%%BoundingBox: " +
llx + " " + lly + " " + urx + " " + ury);
stream.println("%%Title: " + epsTitle);
stream.println("%%Creator: Java Printing");
stream.println("%%CreationDate: " + new java.util.Date());
stream.println("%%EndComments");
stream.println("/pluginSave save def");
stream.println("mark"); // for restoring stack state on return
job = new PSPrinterJob();
job.epsPrinter = this; // modifies the behaviour of PSPrinterJob
job.mPSStream = stream;
job.mDestType = RasterPrinterJob.STREAM; // prevents closure
job.startDoc();
try {
job.printPage(this, 0);
} catch (Throwable t) {
if (t instanceof PrinterException) {
throw (PrinterException)t;
} else {
throw new PrinterException(t.toString());
}
} finally {
stream.println("cleartomark"); // restore stack state
stream.println("pluginSave restore");
job.endDoc();
}
stream.flush();
}
public int getNumberOfPages() {
return 1;
}
public PageFormat getPageFormat(int pgIndex) {
if (pgIndex > 0) {
throw new IndexOutOfBoundsException("pgIndex");
} else {
return pf;
}
}
public Printable getPrintable(int pgIndex) {
if (pgIndex > 0) {
throw new IndexOutOfBoundsException("pgIndex");
} else {
return printable;
}
}
}
}

View File

@ -1,4 +1,4 @@
## Harfbuzz v10.4.0
## Harfbuzz 11.2.0
### Harfbuzz License

View File

@ -949,25 +949,25 @@ struct CBDT
hb_glyph_extents_t extents;
hb_glyph_extents_t pixel_extents;
hb_blob_t *blob = reference_png (font, glyph);
if (unlikely (blob == hb_blob_get_empty ()))
return false;
if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents)))
if (unlikely (!font->get_glyph_extents (glyph, &extents, false)))
return false;
if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
return false;
hb_blob_t *blob = reference_png (font, glyph);
if (unlikely (hb_blob_is_immutable (blob)))
return false;
bool ret = funcs->image (data,
blob,
pixel_extents.width, -pixel_extents.height,
HB_PAINT_IMAGE_FORMAT_PNG,
font->slant_xy,
0.f,
&extents);
hb_blob_destroy (blob);
return ret;
}

View File

@ -33,6 +33,7 @@
#include "../../../hb-open-type.hh"
#include "../../../hb-ot-var-common.hh"
#include "../../../hb-paint.hh"
#include "../../../hb-paint-bounded.hh"
#include "../../../hb-paint-extents.hh"
#include "../CPAL/CPAL.hh"
@ -47,6 +48,12 @@ namespace OT {
struct hb_paint_context_t;
}
struct hb_colr_scratch_t
{
hb_paint_bounded_context_t paint_bounded;
hb_paint_extents_context_t paint_extents;
};
namespace OT {
struct COLR;
@ -90,12 +97,27 @@ public:
font (font_),
palette (
#ifndef HB_NO_COLOR
font->face->table.CPAL->get_palette_colors (palette_)
// https://github.com/harfbuzz/harfbuzz/issues/5116
font->face->table.CPAL->get_palette_colors (palette_ < font->face->table.CPAL->get_palette_count () ? palette_ : 0)
#endif
),
foreground (foreground_),
instancer (instancer_)
{ }
{
if (font->is_synthetic ())
{
font = hb_font_create_sub_font (font);
hb_font_set_synthetic_bold (font, 0, 0, true);
hb_font_set_synthetic_slant (font, 0);
}
else
hb_font_reference (font);
}
~hb_paint_context_t ()
{
hb_font_destroy (font);
}
hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground)
{
@ -932,9 +954,9 @@ struct PaintGlyph
void paint_glyph (hb_paint_context_t *c) const
{
TRACE_PAINT (this);
c->funcs->push_inverse_root_transform (c->data, c->font);
c->funcs->push_inverse_font_transform (c->data, c->font);
c->funcs->push_clip_glyph (c->data, gid, c->font);
c->funcs->push_root_transform (c->data, c->font);
c->funcs->push_font_transform (c->data, c->font);
c->recurse (this+paint);
c->funcs->pop_transform (c->data);
c->funcs->pop_clip (c->data);
@ -1511,10 +1533,12 @@ struct PaintComposite
void paint_glyph (hb_paint_context_t *c) const
{
TRACE_PAINT (this);
c->funcs->push_group (c->data);
c->recurse (this+backdrop);
c->funcs->push_group (c->data);
c->recurse (this+src);
c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode);
c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
}
HBUINT8 format; /* format = 32 */
@ -1612,7 +1636,7 @@ struct ClipBox
void closurev1 (hb_colrv1_closure_context_t* c) const
{
switch (u.format) {
case 2: u.format2.closurev1 (c);
case 2: u.format2.closurev1 (c); return;
default:return;
}
}
@ -2079,6 +2103,8 @@ struct COLR
{
static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
bool has_data () const { return has_v0_data () || version; }
bool has_v0_data () const { return numBaseGlyphs; }
bool has_v1_data () const
{
@ -2112,7 +2138,53 @@ struct COLR
{
accelerator_t (hb_face_t *face)
{ colr = hb_sanitize_context_t ().reference_table<COLR> (face); }
~accelerator_t () { this->colr.destroy (); }
~accelerator_t ()
{
auto *scratch = cached_scratch.get_relaxed ();
if (scratch)
{
scratch->~hb_colr_scratch_t ();
hb_free (scratch);
}
colr.destroy ();
}
bool has_data () const { return colr->has_data (); }
#ifndef HB_NO_PAINT
bool
get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const
{
if (unlikely (!has_data ())) return false;
hb_colr_scratch_t *scratch = acquire_scratch ();
if (unlikely (!scratch)) return true;
bool ret = colr->get_extents (font, glyph, extents, *scratch);
release_scratch (scratch);
return ret;
}
bool paint_glyph (hb_font_t *font,
hb_codepoint_t glyph,
hb_paint_funcs_t *funcs, void *data,
unsigned int palette_index,
hb_color_t foreground,
bool clip = true) const
{
if (unlikely (!has_data ())) return false;
hb_colr_scratch_t *scratch = acquire_scratch ();
if (unlikely (!scratch)) return true;
bool ret = colr->paint_glyph (font, glyph, funcs, data, palette_index, foreground, clip, *scratch);
release_scratch (scratch);
return ret;
}
#endif
bool is_valid () { return colr.get_blob ()->length; }
@ -2148,7 +2220,33 @@ struct COLR
{ return colr->get_delta_set_index_map_ptr (); }
private:
hb_colr_scratch_t *acquire_scratch () const
{
hb_colr_scratch_t *scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_colr_scratch_t *) hb_calloc (1, sizeof (hb_colr_scratch_t));
if (unlikely (!scratch))
return nullptr;
}
return scratch;
}
void release_scratch (hb_colr_scratch_t *scratch) const
{
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_colr_scratch_t ();
hb_free (scratch);
}
}
public:
hb_blob_ptr_t<COLR> colr;
private:
mutable hb_atomic_t<hb_colr_scratch_t *> cached_scratch;
};
void closure_glyphs (hb_codepoint_t glyph,
@ -2520,7 +2618,10 @@ struct COLR
#ifndef HB_NO_PAINT
bool
get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents,
hb_colr_scratch_t &scratch) const
{
ItemVarStoreInstancer instancer (get_var_store_ptr (),
@ -2534,10 +2635,10 @@ struct COLR
}
auto *extents_funcs = hb_paint_extents_get_funcs ();
hb_paint_extents_context_t extents_data;
bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0));
scratch.paint_extents.clear ();
bool ret = paint_glyph (font, glyph, extents_funcs, &scratch.paint_extents, 0, HB_COLOR(0,0,0,0), true, scratch);
hb_extents_t e = extents_data.get_extents ();
auto e = scratch.paint_extents.get_extents ();
if (e.is_void ())
{
extents->x_bearing = 0;
@ -2583,7 +2684,12 @@ struct COLR
#ifndef HB_NO_PAINT
bool
paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
paint_glyph (hb_font_t *font,
hb_codepoint_t glyph,
hb_paint_funcs_t *funcs, void *data,
unsigned int palette_index, hb_color_t foreground,
bool clip,
hb_colr_scratch_t &scratch) const
{
ItemVarStoreInstancer instancer (get_var_store_ptr (),
get_delta_set_index_map_ptr (),
@ -2617,26 +2723,26 @@ struct COLR
}
else
{
auto *extents_funcs = hb_paint_extents_get_funcs ();
hb_paint_extents_context_t extents_data;
clip = false;
is_bounded = false;
}
if (!is_bounded)
{
auto *bounded_funcs = hb_paint_bounded_get_funcs ();
scratch.paint_bounded.clear ();
paint_glyph (font, glyph,
extents_funcs, &extents_data,
bounded_funcs, &scratch.paint_bounded,
palette_index, foreground,
false);
false,
scratch);
hb_extents_t extents = extents_data.get_extents ();
is_bounded = extents_data.is_bounded ();
c.funcs->push_clip_rectangle (c.data,
extents.xmin,
extents.ymin,
extents.xmax,
extents.ymax);
is_bounded = scratch.paint_bounded.is_bounded ();
}
}
c.funcs->push_root_transform (c.data, font);
c.funcs->push_font_transform (c.data, font);
if (is_bounded)
c.recurse (*paint);
@ -2714,9 +2820,7 @@ void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
return;
const Paint &paint = paint_offset_lists.get_paint (i);
c->funcs->push_group (c->data);
c->recurse (paint);
c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
}
}
@ -2728,7 +2832,7 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
if (unlikely (!node.visit (gid)))
return;
c->funcs->push_inverse_root_transform (c->data, c->font);
c->funcs->push_inverse_font_transform (c->data, c->font);
if (c->funcs->color_glyph (c->data, gid, c->font))
{
c->funcs->pop_transform (c->data);

View File

@ -237,27 +237,28 @@ struct sbix
int x_offset = 0, y_offset = 0;
unsigned int strike_ppem = 0;
hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
hb_glyph_extents_t extents;
hb_glyph_extents_t pixel_extents;
if (blob == hb_blob_get_empty ())
return false;
if (!hb_font_get_glyph_extents (font, glyph, &extents))
if (!font->get_glyph_extents (glyph, &extents, false))
return false;
if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
return false;
hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
if (hb_blob_is_immutable (blob))
return false;
bool ret = funcs->image (data,
blob,
pixel_extents.width, -pixel_extents.height,
HB_PAINT_IMAGE_FORMAT_PNG,
font->slant_xy,
0.f,
&extents);
hb_blob_destroy (blob);
return ret;
}

View File

@ -104,15 +104,16 @@ struct SVG
if (blob == hb_blob_get_empty ())
return false;
funcs->image (data,
blob,
0, 0,
HB_PAINT_IMAGE_FORMAT_SVG,
font->slant_xy,
nullptr);
bool ret = funcs->image (data,
blob,
0, 0,
HB_PAINT_IMAGE_FORMAT_SVG,
0.f,
nullptr);
hb_blob_destroy (blob);
return true;
return ret;
}
private:

View File

@ -77,7 +77,7 @@ struct CoverageFormat1_3
bool intersects (const hb_set_t *glyphs) const
{
if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2)
if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len))
{
for (auto g : *glyphs)
if (get_coverage (g) != NOT_COVERED)

View File

@ -120,7 +120,7 @@ struct CoverageFormat2_4
bool intersects (const hb_set_t *glyphs) const
{
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len))
{
for (auto g : *glyphs)
if (get_coverage (g) != NOT_COVERED)

View File

@ -205,20 +205,19 @@ struct CaretValueFormat3
unsigned varidx = (this+deviceTable).get_variation_index ();
hb_pair_t<unsigned, int> *new_varidx_delta;
if (!c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta))
return_trace (false);
if (c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta)) {
uint32_t new_varidx = hb_first (*new_varidx_delta);
int delta = hb_second (*new_varidx_delta);
if (delta != 0)
{
if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
}
uint32_t new_varidx = hb_first (*new_varidx_delta);
int delta = hb_second (*new_varidx_delta);
if (delta != 0)
{
if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
if (!c->serializer->embed (deviceTable))
return_trace (false);
@ -1015,7 +1014,8 @@ struct GDEF
hb_blob_ptr_t<GDEF> table;
#ifndef HB_NO_GDEF_CACHE
hb_vector_t<hb_set_digest_t> mark_glyph_set_digests;
mutable hb_cache_t<21, 3, 8> glyph_props_cache;
mutable hb_cache_t<21, 3> glyph_props_cache;
static_assert (sizeof (glyph_props_cache) == 512, "");
#endif
};

View File

@ -152,8 +152,11 @@ GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
for (unsigned i = 0; i < len; i++)
propagate_attachment_offsets (pos, len, i, direction);
if (unlikely (font->slant))
if (unlikely (font->slant_xy) &&
HB_DIRECTION_IS_HORIZONTAL (direction))
{
/* Slanting shaping results is only supported for horizontal text,
* as it gets weird otherwise. */
for (unsigned i = 0; i < len; i++)
if (unlikely (pos[i].y_offset))
pos[i].x_offset += roundf (font->slant_xy * pos[i].y_offset);

View File

@ -54,7 +54,7 @@ struct PairPosFormat1_3
{
auto &cov = this+coverage;
if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4)
if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len))
{
for (hb_codepoint_t g : glyphs->iter())
{

View File

@ -21,6 +21,24 @@ namespace OT {
#ifndef HB_NO_VAR_COMPOSITES
struct hb_varc_scratch_t
{
hb_vector_t<unsigned> axisIndices;
hb_vector_t<float> axisValues;
hb_glyf_scratch_t glyf_scratch;
};
struct hb_varc_context_t
{
hb_font_t *font;
hb_draw_session_t *draw_session;
hb_extents_t *extents;
mutable hb_decycler_t decycler;
mutable signed edges_left;
mutable signed depth_left;
hb_varc_scratch_t &scratch;
};
struct VarComponent
{
enum class flags_t : uint32_t
@ -44,41 +62,32 @@ struct VarComponent
};
HB_INTERNAL hb_ubytes_t
get_path_at (hb_font_t *font,
get_path_at (const hb_varc_context_t &c,
hb_codepoint_t parent_gid,
hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_ubytes_t record,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
hb_glyf_scratch_t &scratch,
VarRegionList::cache_t *cache = nullptr) const;
};
struct VarCompositeGlyph
{
static void
get_path_at (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_session_t &draw_session,
get_path_at (const hb_varc_context_t &c,
hb_codepoint_t gid,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_ubytes_t record,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
hb_glyf_scratch_t &scratch,
VarRegionList::cache_t *cache = nullptr)
VarRegionList::cache_t *cache)
{
while (record)
{
const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
record = comp.get_path_at (font, glyph,
draw_session, coords, transform,
record = comp.get_path_at (c,
gid,
coords, transform,
record,
decycler, edges_left, depth_left, scratch, cache);
cache);
}
}
};
@ -92,36 +101,47 @@ struct VARC
static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
HB_INTERNAL bool
get_path_at (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_session_t &draw_session,
get_path_at (const hb_varc_context_t &c,
hb_codepoint_t gid,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_codepoint_t parent_glyph,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left,
hb_glyf_scratch_t &scratch) const;
hb_transform_t transform = HB_TRANSFORM_IDENTITY,
hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID,
VarRegionList::cache_t *parent_cache = nullptr) const;
bool
get_path (hb_font_t *font,
hb_codepoint_t gid,
hb_draw_session_t &draw_session,
hb_glyf_scratch_t &scratch) const
hb_varc_scratch_t &scratch) const
{
hb_decycler_t decycler;
signed edges = HB_MAX_GRAPH_EDGE_COUNT;
hb_varc_context_t c {font,
&draw_session,
nullptr,
hb_decycler_t {},
HB_MAX_GRAPH_EDGE_COUNT,
HB_MAX_NESTING_LEVEL,
scratch};
return get_path_at (font,
gid,
draw_session,
hb_array (font->coords, font->num_coords),
HB_TRANSFORM_IDENTITY,
HB_CODEPOINT_INVALID,
&decycler,
&edges,
HB_MAX_NESTING_LEVEL,
scratch);
return get_path_at (c, gid,
hb_array (font->coords, font->num_coords));
}
bool
get_extents (hb_font_t *font,
hb_codepoint_t gid,
hb_extents_t *extents,
hb_varc_scratch_t &scratch) const
{
hb_varc_context_t c {font,
nullptr,
extents,
hb_decycler_t {},
HB_MAX_GRAPH_EDGE_COUNT,
HB_MAX_NESTING_LEVEL,
scratch};
return get_path_at (c, gid,
hb_array (font->coords, font->num_coords));
}
bool sanitize (hb_sanitize_context_t *c) const
@ -150,7 +170,7 @@ struct VARC
auto *scratch = cached_scratch.get_relaxed ();
if (scratch)
{
scratch->~hb_glyf_scratch_t ();
scratch->~hb_varc_scratch_t ();
hb_free (scratch);
}
@ -162,34 +182,60 @@ struct VARC
{
if (!table->has_data ()) return false;
hb_glyf_scratch_t *scratch;
// Borrow the cached strach buffer.
{
scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
if (unlikely (!scratch))
return true;
}
}
auto *scratch = acquire_scratch ();
if (unlikely (!scratch)) return true;
bool ret = table->get_path (font, gid, draw_session, *scratch);
release_scratch (scratch);
return ret;
}
// Put it back.
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_glyf_scratch_t ();
hb_free (scratch);
}
bool
get_extents (hb_font_t *font,
hb_codepoint_t gid,
hb_glyph_extents_t *extents) const
{
if (!table->has_data ()) return false;
hb_extents_t f_extents;
auto *scratch = acquire_scratch ();
if (unlikely (!scratch)) return true;
bool ret = table->get_extents (font, gid, &f_extents, *scratch);
release_scratch (scratch);
if (ret)
*extents = f_extents.to_glyph_extents (font->x_scale < 0, font->y_scale < 0);
return ret;
}
private:
hb_varc_scratch_t *acquire_scratch () const
{
hb_varc_scratch_t *scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_varc_scratch_t *) hb_calloc (1, sizeof (hb_varc_scratch_t));
if (unlikely (!scratch))
return nullptr;
}
return scratch;
}
void release_scratch (hb_varc_scratch_t *scratch) const
{
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_varc_scratch_t ();
hb_free (scratch);
}
}
private:
hb_blob_ptr_t<VARC> table;
hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
mutable hb_atomic_t<hb_varc_scratch_t *> cached_scratch;
};
bool has_data () const { return version.major != 0; }

View File

@ -429,16 +429,27 @@ struct glyf_accelerator_t
}
public:
bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
bool get_extents (hb_font_t *font,
hb_codepoint_t gid,
hb_glyph_extents_t *extents) const
{ return get_extents_at (font, gid, extents, hb_array (font->coords, font->num_coords)); }
bool get_extents_at (hb_font_t *font,
hb_codepoint_t gid,
hb_glyph_extents_t *extents,
hb_array_t<const int> coords) const
{
if (unlikely (gid >= num_glyphs)) return false;
#ifndef HB_NO_VAR
if (font->num_coords)
if (coords)
{
hb_glyf_scratch_t scratch;
return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true),
hb_array (font->coords, font->num_coords),
return get_points (font,
gid,
points_aggregator_t (font, extents, nullptr, true),
coords,
scratch);
}
#endif
@ -532,7 +543,7 @@ struct glyf_accelerator_t
unsigned int num_glyphs;
hb_blob_ptr_t<loca> loca_table;
hb_blob_ptr_t<glyf> glyf_table;
hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
mutable hb_atomic_t<hb_glyf_scratch_t *> cached_scratch;
};

View File

@ -29,6 +29,8 @@
#include "hb-aat-layout.hh"
#include "hb-aat-map.hh"
#include "hb-ot-layout-common.hh"
#include "hb-ot-layout-gdef-table.hh"
#include "hb-open-type.hh"
#include "hb-cache.hh"
#include "hb-bit-set.hh"
@ -48,6 +50,61 @@ struct ankr;
using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>;
static_assert (sizeof (hb_aat_class_cache_t) == 256, "");
struct hb_aat_scratch_t
{
hb_aat_scratch_t () = default;
hb_aat_scratch_t (const hb_aat_scratch_t &) = delete;
hb_aat_scratch_t (hb_aat_scratch_t &&o)
{
buffer_glyph_set.set_relaxed (o.buffer_glyph_set.get_relaxed ());
o.buffer_glyph_set.set_relaxed (nullptr);
}
hb_aat_scratch_t & operator = (hb_aat_scratch_t &&o)
{
buffer_glyph_set.set_relaxed (o.buffer_glyph_set.get_relaxed ());
o.buffer_glyph_set.set_relaxed (nullptr);
return *this;
}
~hb_aat_scratch_t ()
{
auto *s = buffer_glyph_set.get_relaxed ();
if (unlikely (!s))
return;
s->fini ();
hb_free (s);
}
hb_bit_set_t *create_buffer_glyph_set () const
{
hb_bit_set_t *s = buffer_glyph_set.get_acquire ();
if (s && buffer_glyph_set.cmpexch (s, nullptr))
return s;
s = (hb_bit_set_t *) hb_calloc (1, sizeof (hb_bit_set_t));
if (unlikely (!s))
return nullptr;
s->init ();
return s;
}
void destroy_buffer_glyph_set (hb_bit_set_t *s) const
{
if (unlikely (!s))
return;
if (buffer_glyph_set.cmpexch (nullptr, s))
return;
s->fini ();
hb_free (s);
}
mutable hb_atomic_t<hb_bit_set_t *> buffer_glyph_set;
};
enum { DELETED_GLYPH = 0xFFFF };
#define HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED HB_BUFFER_SCRATCH_FLAG_SHAPER0
struct hb_aat_apply_context_t :
hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
{
@ -64,10 +121,11 @@ struct hb_aat_apply_context_t :
hb_buffer_t *buffer;
hb_sanitize_context_t sanitizer;
const ankr *ankr_table;
const OT::GDEF *gdef_table;
const OT::GDEF &gdef;
bool has_glyph_classes;
const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
bool using_buffer_glyph_set = false;
hb_bit_set_t buffer_glyph_set;
hb_bit_set_t *buffer_glyph_set = nullptr;
const hb_bit_set_t *left_set = nullptr;
const hb_bit_set_t *right_set = nullptr;
const hb_bit_set_t *machine_glyph_set = nullptr;
@ -90,15 +148,15 @@ struct hb_aat_apply_context_t :
void setup_buffer_glyph_set ()
{
using_buffer_glyph_set = buffer->len >= 4;
using_buffer_glyph_set = buffer->len >= 4 && buffer_glyph_set;
if (using_buffer_glyph_set)
buffer->collect_codepoints (buffer_glyph_set);
if (likely (using_buffer_glyph_set))
buffer->collect_codepoints (*buffer_glyph_set);
}
bool buffer_intersects_machine () const
{
if (using_buffer_glyph_set)
return buffer_glyph_set.intersects (*machine_glyph_set);
if (likely (using_buffer_glyph_set))
return buffer_glyph_set->intersects (*machine_glyph_set);
// Faster for shorter buffers.
for (unsigned i = 0; i < buffer->len; i++)
@ -106,6 +164,69 @@ struct hb_aat_apply_context_t :
return true;
return false;
}
template <typename T>
HB_NODISCARD bool output_glyphs (unsigned int count,
const T *glyphs)
{
if (likely (using_buffer_glyph_set))
buffer_glyph_set->add_array (glyphs, count);
for (unsigned int i = 0; i < count; i++)
{
if (glyphs[i] == DELETED_GLYPH)
{
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED;
_hb_glyph_info_set_aat_deleted (&buffer->cur());
}
else
{
#ifndef HB_NO_OT_LAYOUT
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->cur(),
gdef.get_glyph_props (glyphs[i]));
#endif
}
if (unlikely (!buffer->output_glyph (glyphs[i]))) return false;
}
return true;
}
HB_NODISCARD bool replace_glyph (hb_codepoint_t glyph)
{
if (glyph == DELETED_GLYPH)
{
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED;
_hb_glyph_info_set_aat_deleted (&buffer->cur());
}
if (likely (using_buffer_glyph_set))
buffer_glyph_set->add (glyph);
#ifndef HB_NO_OT_LAYOUT
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->cur(),
gdef.get_glyph_props (glyph));
#endif
return buffer->replace_glyph (glyph);
}
HB_NODISCARD bool delete_glyph ()
{
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED;
_hb_glyph_info_set_aat_deleted (&buffer->cur());
return buffer->replace_glyph (DELETED_GLYPH);
}
void replace_glyph_inplace (unsigned i, hb_codepoint_t glyph)
{
buffer->info[i].codepoint = glyph;
if (likely (using_buffer_glyph_set))
buffer_glyph_set->add (glyph);
#ifndef HB_NO_OT_LAYOUT
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->info[i],
gdef.get_glyph_props (glyph));
#endif
}
};
@ -113,8 +234,6 @@ struct hb_aat_apply_context_t :
* Lookup Table
*/
enum { DELETED_GLYPH = 0xFFFF };
template <typename T> struct Lookup;
template <typename T>
@ -179,6 +298,7 @@ struct LookupSegmentSingle
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (first == DELETED_GLYPH) return;
if (!filter (value)) return;
glyphs.add_range (first, last);
}
@ -268,6 +388,7 @@ struct LookupSegmentArray
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const void *base, const filter_t &filter) const
{
if (first == DELETED_GLYPH) return;
const auto &values = base+valuesZ;
for (hb_codepoint_t i = first; i <= last; i++)
if (filter (values[i - first]))
@ -368,6 +489,7 @@ struct LookupSingle
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (glyph == DELETED_GLYPH) return;
if (!filter (value)) return;
glyphs.add (glyph);
}
@ -746,6 +868,10 @@ struct StateTable
}
// And glyphs in those classes.
if (filter (CLASS_DELETED_GLYPH))
glyphs.add (DELETED_GLYPH);
(this+classTable).collect_glyphs_filtered (glyphs, num_glyphs, filter);
}

View File

@ -185,6 +185,9 @@ struct Format1Entry<true>
DEFINE_SIZE_STATIC (2);
};
static bool initiateAction (const Entry<EntryData> &entry)
{ return entry.flags & Push; }
static bool performAction (const Entry<EntryData> &entry)
{ return entry.data.kernActionIndex != 0xFFFF; }
@ -325,8 +328,9 @@ struct KerxSubTableFormat1
}
else if (buffer->info[idx].mask & kern_mask)
{
o.x_advance += c->font->em_scale_x (v);
o.x_offset += c->font->em_scale_x (v);
auto scaled = c->font->em_scale_x (v);
o.x_advance += scaled;
o.x_offset += scaled;
}
}
else
@ -394,10 +398,8 @@ struct KerxSubTableFormat1
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
set_t set;
machine.collect_glyphs (set, num_glyphs);
left_set.union_ (set);
right_set.union_ (set);
machine.collect_initial_glyphs (left_set, num_glyphs, *this);
//machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning
}
protected:
@ -671,10 +673,8 @@ struct KerxSubTableFormat4
template <typename set_t>
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
{
set_t set;
machine.collect_glyphs (set, num_glyphs);
left_set.union_ (set);
right_set.union_ (set);
machine.collect_initial_glyphs (left_set, num_glyphs, *this);
//machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning
}
protected:
@ -921,7 +921,18 @@ struct KerxSubTable
* The 'kerx' Table
*/
using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_bit_set_t, hb_bit_set_t>>;
struct kern_subtable_accelerator_data_t
{
hb_bit_set_t left_set;
hb_bit_set_t right_set;
mutable hb_aat_class_cache_t class_cache;
};
struct kern_accelerator_data_t
{
hb_vector_t<kern_subtable_accelerator_data_t> subtable_accels;
hb_aat_scratch_t scratch;
};
template <typename T>
struct KerxTable
@ -985,6 +996,8 @@ struct KerxTable
{
c->buffer->unsafe_to_concat ();
c->setup_buffer_glyph_set ();
typedef typename T::SubTable SubTable;
bool ret = false;
@ -996,12 +1009,25 @@ struct KerxTable
{
bool reverse;
auto &subtable_accel = accel_data.subtable_accels[i];
if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
goto skip;
if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
goto skip;
c->left_set = &subtable_accel.left_set;
c->right_set = &subtable_accel.right_set;
c->machine_glyph_set = &subtable_accel.left_set;
c->machine_class_cache = &subtable_accel.class_cache;
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped subtable %u because no glyph matches", c->lookup_index);
goto skip;
}
reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
@ -1028,9 +1054,6 @@ struct KerxTable
if (reverse)
c->buffer->reverse ();
c->left_set = &accel_data[i].first;
c->right_set = &accel_data[i].second;
{
/* See comment in sanitize() for conditional here. */
hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr);
@ -1106,9 +1129,13 @@ struct KerxTable
unsigned int count = thiz()->tableCount;
for (unsigned int i = 0; i < count; i++)
{
hb_bit_set_t left_set, right_set;
st->collect_glyphs (left_set, right_set, num_glyphs);
accel_data.push (hb_pair (left_set, right_set));
auto &subtable_accel = *accel_data.subtable_accels.push ();
if (unlikely (accel_data.subtable_accels.in_error ()))
return accel_data;
st->collect_glyphs (subtable_accel.left_set, subtable_accel.right_set, num_glyphs);
subtable_accel.class_cache.clear ();
st = &StructAfter<SubTable> (*st);
}
@ -1137,6 +1164,7 @@ struct KerxTable
hb_blob_ptr_t<T> table;
kern_accelerator_data_t accel_data;
hb_aat_scratch_t scratch;
};
};

View File

@ -30,8 +30,6 @@
#include "hb-open-type.hh"
#include "hb-aat-layout-common.hh"
#include "hb-ot-layout.hh"
#include "hb-ot-layout-common.hh"
#include "hb-ot-layout-gdef-table.hh"
#include "hb-aat-map.hh"
/*
@ -178,12 +176,6 @@ struct RearrangementSubtable
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
return_trace (dc.ret);
@ -242,9 +234,7 @@ struct ContextualSubtable
ret (false),
c (c_),
table (table_),
gdef (*c->gdef_table),
mark_set (false),
has_glyph_classes (gdef.has_glyph_classes ()),
mark (0),
subs (table+table->substitutionTables) {}
@ -281,12 +271,7 @@ struct ContextualSubtable
if (replacement)
{
buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
hb_codepoint_t glyph = *replacement;
buffer->info[mark].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->info[mark],
gdef.get_glyph_props (*replacement));
c->replace_glyph_inplace (mark, *replacement);
ret = true;
}
@ -312,12 +297,7 @@ struct ContextualSubtable
}
if (replacement)
{
hb_codepoint_t glyph = *replacement;
buffer->info[idx].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->info[idx],
gdef.get_glyph_props (*replacement));
c->replace_glyph_inplace (idx, *replacement);
ret = true;
}
@ -333,9 +313,7 @@ struct ContextualSubtable
hb_aat_apply_context_t *c;
const ContextualSubtable *table;
private:
const OT::GDEF &gdef;
bool mark_set;
bool has_glyph_classes;
unsigned int mark;
const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs;
};
@ -348,12 +326,6 @@ struct ContextualSubtable
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
return_trace (dc.ret);
@ -581,7 +553,7 @@ struct LigatureSubtable
hb_codepoint_t lig = ligatureData;
DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig);
if (unlikely (!buffer->replace_glyph (lig))) return;
if (unlikely (!c->replace_glyph (lig))) return;
unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u;
/* Now go and delete all subsequent components. */
@ -589,8 +561,7 @@ struct LigatureSubtable
{
DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
_hb_glyph_info_set_default_ignorable (&buffer->cur());
if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
if (!c->delete_glyph ()) return;
}
if (unlikely (!buffer->move_to (lig_end))) return;
@ -624,12 +595,6 @@ struct LigatureSubtable
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
return_trace (dc.ret);
@ -665,15 +630,6 @@ struct NoncontextualSubtable
{
TRACE_APPLY (this);
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
const OT::GDEF &gdef (*c->gdef_table);
bool has_glyph_classes = gdef.has_glyph_classes ();
bool ret = false;
unsigned int num_glyphs = c->face->get_num_glyphs ();
@ -703,12 +659,7 @@ struct NoncontextualSubtable
const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
if (replacement)
{
hb_codepoint_t glyph = *replacement;
info[i].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&info[i],
gdef.get_glyph_props (*replacement));
c->replace_glyph_inplace (i, *replacement);
ret = true;
}
}
@ -850,9 +801,7 @@ struct InsertionSubtable
if (buffer->idx < buffer->len && !before)
if (unlikely (!buffer->copy_glyph ())) return;
/* TODO We ignore KashidaLike setting. */
if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
for (unsigned int i = 0; i < count; i++)
c->buffer_glyph_set.add (glyphs[i]);
if (unlikely (!c->output_glyphs (count, glyphs))) return;
ret = true;
if (buffer->idx < buffer->len && !before)
buffer->skip_glyph ();
@ -881,7 +830,8 @@ struct InsertionSubtable
if (buffer->idx < buffer->len && !before)
if (unlikely (!buffer->copy_glyph ())) return;
/* TODO We ignore KashidaLike setting. */
if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
if (unlikely (!c->output_glyphs (count, glyphs))) return;
ret = true;
if (buffer->idx < buffer->len && !before)
buffer->skip_glyph ();
@ -921,12 +871,6 @@ struct InsertionSubtable
StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
driver.drive (&dc, c);
return_trace (dc.ret);
@ -1224,6 +1168,7 @@ struct Chain
if (hb_none (hb_iter (c->range_flags) |
hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); })))
goto skip;
c->subtable_flags = subtable_flags;
c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t);
c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr;
@ -1233,6 +1178,12 @@ struct Chain
bool (coverage & ChainSubtable<Types>::Vertical))
goto skip;
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable %u because no glyph matches", c->lookup_index);
goto skip;
}
/* Buffer contents is always in logical direction. Determine if
* we need to reverse before applying this subtable. We reverse
* back after if we did reverse indeed.
@ -1376,7 +1327,7 @@ struct mortmorx
this->chain_count = table->get_chain_count ();
this->accels = (hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *) hb_calloc (this->chain_count, sizeof (*accels));
this->accels = (hb_atomic_t<hb_aat_layout_chain_accelerator_t *> *) hb_calloc (this->chain_count, sizeof (*accels));
if (unlikely (!this->accels))
{
this->chain_count = 0;
@ -1423,7 +1374,8 @@ struct mortmorx
hb_blob_ptr_t<T> table;
unsigned int chain_count;
hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *accels;
hb_atomic_t<hb_aat_layout_chain_accelerator_t *> *accels;
hb_aat_scratch_t scratch;
};

View File

@ -31,6 +31,7 @@
#include "hb-aat-layout-common.hh"
#include "hb-ot-layout.hh"
#include "hb-open-type.hh"
#include "hb-ot-stat-table.hh"
/*
* trak -- Tracking
@ -115,7 +116,7 @@ struct TrackTableEntry
protected:
F16DOT16 track; /* Track value for this record. */
NameID trackNameID; /* The 'name' table index for this track.
OT::NameID trackNameID; /* The 'name' table index for this track.
* (a short word or phrase like "loose"
* or "very tight") */
NNOffset16To<UnsizedArrayOf<FWORD>>
@ -142,9 +143,9 @@ struct TrackData
unsigned j = count - 1;
// Find the two entries that track is between.
while (i + 1 < count && trackTable[i + 1].get_track_value () < track)
while (i + 1 < count && trackTable[i + 1].get_track_value () <= track)
i++;
while (j > 0 && trackTable[j - 1].get_track_value () > track)
while (j > 0 && trackTable[j - 1].get_track_value () >= track)
j--;
// Exact match.
@ -200,6 +201,46 @@ struct trak
float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track));
}
hb_position_t get_tracking (hb_font_t *font, hb_direction_t dir, float track = 0.f) const
{
#ifndef HB_NO_STYLE
if (!font->face->table.STAT->has_data ())
return 0;
return HB_DIRECTION_IS_HORIZONTAL (dir) ?
get_h_tracking (font, track) :
get_v_tracking (font, track);
#else
return 0;
#endif
}
bool apply (hb_aat_apply_context_t *c, float track = 0.f) const
{
TRACE_APPLY (this);
float ptem = c->font->ptem;
if (unlikely (ptem <= 0.f))
{
/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
ptem = HB_CORETEXT_DEFAULT_FONT_SIZE;
}
hb_buffer_t *buffer = c->buffer;
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{
hb_position_t advance_to_add = get_h_tracking (c->font, track);
foreach_grapheme (buffer, start, end)
buffer->pos[start].x_advance += advance_to_add;
}
else
{
hb_position_t advance_to_add = get_v_tracking (c->font, track);
foreach_grapheme (buffer, start, end)
buffer->pos[start].y_advance += advance_to_add;
}
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{

View File

@ -34,7 +34,7 @@
#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-layout-kerx-table.hh"
#include "hb-aat-layout-morx-table.hh"
#include "hb-aat-layout-trak-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-layout-trak-table.hh"
#include "hb-aat-ltag-table.hh"
#include "hb-ot-layout-gsub-table.hh"
@ -58,13 +58,14 @@ AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *p
buffer (buffer_),
sanitizer (),
ankr_table (&Null (AAT::ankr)),
gdef_table (
gdef (
#ifndef HB_NO_OT_LAYOUT
face->table.GDEF->table
*face->table.GDEF->table
#else
&Null (GDEF)
Null (GDEF)
#endif
),
has_glyph_classes (gdef.has_glyph_classes ()),
lookup_index (0)
{
sanitizer.init (blob);
@ -203,7 +204,7 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag)
#endif
#ifndef HB_NO_AAT
#ifndef HB_NO_AAT_SHAPE
/*
* mort/morx/kerx/trak
@ -287,11 +288,14 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
const hb_feature_t *features,
unsigned num_features)
{
hb_aat_map_builder_t builder (font->face, plan->props);
for (unsigned i = 0; i < num_features; i++)
builder.add_feature (features[i]);
hb_aat_map_t map;
builder.compile (map);
if (num_features)
{
hb_aat_map_builder_t builder (font->face, plan->props);
for (unsigned i = 0; i < num_features; i++)
builder.add_feature (features[i]);
builder.compile (map);
}
{
auto &accel = *font->face->table.morx;
@ -300,7 +304,10 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
{
AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
if (!buffer->message (font, "start table morx")) return;
morx.apply (&c, map, accel);
c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set ();
morx.apply (&c, num_features ? map : plan->aat_map, accel);
accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set);
c.buffer_glyph_set = nullptr;
(void) buffer->message (font, "end table morx");
return;
}
@ -313,34 +320,24 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
{
AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
if (!buffer->message (font, "start table mort")) return;
mort.apply (&c, map, accel);
mort.apply (&c, num_features ? map : plan->aat_map, accel);
(void) buffer->message (font, "end table mort");
return;
}
}
}
void
hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer)
{
unsigned int count = buffer->len;
hb_glyph_info_t *info = buffer->info;
hb_glyph_position_t *pos = buffer->pos;
for (unsigned int i = 0; i < count; i++)
if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH))
pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
}
static bool
is_deleted_glyph (const hb_glyph_info_t *info)
{
return info->codepoint == AAT::DELETED_GLYPH;
return _hb_glyph_info_is_aat_deleted (info);
}
void
hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
{
buffer->delete_glyphs_inplace (is_deleted_glyph);
if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED)
buffer->delete_glyphs_inplace (is_deleted_glyph);
}
/**
@ -371,8 +368,11 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
AAT::hb_aat_apply_context_t c (plan, font, buffer, accel.get_blob ());
if (!buffer->message (font, "start table kerx")) return;
c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set ();
c.set_ankr_table (font->face->table.ankr.get ());
accel.apply (&c);
accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set);
c.buffer_glyph_set = nullptr;
(void) buffer->message (font, "end table kerx");
}
@ -394,6 +394,17 @@ hb_aat_layout_has_tracking (hb_face_t *face)
return face->table.trak->has_data ();
}
void
hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer)
{
const AAT::trak& trak = *font->face->table.trak;
AAT::hb_aat_apply_context_t c (plan, font, buffer);
trak.apply (&c);
}
/**
* hb_aat_layout_get_feature_types:
* @face: #hb_face_t to work upon

View File

@ -60,9 +60,6 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
const hb_feature_t *features,
unsigned num_features);
HB_INTERNAL void
hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer);
HB_INTERNAL void
hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer);
@ -71,5 +68,10 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
HB_INTERNAL void
hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
#endif /* HB_AAT_LAYOUT_HH */

View File

@ -85,6 +85,11 @@ void
hb_aat_map_builder_t::compile (hb_aat_map_t &m)
{
/* Compute active features per range, and compile each. */
if (!features.length)
{
hb_aat_layout_compile_map (this, &m);
return;
}
/* Sort features by start/end events. */
hb_vector_t<feature_event_t> feature_events;

View File

@ -80,15 +80,14 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
#include <atomic>
#define _hb_memory_barrier() std::atomic_thread_fence(std::memory_order_ack_rel)
#define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire)
#define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release)
#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_relaxed))
#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_release))
#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_relaxed))
#define hb_atomic_int_impl_get(AI) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_acquire))
#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast<std::atomic<typename std::decay<decltype (*(AI))>::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast<std::atomic<typename std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_relaxed))
#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast<std::atomic<typename std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_release))
#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast<std::atomic<typename std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_relaxed))
#define hb_atomic_int_impl_get(AI) (reinterpret_cast<std::atomic<typename std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_acquire))
#define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed))
#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast<std::atomic<void*> const *> (P)->load (std::memory_order_relaxed))
@ -149,68 +148,53 @@ static inline void _hb_compiler_memory_r_barrier () {}
#define hb_atomic_ptr_impl_get_relaxed(P) (*(P))
#endif
#ifndef hb_atomic_int_impl_set
inline void hb_atomic_int_impl_set (int *AI, int v) { _hb_memory_w_barrier (); *AI = v; }
inline void hb_atomic_int_impl_set (short *AI, short v) { _hb_memory_w_barrier (); *AI = v; }
template <typename T>
inline void hb_atomic_int_impl_set (T *AI, T v) { _hb_memory_w_barrier (); *AI = v; }
#endif
#ifndef hb_atomic_int_impl_get
inline int hb_atomic_int_impl_get (const int *AI) { int v = *AI; _hb_memory_r_barrier (); return v; }
inline short hb_atomic_int_impl_get (const short *AI) { short v = *AI; _hb_memory_r_barrier (); return v; }
template <typename T>
inline T hb_atomic_int_impl_get (const T *AI) { T v = *AI; _hb_memory_r_barrier (); return v; }
#endif
#ifndef hb_atomic_ptr_impl_get
inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; }
#endif
struct hb_atomic_short_t
template <typename T>
struct hb_atomic_t
{
hb_atomic_short_t () = default;
constexpr hb_atomic_short_t (short v) : v (v) {}
hb_atomic_t () = default;
constexpr hb_atomic_t (T v) : v (v) {}
hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; }
operator short () const { return get_relaxed (); }
hb_atomic_t& operator = (T v_) { set_relaxed (v_); return *this; }
operator T () const { return get_relaxed (); }
void set_relaxed (short v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
void set_release (short v_) { hb_atomic_int_impl_set (&v, v_); }
short get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
short get_acquire () const { return hb_atomic_int_impl_get (&v); }
short inc () { return hb_atomic_int_impl_add (&v, 1); }
short dec () { return hb_atomic_int_impl_add (&v, -1); }
void set_relaxed (T v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
void set_release (T v_) { hb_atomic_int_impl_set (&v, v_); }
T get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
T get_acquire () const { return hb_atomic_int_impl_get (&v); }
T inc () { return hb_atomic_int_impl_add (&v, 1); }
T dec () { return hb_atomic_int_impl_add (&v, -1); }
short v = 0;
int operator ++ (int) { return inc (); }
int operator -- (int) { return dec (); }
long operator |= (long v_) { set_relaxed (get_relaxed () | v_); return *this; }
T v = 0;
};
struct hb_atomic_int_t
template <typename T>
struct hb_atomic_t<T*>
{
hb_atomic_int_t () = default;
constexpr hb_atomic_int_t (int v) : v (v) {}
hb_atomic_int_t& operator = (int v_) { set_relaxed (v_); return *this; }
operator int () const { return get_relaxed (); }
void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
void set_release (int v_) { hb_atomic_int_impl_set (&v, v_); }
int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
int get_acquire () const { return hb_atomic_int_impl_get (&v); }
int inc () { return hb_atomic_int_impl_add (&v, 1); }
int dec () { return hb_atomic_int_impl_add (&v, -1); }
int v = 0;
};
template <typename P>
struct hb_atomic_ptr_t
{
typedef hb_remove_pointer<P> T;
hb_atomic_ptr_t () = default;
constexpr hb_atomic_ptr_t (T* v) : v (v) {}
hb_atomic_ptr_t (const hb_atomic_ptr_t &other) = delete;
hb_atomic_t () = default;
constexpr hb_atomic_t (T* v) : v (v) {}
hb_atomic_t (const hb_atomic_t &other) = delete;
void init (T* v_ = nullptr) { set_relaxed (v_); }
void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); }
T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
bool cmpexch (const T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
operator bool () const { return get_acquire () != nullptr; }
T * operator -> () const { return get_acquire (); }

View File

@ -77,7 +77,7 @@ struct hb_bit_set_t
bool successful = true; /* Allocations successful */
mutable unsigned int population = 0;
mutable hb_atomic_int_t last_page_lookup = 0;
mutable hb_atomic_t<unsigned> last_page_lookup = 0;
hb_sorted_vector_t<page_map_t> page_map;
hb_vector_t<page_t> pages;
@ -88,7 +88,7 @@ struct hb_bit_set_t
{
if (unlikely (!successful)) return false;
if (pages.length < count && count <= 2)
if (pages.length < count && (unsigned) pages.allocated < count && count <= 2)
exact_size = true; // Most sets are small and local
if (unlikely (!pages.resize (count, clear, exact_size) ||

View File

@ -0,0 +1,195 @@
/*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_BIT_VECTOR_HH
#define HB_BIT_VECTOR_HH
#include "hb.hh"
#include "hb-atomic.hh"
struct hb_min_max_t
{
void add (hb_codepoint_t v) { min_v = hb_min (min_v, v); max_v = hb_max (max_v, v); }
void add_range (hb_codepoint_t a, hb_codepoint_t b)
{
min_v = hb_min (min_v, a);
max_v = hb_max (max_v, b);
}
template <typename set_t>
void union_ (const set_t &set)
{
hb_codepoint_t set_min = set.get_min ();
if (unlikely (set_min == HB_CODEPOINT_INVALID))
return;
hb_codepoint_t set_max = set.get_max ();
min_v = hb_min (min_v, set_min);
max_v = hb_max (max_v, set_max);
}
hb_codepoint_t get_min () const { return min_v; }
hb_codepoint_t get_max () const { return max_v; }
private:
hb_codepoint_t min_v = HB_CODEPOINT_INVALID;
hb_codepoint_t max_v = 0;
};
template <bool atomic = false>
struct hb_bit_vector_t
{
using int_t = uint64_t;
using elt_t = typename std::conditional<atomic, hb_atomic_t<int_t>, int_t>::type;
hb_bit_vector_t () = delete;
hb_bit_vector_t (const hb_bit_vector_t &other) = delete;
hb_bit_vector_t &operator= (const hb_bit_vector_t &other) = delete;
// Move
hb_bit_vector_t (hb_bit_vector_t &&other)
: min_v (other.min_v), max_v (other.max_v), count (other.count), elts (other.elts)
{
other.min_v = other.max_v = other.count = 0;
other.elts = nullptr;
}
hb_bit_vector_t &operator= (hb_bit_vector_t &&other)
{
hb_swap (min_v, other.min_v);
hb_swap (max_v, other.max_v);
hb_swap (count, other.count);
hb_swap (elts, other.elts);
return *this;
}
hb_bit_vector_t (unsigned min_v, unsigned max_v)
: min_v (min_v), max_v (max_v)
{
if (unlikely (min_v >= max_v))
{
min_v = max_v = count = 0;
return;
}
unsigned num = (max_v - min_v + sizeof (int_t) * 8) / (sizeof (int_t) * 8);
elts = (elt_t *) hb_calloc (num, sizeof (int_t));
if (unlikely (!elts))
{
min_v = max_v = count = 0;
return;
}
count = max_v - min_v + 1;
}
~hb_bit_vector_t ()
{
hb_free (elts);
}
void add (hb_codepoint_t g) { elt (g) |= mask (g); }
void del (hb_codepoint_t g) { elt (g) &= ~mask (g); }
void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
bool has (hb_codepoint_t g) const { return get (g); }
bool may_have (hb_codepoint_t g) const { return get (g); }
bool operator [] (hb_codepoint_t g) const { return get (g); }
bool operator () (hb_codepoint_t g) const { return get (g); }
void add_range (hb_codepoint_t a, hb_codepoint_t b)
{
if (unlikely (!count || a > b || a < min_v || b > max_v))
return;
elt_t *la = &elt (a);
elt_t *lb = &elt (b);
if (la == lb)
*la |= (mask (b) << 1) - mask(a);
else
{
*la |= ~(mask (a) - 1llu);
la++;
hb_memset (la, 0xff, (char *) lb - (char *) la);
*lb |= ((mask (b) << 1) - 1llu);
}
}
void del_range (hb_codepoint_t a, hb_codepoint_t b)
{
if (unlikely (!count || a > b || a < min_v || b > max_v))
return;
elt_t *la = &elt (a);
elt_t *lb = &elt (b);
if (la == lb)
*la &= ~((mask (b) << 1llu) - mask(a));
else
{
*la &= mask (a) - 1;
la++;
hb_memset (la, 0, (char *) lb - (char *) la);
*lb &= ~((mask (b) << 1) - 1llu);
}
}
void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v)
{ if (v) add_range (a, b); else del_range (a, b); }
template <typename set_t>
void union_ (const set_t &set)
{
for (hb_codepoint_t g : set)
add (g);
}
static const unsigned int ELT_BITS = sizeof (elt_t) * 8;
static constexpr unsigned ELT_MASK = ELT_BITS - 1;
static constexpr elt_t zero = 0;
elt_t &elt (hb_codepoint_t g)
{
g -= min_v;
if (unlikely (g >= count))
return Crap(elt_t);
return elts[g / ELT_BITS];
}
const elt_t& elt (hb_codepoint_t g) const
{
g -= min_v;
if (unlikely (g >= count))
return Null(elt_t);
return elts[g / ELT_BITS];
}
static constexpr int_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); }
hb_codepoint_t min_v = 0, max_v = 0, count = 0;
elt_t *elts = nullptr;
};
#endif /* HB_BIT_VECTOR_HH */

Some files were not shown because too many files have changed in this diff Show More