mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-03 22:55:40 +00:00
Merge branch 'master' into JDK-8348611
This commit is contained in:
commit
102beb5ece
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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), )
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
])
|
||||
|
||||
################################################################################
|
||||
|
||||
@ -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@
|
||||
|
||||
@ -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],
|
||||
[
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) %{
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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!).
|
||||
|
||||
@ -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??
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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); }
|
||||
|
||||
@ -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 "
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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(); }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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();)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
|
||||
@ -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\
|
||||
|
||||
@ -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)";
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
## Harfbuzz v10.4.0
|
||||
## Harfbuzz 11.2.0
|
||||
|
||||
### Harfbuzz License
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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())
|
||||
{
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 (); }
|
||||
|
||||
@ -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) ||
|
||||
|
||||
195
src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh
Normal file
195
src/java.desktop/share/native/libharfbuzz/hb-bit-vector.hh
Normal 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 */
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user