Compare commits

..

34 Commits

Author SHA1 Message Date
Andrew Leonard
138596467d JBR-4062 8284437: Building from different users/workspace is not always deterministic
Reviewed-by: erikj
(cherry picked from commit 4451257b14)
2022-04-08 10:41:39 +03:00
Dmitry Batrak
f7a0716ccd JBR-4346 [Xfce] Windows are moved unexpectedly between workspaces when modal dialog is shown
(cherry picked from commit ad299f1e74)
2022-04-06 22:41:56 +03:00
Zhengyu Gu
bdb9d3f12f JBR-4360 284023: java.sun.awt.X11GraphicsDevice.getDoubleBufferVisuals() leaks XdbeScreenVisualInfo
Reviewed-by: prr, serb
2022-04-06 16:07:42 +03:00
Maxim Kartashev
1e8d2c0873 JBR-4357 8284033: Leak XVisualInfo in getAllConfigs in awt_GraphicsEnv.c
Reviewed-by: serb, prr
2022-04-04 23:03:35 -07:00
Vladimir Dvorak
360f8f332c Remove version-numbers left over from the merge of dcevm17 2022-04-05 04:38:32 +07:00
Vladimir Dvorak
13512decff JBR-4352 - fix AARCH64 compilation issues
- use correct INCLUDE_JFR condition for jfr code
- exclude jvmtiEnhancedRedefineClasses.cpp if INCLUDE_JVMTI=0
2022-04-02 16:33:54 +01:00
Vitaly Provodin
28ebc0c2e0 excludejava/awt/Mouse/GetMousePositionTest/GetMousePositionWithPopup.java on windows
and java/io/File/createTempFile/SpecialTempFile.java on windows11
2022-04-02 06:29:47 +07:00
Anton Tarasov
9a35ca0b25 JBR-4349 jb/java/awt/Toolkit/LWCToolkitInvokeAndWaitTest.java: Test FAILED! (not found in the log: “lost”) 2022-04-01 21:34:03 +03:00
Anton Tarasov
e424a5bf19 JBR-4347 jb/java/awt/Toolkit/AWTThreadingCMenuTest.java fails by ClassNotFoundException 2022-04-01 21:32:14 +03:00
Alexey Ushakov
ca4d838caf 8273355: Flickering on tooltip appearance IntelliJ IDEA 2021.2.1
Reviewed-by: serb, aghaisas
2022-03-30 20:42:06 +02:00
Alexey Ushakov
6ed5106466 Revert "JBR-3727 JBR17-Metal: Flickering on tooltip appearance"
This reverts commit eaa9c1618e.
2022-03-30 20:27:45 +02:00
Dmitry Batrak
098a50d91a JBR-4334 java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersUnitTest_Extra.java fails
re-fix JBR-4306 in a different way

(cherry picked from commit 3b5ded0d02)
2022-03-30 12:16:24 +03:00
Dmitry Batrak
050990303b JBR-4334 java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersUnitTest_Extra.java fails
revert original fix for JBR-4306

(cherry picked from commit 0f33031484)
2022-03-30 12:16:24 +03:00
Anton Tarasov
9f9416a12e JBR-4328 remove LWCToolkit.unsafeNonblockingExecute
(cherry picked from commit 9a3f31a6c4)
2022-03-28 22:00:59 +03:00
Anton Tarasov
b6cb489fee JBR-4134 PyCharm is slow and unusable on MacBook Pro with M1
(cherry picked from commit 36190505f5)
2022-03-28 22:00:52 +03:00
Anton Tarasov
446535b706 JBR-4284 Sub items of main menu options are not displayed
A regression of JBR-4208 LWCToolkit.invokeAndWait should not stuck on invocation loss

(cherry picked from commit c464e4748e)
2022-03-28 21:59:41 +03:00
Anton Tarasov
c18934bb98 JBR-4119 UI freezes at sun.lwawt.macosx.CAccessibility.getChildrenAndRoles
(cherry picked from commit 6ba79774d8)
2022-03-28 21:59:36 +03:00
Anton Tarasov
e8a6e8bd6e JBR-4106 PyCharm hangs with 100% CPU usage on one core 2022-03-28 21:59:28 +03:00
Mikhail Grishchenko
4a7241931f JBR-3975 print Teamcity messages 2022-03-26 05:32:40 +07:00
Mikhail Grishchenko
b45b8c9913 JBR-3975 add processing of "." java version separator 2022-03-24 08:55:51 +07:00
Artem Semenov
f6cf71ce68 JBR-4325 Implement creating an event to call NSAccessibilityShowMenuAction 2022-03-23 21:29:36 +03:00
Dmitry Batrak
803039d6db JBR-4306 Robot doesn't work as expected in some cases on macOS
(cherry picked from commit 7d69734465)
2022-03-23 12:53:42 +03:00
Vladimir Dvorak
436223316f JBR-4312 - fix crash call ResolvedMethodTable from ServiceThread
adjust_metod_entries_dcevm incorrectly changed the hashes of resolved
method oops stored in ResolvedMethodTable. Now all oops of old methods
are first removed, then updated and then added to table again
2022-03-23 11:08:36 +07:00
Dmitry Batrak
9a0ab296fe better handle multi-monitor configurations in tests related to macOS spaces
Detect the presence of an additional space by looking at number of spaces for the first monitor, not at the total number of spaces.
The latter is larger than one in a multi-monitor configuration, as each monitor corresponds to a separate space.
This approach should be better than the original one, but it relies on the primary (default) monitor to be mentioned first
in the plist file, which isn't known to be always true.

(cherry picked from commit 9040fd56cd)
2022-03-21 21:30:12 +03:00
Artem Semenov
bf4a191246 JBR-4318 On MacOS, some context menu items are not voiced 2022-03-21 15:40:18 +03:00
Andrew Leonard
cdfff8f6ac JBR-4062 8283315: jrt-fs.jar not always deterministically built
Reviewed-by: ihse
2022-03-21 14:03:00 +03:00
Alexey Ushakov
6d6de1034a JBR-3827 SIGILL at [libsystem_kernel] __kill in Java Exception at -[CDragSource convertData:]
Added check for drag source
2022-03-18 18:36:10 +01:00
Vitaly Provodin
2333999906 JBR-4303 add a regression test
(cherry picked from commit bc7cb5711b)
2022-03-18 09:50:29 +07:00
Alexey Ushakov
6ffcd2bf91 JBR-3366 SIGILL at [libsystem_kernel] __kill in NSWindowStyleMaskFullScreen cleared on a window outside of a full screen transition
Wrapped the native exception and added logging
2022-03-17 18:09:49 +01:00
Artem Semenov
822b4a8c49 JBR-4235 Context menu not readable after opening on Mac OS 2022-03-17 16:34:39 +03:00
Dmitry Batrak
debc6336c0 JBR-4314 2022.1 EAP version's JBR use incorrect fallback font to render Chinese characters in macOS
(cherry picked from commit 82048d1d73)
2022-03-17 15:15:34 +03:00
Mikhail Grishchenko
1f93154f62 JBR-3975 add PASSED message in check_jbr_size.sh 2022-03-17 03:35:05 +07:00
Mikhail Grishchenko
9f069ca59e JBR-3975 Fix regexp in check_jbr_size.sh
(cherry picked from commit 23131fc435)
2022-03-17 01:45:51 +07:00
Vladimir Kempik
53f1a647fd JBR-4309: jdk/tools/launcher/modules/illegalaccess/IllegalAccessTest.java fails after JBR-4291 2022-03-14 11:27:16 +03:00
37 changed files with 1297 additions and 469 deletions

View File

@@ -1,5 +1,16 @@
#!/bin/bash
while getopts ":t" o; do
case "${o}" in
t)
t="With Teamcity tests info"
TC_PRINT=1
;;
esac
done
shift $((OPTIND-1))
NEWFILEPATH=$1
CONFIGID=$2
BUILDID=$3
@@ -7,6 +18,7 @@ TOKEN=$4
#
# Get the size of new artifact
#
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*)
@@ -16,7 +28,11 @@ case "${unameOut}" in
NEWFILESIZE=$(stat -f%z "$NEWFILEPATH")
;;
CYGWIN*)
NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
NEWFILESIZE=$(stat -c%s$4
#
# Get the size of new artifact
#
"$NEWFILEPATH")
;;
MINGW*)
NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
@@ -25,32 +41,88 @@ case "${unameOut}" in
echo "Unknown machine: ${unameOut}"
exit 1
esac
FILENAME=$(basename ${NEWFILEPATH})
echo "New size of $NEWFILEPATH = $NEWFILESIZE bytes."
#
# Get pattern of artifact name
# Base filename pattern: <BUNDLE_TYPE>-<JDK_VERSION>-<OS>-<ARCH>-b<BUILD>.tar.gz: jbr_dcevm-17.0.2-osx-x64-b1234.tar.gz
# BUNDLE_TYPE: jbr, jbrsdk, jbr_dcevm, jbrsdk_jcef etc.
# OS_ARCH_PATTERN - <os_architecture>: osx-x64, linux-aarch64, windows-x64 etc.
# example: IntellijCustomJdk_Jdk17_Master_LinuxX64jcef
BUNDLE_TYPE=jbrsdk
OS_ARCH_PATTERN=""
FILE_EXTENSION=tar.gz
re='(jbr[a-z_]*).*-[0-9_\.]+-(.+)-b[0-9]+(.+)'
if [[ $FILENAME =~ $re ]]; then
BUNDLE_TYPE=${BASH_REMATCH[1]}
OS_ARCH_PATTERN=${BASH_REMATCH[2]}
FILE_EXTENSION=${BASH_REMATCH[3]}
fi
if [ $TC_PRINT -eq 1 ]; then
testname_file_ext=`echo $FILE_EXTENSION | sed 's/\./_/g'`
testname=$BUNDLE_TYPE"_"$OS_ARCH_PATTERN$testname_file_ext
echo \#\#teamcity[testStarted name=\'$testname\']
fi
echo "BUNDLE_TYPE: " $BUNDLE_TYPE
echo "OS_ARCH_PATTERN: " $OS_ARCH_PATTERN
echo "FILE_EXTENSION: " $FILE_EXTENSION
echo "New size of $FILENAME = $NEWFILESIZE bytes."
function test_failed_msg() {
if [ $3 -eq 1 ]; then
echo \#\#teamcity[testFailed name=\'$1\' message=\'$2\']
fi
}
function test_finished_msg() {
if [ $2 -eq 1 ]; then
echo \#\#teamcity[testFinished name=\'$1\']
fi
}
#
# Get previous successful build ID
# Example:
# CONFIGID=IntellijCustomJdk_Jdk17_Master_LinuxX64jcef
# BUILDID=12345678
#
# expected return value
# id="123".number="567"
#
CURL_RESPONSE=$(curl --header "Authorization: Bearer $TOKEN" "https://buildserver.labs.intellij.net/app/rest/builds/?locator=buildType:(id:$CONFIGID),status:success,count:1,finishDate:(build:$BUILDID,condition:before)")
re='id=\"([0-9]+)\".+number=\"([0-9\.]+)\"'
re='id=\"([0-9]+)\".+number=\"([0-9]+)\"'
# ID: Previous successful build id
ID=0
if [[ $CURL_RESPONSE =~ $re ]]; then
ID=${BASH_REMATCH[1]}
echo "BUILD Number: ${BASH_REMATCH[2]}"
else
echo "ERROR: can't find previous build"
msg="ERROR: can't find previous build"
echo $msg
echo $CURL_RESPONSE
test_failed_msg $testname $msg $TC_PRINT
test_finished_msg $testname $TC_PRINT
exit 1
fi
#
# Get artifacts from previous successful build
#
# expected return value
# name="jbrsdk_jcef*.tar.gz size="123'
#
CURL_RESPONSE=$(curl --header "Authorization: Bearer $TOKEN" "https://buildserver.labs.intellij.net/app/rest/builds/$ID?fields=id,number,artifacts(file(name,size))")
echo "Atrifacts of last pinned build of $CONFIGID :\n"
echo "Atrifacts of previous build of $CONFIGID :"
echo $CURL_RESPONSE
# Find size (in response) with reg exp
re='name=\"(jbrsdk_jcef[^\"]+\.tar\.gz)\" size=\"([0-9]+)\"'
# Find binary size (in response) with reg exp
re='name=\"('$BUNDLE_TYPE'[^\"]+'${OS_ARCH_PATTERN}'[^\"]+'${FILE_EXTENSION}')\" size=\"([0-9]+)\"'
if [[ $CURL_RESPONSE =~ $re ]]; then
OLDFILENAME=${BASH_REMATCH[1]}
@@ -61,12 +133,20 @@ if [[ $CURL_RESPONSE =~ $re ]]; then
let allowedSize=OLDFILESIZE+OLDFILESIZE/20 # use 5% threshold
echo "Allowed size = $allowedSize"
if [[ "$NEWFILESIZE" -gt "$allowedSize" ]]; then
echo "ERROR: new size is significally greater than prev size (need to investigate)"
msg="ERROR: new size is significally greater than prev size (need to investigate)"
echo $msg
test_failed_msg $testname $msg $TC_PRINT
test_finished_msg $testname $TC_PRINT
exit 1
else
echo "PASSED"
test_finished_msg $testname $TC_PRINT
fi
else
echo "ERROR: can't find string with size in xml response:"
msg="ERROR: can't find string with size in xml response:"
echo $msg
echo $CURL_RESPONSE
test_failed_msg $testname $msg $TC_PRINT
test_finished_msg $testname $TC_PRINT
exit 1
fi

View File

@@ -95,8 +95,24 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS],
# info flags for toolchains unless we know they work.
# See JDK-8207057.
ASFLAGS_DEBUG_SYMBOLS=""
# Debug prefix mapping if supported by compiler
DEBUG_PREFIX_CFLAGS=
# Debug symbols
if test "x$TOOLCHAIN_TYPE" = xgcc; then
if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then
# Check if compiler supports -fdebug-prefix-map. If so, use that to make
# the debug symbol paths resolve to paths relative to the workspace root.
workspace_root_trailing_slash="${WORKSPACE_ROOT%/}/"
DEBUG_PREFIX_CFLAGS="-fdebug-prefix-map=${workspace_root_trailing_slash}="
FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_PREFIX_CFLAGS}],
IF_FALSE: [
DEBUG_PREFIX_CFLAGS=
]
)
fi
CFLAGS_DEBUG_SYMBOLS="-g"
ASFLAGS_DEBUG_SYMBOLS="-g"
elif test "x$TOOLCHAIN_TYPE" = xclang; then
@@ -108,6 +124,11 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS],
CFLAGS_DEBUG_SYMBOLS="-Z7"
fi
if test "x$DEBUG_PREFIX_CFLAGS" != x; then
CFLAGS_DEBUG_SYMBOLS="$CFLAGS_DEBUG_SYMBOLS $DEBUG_PREFIX_CFLAGS"
ASFLAGS_DEBUG_SYMBOLS="$ASFLAGS_DEBUG_SYMBOLS $DEBUG_PREFIX_CFLAGS"
fi
AC_SUBST(CFLAGS_DEBUG_SYMBOLS)
AC_SUBST(ASFLAGS_DEBUG_SYMBOLS)
])

View File

@@ -1,55 +0,0 @@
#
# Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# Default version, product, and vendor information to use,
# unless overridden by configure
DEFAULT_VERSION_FEATURE=15
DEFAULT_VERSION_INTERIM=0
DEFAULT_VERSION_UPDATE=0
DEFAULT_VERSION_PATCH=0
DEFAULT_VERSION_EXTRA1=0
DEFAULT_VERSION_EXTRA2=0
DEFAULT_VERSION_EXTRA3=0
DEFAULT_VERSION_DATE=2020-09-15
DEFAULT_VERSION_CLASSFILE_MAJOR=59 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
DEFAULT_VERSION_CLASSFILE_MINOR=0
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="14 15"
DEFAULT_JDK_SOURCE_TARGET_VERSION=15
DEFAULT_PROMOTED_VERSION_PRE=
LAUNCHER_NAME=openjdk
PRODUCT_NAME=OpenJDK
PRODUCT_SUFFIX="Runtime Environment"
JDK_RC_PLATFORM_NAME=Platform
COMPANY_NAME=N/A
HOTSPOT_VM_DISTRO="Dynamic Code Evolution"
VENDOR_URL=https://openjdk.java.net/
VENDOR_URL_BUG=https://bugreport.java.com/bugreport/
VENDOR_URL_VM_BUG=https://bugreport.java.com/bugreport/crash.jsp
# Might need better names for these
MACOSX_BUNDLE_NAME_BASE="OpenJDK"
MACOSX_BUNDLE_ID_BASE="net.java.openjdk"

View File

@@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -193,7 +193,8 @@ define SetupJarArchiveBody
$1_UPDATE_CONTENTS=\
if [ "`$(WC) -l $$($1_BIN)/_the.$$($1_JARNAME)_contents | $(AWK) '{ print $$$$1 }'`" -gt "0" ]; then \
$(ECHO) " updating" `$(WC) -l $$($1_BIN)/_the.$$($1_JARNAME)_contents | $(AWK) '{ print $$$$1 }'` files && \
$$($1_JAR_CMD) --update $$($1_JAR_OPTIONS) --file $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents; \
$(SORT) $$($1_BIN)/_the.$$($1_JARNAME)_contents > $$($1_BIN)/_the.$$($1_JARNAME)_contents_sorted && \
$$($1_JAR_CMD) --update $$($1_JAR_OPTIONS) --file $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents_sorted; \
fi $$(NEWLINE)
# The s-variants of the above macros are used when the jar is created from scratch.
# NOTICE: please leave the parentheses space separated otherwise the AIX build will break!
@@ -212,7 +213,9 @@ define SetupJarArchiveBody
| $(SED) 's|$$(src)/|-C $$(src) |g' >> \
$$($1_BIN)/_the.$$($1_JARNAME)_contents) $$(NEWLINE) )
endif
$1_SUPDATE_CONTENTS=$$($1_JAR_CMD) --update $$($1_JAR_OPTIONS) --file $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents $$(NEWLINE)
$1_SUPDATE_CONTENTS=\
$(SORT) $$($1_BIN)/_the.$$($1_JARNAME)_contents > $$($1_BIN)/_the.$$($1_JARNAME)_contents_sorted && \
$$($1_JAR_CMD) --update $$($1_JAR_OPTIONS) --file $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents_sorted $$(NEWLINE)
# Use a slightly shorter name for logging, but with enough path to identify this jar.
$1_NAME:=$$(subst $$(OUTPUTDIR)/,,$$($1_JAR))

View File

@@ -1,5 +1,5 @@
#
# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -389,6 +389,12 @@ define SetupCompileNativeFileBody
$1_OBJ_DEPS := $$($1_SRC_FILE) $$($$($1_BASE)_COMPILE_VARDEPS_FILE) \
$$($$($1_BASE)_EXTRA_DEPS) $$($1_VARDEPS_FILE)
$1_COMPILE_OPTIONS := $$($1_FLAGS) $(CC_OUT_OPTION)$$($1_OBJ) $$($1_SRC_FILE)
# For reproducible builds with gcc ensure random symbol generation is seeded deterministically
ifeq ($(TOOLCHAIN_TYPE), gcc)
ifeq ($$(ENABLE_REPRODUCIBLE_BUILD), true)
$1_COMPILE_OPTIONS += -frandom-seed="$$($1_FILENAME)"
endif
endif
$$($1_OBJ_JSON): $$($1_OBJ_DEPS)
$$(call WriteCompileCommandsFragment, $$@, $$(PWD), $$($1_SRC_FILE), \
@@ -1143,6 +1149,19 @@ define SetupNativeCompilationBody
endif
endif
# Debuginfo of ASM objects always embeds the absolute object path,
# as ASM debuginfo paths do not get prefix mapped.
# So for reproducible builds use relative paths to ensure a reproducible
# debuginfo and libs, when creating debug symbols.
ifeq ($$(ENABLE_REPRODUCIBLE_BUILD), true)
ifeq ($(call isTargetOs, linux), true)
ifeq ($$($1_COMPILE_WITH_DEBUG_SYMBOLS), true)
$1_LINK_OBJS_RELATIVE := true
$1_ALL_OBJS_RELATIVE := $$(patsubst $$(OUTPUTDIR)/%, %, $$($1_ALL_OBJS))
endif
endif
endif
$1_TARGET_DEPS := $$($1_ALL_OBJS) $$($1_RES) $$($1_MANIFEST) \
$$($1_REAL_MAPFILE) $$($1_VARDEPS_FILE)

View File

@@ -58,6 +58,9 @@ ifeq ($(call check-jvm-feature, compiler2), true)
ADLC_CFLAGS += -I$(TOPDIR)/src/hotspot/share
# Add file macro mappings
ADLC_CFLAGS += $(FILE_MACRO_CFLAGS)
$(eval $(call SetupNativeCompilation, BUILD_ADLC, \
NAME := adlc, \
TYPE := EXECUTABLE, \

View File

@@ -84,7 +84,7 @@ ifneq ($(call check-jvm-feature, jvmti), true)
jvmtiImpl.cpp jvmtiManageCapabilities.cpp jvmtiRawMonitor.cpp jvmtiUtil.cpp jvmtiTrace.cpp \
jvmtiCodeBlobEvents.cpp jvmtiEnv.cpp jvmtiRedefineClasses.cpp jvmtiEnvBase.cpp jvmtiEnvThreadState.cpp \
jvmtiTagMap.cpp jvmtiEventController.cpp evmCompat.cpp jvmtiEnter.xsl jvmtiExport.cpp \
jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp
jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp jvmtiEnhancedRedefineClasses.cpp
endif
ifneq ($(call check-jvm-feature, jvmci), true)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -204,6 +204,10 @@ public class MakeZipReproducible {
entry.setTimeLocal(timestamp);
}
// Ensure "extra" field is not set from original ZipEntry info that may be not deterministic
// eg.may contain specific UID/GID
entry.setExtra(null);
zos.putNextEntry(entry);
if (entry.getSize() > 0 && entryInputStream != null) {
entryInputStream.transferTo(zos);

View File

@@ -372,10 +372,12 @@ public:
};
class AdjustMethodEntriesDcevm : public StackObj {
bool* _trace_name_printed;
GrowableArray<oop>* _oops_to_add;
GrowableArray<oop>* _oops_to_update;
GrowableArray<Method*>* _old_methods;
public:
AdjustMethodEntriesDcevm(GrowableArray<oop>* oops_to_add, bool* trace_name_printed) : _trace_name_printed(trace_name_printed), _oops_to_add(oops_to_add) {};
AdjustMethodEntriesDcevm(GrowableArray<oop>* oops_to_update, GrowableArray<Method*>* old_methods) :
_oops_to_update(oops_to_update), _old_methods(old_methods) {};
bool operator()(WeakHandle* entry) {
oop mem_name = entry->peek();
if (mem_name == NULL) {
@@ -386,46 +388,8 @@ public:
Method* old_method = (Method*)java_lang_invoke_ResolvedMethodName::vmtarget(mem_name);
if (old_method->is_old()) {
InstanceKlass* newer_klass = InstanceKlass::cast(old_method->method_holder()->new_version());
Method* newer_method;
// Method* new_method;
if (old_method->is_deleted()) {
newer_method = Universe::throw_no_such_method_error();
} else {
newer_method = newer_klass->method_with_idnum(old_method->orig_method_idnum());
log_debug(redefine, class, update)("Adjusting method: '%s' of new class %s", newer_method->name_and_sig_as_C_string(), newer_klass->name()->as_C_string());
assert(newer_klass == newer_method->method_holder(), "call after swapping redefined guts");
assert(newer_method != NULL, "method_with_idnum() should not be NULL");
assert(old_method != newer_method, "sanity check");
Thread* thread = Thread::current();
ResolvedMethodTableLookup lookup(thread, method_hash(newer_method), newer_method);
ResolvedMethodGet rmg(thread, newer_method);
if (_local_table->get(thread, lookup, rmg)) {
// old method was already adjusted if new method exists in _the_table
return true;
}
}
java_lang_invoke_ResolvedMethodName::set_vmtarget(mem_name, newer_method);
java_lang_invoke_ResolvedMethodName::set_vmholder(mem_name, newer_method->method_holder()->java_mirror());
newer_klass->set_has_resolved_methods();
_oops_to_add->append(mem_name);
ResourceMark rm;
if (!(*_trace_name_printed)) {
log_debug(redefine, class, update)("adjust: name=%s", old_method->method_holder()->external_name());
*_trace_name_printed = true;
}
log_debug(redefine, class, update, constantpool)
("ResolvedMethod method update: %s(%s)",
newer_method->name()->as_C_string(), newer_method->signature()->as_C_string());
_oops_to_update->append(mem_name);
_old_methods->append(old_method);
}
return true;
@@ -440,36 +404,70 @@ void ResolvedMethodTable::adjust_method_entries(bool * trace_name_printed) {
_local_table->do_safepoint_scan(adjust);
}
// It is called at safepoint only for RedefineClasses
// It is called at safepoint only for EnhancedRedefineClasses
void ResolvedMethodTable::adjust_method_entries_dcevm(bool * trace_name_printed) {
assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint");
// For each entry in RMT, change to new method
GrowableArray<oop> oops_to_add(0);
AdjustMethodEntriesDcevm adjust(&oops_to_add, trace_name_printed);
GrowableArray<oop> oops_to_update(0);
GrowableArray<Method*> old_methods(0);
AdjustMethodEntriesDcevm adjust(&oops_to_update, &old_methods);
_local_table->do_safepoint_scan(adjust);
Thread* thread = Thread::current();
for (int i = 0; i < oops_to_add.length(); i++) {
oop mem_name = oops_to_add.at(i);
Method* method = (Method*)java_lang_invoke_ResolvedMethodName::vmtarget(mem_name);
// The hash table takes ownership of the WeakHandle, even if it's not inserted.
for (int i = 0; i < oops_to_update.length(); i++) {
oop mem_name = oops_to_update.at(i);
Method *old_method = old_methods.at(i);
ResolvedMethodTableLookup lookup(thread, method_hash(method), method);
ResolvedMethodGet rmg(thread, method);
// 1. Remove old method, since we are going to update class that could be used for hash evaluation in parallel running ServiceThread
ResolvedMethodTableLookup lookup(thread, method_hash(old_method), old_method);
_local_table->remove(thread, lookup);
InstanceKlass* newer_klass = InstanceKlass::cast(old_method->method_holder()->new_version());
Method* newer_method;
// Method* new_method;
if (old_method->is_deleted()) {
newer_method = Universe::throw_no_such_method_error();
} else {
newer_method = newer_klass->method_with_idnum(old_method->orig_method_idnum());
assert(newer_method != NULL, "method_with_idnum() should not be NULL");
assert(newer_klass == newer_method->method_holder(), "call after swapping redefined guts");
assert(old_method != newer_method, "sanity check");
Thread* thread = Thread::current();
ResolvedMethodTableLookup lookup(thread, method_hash(newer_method), newer_method);
ResolvedMethodGet rmg(thread, newer_method);
while (true) {
if (_local_table->get(thread, lookup, rmg)) {
break;
}
WeakHandle wh(_oop_storage, mem_name);
// The hash table takes ownership of the WeakHandle, even if it's not inserted.
if (_local_table->insert(thread, lookup, wh)) {
log_insert(method);
wh.resolve();
break;
// old method was already adjusted if new method exists in _the_table
continue;
}
}
log_debug(redefine, class, update)("Adjusting method: '%s' of new class %s", newer_method->name_and_sig_as_C_string(), newer_klass->name()->as_C_string());
// 2. Update method
java_lang_invoke_ResolvedMethodName::set_vmtarget(mem_name, newer_method);
java_lang_invoke_ResolvedMethodName::set_vmholder(mem_name, newer_method->method_holder()->java_mirror());
newer_klass->set_has_resolved_methods();
ResourceMark rm;
if (!(*trace_name_printed)) {
log_debug(redefine, class, update)("adjust: name=%s", old_method->method_holder()->external_name());
*trace_name_printed = true;
}
log_debug(redefine, class, update, constantpool)
("ResolvedMethod method update: %s(%s)",
newer_method->name()->as_C_string(), newer_method->signature()->as_C_string());
// 3. add updated method to table again
add_method(newer_method, Handle(thread, mem_name));
}
}
#endif // INCLUDE_JVMTI

View File

@@ -2501,6 +2501,10 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
if (res != JNI_OK) {
return res;
}
} else if (match_option(option, "--illegal-access=", &tail)) {
char version[256];
JDK_Version::jdk(17).to_string(version, sizeof(version));
warning("Ignoring option %s; support was removed in %s", option->optionString, version);
} else if (match_option(option, "--jbr-illegal-access", &tail)) {
warning("Option --jbr-illegal-access is deprecated and will be removed in a future release.");
if (!create_module_property("jdk.module.illegalAccess", "permit", ExternalProperty)) {
@@ -4030,7 +4034,7 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
// Set object alignment values.
set_object_alignment();
#ifndef ZERO
#if INCLUDE_JFR
if (FlightRecorder) {
if (AllowEnhancedClassRedefinition) {
warning("EnhancedClassRedefinition was disabled, it is not allowed in FlightRecorder.");

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -59,6 +59,7 @@ import java.awt.peer.KeyboardFocusManagerPeer;
import java.awt.peer.WindowPeer;
import java.util.List;
import java.util.Objects;
import javax.swing.JComponent;
import sun.awt.AWTAccessor;
@@ -283,6 +284,16 @@ public class LWWindowPeer
super.disposeImpl();
}
@Override
public void setBackground(final Color c) {
Color oldBg = getBackground();
if (Objects.equals(oldBg, c)) {
return;
}
super.setBackground(c);
updateOpaque();
}
@Override
protected void setVisibleImpl(final boolean visible) {
if (!visible && warningWindow != null) {
@@ -291,10 +302,6 @@ public class LWWindowPeer
updateFocusableWindowState();
super.setVisibleImpl(visible);
// TODO: update graphicsConfig, see 4868278
if (visible) {
// Set correct background for a window before making it visible
platformWindow.setOpaque(!isTranslucent());
}
platformWindow.setVisible(visible);
}

View File

@@ -76,6 +76,7 @@ import sun.lwawt.LWWindowPeer;
class CAccessibility implements PropertyChangeListener {
private static Set<String> ignoredRoles;
private static final int INVOKE_TIMEOUT_SECONDS;
private static final boolean ENABLE_SHOW_CONTEXT_MENU_EVENT;
static {
// (-1) for the infinite timeout
@@ -87,6 +88,28 @@ class CAccessibility implements PropertyChangeListener {
return Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
});
INVOKE_TIMEOUT_SECONDS = value;
@SuppressWarnings("removal")
boolean enableShowContextMenuEvent = java.security.AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
return Boolean.getBoolean("sun.lwawt.macosx.CAccessibility.enableShowContextMenuEvent");
});
ENABLE_SHOW_CONTEXT_MENU_EVENT = enableShowContextMenuEvent;
}
private static boolean isEnableShowContextMenuEvent() {
return ENABLE_SHOW_CONTEXT_MENU_EVENT;
}
private static void accessibleShowContextMenuEvent(Accessible a, Component c) {
if (a == null) return;
invokeLater(new Runnable() {
@Override
public void run() {
AccessibleContext ac = a.getAccessibleContext();
if (ac != null) {
ac.firePropertyChange("accessibleContextMenuShow", null, null);
}
}
}, c);
}
static CAccessibility sAccessibility;

View File

@@ -205,8 +205,10 @@ class CAccessible extends CFRetainedResource implements Accessible {
} else if (oldValue != null &&
((AccessibleState) oldValue) == AccessibleState.VISIBLE) {
execute(ptr -> menuClosed(ptr));
execute(ptr -> unregisterFromCocoaAXSystem(ptr));
}
} else if (thisRole == AccessibleRole.MENU_ITEM) {
} else if (thisRole == AccessibleRole.MENU_ITEM ||
((parentRole == AccessibleRole.POPUP_MENU) && (thisRole == AccessibleRole.MENU))) {
if (newValue != null &&
((AccessibleState) newValue) == AccessibleState.FOCUSED) {
execute(ptr -> menuItemSelected(ptr));

View File

@@ -25,6 +25,7 @@
package sun.lwawt.macosx;
import sun.awt.AWTThreading;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
@@ -64,14 +65,14 @@ public class CMenu extends CMenuItem implements MenuPeer {
LWCToolkit.targetToPeer(getTarget().getParent());
if (parent instanceof CMenu) {
return parent.executeGet(this::nativeCreateSubMenu);
return AWTThreading.executeWaitToolkit(() -> parent.executeGet(this::nativeCreateSubMenu));
}
if (parent instanceof CMenuBar) {
MenuBar parentContainer = (MenuBar)getTarget().getParent();
boolean isHelpMenu = parentContainer.getHelpMenu() == getTarget();
int insertionLocation = ((CMenuBar)parent).getNextInsertionIndex();
return parent.executeGet(ptr -> nativeCreateMenu(ptr, isHelpMenu,
insertionLocation));
return AWTThreading.executeWaitToolkit(() -> parent.executeGet(ptr -> nativeCreateMenu(ptr, isHelpMenu,
insertionLocation)));
}
throw new InternalError("Parent must be CMenu or CMenuBar");
}
@@ -84,7 +85,7 @@ public class CMenu extends CMenuItem implements MenuPeer {
@Override
public final void delItem(final int index) {
execute(ptr -> nativeDeleteItem(ptr, index));
AWTThreading.executeWaitToolkit(() -> execute(ptr -> nativeDeleteItem(ptr, index)));
}
@Override

View File

@@ -33,6 +33,7 @@ import java.awt.event.KeyEvent;
import java.awt.peer.MenuItemPeer;
import java.util.concurrent.atomic.AtomicBoolean;
import sun.awt.AWTThreading;
import sun.awt.SunToolkit;
import sun.lwawt.LWToolkit;
@@ -61,7 +62,7 @@ public class CMenuItem extends CMenuComponent implements MenuItemPeer {
@Override
long createModel() {
CMenuComponent parent = (CMenuComponent)LWToolkit.targetToPeer(getTarget().getParent());
return parent.executeGet(ptr->nativeCreate(ptr, isSeparator()));
return AWTThreading.executeWaitToolkit(() -> parent.executeGet(ptr->nativeCreate(ptr, isSeparator())));
}
@SuppressWarnings("deprecation")
public void setLabel(String label, char keyChar, int keyCode, int modifiers) {

View File

@@ -112,6 +112,7 @@ import sun.lwawt.PlatformComponent;
import sun.lwawt.PlatformDropTarget;
import sun.lwawt.PlatformWindow;
import sun.lwawt.SecurityWarningWindow;
import sun.security.action.GetBooleanAction;
import sun.util.logging.PlatformLogger;
@SuppressWarnings("serial") // JDK implementation class
@@ -141,6 +142,13 @@ public final class LWCToolkit extends LWToolkit {
private static CInputMethodDescriptor sInputMethodDescriptor;
// Listens to EDT state in invokeAndWait() and disposes the invocation event
// when EDT becomes free but the invocation event is not yet dispatched (considered lost).
// This prevents a deadlock and makes the invocation return some default result.
@SuppressWarnings("removal")
private static final boolean DISPOSE_INVOCATION_ON_EDT_FREE =
AccessController.doPrivileged(new GetBooleanAction("sun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree"));
static {
System.err.flush();
@@ -187,7 +195,7 @@ public final class LWCToolkit extends LWToolkit {
*/
private static final boolean inAWT;
private static final PlatformLogger log = PlatformLogger.getLogger("sun.lwawt.macosx.LWCToolkit");
private static final PlatformLogger log = PlatformLogger.getLogger(LWCToolkit.class.getName());
public LWCToolkit() {
final String extraButtons = "sun.awt.enableExtraMouseButtons";
@@ -496,7 +504,8 @@ public final class LWCToolkit extends LWToolkit {
if (!(gd instanceof CGraphicsDevice)) {
return AWTThreading.executeWaitToolkit(() -> super.getScreenInsets(gc));
}
return AWTThreading.executeWaitToolkit(() -> ((CGraphicsDevice)gd).getScreenInsets());
CGraphicsDevice cgd = (CGraphicsDevice) gd;
return cgd.getScreenInsets();
}
@Override
@@ -711,30 +720,6 @@ public final class LWCToolkit extends LWToolkit {
}
}
private static final AtomicInteger blockingRunLoopCounter = new AtomicInteger(0);
private static final AtomicBoolean priorityInvocationPending = new AtomicBoolean(false);
@Override
public void unsafeNonblockingExecute(Runnable runnable) {
if (!EventQueue.isDispatchThread()) {
throw new Error("the method must be called on the Event Dispatching thread");
}
if (runnable == null) return;
synchronized (priorityInvocationPending) {
priorityInvocationPending.set(true);
}
AWTAccessor.getEventQueueAccessor().createSecondaryLoop(
getSystemEventQueue(),
() -> blockingRunLoopCounter.get() > 0).enter();
try {
runnable.run();
} finally {
priorityInvocationPending.set(false);
}
}
/**
* Kicks an event over to the appropriate eventqueue and waits for it to
* finish To avoid deadlocking, we manually run the NSRunLoop while waiting
@@ -766,11 +751,18 @@ public final class LWCToolkit extends LWToolkit {
log.fine("invokeAndWait started: " + runnable);
}
boolean nonBlockingRunLoop;
synchronized (priorityInvocationPending) {
nonBlockingRunLoop = priorityInvocationPending.get();
if (!nonBlockingRunLoop) blockingRunLoopCounter.incrementAndGet();
if (isBlockingEventDispatchThread()) {
String msg = "invokeAndWait is discarded as the EventDispatch thread is currently blocked";
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(msg, new Throwable());
log.fine(AWTThreading.getInstance(component).printEventDispatchThreadStackTrace().toString());
} else if (log.isLoggable(PlatformLogger.Level.INFO)) {
StackTraceElement[] stack = new Throwable().getStackTrace();
log.info(msg + ". Originated at " + stack[stack.length - 1]);
Thread dispatchThread = AWTThreading.getInstance(component).getEventDispatchThread();
log.info(dispatchThread.getName() + " at: " + dispatchThread.getStackTrace()[0].toString());
}
return;
}
AWTThreading.TrackedInvocationEvent invocationEvent =
@@ -791,22 +783,21 @@ public final class LWCToolkit extends LWToolkit {
((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
}
CompletableFuture<Void> eventDispatchThreadFreeFuture =
AWTThreading.getInstance(component).onEventDispatchThreadFree(() -> {
if (!invocationEvent.isDone()) {
// EventQueue is now empty but the posted InvocationEvent is still not dispatched,
// consider it lost then.
invocationEvent.dispose("InvocationEvent was lost");
}
});
invocationEvent.onDone(() -> eventDispatchThreadFreeFuture.cancel(false));
if (!doAWTRunLoop(mediator, nonBlockingRunLoop, timeoutSeconds)) {
invocationEvent.dispose("InvocationEvent has timed out");
if (DISPOSE_INVOCATION_ON_EDT_FREE) {
CompletableFuture<Void> eventDispatchThreadFreeFuture =
AWTThreading.getInstance(component).onEventDispatchThreadFree(() -> {
if (!invocationEvent.isCompleted(true)) {
// EventQueue is now empty but the posted InvocationEvent is still not dispatched,
// consider it lost then.
invocationEvent.dispose("InvocationEvent was lost");
}
});
invocationEvent.onDone(() -> eventDispatchThreadFreeFuture.cancel(false));
}
if (!nonBlockingRunLoop) blockingRunLoopCounter.decrementAndGet();
if (!doAWTRunLoop(mediator, false, timeoutSeconds)) {
invocationEvent.dispose("InvocationEvent has timed out");
}
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("invokeAndWait finished: " + runnable);
@@ -815,6 +806,8 @@ public final class LWCToolkit extends LWToolkit {
checkException(invocationEvent);
}
private static native boolean isBlockingEventDispatchThread();
public static void invokeLater(Runnable event, Component component)
throws InvocationTargetException {
Objects.requireNonNull(component, "Null component provided to invokeLater");
@@ -857,6 +850,11 @@ public final class LWCToolkit extends LWToolkit {
*/
static native void performOnMainThreadAfterDelay(Runnable r, long delay);
/**
* Schedules a {@code Runnable} execution on the Appkit thread and waits for completion.
*/
public static native void performOnMainThreadAndWait(Runnable r);
// DnD support
@Override

View File

@@ -1653,7 +1653,15 @@ JNI_COCOA_ENTER(env);
// resets the NSWindow's style mask if the mask intersects any of those bits
if (mask & MASK(_STYLE_PROP_BITMASK)) {
[nsWindow setStyleMask:[AWTWindow styleMaskForStyleBits:newBits]];
NSWindowStyleMask styleMask = [AWTWindow styleMaskForStyleBits:newBits];
@try {
[nsWindow setStyleMask:styleMask];
} @catch (NSException *e) {
NSLog(@"WARNING: suppressed exception from [NSWindow setStyleMask] in CPlatformWindow"
".nativeSetNSWindowStyleBits");
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
[NSApplicationAWT logException:e forProcess:processInfo];
}
}
// calls methods on NSWindow to change other properties, based on the mask

View File

@@ -237,7 +237,7 @@ static BOOL sNeedsEnter;
jobject transferer = [self dataTransferer:env];
jbyteArray data = nil;
if (transferer != NULL) {
if (transferer != NULL && fComponent != NULL) {
GET_DT_CLASS_RETURN(NULL);
DECLARE_METHOD_RETURN(convertDataMethod, DataTransfererClass, "convertData", "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B", NULL);
data = (*env)->CallObjectMethod(env, transferer, convertDataMethod, fComponent, fTransferable, format, fFormatMap, (jboolean) TRUE);

View File

@@ -69,6 +69,9 @@ static int* gsButtonEventNumber;
static NSTimeInterval gNextKeyEventTime;
static NSTimeInterval safeDelay;
#define KEY_CODE_COUNT 128
static CGEventFlags keyOwnFlags[KEY_CODE_COUNT];
static inline CGKeyCode GetCGKeyCode(jint javaKeyCode);
static void PostMouseEvent(const CGPoint point, CGMouseButton button,
@@ -107,6 +110,20 @@ static inline void autoDelay(BOOL isMove) {
gNextKeyEventTime = [[NSDate date] timeIntervalSinceReferenceDate] + safeDelay;
}
static void initKeyFlags() {
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStatePrivate);
for (CGKeyCode keyCode = 0; keyCode < KEY_CODE_COUNT; keyCode++) {
CGEventRef event = CGEventCreateKeyboardEvent(source, keyCode, true);
if (event != NULL) {
keyOwnFlags[keyCode] = CGEventGetFlags(event);
CFRelease(event);
}
}
if (source != NULL) {
CFRelease(source);
}
}
/*
* Class: sun_lwawt_macosx_CRobot
* Method: initRobot
@@ -155,6 +172,8 @@ Java_sun_lwawt_macosx_CRobot_initRobot
for (i = 0; i < gNumberOfButtons; ++i) {
gsButtonEventNumber[i] = ROBOT_EVENT_NUMBER_START;
}
initKeyFlags();
}];
}
}
@@ -283,6 +302,20 @@ Java_sun_lwawt_macosx_CRobot_mouseWheel
}];
}
// CGEventCreateKeyboardEvent incorrectly handles flags pertinent to non-modifier keys
// (e.g. F1-F12 keys always Fn flag set, while arrow keys always have Fn and NumPad flags set).
// Those flags are not cleared for following key presses automatically, so we need to do it ourselves.
// See JBR-4306 for details.
static void clearStickyFlags(CGEventRef event, CGKeyCode keyCode, CGEventFlags flagToCheck) {
if (keyCode < KEY_CODE_COUNT && (keyOwnFlags[keyCode] & flagToCheck) == 0) {
CGEventFlags flags = CGEventGetFlags(event);
CGEventFlags updatedFlags = flags & ~flagToCheck;
if (updatedFlags != flags) {
CGEventSetFlags(event, updatedFlags);
}
}
}
/*
* Class: sun_lwawt_macosx_CRobot
* Method: keyEvent
@@ -298,6 +331,10 @@ Java_sun_lwawt_macosx_CRobot_keyEvent
CGKeyCode keyCode = GetCGKeyCode(javaKeyCode);
CGEventRef event = CGEventCreateKeyboardEvent(source, keyCode, keyPressed);
if (event != NULL) {
// this assumes Robot isn't used to generate Fn key presses
clearStickyFlags(event, keyCode, kCGEventFlagMaskSecondaryFn);
// there is no NumPad key, so this won't hurt in any case
clearStickyFlags(event, keyCode, kCGEventFlagMaskNumericPad);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
}

View File

@@ -620,6 +620,17 @@ JNI_COCOA_EXIT(env);
return result;
}
/*
* Class: sun_lwawt_macosx_LWCToolkit
* Method: isBlockingEventDispatchThread
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isBlockingEventDispatchThread
(JNIEnv *env, jclass clz)
{
return ThreadUtilities.blockingEventDispatchThread;
}
/*
* Class: sun_lwawt_macosx_LWCToolkit
* Method: stopAWTRunLoop
@@ -659,6 +670,23 @@ JNI_COCOA_ENTER(env);
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_LWCToolkit
* Method: performOnMainThreadAndWait
* Signature: (Ljava/lang/Runnable)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_performOnMainThreadAndWait
(JNIEnv *env, jclass clz, jobject runnable)
{
JNI_COCOA_ENTER(env);
jobject gRunnable = (*env)->NewGlobalRef(env, runnable);
CHECK_NULL(gRunnable);
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
JavaRunnable* performer = [[JavaRunnable alloc] initWithRunnable:gRunnable];
[performer perform];
}];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_LWCToolkit

View File

@@ -126,7 +126,7 @@ static jobject sAccessibilityClass = NULL;
/*
* Here we should keep all the mapping between the accessibility roles and implementing classes
*/
rolesMap = [[NSMutableDictionary alloc] initWithCapacity:51];
rolesMap = [[NSMutableDictionary alloc] initWithCapacity:50];
[rolesMap setObject:@"ButtonAccessibility" forKey:@"pushbutton"];
[rolesMap setObject:@"ImageAccessibility" forKey:@"icon"];
@@ -158,7 +158,6 @@ static jobject sAccessibilityClass = NULL;
[rolesMap setObject:@"TableAccessibility" forKey:@"table"];
[rolesMap setObject:@"MenuBarAccessibility" forKey:@"menubar"];
[rolesMap setObject:@"MenuAccessibility" forKey:@"menu"];
[rolesMap setObject:@"MenuItemAccessibility" forKey:@"menuitem"];
[rolesMap setObject:@"MenuAccessibility" forKey:@"popupmenu"];
[rolesMap setObject:@"ProgressIndicatorAccessibility" forKey:@"progressbar"];
@@ -186,10 +185,11 @@ static jobject sAccessibilityClass = NULL;
[rolesMap setObject:IgnoreClassName forKey:@"viewport"];
[rolesMap setObject:IgnoreClassName forKey:@"window"];
rowRolesMapForParent = [[NSMutableDictionary alloc] initWithCapacity:2];
rowRolesMapForParent = [[NSMutableDictionary alloc] initWithCapacity:3];
[rowRolesMapForParent setObject:@"ListRowAccessibility" forKey:@"ListAccessibility"];
[rowRolesMapForParent setObject:@"OutlineRowAccessibility" forKey:@"OutlineAccessibility"];
[rowRolesMapForParent setObject:@"MenuItemAccessibility" forKey:@"MenuAccessibility"];
/*
* Initialize CAccessibility instance
@@ -1186,7 +1186,25 @@ static jobject sAccessibilityClass = NULL;
// NSAccessibilityActions methods
- (BOOL)isEnableShowMenuEvent
{
static NSNumber *sEnableShowContextMenuEvent = nil;
if (sEnableShowContextMenuEvent == nil) {
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CACCESSIBILITY_CLASS_RETURN(NO);
DECLARE_STATIC_METHOD_RETURN(sjm_enableShowMenuEvent, sjc_CAccessibility, "isEnableShowContextMenuEvent", "()Z", NO);
sEnableShowContextMenuEvent = [[NSNumber alloc] initWithBool:(*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_enableShowMenuEvent)];
CHECK_EXCEPTION();
}
return sEnableShowContextMenuEvent.boolValue;
}
- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector {
if ([self isEnableShowMenuEvent] &&
[NSStringFromSelector(selector) isEqualToString:@"accessibilityPerformShowMenu"]) {
return YES;
}
if ([sAllActionSelectors containsObject:NSStringFromSelector(selector)] &&
![[self actionSelectors] containsObject:NSStringFromSelector(selector)]) {
return NO;
@@ -1203,6 +1221,14 @@ static jobject sAccessibilityClass = NULL;
}
- (BOOL)accessibilityPerformShowMenu {
if ([self isEnableShowMenuEvent]) {
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CACCESSIBILITY_CLASS_RETURN(NO);
DECLARE_STATIC_METHOD_RETURN(sjm_accessibleShowContextMenuEvent, sjc_CAccessibility, "accessibleShowContextMenuEvent", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)V", NO);
(*env)->CallStaticVoidMethod(env, sjc_CAccessibility, sjm_accessibleShowContextMenuEvent, fAccessible, fComponent);
CHECK_EXCEPTION();
return YES;
}
return [self accessiblePerformAction:NSAccessibilityShowMenuAction];
}

View File

@@ -39,7 +39,7 @@
- (BOOL)isAccessibilityElement
{
return YES;
return [[[self accessibilityParent] accessibilityRole] isEqualToString:NSAccessibilityComboBoxRole];
}
@end

View File

@@ -3371,7 +3371,7 @@ JNI_COCOA_ENTER(env);
anotherBaseFont = true;
}
CTFontRef font = (CTFontRef)nsFont;
CFArrayRef codes = CFLocaleCopyISOLanguageCodes();
CFArrayRef codes = CFLocaleCopyPreferredLanguages();
CFArrayRef fds = CTFontCopyDefaultCascadeListForLanguages(font, codes);
CFRelease(codes);

View File

@@ -127,6 +127,13 @@ do { \
__attribute__((visibility("default")))
@interface ThreadUtilities : NSObject { } /* Extend NSObject so can call performSelectorOnMainThread */
/*
* When a blocking performSelectorOnMainThread is executed from the EventDispatch thread,
* and the executed code triggers an opposite blocking a11y call (via LWCToolkit.invokeAndWait)
* this is a deadlock case, and then this property is used to discard LWCToolkit.invokeAndWait.
*/
@property (class, nonatomic, readonly) BOOL blockingEventDispatchThread;
+ (JNIEnv*)getJNIEnv;
+ (JNIEnv*)getJNIEnvUncached;
+ (void)detachCurrentThread;

View File

@@ -50,6 +50,24 @@ static inline void attachCurrentThread(void** env) {
@implementation ThreadUtilities
static BOOL _blockingEventDispatchThread = NO;
static long eventDispatchThreadPtr = (long)nil;
static BOOL isEventDispatchThread() {
return (long)[NSThread currentThread] == eventDispatchThreadPtr;
}
// The [blockingEventDispatchThread] property is readonly, so we implement a private setter
static void setBlockingEventDispatchThread(BOOL value) {
assert([NSThread isMainThread]);
_blockingEventDispatchThread = value;
}
+ (BOOL) blockingEventDispatchThread {
assert([NSThread isMainThread]);
return _blockingEventDispatchThread;
}
+ (void)initialize {
/* All the standard modes plus ours */
javaModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode,
@@ -82,10 +100,10 @@ AWT_ASSERT_APPKIT_THREAD;
}
/* This is needed because we can't directly pass a block to
* performSelectorOnMainThreadWaiting .. since it expects a selector
* performSelectorOnMainThreadWaiting:..waitUntilDone:YES.. since it expects a selector
*/
+ (void)invokeBlock:(void (^)())block {
block();
block();
}
/*
@@ -116,7 +134,19 @@ AWT_ASSERT_APPKIT_THREAD;
if ([NSThread isMainThread] && wait == YES) {
[target performSelector:aSelector withObject:arg];
} else {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
if (wait && isEventDispatchThread()) {
void (^block)(void) = ^{
setBlockingEventDispatchThread(YES);
@try {
[target performSelector:aSelector withObject:arg];
} @finally {
setBlockingEventDispatchThread(NO);
}
};
[self performSelectorOnMainThread:@selector(invokeBlock:) withObject:block waitUntilDone:YES modes:javaModes];
} else {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
}
}
}
@@ -143,3 +173,16 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CThreading_isMainThread
return [NSThread isMainThread];
}
/*
* Class: sun_awt_AWTThreading
* Method: notifyEventDispatchThreadStartedNative
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_awt_AWTThreading_notifyEventDispatchThreadStartedNative
(JNIEnv *env, jclass c)
{
@synchronized([ThreadUtilities class]) {
eventDispatchThreadPtr = (long)[NSThread currentThread];
}
}

View File

@@ -31,6 +31,7 @@ import java.awt.event.WindowEvent;
import java.util.ArrayList;
import sun.awt.AWTThreading;
import sun.util.logging.PlatformLogger;
import sun.awt.dnd.SunDragSourceContextPeer;
@@ -86,6 +87,7 @@ class EventDispatchThread extends Thread {
}
public void run() {
AWTThreading.getInstance(Thread.currentThread()).notifyEventDispatchThreadStarted();
try {
pumpEvents(new Conditional() {
public boolean evaluate() {
@@ -97,6 +99,8 @@ class EventDispatchThread extends Thread {
}
}
private static native void registerEventDispatchThread();
void pumpEvents(Conditional cond) {
pumpEvents(ANY_EVENT, cond);
}

View File

@@ -5,6 +5,8 @@ import sun.util.logging.PlatformLogger;
import java.awt.*;
import java.awt.event.InvocationEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -14,6 +16,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Consumer;
/**
* Used to perform a cross threads (EventDispatch, Toolkit) execution so that the execution does not cause a deadlock.
@@ -75,14 +78,25 @@ public class AWTThreading {
}
/**
* Same as {@link #executeWaitToolkit(Callable)}, but without returning a value. If requested (as indicated by
* the passed parameter), the invoked native method is supposed to wait for the result of invocation on AppKit
* thread, and vice versa.
* A boolean value passed to the consumer indicates whether the consumer should perform
* a synchronous invocation on the Toolkit thread and wait, or return immediately.
*
* @see #executeWaitToolkit(Callable).
*/
public static void executeWaitToolkit(Task runnable) {
public static void executeWaitToolkit(Consumer<Boolean> consumer) {
boolean wait = EventQueue.isDispatchThread();
executeWaitToolkit(() -> {
runnable.run(wait);
consumer.accept(wait);
return null;
});
}
/**
* @see #executeWaitToolkit(Callable).
*/
public static void executeWaitToolkit(Runnable runnable) {
executeWaitToolkit(() -> {
runnable.run();
return null;
});
}
@@ -102,12 +116,6 @@ public class AWTThreading {
}
}
if (!isEDT && logger.isLoggable(PlatformLogger.Level.FINE)) {
// this can cause deadlock if calling thread is holding a lock which EDT might require (e.g. AWT tree lock)
logger.fine("AWTThreading.executeWaitToolkit invoked from non-EDT thread", new Throwable());
}
// fallback to default
try {
return callable.call();
} catch (Exception e) {
@@ -246,8 +254,8 @@ public class AWTThreading {
private final Throwable throwable = new Throwable();
private final CompletableFuture<Void> futureResult = new CompletableFuture<>();
// dispatched or disposed
private final AtomicBoolean isFinished = new AtomicBoolean(false);
// dispatch or dispose has been started or already completed
private final AtomicBoolean isCompletionStarted = new AtomicBoolean(false);
static TrackedInvocationEvent create(Object source,
Runnable onDispatch,
@@ -265,7 +273,7 @@ public class AWTThreading {
TrackedInvocationEvent thisEvent = eventRef.get();
if (!thisEvent.isDispatched()) {
// If we're here - this {onDone} is being disposed.
thisEvent.finishIfNotYet(() ->
thisEvent.completeIfNotYet(() ->
// If we're here - this {onDone} is called by the outer AWTAccessor.getInvocationEventAccessor().dispose()
// which we do not control, so complete here.
thisEvent.futureResult.completeExceptionally(new Throwable("InvocationEvent was disposed"))
@@ -298,28 +306,37 @@ public class AWTThreading {
@Override
public void dispatch() {
finishIfNotYet(super::dispatch);
completeIfNotYet(super::dispatch);
futureResult.complete(null);
}
public void dispose(String reason) {
finishIfNotYet(() -> AWTAccessor.getInvocationEventAccessor().dispose(this));
completeIfNotYet(() -> AWTAccessor.getInvocationEventAccessor().dispose(this));
futureResult.completeExceptionally(new Throwable(reason));
}
private void finishIfNotYet(Runnable finish) {
if (!isFinished.getAndSet(true)) {
finish.run();
private void completeIfNotYet(Runnable competeRunnable) {
if (!isCompletionStarted.getAndSet(true)) {
competeRunnable.run();
}
}
/**
* Returns whether the event is dispatched or disposed.
*/
public boolean isDone() {
public boolean isCompleted() {
return futureResult.isDone();
}
/**
* Returns whether the event is dispatched or disposed.
* If {@code isCompletionInProgress} is true then also checks whether the event
* dispatching or disposal is in progress and if so returns true.
*/
public boolean isCompleted(boolean isCompletionInProgress) {
return isCompleted() || (isCompletionInProgress && isCompletionStarted.get());
}
/**
* Calls the runnable when it's done (immediately if it's done).
*/
@@ -357,6 +374,19 @@ public class AWTThreading {
return eventDispatchThread;
}
public StringWriter printEventDispatchThreadStackTrace() {
return printEventDispatchThreadStackTrace(new StringWriter());
}
public StringWriter printEventDispatchThreadStackTrace(StringWriter writer) {
assert writer != null;
var printer = new PrintWriter(writer);
Thread dispatchThread = getEventDispatchThread();
printer.println(dispatchThread.getName());
Arrays.asList(dispatchThread.getStackTrace()).forEach(frame -> printer.append("\tat ").println(frame));
return writer;
}
/**
* Sets {@code AWTThreading} instance factory.
* WARNING: for testing purpose.
@@ -365,6 +395,15 @@ public class AWTThreading {
theAWTThreadingFactory.set(factory);
}
/**
* Must be called on the EventDispatch thread.
*/
public void notifyEventDispatchThreadStarted() {
if (FontUtilities.isMacOSX) notifyEventDispatchThreadStartedNative();
}
private static native void notifyEventDispatchThreadStartedNative();
public void notifyEventDispatchThreadFree() {
List<CompletableFuture<Void>> notifiers = Collections.emptyList();
synchronized (eventDispatchThreadStateNotifiers) {
@@ -413,8 +452,4 @@ public class AWTThreading {
future.complete(null);
return future;
}
public interface Task {
void run(boolean wait);
}
}

View File

@@ -65,6 +65,8 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
static final boolean ENABLE_REPARENTING_CHECK
= "true".equals(GetPropertyAction.privilegedGetProperty("reparenting.check"));
private static final boolean ENABLE_DESKTOP_CHECK
= "true".equals(GetPropertyAction.privilegedGetProperty("transients.desktop.check", "true"));
// should be synchronized on awtLock
private static Set<XWindowPeer> windows = new HashSet<XWindowPeer>();
@@ -95,6 +97,9 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
private static final AtomicBoolean isStartupNotificationRemoved = new AtomicBoolean();
private Long desktopId; // guarded by AWT lock
private boolean desktopIdInvalid; // guarded by AWT lock
/*
* Focus related flags
*/
@@ -159,6 +164,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
params.put(EVENT_MASK, eventMask);
XA_NET_WM_STATE = XAtom.get("_NET_WM_STATE");
XA_NET_WM_DESKTOP = XAtom.get("_NET_WM_DESKTOP");
params.put(OVERRIDE_REDIRECT, Boolean.valueOf(isOverrideRedirect()));
@@ -1730,7 +1736,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
if (!allStates && (window.getWMState() != transientForWindow.getWMState())) {
return;
}
if (window.getScreenNumber() != transientForWindow.getScreenNumber()) {
if (screenOrDesktopDiffers(window, transientForWindow)) {
return;
}
long bpw = window.getWindow();
@@ -1767,7 +1773,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
void updateTransientFor() {
int state = getWMState();
XWindowPeer p = prevTransientFor;
while ((p != null) && ((p.getWMState() != state) || (p.getScreenNumber() != getScreenNumber()))) {
while ((p != null) && ((p.getWMState() != state) || screenOrDesktopDiffers(p, this))) {
p = p.prevTransientFor;
}
if (p != null) {
@@ -1776,7 +1782,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
restoreTransientFor(this);
}
XWindowPeer n = nextTransientFor;
while ((n != null) && ((n.getWMState() != state) || (n.getScreenNumber() != getScreenNumber()))) {
while ((n != null) && ((n.getWMState() != state) || screenOrDesktopDiffers(n, this))) {
n = n.nextTransientFor;
}
if (n != null) {
@@ -1784,6 +1790,38 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
}
}
private Long getDesktopId() {
XToolkit.awtLock();
try {
if (desktopIdInvalid) {
desktopIdInvalid = false;
desktopId = null;
WindowPropertyGetter getter =
new WindowPropertyGetter(window, XA_NET_WM_DESKTOP, 0, 1, false, XAtom.XA_CARDINAL);
try {
if (getter.execute() == XConstants.Success &&
getter.getActualType() == XAtom.XA_CARDINAL &&
getter.getActualFormat() == 32) {
long ptr = getter.getData();
if (ptr != 0) {
desktopId = Native.getCard32(ptr);
}
}
} finally {
getter.dispose();
}
}
return desktopId;
} finally {
XToolkit.awtUnlock();
}
}
private static boolean screenOrDesktopDiffers(XWindowPeer p1, XWindowPeer p2) {
return p1.getScreenNumber() != p2.getScreenNumber() ||
ENABLE_DESKTOP_CHECK && !Objects.equals(p1.getDesktopId(), p2.getDesktopId());
}
/*
* Removes the TRANSIENT_FOR hint from the given top-level window.
* If window or transientForWindow are embedded frames, the containing
@@ -2163,6 +2201,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
cachedFocusableWindow = isFocusableWindow();
}
XAtom XA_NET_WM_DESKTOP;
XAtom XA_NET_WM_STATE;
XAtomList net_wm_state;
public XAtomList getNETWMState() {
@@ -2179,6 +2218,18 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
}
}
@Override
public void handlePropertyNotify(XEvent xev) {
super.handlePropertyNotify(xev);
XPropertyEvent ev = xev.get_xproperty();
if (ev.get_atom() == XA_NET_WM_DESKTOP.getAtom()) {
desktopIdInvalid = true;
if (ENABLE_DESKTOP_CHECK) {
updateTransientFor();
}
}
}
public PropMwmHints getMWMHints() {
if (mwm_hints == null) {
mwm_hints = new PropMwmHints();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -583,6 +583,8 @@ cleanup:
XFree (pVI8sg);
if (n1sg != 0)
XFree (pVI1sg);
if (nTrue != 0)
XFree (pVITrue);
}
/*
@@ -1430,6 +1432,9 @@ Java_sun_awt_X11GraphicsDevice_getDoubleBufferVisuals(JNIEnv *env,
break;
}
}
AWT_LOCK();
XdbeFreeVisualInfo(visScreenInfo);
AWT_UNLOCK();
}
/*

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @summary Test implementation of NSAccessibilityMenu and NSAccessibilityMenuItem roles peer
* @author Artem.Semenov@jetbrains.com
* @run main/manual AccessibleJPopupMenuTest
* @requires (os.family == "mac")
*/
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.concurrent.CountDownLatch;
public class AccessibleJPopupMenuTest extends AccessibleComponentTest {
@Override
public CountDownLatch createCountDownLatch() {
return new CountDownLatch(1);
}
private static JPopupMenu createPopup() {
JPopupMenu popup = new JPopupMenu("MENU");
popup.add("One");
popup.add("Two");
popup.add("Three");
popup.addSeparator();
JMenu menu = new JMenu("For submenu");
menu.add("subOne");
menu.add("subTwo");
menu.add("subThree");
popup.add(menu);
return popup;
}
public void createTest() {
INSTRUCTIONS = "INSTRUCTIONS:\n"
+ "Check a11y of JPopupMenu.\n\n"
+ "Turn screen reader on, and Tab to the show button and press space.\n"
+ "Press the up and down arrow buttons to move through the menu, and open submenu.\n\n"
+ "If you can hear popup menu items tab further and press PASS, otherwise press FAIL.\n";
JPanel frame = new JPanel();
JButton button = new JButton("show");
button.setPreferredSize(new Dimension(100, 35));
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
createPopup().show(button, button.getX(), button.getY());
}
});
frame.setLayout(new FlowLayout());
frame.add(button);
exceptionString = "Accessible JPopupMenu test failed!";
super.createUI(frame, "Accessible JPopupMenu test");
}
public static void main(String[] args) throws Exception {
AccessibleJPopupMenuTest a11yTest = new AccessibleJPopupMenuTest();
CountDownLatch countDownLatch = a11yTest.createCountDownLatch();
SwingUtilities.invokeLater(a11yTest::createTest);
countDownLatch.await();
if (!testResult) {
throw new RuntimeException(a11yTest.exceptionString);
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2000-2022 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
@test
@key headful
@summary a regression test for JBR-4303.
@run main GetPointerInfoTest
*/
import java.awt.*;
/**
* The test checks <code>MouseInfo.getPointerInfo()</code> for all locations with the steps <code>X_STEP</code> and
* <code>Y_STEP</code> on all graphic devices.
* It moves mouse to the current location via <code>Robot.mouseMove()</code> and checks that
* <code>MouseInfo.getPointerInfo()</code> is not NULL.
* It also checks <code>MouseInfo.getPointerInfo().getLocation()</code> returns expected values.
*/
public class GetPointerInfoTest {
public static int X_STEP = 100;
public static int Y_STEP = 100;
static boolean isPassed = true;
public static void main(String[] args) throws Exception {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] graphicsDevices = ge.getScreenDevices();
for (GraphicsDevice gd : graphicsDevices) {
String name = gd.getIDstring();
int width = gd.getDisplayMode().getWidth();
int height = gd.getDisplayMode().getHeight();
System.out.println("Check for device: " + name + " " + width + "x" + height);
Robot robot = new Robot(gd);
robot.setAutoDelay(0);
robot.setAutoWaitForIdle(true);
robot.delay(10);
robot.waitForIdle();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
Rectangle bounds = gc.getBounds();
for (double y = bounds.getY(); y < bounds.getY() + bounds.getHeight(); y += Y_STEP) {
for (double x = bounds.getX(); x < bounds.getX() + bounds.getWidth(); x += X_STEP) {
Point p = new Point((int)x, (int)y);
System.out.println("\tmouse move to x=" + p.x + " y=" + p.y);
robot.mouseMove(p.x, p.y);
System.out.print("\t\tMouseInfo.getPointerInfo.getLocation");
PointerInfo pi = MouseInfo.getPointerInfo();
if (pi == null) {
throw new RuntimeException("Test failed. getPointerInfo() returned null value.");
}
Point piLocation = pi.getLocation();
if (piLocation.x != p.x || piLocation.y != p.y) {
System.out.println(" - ***FAILED*** x=" + piLocation.x + " y=" + piLocation.y);
isPassed = false;
} else {
System.out.println(" x=" + p.x + ", y=" + p.y + " - passed");
}
}
}
}
if ( !isPassed )
throw new RuntimeException("PointerInfo.getLocation() returns unexpected value(s).");
System.out.println("Test PASSED.");
}
}

View File

@@ -0,0 +1,71 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
import javax.swing.*;
import java.util.function.Consumer;
import static helper.ToolkitTestHelper.*;
import static helper.ToolkitTestHelper.TestCase.*;
/*
* @test
* @summary tests that CMenu/CMenuItem methods wrapped with {AWTThreading.executeWaitToolkit} are normally callable
* @requires (os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
* @run main/othervm -Dapple.laf.useScreenMenuBar=true AWTThreadingCMenuTest
* @author Anton Tarasov
*/
public class AWTThreadingCMenuTest {
public static void main(String[] args) {
initTest(AWTThreadingCMenuTest.class);
testCase().
withCaption("populate Menu").
withRunnable(AWTThreadingCMenuTest::test1, true).
withExpectedInLog("discarded", false).
withCompletionTimeout(1).
run();
testCase().
withCaption("de-populate Menu").
withRunnable(AWTThreadingCMenuTest::test2, true).
withExpectedInLog("discarded", false).
withCompletionTimeout(1).
run();
System.out.println("Test PASSED");
}
@SuppressWarnings("deprecation")
private static void test1() {
var bar = new JMenuBar();
FRAME.setJMenuBar(bar);
var menu = new JMenu("Menu");
Consumer<String> addItem = text -> {
var item = new JMenuItem(text);
item.setLabel("label " + text);
menu.add(item);
menu.addSeparator();
};
addItem.accept("one");
addItem.accept("two");
addItem.accept("three");
bar.add(menu);
TEST_CASE_RESULT.complete(true);
}
private static void test2() {
var bar = FRAME.getJMenuBar();
var menu = bar.getMenu(0);
for (int i = 0; i < FRAME.getJMenuBar().getMenuCount(); i++) {
menu.remove(i);
}
TEST_CASE_RESULT.complete(true);
}
}

View File

@@ -1,139 +1,134 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
import java.awt.*;
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.Arrays;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import sun.lwawt.macosx.CThreading;
import sun.lwawt.macosx.LWCToolkit;
import sun.awt.AWTThreading;
import static helper.ToolkitTestHelper.*;
import static helper.ToolkitTestHelper.TestCase.*;
/*
* @test
* @summary tests that AWTThreading can manage a stream of cross EDT/AppKit invocation requests
* @summary tests that AWTThreading can manage cross EDT/AppKit blocking invocation requests
* @requires (os.family == "mac")
* @compile --add-exports=java.desktop/sun.lwawt.macosx=ALL-UNNAMED --add-exports=java.desktop/sun.awt=ALL-UNNAMED AWTThreadingTest.java
* @run main/othervm --add-exports=java.desktop/sun.lwawt.macosx=ALL-UNNAMED --add-exports=java.desktop/sun.awt=ALL-UNNAMED AWTThreadingTest
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
* @run main AWTThreadingTest
* @author Anton Tarasov
*/
@SuppressWarnings("ConstantConditions")
public class AWTThreadingTest {
static final ReentrantLock LOCK = new ReentrantLock();
static final Condition COND = LOCK.newCondition();
static final CountDownLatch LATCH = new CountDownLatch(1);
static final int TIMEOUT_SECONDS = 1;
static JFrame frame;
static Thread thread;
static final AtomicInteger ITER_COUNTER = new AtomicInteger();
static final AtomicBoolean DUMP_STACK = new AtomicBoolean(false);
final static AtomicBoolean passed = new AtomicBoolean(true);
final static AtomicInteger counter = new AtomicInteger(0);
static volatile Thread THREAD;
public static void main(String[] args) throws InterruptedException {
EventQueue.invokeLater(AWTThreadingTest::runGui);
public static void main(String[] args) {
DUMP_STACK.set(args.length > 0 && "dumpStack".equals(args[0]));
LATCH.await(5, TimeUnit.SECONDS);
initTest(AWTThreadingTest.class);
frame.dispose();
thread.interrupt();
testCase().
withCaption("certain threads superposition").
withRunnable(AWTThreadingTest::test, false).
run();
testCase().
withCaption("random threads superposition").
withRunnable(AWTThreadingTest::test, false).
run();
if (!passed.get()) {
throw new RuntimeException("Test FAILED!");
}
System.out.println("Test PASSED");
}
static void runGui() {
frame = new JFrame("frame");
frame.getContentPane().setBackground(Color.green);
frame.setLocationRelativeTo(null);
frame.setSize(200, 200);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
startThread();
}
});
frame.setVisible(true);
static void test() {
ITER_COUNTER.set(0);
var timer = new TestTimer(TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
EventQueue.invokeLater(() -> startThread(() ->
TEST_CASE_RESULT.isDone() ||
timer.hasFinished()));
waitTestCaseCompletion(TIMEOUT_SECONDS * 4);
tryRun(THREAD::join);
System.out.println(ITER_COUNTER + " iterations passed");
}
static void startThread() {
thread = new Thread(() -> {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
static void startThread(Supplier<Boolean> shouldExitLoop) {
THREAD = new Thread(() -> {
while (!shouldExitLoop.get()) {
ITER_COUNTER.incrementAndGet();
var point_1 = new CountDownLatch(1);
var point_2 = new CountDownLatch(1);
var point_3 = new CountDownLatch(1);
var invocations = new CountDownLatch(2);
//
// 1. Execute invokeAndWait() from AppKit to EDT
// 1. Blocking invocation from AppKit to EDT
//
CThreading.executeOnAppKit(() -> {
try {
LWCToolkit.invokeAndWait(counter::incrementAndGet, Window.getWindows()[0]);
} catch (Exception e) {
fail(e);
}
// We're on AppKit, wait for the 2nd invocation to be on the AWTThreading-pool thread.
if (TEST_CASE_NUM == 1) await(point_1, TIMEOUT_SECONDS);
tryRun(() -> LWCToolkit.invokeAndWait(() -> {
// We're being dispatched on EDT.
if (TEST_CASE_NUM == 1) point_2.countDown();
// Wait for the 2nd invocation to be executed on AppKit.
if (TEST_CASE_NUM == 1) await(point_3, TIMEOUT_SECONDS);
}, FRAME));
invocations.countDown();
});
//
// 2. Execute invokeAndBlock() from EDT to AppKit
// 2. Blocking invocation from EDT to AppKit
//
EventQueue.invokeLater(() -> {
passed.set(false);
EventQueue.invokeLater(() -> AWTThreading.executeWaitToolkit(() -> {
// We're on the AWTThreading-pool thread.
if (TEST_CASE_NUM == 1) point_1.countDown();
Boolean success = AWTThreading.executeWaitToolkit(() -> {
try {
return CThreading.executeOnAppKit(() -> Boolean.TRUE);
} catch (Throwable e) {
fail(e);
// Wait for the 1st invocation to start NSRunLoop and be dispatched
if (TEST_CASE_NUM == 1) await(point_2, TIMEOUT_SECONDS);
// Perform in JavaRunLoopMode to be accepted by NSRunLoop started by LWCToolkit.invokeAndWait.
LWCToolkit.performOnMainThreadAndWait(() -> {
if (DUMP_STACK.get()) {
dumpAllThreads();
}
return null;
// We're being executed on AppKit.
if (TEST_CASE_NUM == 1) point_3.countDown();
});
System.out.println("Success: " + counter.get() + ": " + success);
passed.set(Boolean.TRUE.equals(success));
invocations.countDown();
}));
if (passed.get()) {
lock(COND::signal);
}
else {
fail(null);
}
});
await(invocations, TIMEOUT_SECONDS * 2);
} // while
lock(COND::await);
}
TEST_CASE_RESULT.complete(true);
});
thread.setDaemon(true);
thread.start();
THREAD.setDaemon(true);
THREAD.start();
}
static void lock(MyRunnable runnable) {
LOCK.lock();
try {
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
} finally {
LOCK.unlock();
}
}
interface MyRunnable {
void run() throws Exception;
}
static void fail(Throwable e) {
if (e != null) e.printStackTrace();
passed.set(false);
LATCH.countDown();
static void dumpAllThreads() {
Thread.getAllStackTraces().keySet().forEach(t -> {
System.out.printf("%s\t%s\t%d\t%s\n", t.getName(), t.getState(), t.getPriority(), t.isDaemon() ? "Daemon" : "Normal");
Arrays.asList(t.getStackTrace()).forEach(frame -> System.out.println("\tat " + frame));
});
System.out.println("\n\n");
}
}

View File

@@ -1,38 +1,35 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
import java.awt.*;
import javax.swing.*;
import java.awt.event.InvocationEvent;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.logging.*;
import sun.awt.AWTAccessor;
import sun.awt.AWTThreading;
import sun.lwawt.macosx.CThreading;
import sun.lwawt.macosx.LWCToolkit;
import static helper.ToolkitTestHelper.*;
import static helper.ToolkitTestHelper.TestCase.*;
/*
* @test
* @summary Tests different scenarios for LWCToolkit.invokeAndWait().
* @requires (os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
* @run main LWCToolkitInvokeAndWaitTest
* @run main/othervm -DAWTThreading.level.FINER=true LWCToolkitInvokeAndWaitTest
* @run main/othervm -Dsun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree=true LWCToolkitInvokeAndWaitTest
* @run main/othervm -Dlog.level.FINER=true -Dsun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree=true LWCToolkitInvokeAndWaitTest
* @author Anton Tarasov
*/
@SuppressWarnings("ConstantConditions")
public class LWCToolkitInvokeAndWaitTest {
// This property is used in {CAccessibility}
private static final int INVOKE_TIMEOUT_SECONDS = Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
static final int INVOKE_TIMEOUT_SECONDS = Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
static final Runnable CONSUME_DISPATCHING = () -> TEST_CASE_RESULT.completeExceptionally(new Throwable("Unexpected dispatching!"));
static TestLogHandler LOG_HANDLER = new TestLogHandler();
static volatile CompletableFuture<Boolean> FUTURE;
static volatile CountDownLatch EDT_FAST_FREE_LATCH;
static volatile JFrame FRAME;
static volatile Thread MAIN_THREAD;
static int TEST_COUNTER;
static final Runnable CONSUME_DISPATCHING = () -> FUTURE.completeExceptionally(new Throwable("Unexpected dispatching!"));
static {
AWTThreading.setAWTThreadingFactory(edt -> new AWTThreading(edt) {
@@ -41,7 +38,7 @@ public class LWCToolkitInvokeAndWaitTest {
if (EDT_FAST_FREE_LATCH != null) {
// 1. wait for the invocation event to be dispatched
// 2. wait for EDT to become free
trycatch(() -> EDT_FAST_FREE_LATCH.await());
tryRun(EDT_FAST_FREE_LATCH::await);
EDT_FAST_FREE_LATCH = null;
}
@@ -62,101 +59,69 @@ public class LWCToolkitInvokeAndWaitTest {
}
public static void main(String[] args) {
MAIN_THREAD = Thread.currentThread();
trycatch(() -> {
Logger log = LogManager.getLogManager().getLogger(AWTThreading.class.getName());
log.setUseParentHandlers(false);
log.addHandler(LOG_HANDLER);
if (Boolean.getBoolean("AWTThreading.level.FINER")) {
log.setLevel(Level.FINER);
}
});
initTest(LWCToolkitInvokeAndWaitTest.class);
Consumer<InvocationEvent> noop = e -> {};
try {
trycatch(() -> EventQueue.invokeAndWait(LWCToolkitInvokeAndWaitTest::runGui));
testCase().
withCaption("InvocationEvent is normally dispatched").
withRunnable(() -> test1(noop, () -> System.out.println("I'm dispatched")), true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
run();
test("InvocationEvent is normally dispatched",
"",
noop,
() -> System.out.println("I'm dispatched"));
testCase().
withCaption("InvocationEvent is lost").
withRunnable(() -> test1(noop, CONSUME_DISPATCHING), true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
withExpectedInLog("lost", true).
run();
test("InvocationEvent is lost",
"lost",
noop,
CONSUME_DISPATCHING);
EDT_FAST_FREE_LATCH = new CountDownLatch(2);
testCase().
withCaption("InvocationEvent is lost (EDT becomes fast free)").
withRunnable(() -> test1(invocationEvent -> EDT_FAST_FREE_LATCH.countDown(), CONSUME_DISPATCHING), true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
withExpectedInLog("lost", true).
run();
EDT_FAST_FREE_LATCH = new CountDownLatch(2);
test("InvocationEvent is lost (EDT becomes fast free)",
"lost",
// notify the invocationEvent has been dispatched
invocationEvent -> EDT_FAST_FREE_LATCH.countDown(),
CONSUME_DISPATCHING);
testCase().
withCaption("InvocationEvent is disposed").
withRunnable(() -> test1(invocationEvent -> AWTAccessor.getInvocationEventAccessor().dispose(invocationEvent), CONSUME_DISPATCHING), true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
withExpectedInLog("disposed", true).
run();
test("InvocationEvent is disposed",
"disposed",
invocationEvent -> AWTAccessor.getInvocationEventAccessor().dispose(invocationEvent),
CONSUME_DISPATCHING);
testCase().
withCaption("InvocationEvent is timed out (delayed before dispatching)").
withRunnable(() -> test1(invocationEvent -> sleep(INVOKE_TIMEOUT_SECONDS * 4), CONSUME_DISPATCHING), true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
withExpectedInLog("timed out", true).
run();
test("InvocationEvent is timed out (delayed before dispatching)",
"timed out",
invocationEvent -> sleep(INVOKE_TIMEOUT_SECONDS * 4),
CONSUME_DISPATCHING);
testCase().
withCaption("InvocationEvent is timed out (delayed during dispatching)").
withRunnable(() -> test1(noop, () -> sleep(INVOKE_TIMEOUT_SECONDS * 4)), true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
withExpectedInLog("timed out", true).
run();
test("InvocationEvent is timed out (delayed during dispatching)",
"timed out",
noop,
() -> sleep(INVOKE_TIMEOUT_SECONDS * 4));
testCase().
withCaption("invokeAndWait is discarded").
withRunnable(LWCToolkitInvokeAndWaitTest::test2, true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
withExpectedInLog("discarded", true).
run();
testCase().
withCaption("invokeAndWait is passed").
withRunnable(LWCToolkitInvokeAndWaitTest::test3, true).
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
run();
} finally {
FRAME.dispose();
}
System.out.println("Test PASSED");
}
static void runGui() {
FRAME = new JFrame(LWCToolkitInvokeAndWaitTest.class.getSimpleName());
FRAME.getContentPane().setBackground(Color.green);
FRAME.setLocationRelativeTo(null);
FRAME.setSize(200, 200);
FRAME.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
FRAME.setVisible(true);
}
static void test(String testCaption,
String expectedInLog,
Consumer<InvocationEvent> onBeforeDispatching,
Runnable onDispatching)
{
System.out.println("\n(" + (++TEST_COUNTER) + ") TEST: " + testCaption);
FUTURE = new CompletableFuture<>();
FUTURE.whenComplete((r, ex) -> Optional.of(ex).ifPresent(Throwable::printStackTrace));
EventQueue.invokeLater(() -> subTest(onBeforeDispatching, onDispatching));
trycatch(() -> {
if (!FUTURE.get(INVOKE_TIMEOUT_SECONDS * 2L, TimeUnit.SECONDS)) {
throw new RuntimeException("Test FAILED! (negative result)");
}
});
// let AppKit and EDT print all the logging
var latch = new CountDownLatch(1);
CThreading.executeOnAppKit(latch::countDown);
trycatch(latch::await);
trycatch(() -> EventQueue.invokeAndWait(() -> {}));
if (!LOG_HANDLER.testContains(expectedInLog)) {
throw new RuntimeException("Test FAILED! (not found in the log: \"" + expectedInLog + "\")");
}
System.out.println("(" + TEST_COUNTER + ") SUCCEEDED\n");
}
static void subTest(Consumer<InvocationEvent> onBeforeDispatching, Runnable onDispatching) {
static void test1(Consumer<InvocationEvent> onBeforeDispatching, Runnable onDispatching) {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new EventQueue() {
@Override
protected void dispatchEvent(AWTEvent event) {
@@ -175,55 +140,54 @@ public class LWCToolkitInvokeAndWaitTest {
super.dispatchEvent(event);
}
});
CThreading.executeOnAppKit(() -> trycatch(() -> {
CThreading.executeOnAppKit(() -> tryRun(() -> {
//
// Post an invocation from AppKit.
//
LWCToolkit.invokeAndWait(onDispatching, FRAME, false, INVOKE_TIMEOUT_SECONDS);
FUTURE.complete(true);
TEST_CASE_RESULT.complete(true);
}));
}
static void sleep(int seconds) {
trycatch(() -> Thread.sleep(seconds * 1000L));
static void test2() {
EventQueue.invokeLater(() ->
//
// Blocking EDT.
//
LWCToolkit.performOnMainThreadAndWait(() -> {
//
// The invocation from AppKit should be discarded.
//
tryRun(() -> LWCToolkit.invokeAndWait(EMPTY_RUNNABLE, FRAME, false, INVOKE_TIMEOUT_SECONDS * 4));
TEST_CASE_RESULT.complete(true);
}));
}
static void trycatch(ThrowableRunnable runnable) {
try {
runnable.run();
} catch (Exception e) {
if (Thread.currentThread() == MAIN_THREAD) {
throw new RuntimeException("Test FAILED!", e);
} else {
FUTURE.completeExceptionally(e);
static void test3() {
var point = new CountDownLatch(1);
EventQueue.invokeLater(() -> {
// We're on EDT, wait for the second invocation to perform on AppKit.
await(point, INVOKE_TIMEOUT_SECONDS * 2);
// This should be dispatched in the RunLoop started by LWCToolkit.invokeAndWait from the second invocation.
LWCToolkit.performOnMainThreadAndWait(() -> TEST_CASE_RESULT.complete(true));
});
LWCToolkit.performOnMainThreadAndWait(() -> {
// Notify we're on AppKit.
point.countDown();
// The LWCToolkit.invokeAndWait call starts a native RunLoop.
tryRun(() -> LWCToolkit.invokeAndWait(EMPTY_RUNNABLE, FRAME));
});
}
static void check(String expectedInLog) {
tryRun(() -> {
if (!TEST_CASE_RESULT.get(INVOKE_TIMEOUT_SECONDS * 2L, TimeUnit.SECONDS)) {
throw new RuntimeException("Test FAILED! (negative result)");
}
}
}
interface ThrowableRunnable {
void run() throws Exception;
}
static class TestLogHandler extends StreamHandler {
public StringBuilder buffer = new StringBuilder();
public TestLogHandler() {
// Use System.out to merge with the test printing.
super(System.out, new SimpleFormatter());
setLevel(Level.ALL);
}
@Override
public void publish(LogRecord record) {
buffer.append(record.getMessage());
super.publish(record);
flush();
}
public boolean testContains(String str) {
boolean contains = buffer.toString().contains(str);
buffer.setLength(0);
return contains;
}
});
}
}

View File

@@ -0,0 +1,239 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package helper;
import sun.awt.AWTThreading;
import sun.lwawt.macosx.CThreading;
import sun.lwawt.macosx.LWCToolkit;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.logging.*;
@SuppressWarnings("ConstantConditions")
public class ToolkitTestHelper {
public static final Runnable EMPTY_RUNNABLE = () -> {};
public static final TestLogHandler LOG_HANDLER = new TestLogHandler();
public static volatile CompletableFuture<Boolean> TEST_CASE_RESULT;
public static volatile int TEST_CASE_NUM;
public static volatile JFrame FRAME;
private static volatile JLabel LABEL;
private static volatile Thread MAIN_THREAD;
private static final Runnable UPDATE_LABEL = new Runnable() {
final Random rand = new Random();
@Override
public void run() {
LABEL.setForeground(new Color(rand.nextFloat(), 0, rand.nextFloat()));
LABEL.setText("(" + TEST_CASE_NUM + ")");
}
};
public static void initTest(Class<?> testClass) {
MAIN_THREAD = Thread.currentThread();
assert MAIN_THREAD.getName().toLowerCase().contains("main");
loop(); // init the event threads
tryRun(() -> {
Consumer<Class<?>> setLog = cls -> {
Logger log = LogManager.getLogManager().getLogger(cls.getName());
log.setUseParentHandlers(false);
log.addHandler(LOG_HANDLER);
if (Boolean.getBoolean("log.level.FINER")) {
log.setLevel(Level.FINER);
}
};
setLog.accept(AWTThreading.class);
setLog.accept(LWCToolkit.class);
});
Thread.UncaughtExceptionHandler handler = MAIN_THREAD.getUncaughtExceptionHandler();
MAIN_THREAD.setUncaughtExceptionHandler((t, e) -> {
if (FRAME != null) FRAME.dispose();
handler.uncaughtException(t, e);
});
tryRun(() -> EventQueue.invokeAndWait(() -> {
FRAME = new JFrame(testClass.getSimpleName());
LABEL = new JLabel("0");
LABEL.setFont(LABEL.getFont().deriveFont((float)30));
LABEL.setHorizontalAlignment(SwingConstants.CENTER);
FRAME.add(LABEL, BorderLayout.CENTER);
FRAME.getContentPane().setBackground(Color.green);
FRAME.setLocationRelativeTo(null);
FRAME.setSize(200, 200);
FRAME.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
FRAME.setVisible(true);
}));
Timer timer = new Timer(100, e -> UPDATE_LABEL.run());
timer.setRepeats(true);
timer.start();
new Thread(() -> {
try {
MAIN_THREAD.join();
} catch (InterruptedException ignored) {
}
if (FRAME != null) FRAME.dispose();
timer.stop();
}).start();
}
public static void waitTestCaseCompletion(int seconds) {
tryRun(() -> {
if (!TEST_CASE_RESULT.get(seconds, TimeUnit.SECONDS)) {
throw new RuntimeException("Test FAILED! (negative result)");
}
});
loop(); // wait for the logging to be printed
}
public static class TestCase {
volatile String caption = "";
volatile Runnable testRunnable = EMPTY_RUNNABLE;
volatile int completionTimeoutSeconds = -1;
volatile String expectedInLog = "";
volatile boolean expectedIncludedInLog = true;
public static TestCase testCase() {
return new TestCase();
}
public TestCase withCaption(String caption) {
this.caption = caption;
return this;
}
public TestCase withRunnable(Runnable testRunnable, boolean invokeOnEdt) {
this.testRunnable = invokeOnEdt ? () -> EventQueue.invokeLater(testRunnable) : testRunnable;
return this;
}
public TestCase withCompletionTimeout(int seconds) {
this.completionTimeoutSeconds = seconds;
return this;
}
public TestCase withExpectedInLog(String expectedInLog, boolean included) {
this.expectedInLog = expectedInLog;
this.expectedIncludedInLog = included;
return this;
}
public void run() {
TEST_CASE_RESULT = new CompletableFuture<>();
TEST_CASE_RESULT.whenComplete((r, e) -> Optional.of(e).ifPresent(Throwable::printStackTrace));
//noinspection NonAtomicOperationOnVolatileField
String prefix = "(" + (++TEST_CASE_NUM) + ")";
System.out.println("\n" + prefix + " TEST: " + caption);
UPDATE_LABEL.run();
testRunnable.run();
if (completionTimeoutSeconds >= 0) {
waitTestCaseCompletion(completionTimeoutSeconds);
if (expectedIncludedInLog && !LOG_HANDLER.testContains(expectedInLog)) {
throw new RuntimeException("Test FAILED! (not found in the log: \"" + expectedInLog + "\")");
} else if (!expectedIncludedInLog && LOG_HANDLER.testContains(expectedInLog)) {
throw new RuntimeException("Test FAILED! (found in the log: \"" + expectedInLog + "\")");
}
}
System.out.println(prefix + " SUCCEEDED\n");
}
}
public static void tryRun(ThrowableRunnable runnable) {
tryCall(() -> {
runnable.run();
return null;
}, null);
}
public static <T> T tryCall(Callable<T> callable, T defValue) {
try {
return callable.call();
} catch (Exception e) {
if (Thread.currentThread() == MAIN_THREAD) {
throw new RuntimeException("Test FAILED!", e);
} else {
TEST_CASE_RESULT.completeExceptionally(e);
}
}
return defValue;
}
public static void await(CountDownLatch latch, int seconds) {
if (!tryCall(() -> latch.await(seconds, TimeUnit.SECONDS), false)) {
TEST_CASE_RESULT.completeExceptionally(new Throwable("Awaiting has timed out"));
}
}
public static void sleep(int seconds) {
tryRun(() -> Thread.sleep(seconds * 1000L));
}
private static void loop() {
tryRun(() -> EventQueue.invokeAndWait(EMPTY_RUNNABLE));
var latch = new CountDownLatch(1);
CThreading.executeOnAppKit(latch::countDown);
tryRun(latch::await);
}
public interface ThrowableRunnable {
void run() throws Exception;
}
public static class TestTimer {
private final long finishTime;
public TestTimer(long timeFromNow, TimeUnit unit) {
finishTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(timeFromNow, unit);
}
public boolean hasFinished() {
return System.currentTimeMillis() >= finishTime;
}
}
public static class TestLogHandler extends StreamHandler {
public StringBuilder buffer = new StringBuilder();
public TestLogHandler() {
// Use System.out to merge with the test printing.
super(System.out, new SimpleFormatter());
setLevel(Level.ALL);
}
@Override
public void publish(LogRecord record) {
buffer.append(record.getMessage());
super.publish(record);
flush();
}
public boolean testContains(String str) {
boolean contains = buffer.toString().contains(str);
buffer.setLength(0);
return contains;
}
}
}

View File

@@ -33,7 +33,7 @@ public class MacSpacesUtil {
return Runtime.getRuntime().exec(new String[]{
"plutil",
"-extract",
"SpacesDisplayConfiguration.Space Properties.1",
"SpacesDisplayConfiguration.Management Data.Monitors.0.Spaces.1",
"json",
"-o",
"-",
@@ -45,13 +45,15 @@ public class MacSpacesUtil {
toggleMissionControl();
// press button at top right corner to add a new space
int screenWidth = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
.getDefaultConfiguration().getBounds().width;
Rectangle screenBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
.getDefaultConfiguration().getBounds();
int rightX = screenBounds.x + screenBounds.width;
int topY = screenBounds.y;
Robot robot = new Robot();
robot.setAutoDelay(50);
robot.mouseMove(screenWidth, 0);
robot.mouseMove(screenWidth - 5, 5);
robot.mouseMove(screenWidth - 10, 10);
robot.mouseMove(rightX, topY);
robot.mouseMove(rightX - 5, topY + 5);
robot.mouseMove(rightX - 10, topY + 10);
robot.delay(1000);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);

View File

@@ -524,6 +524,7 @@ java/awt/image/VolatileImage/BitmaskVolatileImage.java 8133102 linux-all
java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java 8134231 windows-all,linux-all,macosx-all
java/awt/SplashScreen/MultiResolutionSplash/unix/UnixMultiResolutionSplashTest.java 8203004 linux-all
java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java 7107528 linux-all,macosx-all
java/awt/Mouse/GetMousePositionTest/GetMousePositionWithPopup.java 8282232 windows-all
java/awt/Mouse/MouseDragEvent/MouseDraggedTest.java 8080676 linux-all
java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersInKeyEvent.java 8157147 linux-all,windows-all,macosx-all
java/awt/Mouse/TitleBarDoubleClick/TitleBarDoubleClick.java 8148041 linux-all
@@ -592,6 +593,7 @@ javax/management/monitor/DerivedGaugeMonitorTest.java 8042211 generic-al
# jdk_io
java/io/pathNames/GeneralWin32.java 8180264 windows-all
java/io/File/createTempFile/SpecialTempFile.java 8274122 windows11
############################################################################